diff --git a/.github/azure-workflows/build-mac-arm64.yml b/.github/azure-workflows/build-mac-arm64.yml
deleted file mode 100644
index 721c6acc..00000000
--- a/.github/azure-workflows/build-mac-arm64.yml
+++ /dev/null
@@ -1,26 +0,0 @@
-trigger:
-- master
-
-pool:
- name: Default
- demands:
- - agent.name -equals MacStadium-ARM64-Mac
-
-workspace:
- clean: all
-
-steps:
-- script: mkdir $(Pipeline.Workspace)/build
- displayName: 'Create build environment'
-
-- script: arch -arm64 cmake $(Build.SourcesDirectory) -DCMAKE_BUILD_TYPE=$BUILD_TYPE -DCMAKE_PREFIX_PATH="$(brew --prefix qt@6);$(brew --prefix libarchive)" -DMACOS_BUNDLE_LIBS=ON -DMACOS_BUILD_DMG=ON -DUSE_QT6=ON
- displayName: 'Configure'
- workingDirectory: $(Pipeline.Workspace)/build
-
-- script: arch -arm64 make -j$(sysctl -n hw.logicalcpu)
- displayName: 'Make'
- workingDirectory: $(Pipeline.Workspace)/build
-
-- publish: $(Pipeline.Workspace)/build/melonDS.dmg
- artifact: melonDS.dmg
-
diff --git a/.github/azure-workflows/build-mac-x86_64.yml b/.github/azure-workflows/build-mac-x86_64.yml
deleted file mode 100644
index 3151c256..00000000
--- a/.github/azure-workflows/build-mac-x86_64.yml
+++ /dev/null
@@ -1,24 +0,0 @@
-trigger:
-- master
-
-pool:
- vmImage: macOS-10.15
-
-steps:
-- script: brew install llvm sdl2 qt@6 libslirp libarchive libepoxy
- displayName: 'Install dependencies'
-
-- script: mkdir $(Pipeline.Workspace)/build
- displayName: 'Create build environment'
-
-- script: cmake $(Build.SourcesDirectory) -DCMAKE_BUILD_TYPE=$BUILD_TYPE -DCMAKE_PREFIX_PATH="$(brew --prefix qt@6);$(brew --prefix libarchive)" -DMACOS_BUNDLE_LIBS=ON -DMACOS_BUILD_DMG=ON -DUSE_QT6=ON
- displayName: 'Configure'
- workingDirectory: $(Pipeline.Workspace)/build
-
-- script: make -j$(sysctl -n hw.logicalcpu)
- displayName: 'Make'
- workingDirectory: $(Pipeline.Workspace)/build
-
-- publish: $(Pipeline.Workspace)/build/melonDS.dmg
- artifact: melonDS.dmg
-
diff --git a/.github/workflows/build-macos-universal.yml b/.github/workflows/build-macos-universal.yml
new file mode 100644
index 00000000..0f0089f3
--- /dev/null
+++ b/.github/workflows/build-macos-universal.yml
@@ -0,0 +1,73 @@
+name: CMake Build (macOS Universal)
+
+on:
+ push:
+ branches:
+ - master
+ pull_request:
+ branches:
+ - master
+
+jobs:
+ prepare:
+ runs-on: [self-hosted, macOS, ARM64]
+
+ steps:
+ - name: Clean workspace
+ run: rm -rf ${{runner.workspace}}/build
+
+ - uses: actions/checkout@v3
+
+
+ build-arm64:
+ needs: prepare
+ runs-on: [self-hosted, macOS, ARM64]
+ env:
+ homebrew_prefix: /opt/homebrew
+
+ steps:
+ - name: Create build directory
+ run: mkdir -p ${{runner.workspace}}/build/arm64
+
+ - name: Configure
+ working-directory: ${{runner.workspace}}/build/arm64
+ run: arch -arm64 ${{env.homebrew_prefix}}/bin/cmake $GITHUB_WORKSPACE -DCMAKE_BUILD_TYPE=$BUILD_TYPE -DCMAKE_PREFIX_PATH="${{env.homebrew_prefix}}/opt/qt@6;${{env.homebrew_prefix}}/opt/libarchive" -DPKG_CONFIG_EXECUTABLE=${{env.homebrew_prefix}}/bin/pkg-config -DMACOS_BUNDLE_LIBS=ON -DUSE_QT6=ON
+
+ - name: Make
+ working-directory: ${{runner.workspace}}/build/arm64
+ run: arch -arm64 make -j$(sysctl -n hw.logicalcpu)
+
+ build-x86_64:
+ needs: prepare
+ runs-on: [self-hosted, macOS, ARM64]
+ env:
+ homebrew_prefix: /usr/local
+
+ steps:
+ - name: Create build directory
+ run: mkdir -p ${{runner.workspace}}/build/x86_64
+
+ - name: Configure
+ working-directory: ${{runner.workspace}}/build/x86_64
+ run: arch -x86_64 ${{env.homebrew_prefix}}/bin/cmake $GITHUB_WORKSPACE -DCMAKE_BUILD_TYPE=$BUILD_TYPE -DCMAKE_PREFIX_PATH="${{env.homebrew_prefix}}/opt/qt@6;${{env.homebrew_prefix}}/opt/libarchive" -DPKG_CONFIG_EXECUTABLE=${{env.homebrew_prefix}}/bin/pkg-config -DMACOS_BUNDLE_LIBS=ON -DUSE_QT6=ON
+
+ - name: Make
+ working-directory: ${{runner.workspace}}/build/x86_64
+ run: arch -x86_64 make -j$(sysctl -n hw.logicalcpu)
+
+ universal-binary:
+ needs: [build-arm64, build-x86_64]
+ runs-on: [self-hosted, macOS, ARM64]
+
+ steps:
+ - name: Merge binaries
+ run: $GITHUB_WORKSPACE/tools/mac-universal.py ${{runner.workspace}}/build/arm64/melonDS.app ${{runner.workspace}}/build/x86_64/melonDS.app ${{runner.workspace}}/build/universal/melonDS.app
+
+ - name: Create DMG
+ run: hdiutil create -fs HFS+ -volname melonDS -srcfolder ${{runner.workspace}}/build/universal/melonDS.app -ov -format UDBZ ${{runner.workspace}}/build/universal/melonDS.dmg
+
+ - uses: actions/upload-artifact@v3
+ with:
+ name: macOS-universal
+ path: ${{runner.workspace}}/build/universal/melonDS.dmg
+
diff --git a/.github/workflows/build-windows.yml b/.github/workflows/build-windows.yml
index a4026a7b..bf44f6de 100644
--- a/.github/workflows/build-windows.yml
+++ b/.github/workflows/build-windows.yml
@@ -35,7 +35,7 @@ jobs:
- name: Configure
working-directory: ${{runner.workspace}}/build
- run: cmake $GITHUB_WORKSPACE -G 'MSYS Makefiles' -DCMAKE_BUILD_TYPE=${{env.BUILD_TYPE}} -DBUILD_STATIC=ON -DQT5_STATIC_DIR=C:/tools/msys64/mingw64/qt5-static
+ run: cmake $GITHUB_WORKSPACE -G 'MSYS Makefiles' -DCMAKE_BUILD_TYPE=${{env.BUILD_TYPE}} -DBUILD_STATIC=ON -DCMAKE_PREFIX_PATH=C:/tools/msys64/mingw64/qt5-static
- name: Make
working-directory: ${{runner.workspace}}/build
diff --git a/CMakeLists.txt b/CMakeLists.txt
index 60dac082..297f71d1 100644
--- a/CMakeLists.txt
+++ b/CMakeLists.txt
@@ -1,43 +1,38 @@
-cmake_minimum_required(VERSION 3.13)
+cmake_minimum_required(VERSION 3.15)
-include(CheckSymbolExists)
-include(CheckLibraryExists)
-
-cmake_policy(VERSION 3.13)
+cmake_policy(VERSION 3.15)
if (POLICY CMP0076)
cmake_policy(SET CMP0076 NEW)
endif()
+set(CMAKE_POLICY_DEFAULT_CMP0069 NEW)
+
+set(CMAKE_MODULE_PATH "${CMAKE_SOURCE_DIR}/cmake" ${CMAKE_MODULE_PATH})
+
+project(melonDS
+ VERSION 0.9.4
+ DESCRIPTION "DS emulator, sorta"
+ HOMEPAGE_URL "https://melonds.kuribo64.net"
+ LANGUAGES C CXX)
+
+include(CheckSymbolExists)
+include(CheckLibraryExists)
+include(CMakeDependentOption)
+include(CheckIPOSupported)
+
set(CMAKE_OSX_DEPLOYMENT_TARGET "10.15" CACHE STRING "Minimum OS X deployment version")
-project(melonDS CXX)
-
set(CMAKE_C_STANDARD 11)
+set(CMAKE_C_STANDARD_REQUIRED ON)
set(CMAKE_CXX_STANDARD 17)
+set(CMAKE_CXX_STANDARD_REQUIRED ON)
set(CMAKE_RUNTIME_OUTPUT_DIRECTORY ${CMAKE_BINARY_DIR})
-set(MELONDS_VERSION "0.9.4")
-add_compile_definitions(MELONDS_VERSION="${MELONDS_VERSION}")
-string(REPLACE "." ";" VERSION_LIST ${MELONDS_VERSION})
-# For the melon.rc file used on Windows
-list(GET VERSION_LIST 0 MELONDS_VERSION_MAJOR)
-list(GET VERSION_LIST 1 MELONDS_VERSION_MINOR)
-# Check if melonDS version is three digits or two digits
-list(LENGTH VERSION_LIST MELONDS_VER_LEN)
-if (${MELONDS_VER_LEN} GREATER 2)
- list(GET VERSION_LIST 2 MELONDS_VERSION_PATCH)
-else()
- set(MELONDS_VERSION_PATCH 0)
-endif()
+add_compile_definitions(MELONDS_VERSION="${melonDS_VERSION}")
-
-check_library_exists(m pow "" LIBM)
-if(LIBM)
- link_libraries(m)
-endif()
-
-if (NOT CMAKE_BUILD_TYPE)
- set(CMAKE_BUILD_TYPE Release)
+if(NOT CMAKE_BUILD_TYPE AND NOT CMAKE_CONFIGURATION_TYPES)
+ set(CMAKE_BUILD_TYPE Release CACHE STRING "Choose the type of build." FORCE)
+ set_property(CACHE CMAKE_BUILD_TYPE PROPERTY STRINGS "Debug" "Release" "MinSizeRel" "RelWithDebInfo")
endif()
function(detect_architecture symbol arch)
@@ -61,74 +56,41 @@ detect_architecture("__i386__" x86)
detect_architecture("__arm__" ARM)
detect_architecture("__aarch64__" ARM64)
-if (ARCHITECTURE STREQUAL x86_64 OR ARCHITECTURE STREQUAL ARM64)
- option(ENABLE_JIT "Enable x64 JIT recompiler" ON)
-endif()
-
-if (ENABLE_JIT)
- add_definitions(-DJIT_ENABLED)
-
- option(ENABLE_JIT_PROFILING "Enable JIT profiling with VTune" OFF)
-
- if (ENABLE_JIT_PROFILING)
- include(cmake/FindVTune.cmake)
- add_definitions(-DJIT_PROFILING_ENABLED)
- endif()
-endif()
-
-if (CMAKE_BUILD_TYPE STREQUAL Release)
- option(ENABLE_LTO "Enable link-time optimization" ON)
-else()
- option(ENABLE_LTO "Enable link-time optimization" OFF)
-endif()
-
+cmake_dependent_option(ENABLE_JIT "Enable JIT recompiler" ON
+ "ARCHITECTURE STREQUAL x86_64 OR ARCHITECTURE STREQUAL ARM64" OFF)
+cmake_dependent_option(ENABLE_JIT_PROFILING "Enable JIT profiling with VTune" OFF "ENABLE_JIT" OFF)
option(ENABLE_OGLRENDERER "Enable OpenGL renderer" ON)
-if (ENABLE_OGLRENDERER)
- add_definitions(-DOGLRENDERER_ENABLED)
+check_ipo_supported(RESULT IPO_SUPPORTED)
+cmake_dependent_option(ENABLE_LTO_RELEASE "Enable link-time optimizations for release builds" ON "IPO_SUPPORTED" OFF)
+cmake_dependent_option(ENABLE_LTO "Enable link-time optimizations" OFF "IPO_SUPPORTED" OFF)
+
+if (ENABLE_LTO_RELEASE)
+ set(CMAKE_INTERPROCEDURAL_OPTIMIZATION_RELEASE TRUE)
endif()
-if (CMAKE_BUILD_TYPE STREQUAL Debug)
- add_compile_options(-Og)
+if (ENABLE_LTO)
+ set(CMAKE_INTERPROCEDURAL_OPTIMIZATION TRUE)
endif()
-if (CMAKE_BUILD_TYPE STREQUAL Release)
- add_compile_options(-O3)
- if (NOT APPLE)
- add_link_options(-s)
- endif()
+set(CMAKE_C_FLAGS_DEBUG "${CMAKE_C_FLAGS_DEBUG} -Og")
+set(CMAKE_CXX_FLAGS_DEBUG "${CMAKE_CXX_FLAGS_DEBUG} -Og")
+set(CMAKE_C_FLAGS_RELEASE "-O3")
+set(CMAKE_CXX_FLAGS_RELEASE "-O3")
+
+if (NOT APPLE)
+ set(CMAKE_EXE_LINKER_FLAGS_RELEASE "${CMAKE_EXE_LINKER_FLAGS_RELEASE} -s")
endif()
if (WIN32)
- option(BUILD_STATIC "Statically link dependencies" OFF)
+ option(BUILD_STATIC "Statically link dependencies" OFF)
endif()
if (BUILD_STATIC AND WIN32)
set(CMAKE_FIND_LIBRARY_SUFFIXES .a)
endif()
-if (ENABLE_LTO)
- if (WIN32 OR APPLE)
- add_compile_options(-flto)
- add_link_options(-flto)
- else()
- add_compile_options(-flto -fPIC)
- add_link_options(-flto -fuse-linker-plugin -pie)
- endif()
- if (CMAKE_CXX_COMPILER_ID STREQUAL "GNU")
- set(CMAKE_AR "gcc-ar")
- set(CMAKE_RANLIB "gcc-ranlib")
- elseif (CMAKE_CXX_COMPILER_ID STREQUAL "Clang")
- find_program(LLD NAMES ld.lld ld64.lld lld-link)
- if (NOT LLD STREQUAL "LLD-NOTFOUND")
- add_link_options(-fuse-ld=lld)
- endif()
- if (NOT APPLE)
- set(CMAKE_AR "llvm-ar")
- set(CMAKE_RANLIB "llvm-ranlib")
- endif()
- endif()
-endif()
+set(CMAKE_POSITION_INDEPENDENT_CODE ON)
find_program(CCACHE "ccache")
if (CCACHE)
@@ -142,5 +104,5 @@ option(BUILD_QT_SDL "Build Qt/SDL frontend" ON)
add_subdirectory(src)
if (BUILD_QT_SDL)
- add_subdirectory(src/frontend/qt_sdl)
+ add_subdirectory(src/frontend/qt_sdl)
endif()
diff --git a/README.md b/README.md
index 028acad8..340f5c3b 100644
--- a/README.md
+++ b/README.md
@@ -1,4 +1,4 @@
-

+
melonDS
@@ -6,9 +6,9 @@
-
-
-
+
+
+
@@ -34,69 +34,75 @@ As for the rest, the interface should be pretty straightforward. If you have a q
## How to build
-### Linux:
+### Linux
+1. Install dependencies:
+ * Ubuntu 22.04: `sudo apt install cmake libcurl4-gnutls-dev libpcap0.8-dev libsdl2-dev qtbase5-dev libslirp-dev libarchive-dev libepoxy-dev`
+ * Older Ubuntu: `sudo apt install cmake libcurl4-gnutls-dev libpcap0.8-dev libsdl2-dev qt5-default libslirp-dev libarchive-dev libepoxy-dev`
+ * Arch Linux: `sudo pacman -S base-devel cmake git libpcap sdl2 qt5-base libslirp libarchive libepoxy`
+3. Download the melonDS repository and prepare:
+ ```bash
+ git clone https://github.com/melonDS-emu/melonDS
+ cd melonDS
+ ```
-1. Install dependencies: `sudo apt install cmake libcurl4-gnutls-dev libpcap0.8-dev libsdl2-dev qt5-default libslirp-dev libarchive-dev libepoxy-dev`
-2. Download the melonDS repository and prepare:
- ```bash
- git clone https://github.com/Arisotura/melonDS
- cd melonDS
- mkdir build && cd build
- ```
3. Compile:
- ```bash
- cmake ..
- make -j$(nproc --all)
- ```
-
-### Windows:
+ ```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. Download the melonDS repository and prepare:
- ```bash
- git clone https://github.com/Arisotura/melonDS
- cd melonDS
- mkdir build && cd build
- ```
+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 git make mingw-w64-x86_64-{cmake,mesa,SDL2,toolchain,qt5,libslirp,libarchive,libepoxy}`
+5. Install dependencies: `pacman -S make mingw-w64-x86_64-{cmake,mesa,SDL2,toolchain,qt5,libslirp,libarchive,libepoxy}`
6. Compile:
```bash
- cmake .. -G "MSYS Makefiles"
- make -j$(nproc --all)
+ cmake -B build -G "MSYS Makefiles"
+ cmake --build build -j$(nproc --all)
+ 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 git make mingw-w64-x86_64-{cmake,mesa,SDL2,toolchain,qt5-static,libslirp,libarchive,libepoxy}`
+5. Install dependencies: `pacman -S make mingw-w64-x86_64-{cmake,mesa,SDL2,toolchain,qt5-static,libslirp,libarchive,libepoxy}`
6. Compile:
```bash
- cmake .. -G 'MSYS Makefiles' -DBUILD_STATIC=ON -DQT5_STATIC_DIR=/mingw64/qt5-static
- make -j$(nproc --all)
- mkdir dist && cp melonDS.exe dist
+ cmake -B build -G 'MSYS Makefiles' -DBUILD_STATIC=ON -DCMAKE_PREFIX_PATH=/mingw64/qt5-static
+ cmake --build build -j$(nproc --all)
```
-If everything went well, melonDS should now be in the `dist` folder.
+If everything went well, melonDS should now be in the `build` folder.
-### macOS:
+### macOS
1. Install the [Homebrew Package Manager](https://brew.sh)
2. Install dependencies: `brew install git pkg-config cmake sdl2 qt@6 libslirp libarchive libepoxy`
3. Download the melonDS repository and prepare:
- ```zsh
- git clone https://github.com/Arisotura/melonDS
- cd melonDS
- mkdir build && cd build
- ```
+ ```zsh
+ git clone https://github.com/melonDS-emu/melonDS
+ cd melonDS
+ ```
4. Compile:
```zsh
- cmake .. -DCMAKE_PREFIX_PATH="$(brew --prefix qt@6);$(brew --prefix libarchive)" -DUSE_QT6=ON -DMACOS_BUNDLE_LIBS=ON
- make -j$(sysctl -n hw.logicalcpu)
+ cmake -B build -DCMAKE_PREFIX_PATH="$(brew --prefix qt@6);$(brew --prefix libarchive)" -DUSE_QT6=ON
+ cmake --build build -j$(sysctl -n hw.logicalcpu)
```
-If everything went well, melonDS.app should now be in the current directory.
+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-bundle.rb melonDS.app` after the build is completed, or add `-DMACOS_BUNDLE_LIBS=ON` to the first CMake command.
-
## TODO LIST
* better DSi emulation
diff --git a/cmake/FixInterfaceIncludes.cmake b/cmake/FixInterfaceIncludes.cmake
new file mode 100644
index 00000000..513c1117
--- /dev/null
+++ b/cmake/FixInterfaceIncludes.cmake
@@ -0,0 +1,28 @@
+# The entire codebase quite reasonably does things like #include or
+# CMake apparently doesn't think you should be doing this, so just includes $PREFIX/include/packagename for a given
+# package as include directories when using `target_link_libraries` with an imported target, this hacky function fixes
+# that up so includes can keep working as they always did but we can still use fancy imported targets.
+# This is stupid.
+
+function(fix_interface_includes)
+ foreach (target ${ARGN})
+ set(NEW_DIRS)
+ get_target_property(DIRS "${target}" INTERFACE_INCLUDE_DIRECTORIES)
+
+ if (NOT DIRS)
+ continue()
+ endif()
+
+ foreach (DIR ${DIRS})
+ get_filename_component(PARENT_DIR "${DIR}" DIRECTORY)
+
+ if (PARENT_DIR MATCHES "include$")
+ list(APPEND NEW_DIRS "${PARENT_DIR}")
+ endif()
+ endforeach()
+
+ list(APPEND DIRS ${NEW_DIRS})
+ set_target_properties("${target}" PROPERTIES INTERFACE_INCLUDE_DIRECTORIES "${DIRS}")
+ endforeach()
+endfunction()
+
diff --git a/res/melon.plist.in b/res/melon.plist.in
index 1057c474..20d385a0 100644
--- a/res/melon.plist.in
+++ b/res/melon.plist.in
@@ -13,9 +13,9 @@
CFBundlePackageType
APPL
CFBundleVersion
- ${MELONDS_VERSION}
+ ${melonDS_VERSION}
CFBundleShortVersionString
- ${MELONDS_VERSION}
+ ${melonDS_VERSION}
NSHumanReadableCopyright
Licensed under GPLv3
NSHighResolutionCapable
diff --git a/res/melon.rc.in b/res/melon.rc.in
index 3851813c..27c7cbdf 100644
--- a/res/melon.rc.in
+++ b/res/melon.rc.in
@@ -6,8 +6,8 @@
//include version information in .exe, modify these values to match your needs
1 VERSIONINFO
-FILEVERSION ${MELONDS_VERSION_MAJOR},${MELONDS_VERSION_MINOR},${MELONDS_VERSION_PATCH},0
-PRODUCTVERSION ${MELONDS_VERSION_MAJOR},${MELONDS_VERSION_MINOR},${MELONDS_VERSION_PATCH},0
+FILEVERSION ${melonDS_VERSION_MAJOR},${melonDS_VERSION_MINOR},${melonDS_VERSION_PATCH},0
+PRODUCTVERSION ${melonDS_VERSION_MAJOR},${melonDS_VERSION_MINOR},${melonDS_VERSION_PATCH},0
FILETYPE VFT_APP
{
BLOCK "StringFileInfo"
@@ -15,14 +15,14 @@ FILETYPE VFT_APP
BLOCK "040904E4"
{
VALUE "CompanyName", "Melon Factory of Kuribo64"
- VALUE "FileVersion", "${MELONDS_VERSION}"
+ VALUE "FileVersion", "${melonDS_VERSION}"
VALUE "FileDescription", "melonDS emulator"
VALUE "InternalName", "SDnolem"
VALUE "LegalCopyright", "2016-2022 melonDS team"
VALUE "LegalTrademarks", ""
VALUE "OriginalFilename", "zafkflzdasd.exe"
VALUE "ProductName", "melonDS"
- VALUE "ProductVersion", "${MELONDS_VERSION}"
+ VALUE "ProductVersion", "${melonDS_VERSION}"
}
}
BLOCK "VarFileInfo"
diff --git a/src/ARMJIT.cpp b/src/ARMJIT.cpp
index 99e5a3ff..32f20d57 100644
--- a/src/ARMJIT.cpp
+++ b/src/ARMJIT.cpp
@@ -1086,11 +1086,34 @@ void InvalidateByAddr(u32 localAddr)
void CheckAndInvalidateITCM()
{
- for (u32 i = 0; i < ITCMPhysicalSize; i+=16)
+ for (u32 i = 0; i < ITCMPhysicalSize; i+=512)
{
- if (CodeIndexITCM[i / 512].Code & (1 << ((i & 0x1FF) / 16)))
+ if (CodeIndexITCM[i / 512].Code)
{
- InvalidateByAddr(i | (ARMJIT_Memory::memregion_ITCM << 27));
+ // maybe using bitscan would be better here?
+ // The thing is that in densely populated sets
+ // The old fashioned way can actually be faster
+ for (u32 j = 0; j < 512; j += 16)
+ {
+ if (CodeIndexITCM[i / 512].Code & (1 << ((j & 0x1FF) / 16)))
+ InvalidateByAddr((i+j) | (ARMJIT_Memory::memregion_ITCM << 27));
+ }
+ }
+ }
+}
+
+void CheckAndInvalidateWVRAM(int bank)
+{
+ u32 start = bank == 1 ? 0x20000 : 0;
+ for (u32 i = start; i < start+0x20000; i+=512)
+ {
+ if (CodeIndexARM7WVRAM[i / 512].Code)
+ {
+ for (u32 j = 0; j < 512; j += 16)
+ {
+ if (CodeIndexARM7WVRAM[i / 512].Code & (1 << ((j & 0x1FF) / 16)))
+ InvalidateByAddr((i+j) | (ARMJIT_Memory::memregion_VWRAM << 27));
+ }
}
}
}
diff --git a/src/ARMJIT.h b/src/ARMJIT.h
index ec2161a5..97c79cd9 100644
--- a/src/ARMJIT.h
+++ b/src/ARMJIT.h
@@ -44,6 +44,7 @@ void DeInit();
void Reset();
void CheckAndInvalidateITCM();
+void CheckAndInvalidateWVRAM(int bank);
void InvalidateByAddr(u32 pseudoPhysical);
diff --git a/src/ARMJIT_RegisterCache.h b/src/ARMJIT_RegisterCache.h
index 603147ed..e0c4a157 100644
--- a/src/ARMJIT_RegisterCache.h
+++ b/src/ARMJIT_RegisterCache.h
@@ -20,6 +20,7 @@
#define ARMJIT_REGCACHE_H
#include "ARMJIT.h"
+#include "ARMJIT_Internal.h"
// TODO: replace this in the future
#include "dolphin/BitSet.h"
@@ -41,7 +42,7 @@ public:
{
for (int i = 0; i < 16; i++)
Mapping[i] = (Reg)-1;
-
+
PCAllocatableAsSrc = ~(pcAllocatableAsSrc
? 0
: (1 << 15));
diff --git a/src/CMakeLists.txt b/src/CMakeLists.txt
index 7288b540..cdb05871 100644
--- a/src/CMakeLists.txt
+++ b/src/CMakeLists.txt
@@ -1,141 +1,145 @@
-project(core)
-
set (CMAKE_CXX_STANDARD 17)
-add_library(core STATIC
- ARCodeFile.cpp
- AREngine.cpp
- ARM.cpp
- ARM_InstrTable.h
- ARMInterpreter.cpp
- ARMInterpreter_ALU.cpp
- ARMInterpreter_Branch.cpp
- ARMInterpreter_LoadStore.cpp
- CP15.cpp
- CRC32.cpp
- DMA.cpp
- DMA_Timings.h
- DSi.cpp
- DSi_AES.cpp
- DSi_Camera.cpp
- DSi_DSP.cpp
- DSi_I2C.cpp
- DSi_NAND.cpp
- DSi_NDMA.cpp
- DSi_NWifi.cpp
- DSi_SD.cpp
- DSi_SPI_TSC.cpp
- FATStorage.cpp
- FIFO.h
- GBACart.cpp
- GPU.cpp
- GPU2D.cpp
- GPU2D_Soft.cpp
- GPU3D.cpp
- GPU3D_Soft.cpp
- melonDLDI.h
- NDS.cpp
- NDSCart.cpp
- Platform.h
- ROMList.h
- FreeBIOS.h
- RTC.cpp
- Savestate.cpp
- SPI.cpp
- SPU.cpp
- types.h
- version.h
- Wifi.cpp
- WifiAP.cpp
+include(FixInterfaceIncludes)
- fatfs/diskio.c
- fatfs/ff.c
- fatfs/ffsystem.c
- fatfs/ffunicode.c
- fatfs/ffconf.h
-
- sha1/sha1.c
- tiny-AES-c/aes.c
- xxhash/xxhash.c
-)
+add_library(core STATIC
+ ARCodeFile.cpp
+ AREngine.cpp
+ ARM.cpp
+ ARM_InstrTable.h
+ ARMInterpreter.cpp
+ ARMInterpreter_ALU.cpp
+ ARMInterpreter_Branch.cpp
+ ARMInterpreter_LoadStore.cpp
+ CP15.cpp
+ CRC32.cpp
+ DMA.cpp
+ DMA_Timings.h
+ DSi.cpp
+ DSi_AES.cpp
+ DSi_Camera.cpp
+ DSi_DSP.cpp
+ DSi_I2C.cpp
+ DSi_NAND.cpp
+ DSi_NDMA.cpp
+ DSi_NWifi.cpp
+ DSi_SD.cpp
+ DSi_SPI_TSC.cpp
+ FATStorage.cpp
+ FIFO.h
+ GBACart.cpp
+ GPU.cpp
+ GPU2D.cpp
+ GPU2D_Soft.cpp
+ GPU3D.cpp
+ GPU3D_Soft.cpp
+ melonDLDI.h
+ NDS.cpp
+ NDSCart.cpp
+ Platform.h
+ ROMList.h
+ FreeBIOS.h
+ RTC.cpp
+ Savestate.cpp
+ SPI.cpp
+ SPU.cpp
+ types.h
+ version.h
+ Wifi.cpp
+ WifiAP.cpp
+
+ fatfs/diskio.c
+ fatfs/ff.c
+ fatfs/ffsystem.c
+ fatfs/ffunicode.c
+ fatfs/ffconf.h
+
+ sha1/sha1.c
+ tiny-AES-c/aes.c
+ xxhash/xxhash.c)
if (ENABLE_OGLRENDERER)
- target_sources(core PRIVATE
- GPU_OpenGL.cpp
- GPU_OpenGL_shaders.h
- GPU3D_OpenGL.cpp
- GPU3D_OpenGL_shaders.h
- OpenGLSupport.cpp
- )
+ target_sources(core PRIVATE
+ GPU_OpenGL.cpp
+ GPU_OpenGL_shaders.h
+ GPU3D_OpenGL.cpp
+ GPU3D_OpenGL_shaders.h
+ OpenGLSupport.cpp)
endif()
if (ENABLE_JIT)
- enable_language(ASM)
+ enable_language(ASM)
- target_sources(core PRIVATE
- ARM_InstrInfo.cpp
+ target_sources(core PRIVATE
+ ARM_InstrInfo.cpp
- ARMJIT.cpp
- ARMJIT_Memory.cpp
+ ARMJIT.cpp
+ ARMJIT_Memory.cpp
- dolphin/CommonFuncs.cpp
- )
+ dolphin/CommonFuncs.cpp)
- if (ARCHITECTURE STREQUAL x86_64)
- target_sources(core PRIVATE
- dolphin/x64ABI.cpp
- dolphin/x64CPUDetect.cpp
- dolphin/x64Emitter.cpp
+ if (ARCHITECTURE STREQUAL x86_64)
+ target_sources(core PRIVATE
+ dolphin/x64ABI.cpp
+ dolphin/x64CPUDetect.cpp
+ dolphin/x64Emitter.cpp
- ARMJIT_x64/ARMJIT_Compiler.cpp
- ARMJIT_x64/ARMJIT_ALU.cpp
- ARMJIT_x64/ARMJIT_LoadStore.cpp
- ARMJIT_x64/ARMJIT_Branch.cpp
+ ARMJIT_x64/ARMJIT_Compiler.cpp
+ ARMJIT_x64/ARMJIT_ALU.cpp
+ ARMJIT_x64/ARMJIT_LoadStore.cpp
+ ARMJIT_x64/ARMJIT_Branch.cpp
- ARMJIT_x64/ARMJIT_Linkage.S
- )
- endif()
- if (ARCHITECTURE STREQUAL ARM64)
- target_sources(core PRIVATE
- dolphin/Arm64Emitter.cpp
- dolphin/MathUtil.cpp
+ ARMJIT_x64/ARMJIT_Linkage.S)
+ endif()
+ if (ARCHITECTURE STREQUAL ARM64)
+ target_sources(core PRIVATE
+ dolphin/Arm64Emitter.cpp
+ dolphin/MathUtil.cpp
- ARMJIT_A64/ARMJIT_Compiler.cpp
- ARMJIT_A64/ARMJIT_ALU.cpp
- ARMJIT_A64/ARMJIT_LoadStore.cpp
- ARMJIT_A64/ARMJIT_Branch.cpp
+ ARMJIT_A64/ARMJIT_Compiler.cpp
+ ARMJIT_A64/ARMJIT_ALU.cpp
+ ARMJIT_A64/ARMJIT_LoadStore.cpp
+ ARMJIT_A64/ARMJIT_Branch.cpp
- ARMJIT_A64/ARMJIT_Linkage.S
- )
- endif()
+ ARMJIT_A64/ARMJIT_Linkage.S)
+ endif()
endif()
add_subdirectory(teakra EXCLUDE_FROM_ALL)
-target_link_libraries(core teakra)
+target_link_libraries(core PRIVATE teakra)
+find_library(m MATH_LIBRARY)
+
+if (MATH_LIBRARY)
+ target_link_libraries(core PRIVATE ${MATH_LIBRARY})
+endif()
if (ENABLE_OGLRENDERER)
find_package(PkgConfig REQUIRED)
- pkg_check_modules(EPOXY REQUIRED epoxy)
+ pkg_check_modules(Epoxy REQUIRED IMPORTED_TARGET epoxy)
+ fix_interface_includes(PkgConfig::Epoxy)
- target_include_directories(core PRIVATE ${EPOXY_INCLUDE_DIRS})
- if (WIN32)
- target_link_libraries(core ole32 comctl32 ws2_32 ${EPOXY_LIBRARIES})
- elseif (APPLE)
- target_link_libraries(core ${EPOXY_LIBRARIES})
- else()
- target_link_libraries(core rt ${EPOXY_LIBRARIES})
+ target_link_libraries(core PUBLIC PkgConfig::Epoxy)
+
+ target_compile_definitions(core PUBLIC OGLRENDERER_ENABLED)
+endif()
+
+if (ENABLE_JIT)
+ target_compile_definitions(core PUBLIC JIT_ENABLED)
+
+
+ if (ENABLE_JIT_PROFILING)
+ include(cmake/FindVTune.cmake)
+ add_definitions(-DJIT_PROFILING_ENABLED)
endif()
-else()
- if (WIN32)
- target_link_libraries(core ole32 comctl32 ws2_32)
- elseif (APPLE)
- target_link_libraries(core)
- else()
- target_link_libraries(core rt)
- endif()
+endif()
+
+if (WIN32)
+ target_link_libraries(core PRIVATE ole32 comctl32 ws2_32)
+elseif(NOT APPLE)
+ target_link_libraries(core PRIVATE rt)
endif()
if (ENABLE_JIT_PROFILING)
- target_link_libraries(core jitprofiling)
+ target_link_libraries(core PRIVATE jitprofiling)
endif()
diff --git a/src/CP15.cpp b/src/CP15.cpp
index 2548ecec..4fe91cf5 100644
--- a/src/CP15.cpp
+++ b/src/CP15.cpp
@@ -134,6 +134,9 @@ void ARMv5::UpdateITCMSetting()
if (CP15Control & (1<<18))
{
ITCMSize = 0x200 << ((ITCMSetting >> 1) & 0x1F);
+#ifdef JIT_ENABLED
+ FastBlockLookupSize = 0;
+#endif
}
else
{
diff --git a/src/DMA.h b/src/DMA.h
index 7866631e..ad194c11 100644
--- a/src/DMA.h
+++ b/src/DMA.h
@@ -20,6 +20,7 @@
#define DMA_H
#include "types.h"
+#include "Savestate.h"
class DMA
{
@@ -96,7 +97,7 @@ private:
bool IsGXFIFODMA;
u32 MRAMBurstCount;
- u8* MRAMBurstTable;
+ const u8* MRAMBurstTable;
};
#endif
diff --git a/src/DMA_Timings.h b/src/DMA_Timings.h
index 1283751b..4281c783 100644
--- a/src/DMA_Timings.h
+++ b/src/DMA_Timings.h
@@ -19,6 +19,8 @@
#ifndef DMA_TIMINGS_H
#define DMA_TIMINGS_H
+#include "types.h"
+
namespace DMATiming
{
@@ -43,9 +45,9 @@ namespace DMATiming
// setting. Timings are such that the nonseq setting only matters for the first
// access, and minor edge cases (like the last of a 0x20000-byte block).
-u8 MRAMDummy[1] = {0};
+constexpr u8 MRAMDummy[1] = {0};
-u8 MRAMRead16Bursts[][256] =
+constexpr u8 MRAMRead16Bursts[][256] =
{
// main RAM to regular 16bit or 32bit bus (similar)
{7, 3, 2, 2, 2, 2, 2, 2, 2, 2,
@@ -119,7 +121,7 @@ u8 MRAMRead16Bursts[][256] =
0},
};
-u8 MRAMRead32Bursts[][256] =
+constexpr u8 MRAMRead32Bursts[][256] =
{
// main RAM to regular 16bit bus
{9, 4, 3, 3, 3, 3, 3, 3, 3, 3,
@@ -178,7 +180,7 @@ u8 MRAMRead32Bursts[][256] =
0},
};
-u8 MRAMWrite16Bursts[][256] =
+constexpr u8 MRAMWrite16Bursts[][256] =
{
// regular 16bit or 32bit bus to main RAM (similar)
{8, 2, 2, 2, 2, 2, 2, 2, 2, 2,
@@ -209,7 +211,7 @@ u8 MRAMWrite16Bursts[][256] =
0},
};
-u8 MRAMWrite32Bursts[][256] =
+constexpr u8 MRAMWrite32Bursts[][256] =
{
// regular 16bit bus to main RAM
{9, 4, 4, 4, 4, 4, 4, 4, 4, 4,
diff --git a/src/DSi.cpp b/src/DSi.cpp
index cde86bae..02172612 100644
--- a/src/DSi.cpp
+++ b/src/DSi.cpp
@@ -162,7 +162,7 @@ void Reset()
SCFG_Clock7 = 0x0187;
SCFG_EXT[0] = 0x8307F100;
SCFG_EXT[1] = 0x93FFFB06;
- SCFG_MC = 0x0010;//0x0011;
+ SCFG_MC = 0x0010 | (~((u32)NDSCart::CartInserted)&1);//0x0011;
SCFG_RST = 0;
DSi_DSP::SetRstLine(false);
@@ -251,6 +251,14 @@ void DoSavestate(Savestate* file)
SDIO->DoSavestate(file);
}
+void SetCartInserted(bool inserted)
+{
+ if (inserted)
+ SCFG_MC &= ~1;
+ else
+ SCFG_MC |= 1;
+}
+
void DecryptModcryptArea(u32 offset, u32 size, u8* iv)
{
AES_ctx ctx;
@@ -506,30 +514,24 @@ void SetupDirectBoot()
ARM9Write32(0x02FFE000+i, tmp);
}
- FILE* nand = Platform::OpenLocalFile(Platform::GetConfigString(Platform::DSi_NANDPath), "r+b");
- if (nand)
+ if (DSi_NAND::Init(&DSi::ARM7iBIOS[0x8308]))
{
- if (DSi_NAND::Init(nand, &DSi::ARM7iBIOS[0x8308]))
- {
- u8 userdata[0x1B0];
- DSi_NAND::ReadUserData(userdata);
- for (u32 i = 0; i < 0x128; i+=4)
- ARM9Write32(0x02000400+i, *(u32*)&userdata[0x88+i]);
+ u8 userdata[0x1B0];
+ DSi_NAND::ReadUserData(userdata);
+ for (u32 i = 0; i < 0x128; i+=4)
+ ARM9Write32(0x02000400+i, *(u32*)&userdata[0x88+i]);
- u8 hwinfoS[0xA4];
- u8 hwinfoN[0x9C];
- DSi_NAND::ReadHardwareInfo(hwinfoS, hwinfoN);
+ u8 hwinfoS[0xA4];
+ u8 hwinfoN[0x9C];
+ DSi_NAND::ReadHardwareInfo(hwinfoS, hwinfoN);
- for (u32 i = 0; i < 0x14; i+=4)
- ARM9Write32(0x02000600+i, *(u32*)&hwinfoN[0x88+i]);
+ for (u32 i = 0; i < 0x14; i+=4)
+ ARM9Write32(0x02000600+i, *(u32*)&hwinfoN[0x88+i]);
- for (u32 i = 0; i < 0x18; i+=4)
- ARM9Write32(0x02FFFD68+i, *(u32*)&hwinfoS[0x88+i]);
+ for (u32 i = 0; i < 0x18; i+=4)
+ ARM9Write32(0x02FFFD68+i, *(u32*)&hwinfoS[0x88+i]);
- DSi_NAND::DeInit();
- }
-
- fclose(nand);
+ DSi_NAND::DeInit();
}
u8 nwifiver = SPI_Firmware::GetNWifiVersion();
@@ -702,19 +704,14 @@ bool LoadNAND()
{
printf("Loading DSi NAND\n");
- FILE* nand = Platform::OpenLocalFile(Platform::GetConfigString(Platform::DSi_NANDPath), "r+b");
- if (!nand)
- {
- printf("Failed to open DSi NAND\n");
- return false;
- }
-
- if (!DSi_NAND::Init(nand, &DSi::ARM7iBIOS[0x8308]))
+ if (!DSi_NAND::Init(&DSi::ARM7iBIOS[0x8308]))
{
printf("Failed to load DSi NAND\n");
return false;
}
+ FILE* nand = DSi_NAND::GetFile();
+
// Make sure NWRAM is accessible.
// The Bits are set to the startup values in Reset() and we might
// still have them on default (0) or some bits cleared by the previous
@@ -2641,6 +2638,9 @@ u8 ARM7IORead8(u32 addr)
case 0x04004D06: if (SCFG_BIOS & (1<<10)) return 0; return (ConsoleID >> 48) & 0xFF;
case 0x04004D07: if (SCFG_BIOS & (1<<10)) return 0; return ConsoleID >> 56;
case 0x04004D08: return 0;
+
+ case 0x4004700: return DSi_DSP::SNDExCnt;
+ case 0x4004701: return DSi_DSP::SNDExCnt >> 8;
}
return NDS::ARM7IORead8(addr);
@@ -2673,6 +2673,8 @@ u16 ARM7IORead16(u32 addr)
case 0x04004D04: if (SCFG_BIOS & (1<<10)) return 0; return (ConsoleID >> 32) & 0xFFFF;
case 0x04004D06: if (SCFG_BIOS & (1<<10)) return 0; return ConsoleID >> 48;
case 0x04004D08: return 0;
+
+ case 0x4004700: return DSi_DSP::SNDExCnt;
}
if (addr >= 0x04004800 && addr < 0x04004A00)
@@ -2744,6 +2746,10 @@ u32 ARM7IORead32(u32 addr)
case 0x04004D00: if (SCFG_BIOS & (1<<10)) return 0; return ConsoleID & 0xFFFFFFFF;
case 0x04004D04: if (SCFG_BIOS & (1<<10)) return 0; return ConsoleID >> 32;
case 0x04004D08: return 0;
+
+ case 0x4004700:
+ printf("32-Bit SNDExCnt read? %08X\n", NDS::ARM7->R[15]);
+ return DSi_DSP::SNDExCnt;
}
if (addr >= 0x04004800 && addr < 0x04004A00)
@@ -2791,6 +2797,46 @@ void ARM7IOWrite8(u32 addr, u8 val)
case 0x04004500: DSi_I2C::WriteData(val); return;
case 0x04004501: DSi_I2C::WriteCnt(val); return;
+
+ case 0x4004700:
+ DSi_DSP::WriteSNDExCnt((u16)val | (DSi_DSP::SNDExCnt & 0xFF00));
+ return;
+ case 0x4004701:
+ DSi_DSP::WriteSNDExCnt(((u16)val << 8) | (DSi_DSP::SNDExCnt & 0x00FF));
+ return;
+ }
+
+ if (addr >= 0x04004420 && addr < 0x04004430)
+ {
+ u32 shift = (addr&3)*8;
+ addr -= 0x04004420;
+ addr &= ~3;
+ DSi_AES::WriteIV(addr, (u32)val << shift, 0xFF << shift);
+ return;
+ }
+ if (addr >= 0x04004430 && addr < 0x04004440)
+ {
+ u32 shift = (addr&3)*8;
+ addr -= 0x04004430;
+ addr &= ~3;
+ DSi_AES::WriteMAC(addr, (u32)val << shift, 0xFF << shift);
+ return;
+ }
+ if (addr >= 0x04004440 && addr < 0x04004500)
+ {
+ u32 shift = (addr&3)*8;
+ addr -= 0x04004440;
+ addr &= ~3;
+
+ int n = 0;
+ while (addr >= 0x30) { addr -= 0x30; n++; }
+
+ switch (addr >> 4)
+ {
+ case 0: DSi_AES::WriteKeyNormal(n, addr&0xF, (u32)val << shift, 0xFF << shift); return;
+ case 1: DSi_AES::WriteKeyX(n, addr&0xF, (u32)val << shift, 0xFF << shift); return;
+ case 2: DSi_AES::WriteKeyY(n, addr&0xF, (u32)val << shift, 0xFF << shift); return;
+ }
}
return NDS::ARM7IOWrite8(addr, val);
@@ -2822,12 +2868,51 @@ void ARM7IOWrite16(u32 addr, u16 val)
case 0x04004062:
if (!(SCFG_EXT[1] & (1 << 31))) /* no access to SCFG Registers if disabled*/
return;
- u32 tmp = MBK[0][8];
- tmp &= ~(0xffff << ((addr % 4) * 8));
- tmp |= (val << ((addr % 4) * 8));
- MBK[0][8] = tmp & 0x00FFFF0F;
- MBK[1][8] = MBK[0][8];
+ {
+ u32 tmp = MBK[0][8];
+ tmp &= ~(0xffff << ((addr % 4) * 8));
+ tmp |= (val << ((addr % 4) * 8));
+ MBK[0][8] = tmp & 0x00FFFF0F;
+ MBK[1][8] = MBK[0][8];
+ }
return;
+
+ case 0x4004700:
+ DSi_DSP::WriteSNDExCnt(val);
+ return;
+ }
+
+ if (addr >= 0x04004420 && addr < 0x04004430)
+ {
+ u32 shift = (addr&1)*16;
+ addr -= 0x04004420;
+ addr &= ~1;
+ DSi_AES::WriteIV(addr, (u32)val << shift, 0xFFFF << shift);
+ return;
+ }
+ if (addr >= 0x04004430 && addr < 0x04004440)
+ {
+ u32 shift = (addr&1)*16;
+ addr -= 0x04004430;
+ addr &= ~1;
+ DSi_AES::WriteMAC(addr, (u32)val << shift, 0xFFFF << shift);
+ return;
+ }
+ if (addr >= 0x04004440 && addr < 0x04004500)
+ {
+ u32 shift = (addr&1)*16;
+ addr -= 0x04004440;
+ addr &= ~1;
+
+ int n = 0;
+ while (addr >= 0x30) { addr -= 0x30; n++; }
+
+ switch (addr >> 4)
+ {
+ case 0: DSi_AES::WriteKeyNormal(n, addr&0xF, (u32)val << shift, 0xFFFF << shift); return;
+ case 1: DSi_AES::WriteKeyX(n, addr&0xF, (u32)val << shift, 0xFFFF << shift); return;
+ case 2: DSi_AES::WriteKeyY(n, addr&0xF, (u32)val << shift, 0xFFFF << shift); return;
+ }
}
if (addr >= 0x04004800 && addr < 0x04004A00)
@@ -2927,6 +3012,11 @@ void ARM7IOWrite32(u32 addr, u32 val)
case 0x04004400: DSi_AES::WriteCnt(val); return;
case 0x04004404: DSi_AES::WriteBlkCnt(val); return;
case 0x04004408: DSi_AES::WriteInputFIFO(val); return;
+
+ case 0x4004700:
+ printf("32-Bit SNDExCnt write? %08X %08X\n", val, NDS::ARM7->R[15]);
+ DSi_DSP::WriteSNDExCnt(val);
+ return;
}
if (addr >= 0x04004420 && addr < 0x04004430)
diff --git a/src/DSi.h b/src/DSi.h
index 14c13672..4ccddc02 100644
--- a/src/DSi.h
+++ b/src/DSi.h
@@ -59,6 +59,8 @@ void Reset();
void DoSavestate(Savestate* file);
+void SetCartInserted(bool inserted);
+
void SetupDirectBoot();
void SoftReset();
diff --git a/src/DSi_AES.h b/src/DSi_AES.h
index 48fca88e..d8ef98a4 100644
--- a/src/DSi_AES.h
+++ b/src/DSi_AES.h
@@ -20,6 +20,7 @@
#define DSI_AES_H
#include "types.h"
+#include "Savestate.h"
namespace DSi_AES
{
diff --git a/src/DSi_Camera.h b/src/DSi_Camera.h
index 12cec38a..75e97f27 100644
--- a/src/DSi_Camera.h
+++ b/src/DSi_Camera.h
@@ -20,6 +20,7 @@
#define DSI_CAMERA_H
#include "types.h"
+#include "Savestate.h"
namespace DSi_CamModule
{
diff --git a/src/DSi_DSP.cpp b/src/DSi_DSP.cpp
index c889aff9..0525366e 100644
--- a/src/DSi_DSP.cpp
+++ b/src/DSi_DSP.cpp
@@ -27,6 +27,9 @@
namespace DSi_DSP
{
+// not sure whether to not rather put it somewhere else
+u16 SNDExCnt;
+
Teakra::Teakra* TeakraCore;
bool SCFG_RST;
@@ -151,6 +154,8 @@ void Reset()
TeakraCore->Reset();
NDS::CancelEvent(NDS::Event_DSi_DSP);
+
+ SNDExCnt = 0;
}
bool IsRstReleased()
@@ -548,6 +553,21 @@ void Write32(u32 addr, u32 val)
Write16(addr, val & 0xFFFF);
}
+void WriteSNDExCnt(u16 val)
+{
+ // it can be written even in NDS mode
+
+ // mic frequency can only be changed if it was disabled
+ // before the write
+ if (SNDExCnt & 0x8000)
+ {
+ val &= ~0x2000;
+ val |= SNDExCnt & 0x2000;
+ }
+
+ SNDExCnt = val & 0xE00F;
+}
+
void Run(u32 cycles)
{
if (!IsDSPCoreEnabled())
diff --git a/src/DSi_DSP.h b/src/DSi_DSP.h
index ade88bac..5d3427f3 100644
--- a/src/DSi_DSP.h
+++ b/src/DSi_DSP.h
@@ -19,13 +19,17 @@
#ifndef DSI_DSP_H
#define DSI_DSP_H
+#include "types.h"
+#include "Savestate.h"
+
// TODO: for actual sound output
// * audio callbacks
-// * SNDEXCNT
namespace DSi_DSP
{
+extern u16 SNDExCnt;
+
extern u16 DSP_PDATA;
extern u16 DSP_PADR;
extern u16 DSP_PCFG;
@@ -62,6 +66,8 @@ void Write16(u32 addr, u16 val);
u32 Read32(u32 addr);
void Write32(u32 addr, u32 val);
+void WriteSNDExCnt(u16 val);
+
// NOTE: checks SCFG_CLK9
void Run(u32 cycles);
diff --git a/src/DSi_I2C.h b/src/DSi_I2C.h
index 7350fb5a..48c8e884 100644
--- a/src/DSi_I2C.h
+++ b/src/DSi_I2C.h
@@ -20,6 +20,7 @@
#define DSI_I2C_H
#include "types.h"
+#include "Savestate.h"
namespace DSi_BPTWL
{
diff --git a/src/DSi_NAND.cpp b/src/DSi_NAND.cpp
index e24ed313..912fee42 100644
--- a/src/DSi_NAND.cpp
+++ b/src/DSi_NAND.cpp
@@ -49,8 +49,48 @@ UINT FF_ReadNAND(BYTE* buf, LBA_t sector, UINT num);
UINT FF_WriteNAND(BYTE* buf, LBA_t sector, UINT num);
-bool Init(FILE* nandfile, u8* es_keyY)
+bool Init(u8* es_keyY)
{
+ CurFile = nullptr;
+
+ std::string nandpath = Platform::GetConfigString(Platform::DSi_NANDPath);
+ std::string instnand = nandpath + Platform::InstanceFileSuffix();
+
+ FILE* nandfile = Platform::OpenLocalFile(instnand, "r+b");
+ if ((!nandfile) && (Platform::InstanceID() > 0))
+ {
+ FILE* orig = Platform::OpenLocalFile(nandpath, "rb");
+ if (!orig)
+ {
+ printf("Failed to open DSi NAND\n");
+ return false;
+ }
+
+ fseek(orig, 0, SEEK_END);
+ long len = ftell(orig);
+ fseek(orig, 0, SEEK_SET);
+
+ nandfile = Platform::OpenLocalFile(instnand, "w+b");
+ if (nandfile)
+ {
+ u8* tmpbuf = new u8[0x10000];
+ for (long i = 0; i < len; i+=0x10000)
+ {
+ long blklen = 0x10000;
+ if ((i+blklen) > len) blklen = len-i;
+
+ fread(tmpbuf, blklen, 1, orig);
+ fwrite(tmpbuf, blklen, 1, nandfile);
+ }
+ delete[] tmpbuf;
+ }
+
+ fclose(orig);
+ fclose(nandfile);
+
+ nandfile = Platform::OpenLocalFile(instnand, "r+b");
+ }
+
if (!nandfile)
return false;
@@ -138,10 +178,17 @@ void DeInit()
f_unmount("0:");
ff_disk_close();
+ if (CurFile) fclose(CurFile);
CurFile = nullptr;
}
+FILE* GetFile()
+{
+ return CurFile;
+}
+
+
void GetIDs(u8* emmc_cid, u64& consoleid)
{
memcpy(emmc_cid, eMMC_CID, 16);
diff --git a/src/DSi_NAND.h b/src/DSi_NAND.h
index 6feb2d0f..a23e62f6 100644
--- a/src/DSi_NAND.h
+++ b/src/DSi_NAND.h
@@ -34,9 +34,11 @@ enum
TitleData_BannerSav,
};
-bool Init(FILE* nand, u8* es_keyY);
+bool Init(u8* es_keyY);
void DeInit();
+FILE* GetFile();
+
void GetIDs(u8* emmc_cid, u64& consoleid);
void ReadHardwareInfo(u8* dataS, u8* dataN);
diff --git a/src/DSi_NDMA.h b/src/DSi_NDMA.h
index 563c33f9..b1ea4c96 100644
--- a/src/DSi_NDMA.h
+++ b/src/DSi_NDMA.h
@@ -20,6 +20,7 @@
#define DSI_NDMA_H
#include "types.h"
+#include "Savestate.h"
class DSi_NDMA
{
diff --git a/src/DSi_NWifi.h b/src/DSi_NWifi.h
index 14c790a2..ffd56476 100644
--- a/src/DSi_NWifi.h
+++ b/src/DSi_NWifi.h
@@ -21,6 +21,7 @@
#include "DSi_SD.h"
#include "FIFO.h"
+#include "Savestate.h"
class DSi_NWifi : public DSi_SDDevice
{
diff --git a/src/DSi_SD.cpp b/src/DSi_SD.cpp
index 8879a26a..e603347a 100644
--- a/src/DSi_SD.cpp
+++ b/src/DSi_SD.cpp
@@ -136,7 +136,10 @@ void DSi_SDHost::Reset()
else
sd = nullptr;
- mmc = new DSi_MMCStorage(this, true, Platform::GetConfigString(Platform::DSi_NANDPath));
+ std::string nandpath = Platform::GetConfigString(Platform::DSi_NANDPath);
+ std::string instnand = nandpath + Platform::InstanceFileSuffix();
+
+ mmc = new DSi_MMCStorage(this, true, instnand);
mmc->SetCID(DSi::eMMC_CID);
Ports[0] = sd;
@@ -477,16 +480,14 @@ u16 DSi_SDHost::Read(u32 addr)
{
if (Ports[0]) // basic check of whether the SD card is inserted
{
- ret |= 0x0030;
+ ret |= 0x0020;
if (!Ports[0]->ReadOnly) ret |= 0x0080;
}
- else
- ret |= 0x0008;
}
else
{
// SDIO wifi is always inserted, I guess
- ret |= 0x00B0;
+ ret |= 0x00A0;
}
return ret;
}
diff --git a/src/DSi_SD.h b/src/DSi_SD.h
index 1d966068..5f6dbcd3 100644
--- a/src/DSi_SD.h
+++ b/src/DSi_SD.h
@@ -19,10 +19,10 @@
#ifndef DSI_SD_H
#define DSI_SD_H
-#include
+#include
#include "FIFO.h"
#include "FATStorage.h"
-
+#include "Savestate.h"
class DSi_SDDevice;
diff --git a/src/DSi_SPI_TSC.h b/src/DSi_SPI_TSC.h
index 4d25c1ca..7a3acf43 100644
--- a/src/DSi_SPI_TSC.h
+++ b/src/DSi_SPI_TSC.h
@@ -19,6 +19,9 @@
#ifndef DSI_SPI_TSC
#define DSI_SPI_TSC
+#include "types.h"
+#include "Savestate.h"
+
namespace DSi_SPI_TSC
{
diff --git a/src/FIFO.h b/src/FIFO.h
index 68ab126e..80d9b8a0 100644
--- a/src/FIFO.h
+++ b/src/FIFO.h
@@ -20,6 +20,7 @@
#define FIFO_H
#include "types.h"
+#include "Savestate.h"
template
class FIFO
diff --git a/src/GPU.cpp b/src/GPU.cpp
index 47a69cca..f54d771c 100644
--- a/src/GPU.cpp
+++ b/src/GPU.cpp
@@ -21,6 +21,10 @@
#include "NDS.h"
#include "GPU.h"
+#ifdef JIT_ENABLED
+#include "ARMJIT.h"
+#endif
+
#include "GPU2D_Soft.h"
namespace GPU
@@ -653,6 +657,9 @@ void MapVRAM_CD(u32 bank, u8 cnt)
VRAMMap_ARM7[ofs] |= bankmask;
memset(VRAMDirty[bank].Data, 0xFF, sizeof(VRAMDirty[bank].Data));
VRAMSTAT |= (1 << (bank-2));
+#ifdef JIT_ENABLED
+ ARMJIT::CheckAndInvalidateWVRAM(ofs);
+#endif
break;
case 3: // texture
diff --git a/src/GPU_OpenGL.cpp b/src/GPU_OpenGL.cpp
index 2fc97d57..79adf7a2 100644
--- a/src/GPU_OpenGL.cpp
+++ b/src/GPU_OpenGL.cpp
@@ -153,14 +153,14 @@ void GLCompositor::SetRenderSettings(RenderSettings& settings)
glBindTexture(GL_TEXTURE_2D, CompScreenOutputTex[i]);
glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, ScreenW, ScreenH, 0, GL_RGBA, GL_UNSIGNED_BYTE, NULL);
// fill the padding
- u8 zeroPixels[ScreenW*2*scale*4];
- memset(zeroPixels, 0, sizeof(zeroPixels));
+ u8* zeroPixels = (u8*) calloc(1, ScreenW*2*scale*4);
glTexSubImage2D(GL_TEXTURE_2D, 0, 0, 192*scale, ScreenW, 2*scale, GL_RGBA, GL_UNSIGNED_BYTE, zeroPixels);
GLenum fbassign[] = {GL_COLOR_ATTACHMENT0};
glBindFramebuffer(GL_FRAMEBUFFER, CompScreenOutputFB[i]);
glFramebufferTexture(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, CompScreenOutputTex[i], 0);
glDrawBuffers(1, fbassign);
+ free(zeroPixels);
}
glBindFramebuffer(GL_FRAMEBUFFER, 0);
diff --git a/src/NDS.cpp b/src/NDS.cpp
index 029870a2..9c6a7f03 100644
--- a/src/NDS.cpp
+++ b/src/NDS.cpp
@@ -176,6 +176,7 @@ bool RunningGame;
void DivDone(u32 param);
void SqrtDone(u32 param);
void RunTimer(u32 tid, s32 cycles);
+void UpdateWifiTimings();
void SetWifiWaitCnt(u16 val);
void SetGBASlotTimings();
@@ -892,9 +893,7 @@ bool DoSavestate(Savestate* file)
InitTimings();
SetGBASlotTimings();
- u16 tmp = WifiWaitCnt;
- WifiWaitCnt = 0xFFFF;
- SetWifiWaitCnt(tmp); // force timing table update
+ UpdateWifiTimings();
}
for (int i = 0; i < 8; i++)
@@ -918,6 +917,9 @@ bool DoSavestate(Savestate* file)
if (!file->Saving)
{
GPU::SetPowerCnt(PowerControl9);
+
+ SPU::SetPowerCnt(PowerControl7 & 0x0001);
+ Wifi::SetPowerCnt(PowerControl7 & 0x0002);
}
#ifdef JIT_ENABLED
@@ -1198,6 +1200,25 @@ void ScheduleEvent(u32 id, bool periodic, s32 delay, void (*func)(u32), u32 para
Reschedule(evt->Timestamp);
}
+void ScheduleEvent(u32 id, u64 timestamp, void (*func)(u32), u32 param)
+{
+ if (SchedListMask & (1<Timestamp = timestamp;
+ evt->Func = func;
+ evt->Param = param;
+
+ SchedListMask |= (1<Timestamp);
+}
+
void CancelEvent(u32 id)
{
SchedListMask &= ~(1<>3) & 0x3], (val & 0x20) ? 4 : 10);
+ }
+ else
+ {
+ SetARM7RegionTimings(0x04800, 0x04808, Mem7_Wifi0, 32, 1, 1);
+ SetARM7RegionTimings(0x04808, 0x04810, Mem7_Wifi1, 32, 1, 1);
+ }
+}
+
void SetWifiWaitCnt(u16 val)
{
if (WifiWaitCnt == val) return;
WifiWaitCnt = val;
-
- const int ntimings[4] = {10, 8, 6, 18};
- SetARM7RegionTimings(0x04800, 0x04808, Mem7_Wifi0, 16, ntimings[val & 0x3], (val & 0x4) ? 4 : 6);
- SetARM7RegionTimings(0x04808, 0x04810, Mem7_Wifi1, 16, ntimings[(val>>3) & 0x3], (val & 0x20) ? 4 : 10);
+ UpdateWifiTimings();
}
void SetGBASlotTimings()
@@ -1956,8 +1991,8 @@ void debug(u32 param)
//for (int i = 0; i < 9; i++)
// printf("VRAM %c: %02X\n", 'A'+i, GPU::VRAMCNT[i]);
- /*FILE*
- shit = fopen("debug/construct.bin", "wb");
+ FILE*
+ shit = fopen("debug/inazuma.bin", "wb");
fwrite(ARM9->ITCM, 0x8000, 1, shit);
for (u32 i = 0x02000000; i < 0x02400000; i+=4)
{
@@ -1969,9 +2004,14 @@ void debug(u32 param)
u32 val = ARM7Read32(i);
fwrite(&val, 4, 1, shit);
}
- fclose(shit);*/
+ for (u32 i = 0x06000000; i < 0x06040000; i+=4)
+ {
+ u32 val = ARM7Read32(i);
+ fwrite(&val, 4, 1, shit);
+ }
+ fclose(shit);
- FILE*
+ /*FILE*
shit = fopen("debug/camera9.bin", "wb");
for (u32 i = 0x02000000; i < 0x04000000; i+=4)
{
@@ -1985,7 +2025,7 @@ void debug(u32 param)
u32 val = DSi::ARM7Read32(i);
fwrite(&val, 4, 1, shit);
}
- fclose(shit);
+ fclose(shit);*/
}
@@ -2411,6 +2451,7 @@ u8 ARM7Read8(u32 addr)
case 0x04800000:
if (addr < 0x04810000)
{
+ if (!(PowerControl7 & (1<<1))) return 0;
if (addr & 0x1) return Wifi::Read(addr-1) >> 8;
return Wifi::Read(addr) & 0xFF;
}
@@ -2475,6 +2516,7 @@ u16 ARM7Read16(u32 addr)
case 0x04800000:
if (addr < 0x04810000)
{
+ if (!(PowerControl7 & (1<<1))) return 0;
return Wifi::Read(addr);
}
break;
@@ -2538,6 +2580,7 @@ u32 ARM7Read32(u32 addr)
case 0x04800000:
if (addr < 0x04810000)
{
+ if (!(PowerControl7 & (1<<1))) return 0;
return Wifi::Read(addr) | (Wifi::Read(addr+2) << 16);
}
break;
@@ -2629,7 +2672,8 @@ void ARM7Write8(u32 addr, u8 val)
return;
}
- if (ARM7->R[15] > 0x00002F30) // ARM7 BIOS bug
+ //if (ARM7->R[15] > 0x00002F30) // ARM7 BIOS bug
+ if (addr >= 0x01000000)
printf("unknown arm7 write8 %08X %02X @ %08X\n", addr, val, ARM7->R[15]);
}
@@ -2677,6 +2721,7 @@ void ARM7Write16(u32 addr, u16 val)
case 0x04800000:
if (addr < 0x04810000)
{
+ if (!(PowerControl7 & (1<<1))) return;
Wifi::Write(addr, val);
return;
}
@@ -2706,7 +2751,8 @@ void ARM7Write16(u32 addr, u16 val)
return;
}
- printf("unknown arm7 write16 %08X %04X @ %08X\n", addr, val, ARM7->R[15]);
+ if (addr >= 0x01000000)
+ printf("unknown arm7 write16 %08X %04X @ %08X\n", addr, val, ARM7->R[15]);
}
void ARM7Write32(u32 addr, u32 val)
@@ -2753,6 +2799,7 @@ void ARM7Write32(u32 addr, u32 val)
case 0x04800000:
if (addr < 0x04810000)
{
+ if (!(PowerControl7 & (1<<1))) return;
Wifi::Write(addr, val & 0xFFFF);
Wifi::Write(addr+2, val >> 16);
return;
@@ -2786,7 +2833,8 @@ void ARM7Write32(u32 addr, u32 val)
return;
}
- printf("unknown arm7 write32 %08X %08X @ %08X\n", addr, val, ARM7->R[15]);
+ if (addr >= 0x01000000)
+ printf("unknown arm7 write32 %08X %08X @ %08X\n", addr, val, ARM7->R[15]);
}
bool ARM7GetMemRegion(u32 addr, bool write, MemRegion* region)
@@ -2946,7 +2994,8 @@ u8 ARM9IORead8(u32 addr)
return (u8)(emuID[idx]);
}
- printf("unknown ARM9 IO read8 %08X %08X\n", addr, ARM9->R[15]);
+ if ((addr & 0xFFFFF000) != 0x04004000)
+ printf("unknown ARM9 IO read8 %08X %08X\n", addr, ARM9->R[15]);
return 0;
}
@@ -3092,7 +3141,8 @@ u16 ARM9IORead16(u32 addr)
return GPU3D::Read16(addr);
}
- printf("unknown ARM9 IO read16 %08X %08X\n", addr, ARM9->R[15]);
+ if ((addr & 0xFFFFF000) != 0x04004000)
+ printf("unknown ARM9 IO read16 %08X %08X\n", addr, ARM9->R[15]);
return 0;
}
@@ -3235,7 +3285,8 @@ u32 ARM9IORead32(u32 addr)
return GPU3D::Read32(addr);
}
- printf("unknown ARM9 IO read32 %08X %08X\n", addr, ARM9->R[15]);
+ if ((addr & 0xFFFFF000) != 0x04004000)
+ printf("unknown ARM9 IO read32 %08X %08X\n", addr, ARM9->R[15]);
return 0;
}
@@ -3763,6 +3814,7 @@ u8 ARM7IORead8(u32 addr)
case 0x04000241: return WRAMCnt;
case 0x04000300: return PostFlag7;
+ case 0x04000304: return PowerControl7;
}
if (addr >= 0x04000400 && addr < 0x04000520)
@@ -3770,7 +3822,8 @@ u8 ARM7IORead8(u32 addr)
return SPU::Read8(addr);
}
- printf("unknown ARM7 IO read8 %08X %08X\n", addr, ARM7->R[15]);
+ if ((addr & 0xFFFFF000) != 0x04004000)
+ printf("unknown ARM7 IO read8 %08X %08X\n", addr, ARM7->R[15]);
return 0;
}
@@ -3845,7 +3898,9 @@ u16 ARM7IORead16(u32 addr)
case 0x040001C2: return SPI::ReadData();
case 0x04000204: return ExMemCnt[1];
- case 0x04000206: return WifiWaitCnt;
+ case 0x04000206:
+ if (!(PowerControl7 & (1<<1))) return 0;
+ return WifiWaitCnt;
case 0x04000208: return IME[1];
case 0x04000210: return IE[1] & 0xFFFF;
@@ -3861,7 +3916,8 @@ u16 ARM7IORead16(u32 addr)
return SPU::Read16(addr);
}
- printf("unknown ARM7 IO read16 %08X %08X\n", addr, ARM7->R[15]);
+ if ((addr & 0xFFFFF000) != 0x04004000)
+ printf("unknown ARM7 IO read16 %08X %08X\n", addr, ARM7->R[15]);
return 0;
}
@@ -3927,6 +3983,7 @@ u32 ARM7IORead32(u32 addr)
case 0x04000210: return IE[1];
case 0x04000214: return IF[1];
+ case 0x04000304: return PowerControl7;
case 0x04000308: return ARM7BIOSProt;
case 0x04100000:
@@ -3960,7 +4017,8 @@ u32 ARM7IORead32(u32 addr)
return SPU::Read32(addr);
}
- printf("unknown ARM7 IO read32 %08X %08X\n", addr, ARM7->R[15]);
+ if ((addr & 0xFFFFF000) != 0x04004000)
+ printf("unknown ARM7 IO read32 %08X %08X\n", addr, ARM7->R[15]);
return 0;
}
@@ -4155,6 +4213,7 @@ void ARM7IOWrite16(u32 addr, u16 val)
return;
}
case 0x04000206:
+ if (!(PowerControl7 & (1<<1))) return;
SetWifiWaitCnt(val);
return;
@@ -4170,7 +4229,15 @@ void ARM7IOWrite16(u32 addr, u16 val)
PostFlag7 = val & 0x01;
return;
- case 0x04000304: PowerControl7 = val; return;
+ case 0x04000304:
+ {
+ u16 change = PowerControl7 ^ val;
+ PowerControl7 = val & 0x0003;
+ SPU::SetPowerCnt(val & 0x0001);
+ Wifi::SetPowerCnt(val & 0x0002);
+ if (change & 0x0002) UpdateWifiTimings();
+ }
+ return;
case 0x04000308:
if (ARM7BIOSProt == 0)
@@ -4292,7 +4359,15 @@ void ARM7IOWrite32(u32 addr, u32 val)
case 0x04000210: IE[1] = val; UpdateIRQ(1); return;
case 0x04000214: IF[1] &= ~val; UpdateIRQ(1); return;
- case 0x04000304: PowerControl7 = val & 0xFFFF; return;
+ case 0x04000304:
+ {
+ u16 change = PowerControl7 ^ val;
+ PowerControl7 = val & 0x0003;
+ SPU::SetPowerCnt(val & 0x0001);
+ Wifi::SetPowerCnt(val & 0x0002);
+ if (change & 0x0002) UpdateWifiTimings();
+ }
+ return;
case 0x04000308:
if (ARM7BIOSProt == 0)
diff --git a/src/NDS.h b/src/NDS.h
index df3c41c8..824c2bc9 100644
--- a/src/NDS.h
+++ b/src/NDS.h
@@ -264,6 +264,7 @@ void CamInputFrame(int cam, u32* data, int width, int height, bool rgb);
void MicInputFrame(s16* data, int samples);
void ScheduleEvent(u32 id, bool periodic, s32 delay, void (*func)(u32), u32 param);
+void ScheduleEvent(u32 id, u64 timestamp, void (*func)(u32), u32 param);
void CancelEvent(u32 id);
void debug(u32 p);
diff --git a/src/NDSCart.cpp b/src/NDSCart.cpp
index fe02cb17..cdc26ef1 100644
--- a/src/NDSCart.cpp
+++ b/src/NDSCart.cpp
@@ -1584,6 +1584,9 @@ bool LoadROM(const u8* romdata, u32 romlen)
if (CartInserted)
EjectCart();
+ memset(&Header, 0, sizeof(Header));
+ memset(&Banner, 0, sizeof(Banner));
+
CartROMSize = 0x200;
while (CartROMSize < romlen)
CartROMSize <<= 1;
@@ -1602,7 +1605,15 @@ bool LoadROM(const u8* romdata, u32 romlen)
memcpy(CartROM, romdata, romlen);
memcpy(&Header, CartROM, sizeof(Header));
- memcpy(&Banner, CartROM + Header.BannerOffset, sizeof(Banner));
+
+ u8 unitcode = Header.UnitCode;
+ bool dsi = (unitcode & 0x02) != 0;
+
+ size_t bannersize = dsi ? 0x23C0 : 0xA40;
+ if (Header.BannerOffset >= 0x200 && Header.BannerOffset < (CartROMSize - bannersize))
+ {
+ memcpy(&Banner, CartROM + Header.BannerOffset, bannersize);
+ }
printf("Game code: %.4s\n", Header.GameCode);
@@ -1611,9 +1622,6 @@ bool LoadROM(const u8* romdata, u32 romlen)
(u32)Header.GameCode[1] << 8 |
(u32)Header.GameCode[0];
- u8 unitcode = Header.UnitCode;
- bool dsi = (unitcode & 0x02) != 0;
-
u32 arm9base = Header.ARM9ROMOffset;
bool homebrew = (arm9base < 0x4000) || (gamecode == 0x23232323);
@@ -1679,6 +1687,7 @@ bool LoadROM(const u8* romdata, u32 romlen)
}
CartInserted = true;
+ DSi::SetCartInserted(true);
u32 irversion = 0;
if ((gamecode & 0xFF) == 'I')
@@ -1738,6 +1747,8 @@ void EjectCart()
CartROMSize = 0;
CartID = 0;
+ DSi::SetCartInserted(false);
+
// CHECKME: does an eject imply anything for the ROM/SPI transfer registers?
}
diff --git a/src/NDSCart.h b/src/NDSCart.h
index bbeb2ff9..10286ac3 100644
--- a/src/NDSCart.h
+++ b/src/NDSCart.h
@@ -22,6 +22,7 @@
#include
#include "types.h"
+#include "Savestate.h"
#include "NDS_Header.h"
#include "FATStorage.h"
diff --git a/src/Platform.h b/src/Platform.h
index d0562712..f2997ef8 100644
--- a/src/Platform.h
+++ b/src/Platform.h
@@ -32,6 +32,10 @@ void DeInit();
void StopEmu();
+// instance ID, for local multiplayer
+int InstanceID();
+std::string InstanceFileSuffix();
+
// configuration values
enum ConfigEntry
@@ -77,7 +81,6 @@ enum ConfigEntry
Firm_Color,
Firm_Message,
Firm_MAC,
- Firm_RandomizeMAC,
AudioBitrate,
};
@@ -158,8 +161,16 @@ void WriteGBASave(const u8* savedata, u32 savelen, u32 writeoffset, u32 writelen
// packet type: DS-style TX header (12 bytes) + original 802.11 frame
bool MP_Init();
void MP_DeInit();
-int MP_SendPacket(u8* data, int len);
-int MP_RecvPacket(u8* data, bool block);
+void MP_Begin();
+void MP_End();
+int MP_SendPacket(u8* data, int len, u64 timestamp);
+int MP_RecvPacket(u8* data, u64* timestamp);
+int MP_SendCmd(u8* data, int len, u64 timestamp);
+int MP_SendReply(u8* data, int len, u64 timestamp, u16 aid);
+int MP_SendAck(u8* data, int len, u64 timestamp);
+int MP_RecvHostPacket(u8* data, u64* timestamp);
+u16 MP_RecvReplies(u8* data, u64 timestamp, u16 aidmask);
+
// LAN comm interface
// packet type: Ethernet (802.3)
diff --git a/src/ROMList.h b/src/ROMList.h
index 8ed666b7..ab231129 100644
--- a/src/ROMList.h
+++ b/src/ROMList.h
@@ -19,6 +19,8 @@
#ifndef ROMLIST_H
#define ROMLIST_H
+#include "types.h"
+
struct ROMListEntry
{
u32 GameCode;
diff --git a/src/SPI.cpp b/src/SPI.cpp
index 6ecb86c4..e990b3a7 100644
--- a/src/SPI.cpp
+++ b/src/SPI.cpp
@@ -215,7 +215,8 @@ void LoadDefaultFirmware()
// wifi access points
// TODO: WFC ID??
- FILE* f = Platform::OpenLocalFile("wfcsettings.bin", "rb");
+ FILE* f = Platform::OpenLocalFile("wfcsettings.bin"+Platform::InstanceFileSuffix(), "rb");
+ if (!f) f = Platform::OpenLocalFile("wfcsettings.bin", "rb");
if (f)
{
u32 apdata = userdata - 0xA00;
@@ -259,7 +260,7 @@ void LoadDefaultFirmware()
}
}
-void LoadFirmwareFromFile(FILE* f)
+void LoadFirmwareFromFile(FILE* f, bool makecopy)
{
fseek(f, 0, SEEK_END);
@@ -271,7 +272,9 @@ void LoadFirmwareFromFile(FILE* f)
fread(Firmware, 1, FirmwareLength, f);
// take a backup
- std::string fwBackupPath = FirmwarePath + ".bak";
+ std::string fwBackupPath;
+ if (!makecopy) fwBackupPath = FirmwarePath + ".bak";
+ else fwBackupPath = FirmwarePath;
FILE* bf = Platform::OpenLocalFile(fwBackupPath, "rb");
if (!bf)
{
@@ -333,15 +336,24 @@ void Reset()
else
FirmwarePath = Platform::GetConfigString(Platform::FirmwarePath);
+ bool makecopy = false;
+ std::string origpath = FirmwarePath;
+ FirmwarePath += Platform::InstanceFileSuffix();
+
FILE* f = Platform::OpenLocalFile(FirmwarePath, "rb");
if (!f)
+ {
+ f = Platform::OpenLocalFile(origpath, "rb");
+ makecopy = true;
+ }
+ if (!f)
{
printf("Firmware not found! Generating default firmware.\n");
FirmwarePath = "";
}
else
{
- LoadFirmwareFromFile(f);
+ LoadFirmwareFromFile(f, makecopy);
fclose(f);
}
}
@@ -385,28 +397,28 @@ void Reset()
*(u16*)&Firmware[userdata+0x72] = CRC16(&Firmware[userdata], 0x70, 0xFFFF);
- if (firmoverride)
+ //if (firmoverride)
{
u8 mac[6];
- bool rep;
+ bool rep = false;
- if (Platform::GetConfigBool(Platform::Firm_RandomizeMAC))
- {
- mac[0] = 0x00;
- mac[1] = 0x09;
- mac[2] = 0xBF;
- mac[3] = rand()&0xFF;
- mac[4] = rand()&0xFF;
- mac[5] = rand()&0xFF;
- rep = true;
- }
- else
- {
+ memcpy(mac, &Firmware[0x36], 6);
+
+ if (firmoverride)
rep = Platform::GetConfigArray(Platform::Firm_MAC, mac);
+
+ int inst = Platform::InstanceID();
+ if (inst > 0)
+ {
+ rep = true;
+ mac[3] += inst;
+ mac[4] += inst*0x44;
+ mac[5] += inst*0x10;
}
if (rep)
{
+ mac[0] &= 0xFC; // ensure the MAC isn't a broadcast MAC
memcpy(&Firmware[0x36], mac, 6);
*(u16*)&Firmware[0x2A] = CRC16(&Firmware[0x2C], *(u16*)&Firmware[0x2C], 0x0000);
@@ -593,7 +605,12 @@ void Write(u8 val, u32 hold)
}
else
{
- FILE* f = Platform::OpenLocalFile("wfcsettings.bin", "wb");
+ char wfcfile[50] = {0};
+ int inst = Platform::InstanceID();
+ if (inst > 0) snprintf(wfcfile, 49, "wfcsettings.bin", Platform::InstanceID());
+ else strncpy(wfcfile, "wfcsettings.bin", 49);
+
+ FILE* f = Platform::OpenLocalFile(wfcfile, "wb");
if (f)
{
u32 cutoff = 0x7F400 & FirmwareMask;
diff --git a/src/SPU.cpp b/src/SPU.cpp
index cba05586..9f245a20 100644
--- a/src/SPU.cpp
+++ b/src/SPU.cpp
@@ -184,6 +184,12 @@ void DoSavestate(Savestate* file)
}
+void SetPowerCnt(u32 val)
+{
+ // TODO
+}
+
+
void SetInterpolation(int type)
{
InterpType = type;
diff --git a/src/SPU.h b/src/SPU.h
index 1e20c117..1f28c2f8 100644
--- a/src/SPU.h
+++ b/src/SPU.h
@@ -31,6 +31,8 @@ void Stop();
void DoSavestate(Savestate* file);
+void SetPowerCnt(u32 val);
+
// 0=none 1=linear 2=cosine 3=cubic
void SetInterpolation(int type);
diff --git a/src/Wifi.cpp b/src/Wifi.cpp
index 4e3bc17d..c2614e73 100644
--- a/src/Wifi.cpp
+++ b/src/Wifi.cpp
@@ -26,43 +26,59 @@
#include "ARM.h"
#include "GPU.h"
+
namespace Wifi
{
//#define WIFI_LOG printf
#define WIFI_LOG(...) {}
+#define PRINT_MAC(pf, mac) printf("%s: %02X:%02X:%02X:%02X:%02X:%02X\n", pf, (mac)[0], (mac)[1], (mac)[2], (mac)[3], (mac)[4], (mac)[5]);
+
u8 RAM[0x2000];
u16 IO[0x1000>>1];
#define IOPORT(x) IO[(x)>>1]
+#define IOPORT8(x) ((u8*)IO)[x]
+
+// destination MACs for MP frames
+const u8 MPCmdMAC[6] = {0x03, 0x09, 0xBF, 0x00, 0x00, 0x00};
+const u8 MPReplyMAC[6] = {0x03, 0x09, 0xBF, 0x00, 0x00, 0x10};
+const u8 MPAckMAC[6] = {0x03, 0x09, 0xBF, 0x00, 0x00, 0x03};
+
+const int kTimerInterval = 8;
+const u32 kTimeCheckMask = ~(kTimerInterval - 1);
+
+bool Enabled;
+bool PowerOn;
+
+s32 TimerError;
u16 Random;
+// general, always-on microsecond counter
+u64 USTimestamp;
+
u64 USCounter;
u64 USCompare;
bool BlockBeaconIRQ14;
u32 CmdCounter;
-u16 BBCnt;
-u8 BBWrite;
u8 BBRegs[0x100];
u8 BBRegsRO[0x100];
u8 RFVersion;
-u16 RFCnt;
-u16 RFData1;
-u16 RFData2;
u32 RFRegs[0x40];
struct TXSlot
{
+ bool Valid;
u16 Addr;
u16 Length;
u8 Rate;
u8 CurPhase;
- u32 CurPhaseTime;
+ int CurPhaseTime;
u32 HalfwordTimeMask;
};
@@ -70,16 +86,17 @@ TXSlot TXSlots[6];
u8 RXBuffer[2048];
u32 RXBufferPtr;
-u32 RXTime;
+int RXTime;
u32 RXHalfwordTimeMask;
-u16 RXEndAddr;
u32 ComStatus; // 0=waiting for packets 1=receiving 2=sending
u32 TXCurSlot;
u32 RXCounter;
int MPReplyTimer;
-int MPNumReplies;
+u16 MPClientMask, MPClientFail;
+
+u8 MPClientReplies[15*1024];
bool MPInited;
bool LANInited;
@@ -87,6 +104,11 @@ bool LANInited;
int USUntilPowerOn;
bool ForcePowerOn;
+// MULTIPLAYER SYNC APPARATUS
+bool IsMPClient;
+u64 NextSync; // for clients: timestamp for next sync point
+u64 RXTimestamp;
+
// multiplayer host TX sequence:
// 1. preamble
// 2. IRQ7
@@ -112,21 +134,26 @@ bool ForcePowerOn;
// 4 = switching from TX to RX
// 5 = MP host data sent, waiting for replies (RFPINS=0x0084)
// 6 = RX
-// 7 = ??
+// 7 = switching from RX reply to TX ack
// 8 = MP client sending reply, MP host sending ack (RFPINS=0x0046)
// 9 = idle
// wifi TODO:
-// * power saving
-// * RXSTAT, multiplay reply errors
+// * RXSTAT
// * TX errors (if applicable)
bool Init()
{
- MPInited = false;
- LANInited = false;
+ //MPInited = false;
+ //LANInited = false;
+
+ Platform::MP_Init();
+ MPInited = true;
+
+ Platform::LAN_Init();
+ LANInited = true;
WifiAP::Init();
@@ -148,6 +175,9 @@ void Reset()
memset(RAM, 0, 0x2000);
memset(IO, 0, 0x1000);
+ Enabled = false;
+ PowerOn = false;
+
Random = 1;
memset(BBRegs, 0, 0x100);
@@ -200,19 +230,39 @@ void Reset()
memset(&IOPORT(0x018), 0xFF, 6);
memset(&IOPORT(0x020), 0xFF, 6);
+ // TODO: find out what the initial values are
+ IOPORT(W_PowerUS) = 0x0001;
+
+ USTimestamp = 0;
+
USCounter = 0;
USCompare = 0;
BlockBeaconIRQ14 = false;
+ memset(TXSlots, 0, sizeof(TXSlots));
ComStatus = 0;
TXCurSlot = -1;
RXCounter = 0;
+ memset(RXBuffer, 0, sizeof(RXBuffer));
+ RXBufferPtr = 0;
+ RXTime = 0;
+ RXHalfwordTimeMask = 0xFFFFFFFF;
+
MPReplyTimer = 0;
- MPNumReplies = 0;
+ MPClientMask = 0;
+ MPClientFail = 0;
+ memset(MPClientReplies, 0, sizeof(MPClientReplies));
CmdCounter = 0;
+ USUntilPowerOn = 0;
+ ForcePowerOn = false;
+
+ IsMPClient = false;
+ NextSync = 0;
+ RXTimestamp = 0;
+
WifiAP::Reset();
}
@@ -228,8 +278,13 @@ void DoSavestate(Savestate* file)
file->VarArray(RAM, 0x2000);
file->VarArray(IO, 0x1000);
+ file->Bool32(&Enabled);
+ file->Bool32(&PowerOn);
+
file->Var16(&Random);
+ file->Var32((u32*)&TimerError);
+
file->VarArray(BBRegs, 0x100);
file->VarArray(BBRegsRO, 0x100);
@@ -240,17 +295,107 @@ void DoSavestate(Savestate* file)
file->Var64(&USCompare);
file->Bool32(&BlockBeaconIRQ14);
+ file->Var32(&CmdCounter);
+
+ file->Var64(&USTimestamp);
+
+ for (int i = 0; i < 6; i++)
+ {
+ TXSlot* slot = &TXSlots[i];
+
+ file->Bool32(&slot->Valid);
+ file->Var16(&slot->Addr);
+ file->Var16(&slot->Length);
+ file->Var8(&slot->Rate);
+ file->Var8(&slot->CurPhase);
+ file->Var32((u32*)&slot->CurPhaseTime);
+ file->Var32(&slot->HalfwordTimeMask);
+ }
+
+ file->VarArray(RXBuffer, sizeof(RXBuffer));
+ file->Var32(&RXBufferPtr);
+ file->Var32((u32*)&RXTime);
+ file->Var32(&RXHalfwordTimeMask);
+
file->Var32(&ComStatus);
file->Var32(&TXCurSlot);
file->Var32(&RXCounter);
file->Var32((u32*)&MPReplyTimer);
- file->Var32((u32*)&MPNumReplies);
+ file->Var16(&MPClientMask);
+ file->Var16(&MPClientFail);
- file->Var32(&CmdCounter);
+ file->VarArray(MPClientReplies, sizeof(MPClientReplies));
+
+ file->Var32((u32*)&USUntilPowerOn);
+ file->Bool32(&ForcePowerOn);
+
+ file->Bool32(&IsMPClient);
+ file->Var64(&NextSync);
+ file->Var64(&RXTimestamp);
}
+void ScheduleTimer(bool first)
+{
+ if (first) TimerError = 0;
+
+ s32 cycles = 33513982 * kTimerInterval;
+ cycles -= TimerError;
+ s32 delay = (cycles + 999999) / 1000000;
+ TimerError = (delay * 1000000) - cycles;
+
+ NDS::ScheduleEvent(NDS::Event_Wifi, !first, delay, USTimer, 0);
+}
+
+void UpdatePowerOn()
+{
+ bool on = Enabled;
+
+ if (NDS::ConsoleType == 1)
+ {
+ // TODO for DSi:
+ // * W_POWER_US doesn't work (atleast on DWM-W024)
+ // * other registers like GPIO_WIFI may also control wifi power/clock
+ // * turning wifi off via POWCNT2 while sending breaks further attempts at sending frames
+ }
+ else
+ {
+ on = on && ((IOPORT(W_PowerUS) & 0x1) == 0);
+ }
+
+ if (on == PowerOn)
+ return;
+
+ PowerOn = on;
+ if (on)
+ {
+ printf("WIFI: ON\n");
+
+ ScheduleTimer(true);
+
+ Platform::MP_Begin();
+ }
+ else
+ {
+ printf("WIFI: OFF\n");
+
+ NDS::CancelEvent(NDS::Event_Wifi);
+
+ Platform::MP_End();
+ }
+}
+
+void SetPowerCnt(u32 val)
+{
+ Enabled = val & (1<<1);
+ UpdatePowerOn();
+}
+
+
+void PowerDown();
+void StartTX_Beacon();
+
void SetIRQ(u32 irq)
{
u32 oldflags = IOPORT(W_IF) & IOPORT(W_IE);
@@ -269,7 +414,8 @@ void SetIRQ13()
if (!(IOPORT(W_PowerTX) & 0x0002))
{
IOPORT(0x034) = 0x0002;
- // TODO: 03C
+ //PowerDown();
+ // FIXME!!
IOPORT(W_RFPins) = 0x0046;
IOPORT(W_RFStatus) = 9;
}
@@ -318,21 +464,33 @@ void SetIRQ15()
void SetStatus(u32 status)
{
- // TODO, eventually: states 2/4, also find out what state 7 is
+ // TODO, eventually: states 2/4/7
u16 rfpins[10] = {0x04, 0x84, 0, 0x46, 0, 0x84, 0x87, 0, 0x46, 0x04};
IOPORT(W_RFStatus) = status;
IOPORT(W_RFPins) = rfpins[status];
}
-bool MACEqual(u8* a, u8* b)
+void PowerDown()
+{
+ IOPORT(W_TXReqRead) &= ~0x000F;
+ IOPORT(W_PowerState) |= 0x0200;
+
+ // if the RF hardware is powered down while still sending or receiving,
+ // the current frame is completed before going idle
+ if (!ComStatus)
+ {
+ SetStatus(9);
+ }
+}
+
+
+bool MACEqual(const u8* a, const u8* b)
{
return (*(u32*)&a[0] == *(u32*)&b[0]) && (*(u16*)&a[4] == *(u16*)&b[4]);
}
-// TODO: set RFSTATUS/RFPINS
-
int PreambleLen(int rate)
{
if (rate == 1) return 192;
@@ -340,6 +498,16 @@ int PreambleLen(int rate)
return 192;
}
+u32 NumClients(u16 bitmask)
+{
+ u32 ret = 0;
+ for (int i = 1; i < 16; i++)
+ {
+ if (bitmask & (1<Addr + 0x4];
@@ -347,6 +515,19 @@ void IncrementTXCount(TXSlot* slot)
*(u16*)&RAM[slot->Addr + 0x4] = cnt;
}
+void ReportMPReplyErrors(u16 clientfail)
+{
+ // TODO: do these trigger any IRQ?
+
+ for (int i = 1; i < 16; i++)
+ {
+ if (!(clientfail & (1<Addr = (IOPORT(W_TXSlotCmd) & 0x0FFF) << 1;
slot->Length = *(u16*)&RAM[slot->Addr + 0xA] & 0x3FFF;
@@ -401,9 +583,11 @@ void StartTX_Beacon()
IOPORT(W_TXBusy) |= 0x0010;
}
-// TODO eventually: there is a small delay to firing TX
void FireTX()
{
+ if (!(IOPORT(W_RXCnt) & 0x8000))
+ return;
+
u16 txbusy = IOPORT(W_TXBusy);
u16 txreq = IOPORT(W_TXReqRead);
@@ -443,39 +627,6 @@ void FireTX()
}
}
-void SendMPReply(u16 clienttime, u16 clientmask)
-{
- TXSlot* slot = &TXSlots[5];
-
- // mark the last packet as success. dunno what the MSB is, it changes.
- if (IOPORT(W_TXSlotReply2) & 0x8000)
- *(u16*)&RAM[slot->Addr] = 0x0001;
-
- IOPORT(W_TXSlotReply2) = IOPORT(W_TXSlotReply1);
- IOPORT(W_TXSlotReply1) = 0;
-
- // this seems to be set upon IRQ0
- // TODO: how does it behave if the packet addr is changed before it gets sent? (maybe just not possible)
- if (IOPORT(W_TXSlotReply2) & 0x8000)
- {
- slot->Addr = (IOPORT(W_TXSlotReply2) & 0x0FFF) << 1;
- //*(u16*)&RAM[slot->Addr + 0x4] = 0x0001;
- IncrementTXCount(slot);
- }
-
- u16 clientnum = 0;
- for (int i = 1; i < IOPORT(W_AIDLow); i++)
- {
- if (clientmask & (1<CurPhase = 0;
- slot->CurPhaseTime = 16 + ((clienttime + 10) * clientnum);
-
- IOPORT(W_TXBusy) |= 0x0080;
-}
-
void SendMPDefaultReply()
{
u8 reply[12 + 32];
@@ -488,25 +639,92 @@ void SendMPDefaultReply()
// TODO
reply[0x8] = 0x14;
- *(u16*)&reply[0xC + 0x00] = 0x0158;
- *(u16*)&reply[0xC + 0x02] = 0x00F0;//0; // TODO??
- *(u16*)&reply[0xC + 0x04] = IOPORT(W_BSSID0);
- *(u16*)&reply[0xC + 0x06] = IOPORT(W_BSSID1);
- *(u16*)&reply[0xC + 0x08] = IOPORT(W_BSSID2);
- *(u16*)&reply[0xC + 0x0A] = IOPORT(W_MACAddr0);
- *(u16*)&reply[0xC + 0x0C] = IOPORT(W_MACAddr1);
- *(u16*)&reply[0xC + 0x0E] = IOPORT(W_MACAddr2);
- *(u16*)&reply[0xC + 0x10] = 0x0903;
- *(u16*)&reply[0xC + 0x12] = 0x00BF;
- *(u16*)&reply[0xC + 0x14] = 0x1000;
- *(u16*)&reply[0xC + 0x16] = IOPORT(W_TXSeqNo) << 4;
- *(u32*)&reply[0xC + 0x18] = 0;
+ *(u16*)&reply[0xC + 0x00] = 0x0158;
+ *(u16*)&reply[0xC + 0x02] = 0x00F0;//0; // TODO??
+ *(u16*)&reply[0xC + 0x04] = IOPORT(W_BSSID0);
+ *(u16*)&reply[0xC + 0x06] = IOPORT(W_BSSID1);
+ *(u16*)&reply[0xC + 0x08] = IOPORT(W_BSSID2);
+ *(u16*)&reply[0xC + 0x0A] = IOPORT(W_MACAddr0);
+ *(u16*)&reply[0xC + 0x0C] = IOPORT(W_MACAddr1);
+ *(u16*)&reply[0xC + 0x0E] = IOPORT(W_MACAddr2);
+ *(u16*)&reply[0xC + 0x10] = 0x0903;
+ *(u16*)&reply[0xC + 0x12] = 0x00BF;
+ *(u16*)&reply[0xC + 0x14] = 0x1000;
+ *(u16*)&reply[0xC + 0x16] = IOPORT(W_TXSeqNo) << 4;
+ *(u32*)&reply[0xC + 0x18] = 0;
- int txlen = Platform::MP_SendPacket(reply, 12+28);
- WIFI_LOG("wifi: sent %d/40 bytes of MP default reply\n", txlen);
+ int txlen = Platform::MP_SendReply(reply, 12+28, USTimestamp, IOPORT(W_AIDLow));
+ WIFI_LOG("wifi: sent %d/40 bytes of MP default reply\n", txlen);
}
-void SendMPAck()
+void SendMPReply(u16 clienttime, u16 clientmask)
+{
+ TXSlot* slot = &TXSlots[5];
+
+ // mark the last packet as success. dunno what the MSB is, it changes.
+ //if (slot->Valid)
+ if (IOPORT(W_TXSlotReply2) & 0x8000)
+ *(u16*)&RAM[slot->Addr] = 0x0001;
+
+ // CHECKME!!
+ // can the transfer rate for MP replies be set, or is it determined from the CMD transfer rate?
+ // how does it work for default empty replies?
+ slot->Rate = 2;
+
+ IOPORT(W_TXSlotReply2) = IOPORT(W_TXSlotReply1);
+ IOPORT(W_TXSlotReply1) = 0;
+
+ if (!(IOPORT(W_TXSlotReply2) & 0x8000))
+ {
+ slot->Valid = false;
+ }
+ else
+ {
+ slot->Valid = true;
+
+ slot->Addr = (IOPORT(W_TXSlotReply2) & 0x0FFF) << 1;
+ slot->Length = *(u16*)&RAM[slot->Addr + 0xA] & 0x3FFF;
+
+ // the packet is entirely ignored if it lasts longer than the maximum reply time
+ u32 duration = PreambleLen(slot->Rate) + (slot->Length * (slot->Rate==2 ? 4:8));
+ if (duration > clienttime)
+ slot->Valid = false;
+ }
+
+ //if (RAM[slot->Addr+4] > 0)
+ // printf("REPLY RETRY COUNTER %d (%04X)\n", RAM[slot->Addr+4], IOPORT(W_TXSlotReply2));
+
+ // this seems to be set upon IRQ0
+ // TODO: how does it behave if the packet addr is changed before it gets sent? (maybe just not possible)
+ if (slot->Valid)
+ {
+ //*(u16*)&RAM[slot->Addr + 0x4] = 0x0001;
+ IncrementTXCount(slot);
+
+ slot->CurPhase = 0;
+ int txlen = Platform::MP_SendReply(&RAM[slot->Addr], 12 + slot->Length, USTimestamp, IOPORT(W_AIDLow));
+ WIFI_LOG("wifi: sent %d/%d bytes of MP reply\n", txlen, 12 + slot->Length);
+ }
+ else
+ {
+ slot->CurPhase = 10;
+
+ SendMPDefaultReply();
+ }
+
+ u16 clientnum = 0;
+ for (int i = 1; i < IOPORT(W_AIDLow); i++)
+ {
+ if (clientmask & (1<CurPhaseTime = 16 + ((clienttime + 10) * clientnum) + PreambleLen(slot->Rate);
+
+ IOPORT(W_TXBusy) |= 0x0080;
+}
+
+void SendMPAck(u16 clientfail)
{
u8 ack[12 + 32];
@@ -516,41 +734,32 @@ void SendMPAck()
if (TXSlots[1].Rate == 2) ack[0x8] = 0x14;
else ack[0x8] = 0xA;
- *(u16*)&ack[0xC + 0x00] = 0x0218;
- *(u16*)&ack[0xC + 0x02] = 0;
- *(u16*)&ack[0xC + 0x04] = 0x0903;
- *(u16*)&ack[0xC + 0x06] = 0x00BF;
- *(u16*)&ack[0xC + 0x08] = 0x0300;
- *(u16*)&ack[0xC + 0x0A] = IOPORT(W_BSSID0);
- *(u16*)&ack[0xC + 0x0C] = IOPORT(W_BSSID1);
- *(u16*)&ack[0xC + 0x0E] = IOPORT(W_BSSID2);
- *(u16*)&ack[0xC + 0x10] = IOPORT(W_MACAddr0);
- *(u16*)&ack[0xC + 0x12] = IOPORT(W_MACAddr1);
- *(u16*)&ack[0xC + 0x14] = IOPORT(W_MACAddr2);
- *(u16*)&ack[0xC + 0x16] = IOPORT(W_TXSeqNo) << 4;
- *(u16*)&ack[0xC + 0x18] = 0x0033; // ???
- *(u16*)&ack[0xC + 0x1A] = 0;
- *(u32*)&ack[0xC + 0x1C] = 0;
+ *(u16*)&ack[0xC + 0x00] = 0x0218;
+ *(u16*)&ack[0xC + 0x02] = 0;
+ *(u16*)&ack[0xC + 0x04] = 0x0903;
+ *(u16*)&ack[0xC + 0x06] = 0x00BF;
+ *(u16*)&ack[0xC + 0x08] = 0x0300;
+ *(u16*)&ack[0xC + 0x0A] = IOPORT(W_BSSID0);
+ *(u16*)&ack[0xC + 0x0C] = IOPORT(W_BSSID1);
+ *(u16*)&ack[0xC + 0x0E] = IOPORT(W_BSSID2);
+ *(u16*)&ack[0xC + 0x10] = IOPORT(W_MACAddr0);
+ *(u16*)&ack[0xC + 0x12] = IOPORT(W_MACAddr1);
+ *(u16*)&ack[0xC + 0x14] = IOPORT(W_MACAddr2);
+ *(u16*)&ack[0xC + 0x16] = IOPORT(W_TXSeqNo) << 4;
+ *(u16*)&ack[0xC + 0x18] = 0x0033; // ???
+ *(u16*)&ack[0xC + 0x1A] = clientfail;
+ *(u32*)&ack[0xC + 0x1C] = 0;
- int txlen = Platform::MP_SendPacket(ack, 12+32);
- WIFI_LOG("wifi: sent %d/44 bytes of MP ack, %d %d\n", txlen, ComStatus, RXTime);
+ int txlen = Platform::MP_SendAck(ack, 12+32, USTimestamp);
+ WIFI_LOG("wifi: sent %d/44 bytes of MP ack, %d %d\n", txlen, ComStatus, RXTime);
}
-u32 NumClients(u16 bitmask)
-{
- u32 ret = 0;
- for (int i = 1; i < 16; i++)
- {
- if (bitmask & (1<CurPhaseTime--;
+ slot->CurPhaseTime -= kTimerInterval;
if (slot->CurPhaseTime > 0)
{
if (slot->CurPhase == 1)
@@ -560,19 +769,28 @@ bool ProcessTX(TXSlot* slot, int num)
}
else if (slot->CurPhase == 2)
{
- MPReplyTimer--;
- if (MPReplyTimer == 0 && MPNumReplies > 0)
+ MPReplyTimer -= kTimerInterval;
+ if (MPReplyTimer <= 0 && MPClientMask != 0)
{
- if (CheckRX(true))
+ int nclient = 1;
+ while (!(MPClientMask & (1 << nclient))) nclient++;
+
+ u32 curclient = 1 << nclient;
+
+ /*if (CheckRX(1))
{
- ComStatus |= 0x1;
+ // we received a reply, mark it as such
+ // TODO: is any received packet considered a good reply?
+ // hardware probably requires a specific frame-control and/or destination MAC
+
+ MPClientFail &= ~curclient;
}
+ else printf("REPLY %04X NOT RECEIVED\n");*/
+ if (!(MPClientFail & curclient))
+ MPClientReplyRX(nclient);
- // TODO: properly handle reply errors
- // also, if the reply is too big to fit within its window, what happens?
-
- MPReplyTimer = 10 + IOPORT(W_CmdReplyTime);
- MPNumReplies--;
+ MPReplyTimer += 10 + IOPORT(W_CmdReplyTime);
+ MPClientMask &= ~curclient;
}
}
@@ -592,31 +810,16 @@ bool ProcessTX(TXSlot* slot, int num)
SetStatus(8);
- // if no reply is configured, send a default empty reply
- if (!(IOPORT(W_TXSlotReply2) & 0x8000))
- {
- SendMPDefaultReply();
+ //slot->Addr = (IOPORT(W_TXSlotReply2) & 0x0FFF) << 1;
+ //slot->Length = *(u16*)&RAM[slot->Addr + 0xA] & 0x3FFF;
- slot->Addr = 0;
- slot->Length = 28;
- slot->Rate = 2; // TODO
- slot->CurPhase = 4;
- slot->CurPhaseTime = 28*4;
- slot->HalfwordTimeMask = 0xFFFFFFFF;
- IOPORT(W_TXSeqNo) = (IOPORT(W_TXSeqNo) + 1) & 0x0FFF;
- break;
- }
-
- slot->Addr = (IOPORT(W_TXSlotReply2) & 0x0FFF) << 1;
- slot->Length = *(u16*)&RAM[slot->Addr + 0xA] & 0x3FFF;
+ /*u8 rate = RAM[slot->Addr + 0x8];
+ if (rate == 0x14) slot->Rate = 2;
+ else slot->Rate = 1;*/
// TODO: duration should be set by hardware
// doesn't seem to be important
//RAM[slot->Addr + 0xC + 2] = 0x00F0;
-
- u8 rate = RAM[slot->Addr + 0x8];
- if (rate == 0x14) slot->Rate = 2;
- else slot->Rate = 1;
}
else
SetStatus(3);
@@ -625,17 +828,32 @@ bool ProcessTX(TXSlot* slot, int num)
if (slot->Rate == 2)
{
len *= 4;
- slot->HalfwordTimeMask = 0x7;
+ slot->HalfwordTimeMask = 0x7 & kTimeCheckMask;
}
else
{
len *= 8;
- slot->HalfwordTimeMask = 0xF;
+ slot->HalfwordTimeMask = 0xF & kTimeCheckMask;
}
slot->CurPhase = 1;
slot->CurPhaseTime = len;
+ u16 framectl = *(u16*)&RAM[slot->Addr + 0xC];
+ if (framectl & (1<<14))
+ {
+ // WEP frame
+ // TODO: what happens when sending a WEP frame while WEP processing is off?
+ // TODO: some form of actual WEP processing?
+ // for now we just set the WEP FCS to a nonzero value, because some games require it
+
+ if (IOPORT(W_WEPCnt) & (1<<15))
+ {
+ u32 wep_fcs = (slot->Addr + 0xC + slot->Length - 7) & ~0x1;
+ *(u32*)&RAM[wep_fcs] = 0x22334466;
+ }
+ }
+
u64 oldts;
if (num == 4)
{
@@ -644,28 +862,66 @@ bool ProcessTX(TXSlot* slot, int num)
*(u64*)&RAM[slot->Addr + 0xC + 24] = USCounter;
}
- //u32 noseqno = 0;
- //if (num == 1) noseqno = (IOPORT(W_TXSlotCmd) & 0x4000);
+ u32 noseqno = 0;
+ if (num == 1) noseqno = (IOPORT(W_TXSlotCmd) & 0x4000);
- //if (!noseqno)
+ if (!noseqno)
{
*(u16*)&RAM[slot->Addr + 0xC + 22] = IOPORT(W_TXSeqNo) << 4;
IOPORT(W_TXSeqNo) = (IOPORT(W_TXSeqNo) + 1) & 0x0FFF;
}
+ if ((num != 5) && (RAM[slot->Addr+4] > 0))
+ printf("SLOT %d RETRY COUNTER %d\n", RAM[slot->Addr+4]);
+
// set TX addr
IOPORT(W_RXTXAddr) = slot->Addr >> 1;
- // send
- int txlen = Platform::MP_SendPacket(&RAM[slot->Addr], 12 + slot->Length);
- WIFI_LOG("wifi: sent %d/%d bytes of slot%d packet, addr=%04X, framectl=%04X, %04X %04X\n",
- txlen, slot->Length+12, num, slot->Addr, *(u16*)&RAM[slot->Addr + 0xC],
- *(u16*)&RAM[slot->Addr + 0x24], *(u16*)&RAM[slot->Addr + 0x26]);
+ if (num == 1)
+ {
+ // send
+ int txlen = Platform::MP_SendCmd(&RAM[slot->Addr], 12 + slot->Length, USTimestamp);
+ WIFI_LOG("wifi: sent %d/%d bytes of slot%d packet, addr=%04X, framectl=%04X, %04X %04X\n",
+ txlen, slot->Length+12, num, slot->Addr, *(u16*)&RAM[slot->Addr + 0xC],
+ *(u16*)&RAM[slot->Addr + 0x24], *(u16*)&RAM[slot->Addr + 0x26]);
+ }
+ else if (num == 5)
+ {
+ // send
+ /*int txlen = Platform::MP_SendReply(&RAM[slot->Addr], 12 + slot->Length, USTimestamp, IOPORT(W_AIDLow));
+ WIFI_LOG("wifi: sent %d/%d bytes of slot%d packet, addr=%04X, framectl=%04X, %04X %04X\n",
+ txlen, slot->Length+12, num, slot->Addr, *(u16*)&RAM[slot->Addr + 0xC],
+ *(u16*)&RAM[slot->Addr + 0x24], *(u16*)&RAM[slot->Addr + 0x26]);*/
+ }
+ else //if (num != 5)
+ {
+ // send
+ int txlen = Platform::MP_SendPacket(&RAM[slot->Addr], 12 + slot->Length, USTimestamp);
+ WIFI_LOG("wifi: sent %d/%d bytes of slot%d packet, addr=%04X, framectl=%04X, %04X %04X\n",
+ txlen, slot->Length+12, num, slot->Addr, *(u16*)&RAM[slot->Addr + 0xC],
+ *(u16*)&RAM[slot->Addr + 0x24], *(u16*)&RAM[slot->Addr + 0x26]);
+ }
// if the packet is being sent via LOC1..3, send it to the AP
// any packet sent via CMD/REPLY/BEACON isn't going to have much use outside of local MP
if (num == 0 || num == 2 || num == 3)
+ {
+ if ((framectl & 0x00FF) == 0x0010)
+ {
+ u16 aid = *(u16*)&RAM[slot->Addr + 0xC + 24 + 4];
+ if (aid) printf("[HOST] syncing client %04X, sync=%016llX\n", aid, USTimestamp);
+ }
+ else if ((framectl & 0x00FF) == 0x00C0)
+ {
+ if (IsMPClient)
+ {
+ printf("[CLIENT] deauth\n");
+ IsMPClient = false;
+ }
+ }
+
WifiAP::SendPacket(&RAM[slot->Addr], 12 + slot->Length);
+ }
if (num == 4)
{
@@ -674,10 +930,25 @@ bool ProcessTX(TXSlot* slot, int num)
}
break;
+ case 10: // preamble done (default empty MP reply)
+ {
+ SetIRQ(7);
+ SetStatus(8);
+
+ //SendMPDefaultReply();
+
+ //slot->Addr = 0;
+ //slot->Length = 28;
+ slot->CurPhase = 4;
+ slot->CurPhaseTime = 28*4;
+ slot->HalfwordTimeMask = 0xFFFFFFFF;
+ }
+ break;
+
case 1: // transmit done
{
- // for the MP reply slot, this is set later
- if (num != 5)
+ // for the MP CMD and reply slots, this is set later
+ if (num != 1 && num != 5)
*(u16*)&RAM[slot->Addr] = 0x0001;
RAM[slot->Addr + 5] = 0;
@@ -690,12 +961,21 @@ bool ProcessTX(TXSlot* slot, int num)
}
SetStatus(5);
- u16 clientmask = *(u16*)&RAM[slot->Addr + 12 + 24 + 2];
- MPNumReplies = NumClients(clientmask);
- MPReplyTimer = 16;
+ u16 clientmask = *(u16*)&RAM[slot->Addr + 12 + 24 + 2] & 0xFFFE;
+ //MPNumReplies = NumClients(clientmask);
+ MPReplyTimer = 16 + PreambleLen(slot->Rate);
+ MPClientMask = clientmask;
+ MPClientFail = clientmask;
+ u16 res = 0;
+ if (clientmask)
+ res = Platform::MP_RecvReplies(MPClientReplies, USTimestamp, clientmask);
+ MPClientFail &= ~res;
+
+ // TODO: 112 likely includes the ack preamble, which needs adjusted
+ // for long-preamble settings
slot->CurPhase = 2;
- slot->CurPhaseTime = 112 + ((10 + IOPORT(W_CmdReplyTime)) * MPNumReplies);
+ slot->CurPhaseTime = 112 + ((10 + IOPORT(W_CmdReplyTime)) * NumClients(clientmask));
break;
}
@@ -750,7 +1030,10 @@ bool ProcessTX(TXSlot* slot, int num)
if (slot->Rate == 2) slot->CurPhaseTime = 32 * 4;
else slot->CurPhaseTime = 32 * 8;
- SendMPAck();
+ ReportMPReplyErrors(MPClientFail);
+
+ // send
+ SendMPAck(MPClientFail);
slot->CurPhase = 3;
}
@@ -762,11 +1045,15 @@ bool ProcessTX(TXSlot* slot, int num)
IOPORT(W_TXBusy) &= ~(1<<1);
IOPORT(W_TXSlotCmd) &= 0x7FFF; // confirmed
- // seems this is set to indicate which clients failed to reply
- *(u16*)&RAM[slot->Addr + 0x2] = 0;
+ if (!MPClientFail)
+ *(u16*)&RAM[slot->Addr] = 0x0001;
+ else
+ *(u16*)&RAM[slot->Addr] = 0x0005;
+
+ // this is set to indicate which clients failed to reply
+ *(u16*)&RAM[slot->Addr + 0x2] = MPClientFail;
IncrementTXCount(slot);
- SetIRQ(12);
IOPORT(W_TXSeqNo) = (IOPORT(W_TXSeqNo) + 1) & 0x0FFF;
if (IOPORT(W_TXStatCnt) & 0x2000)
@@ -776,12 +1063,19 @@ bool ProcessTX(TXSlot* slot, int num)
}
SetStatus(1);
+ // TODO: retry the whole cycle if some clients failed to respond
+ // AND if there is enough time left in CMDCOUNT
+ // (games seem to always configure CMDCOUNT such that there is no time for retries)
+ SetIRQ(12);
+
FireTX();
}
return true;
case 4: // MP default reply transfer finished
{
+ IOPORT(W_TXSeqNo) = (IOPORT(W_TXSeqNo) + 1) & 0x0FFF;
+
IOPORT(W_TXBusy) &= ~0x80;
SetStatus(1);
FireTX();
@@ -804,126 +1098,21 @@ inline void IncrementRXAddr(u16& addr, u16 inc = 2)
}
}
-bool CheckRX(bool block)
+void StartRX()
{
- if (!(IOPORT(W_RXCnt) & 0x8000))
- return false;
-
- if (IOPORT(W_RXBufBegin) == IOPORT(W_RXBufEnd))
- return false;
-
- u16 framelen;
- u16 framectl;
- u8 txrate;
- bool bssidmatch;
- u16 rxflags;
-
- for (;;)
- {
- int rxlen = Platform::MP_RecvPacket(RXBuffer, block);
- if (rxlen == 0) rxlen = WifiAP::RecvPacket(RXBuffer);
- if (rxlen == 0) return false;
- if (rxlen < 12+24) continue;
-
- framelen = *(u16*)&RXBuffer[10];
- if (framelen != rxlen-12)
- {
- printf("bad frame length\n");
- continue;
- }
- framelen -= 4;
-
- framectl = *(u16*)&RXBuffer[12+0];
- txrate = RXBuffer[8];
-
- u32 a_src, a_dst, a_bss;
- rxflags = 0x0010;
- switch (framectl & 0x000C)
- {
- case 0x0000: // management
- a_src = 10;
- a_dst = 4;
- a_bss = 16;
- if ((framectl & 0x00F0) == 0x0080)
- rxflags |= 0x0001;
- break;
-
- case 0x0004: // control
- printf("blarg\n");
- continue;
-
- case 0x0008: // data
- switch (framectl & 0x0300)
- {
- case 0x0000: // STA to STA
- a_src = 10;
- a_dst = 4;
- a_bss = 16;
- break;
- case 0x0100: // STA to DS
- a_src = 10;
- a_dst = 16;
- a_bss = 4;
- break;
- case 0x0200: // DS to STA
- a_src = 16;
- a_dst = 4;
- a_bss = 10;
- break;
- case 0x0300: // DS to DS
- printf("blarg\n");
- continue;
- }
- // TODO: those also trigger on other framectl values
- // like 0208 -> C
- framectl &= 0xE7FF;
- if (framectl == 0x0228) rxflags |= 0x000C; // MP host frame
- else if (framectl == 0x0218) rxflags |= 0x000D; // MP ack frame
- else if (framectl == 0x0118) rxflags |= 0x000E; // MP reply frame
- else if (framectl == 0x0158) rxflags |= 0x000F; // empty MP reply frame
- else rxflags |= 0x0008;
- break;
- }
-
- if (MACEqual(&RXBuffer[12 + a_src], (u8*)&IOPORT(W_MACAddr0)))
- continue; // oops. we received a packet we just sent.
-
- bssidmatch = MACEqual(&RXBuffer[12 + a_bss], (u8*)&IOPORT(W_BSSID0));
- //if (!(IOPORT(W_BSSID0) & 0x0001) && !(RXBuffer[12 + a_bss] & 0x01) &&
- if (!MACEqual(&RXBuffer[12 + a_dst], (u8*)&IOPORT(W_MACAddr0)) &&
- !(RXBuffer[12 + a_dst] & 0x01))
- {
- printf("received packet %04X but it didn't pass the MAC check\n", framectl);
- continue;
- }
-
- break;
- }
-
- WIFI_LOG("wifi: received packet FC:%04X SN:%04X CL:%04X RXT:%d CMT:%d\n",
- framectl, *(u16*)&RXBuffer[12+4+6+6+6], *(u16*)&RXBuffer[12+4+6+6+6+2+2], framelen*4, IOPORT(W_CmdReplyTime));
-
- // make RX header
-
- if (bssidmatch) rxflags |= 0x8000;
-
- *(u16*)&RXBuffer[0] = rxflags;
- *(u16*)&RXBuffer[2] = 0x0040; // ???
- *(u16*)&RXBuffer[6] = txrate;
- *(u16*)&RXBuffer[8] = framelen;
- *(u16*)&RXBuffer[10] = 0x4080; // min/max RSSI. dunno
-
+ u16 framelen = *(u16*)&RXBuffer[8];
RXTime = framelen;
+ u16 txrate = *(u16*)&RXBuffer[6];
if (txrate == 0x14)
{
RXTime *= 4;
- RXHalfwordTimeMask = 0x7;
+ RXHalfwordTimeMask = 0x7 & kTimeCheckMask;
}
else
{
RXTime *= 8;
- RXHalfwordTimeMask = 0xF;
+ RXHalfwordTimeMask = 0xF & kTimeCheckMask;
}
u16 addr = IOPORT(W_RXBufWriteCursor) << 1;
@@ -934,6 +1123,443 @@ bool CheckRX(bool block)
SetIRQ(6);
SetStatus(6);
+ ComStatus |= 1;
+}
+
+void FinishRX()
+{
+ ComStatus &= ~0x1;
+ RXCounter = 0;
+
+ if (!ComStatus)
+ {
+ if (IOPORT(W_PowerState) & 0x0300)
+ SetStatus(9);
+ else
+ SetStatus(1);
+ }
+
+ // TODO: RX stats
+
+ u16 framectl = *(u16*)&RXBuffer[12];
+
+ // check the frame's destination address
+ // note: the hardware always checks the first address field, regardless of the frame type/etc
+ // similarly, the second address field is used to send acks to non-broadcast frames
+
+ u8* dstmac = &RXBuffer[12 + 4];
+ if (!(dstmac[0] & 0x01))
+ {
+ if (!MACEqual(dstmac, (u8*)&IOPORT(W_MACAddr0)))
+ return;
+ }
+
+ // reject the frame if it's a WEP frame and WEP is off
+ // TODO: check if sending WEP frames with WEP off works at all?
+
+ if (framectl & (1<<14))
+ {
+ if (!(IOPORT(W_WEPCnt) & (1<<15)))
+ return;
+ }
+
+ // apply RX filtering
+ // TODO:
+ // * RXFILTER bits 0, 9, 10, 12 not fully understood
+ // * port 0D8 also affects reception of frames
+ // * MP CMD frames with a duplicate sequence number are ignored
+
+ u16 rxflags = 0x0010;
+
+ switch ((framectl >> 2) & 0x3)
+ {
+ case 0: // management
+ {
+ u8* bssid = &RXBuffer[12 + 16];
+ if (MACEqual(bssid, (u8*)&IOPORT(W_BSSID0)))
+ rxflags |= 0x8000;
+
+ u16 subtype = (framectl >> 4) & 0xF;
+ if (subtype == 0x8) // beacon
+ {
+ if (!(rxflags & 0x8000))
+ {
+ if (!(IOPORT(W_RXFilter) & (1<<0)))
+ return;
+ }
+
+ rxflags |= 0x0001;
+ }
+ else if ((subtype <= 0x5) ||
+ (subtype >= 0xA && subtype <= 0xC))
+ {
+ if (!(rxflags & 0x8000))
+ {
+ // CHECKME!
+ if (!(IOPORT(W_RXFilter) & (3<<9)))
+ return;
+ }
+ }
+ }
+ break;
+
+ case 1: // control
+ {
+ if ((framectl & 0xF0) == 0xA0) // PS-poll
+ {
+ u8* bssid = &RXBuffer[12 + 4];
+ if (MACEqual(bssid, (u8*)&IOPORT(W_BSSID0)))
+ rxflags |= 0x8000;
+
+ if (!(rxflags & 0x8000))
+ {
+ if (!(IOPORT(W_RXFilter) & (1<<11)))
+ return;
+ }
+
+ rxflags |= 0x0005;
+ }
+ else
+ return;
+ }
+ break;
+
+ case 2: // data
+ {
+ u16 fromto = (framectl >> 8) & 0x3;
+ if (IOPORT(W_RXFilter2) & (1<> 4) & 0xF)
+ {
+ case 0x0: break;
+
+ case 0x1:
+ if ((rxflags & 0xF) == 0xD)
+ {
+ if (!(rxfilter & (1<<7))) return;
+ }
+ else if ((rxflags & 0xF) != 0xE)
+ {
+ if (!(rxfilter & (1<<1))) return;
+ }
+ break;
+
+ case 0x2:
+ if ((rxflags & 0xF) != 0xC)
+ {
+ if (!(rxfilter & (1<<2))) return;
+ }
+ break;
+
+ case 0x3:
+ if (!(rxfilter & (1<<3))) return;
+ break;
+
+ case 0x4: break;
+
+ case 0x5:
+ if ((rxflags & 0xF) == 0xF)
+ {
+ if (!(rxfilter & (1<<8))) return;
+ }
+ else
+ {
+ if (!(rxfilter & (1<<4))) return;
+ }
+ break;
+
+ case 0x6:
+ if (!(rxfilter & (1<<5))) return;
+ break;
+
+ case 0x7:
+ if (!(rxfilter & (1<<6))) return;
+ break;
+
+ default:
+ return;
+ }
+ }
+ break;
+ }
+
+ // build the RX header
+
+ u16 headeraddr = IOPORT(W_RXBufWriteCursor) << 1;
+ *(u16*)&RAM[headeraddr] = rxflags;
+ IncrementRXAddr(headeraddr);
+ *(u16*)&RAM[headeraddr] = 0x0040; // ???
+ IncrementRXAddr(headeraddr, 4);
+ *(u16*)&RAM[headeraddr] = *(u16*)&RXBuffer[6]; // TX rate
+ IncrementRXAddr(headeraddr);
+ *(u16*)&RAM[headeraddr] = *(u16*)&RXBuffer[8]; // frame length
+ IncrementRXAddr(headeraddr);
+ *(u16*)&RAM[headeraddr] = 0x4080; // RSSI
+
+ // signal successful reception
+
+ u16 addr = IOPORT(W_RXTXAddr) << 1;
+ if (addr & 0x2) IncrementRXAddr(addr);
+ IOPORT(W_RXBufWriteCursor) = (addr & ~0x3) >> 1;
+
+ SetIRQ(0);
+
+ if ((rxflags & 0x800F) == 0x800C)
+ {
+ // reply to CMD frames
+
+ u16 clientmask = *(u16*)&RXBuffer[0xC + 26];
+ if (IOPORT(W_AIDLow) && (clientmask & (1 << IOPORT(W_AIDLow))))
+ {
+ SendMPReply(*(u16*)&RXBuffer[0xC + 24], clientmask);
+ }
+ else
+ {
+ // send a blank
+ // this is just so the host can have something to receive, instead of hitting a timeout
+ // in the case this client wasn't ready to send a reply
+ // TODO: also send this if we have RX disabled
+
+ Platform::MP_SendReply(nullptr, 0, USTimestamp, 0);
+ }
+ }
+ else if ((rxflags & 0x800F) == 0x8001)
+ {
+ // when receiving a beacon with the right BSSID, the beacon's timestamp
+ // is copied to USCOUNTER
+
+ u32 len = *(u16*)&RXBuffer[8];
+ u16 txrate = *(u16*)&RXBuffer[6];
+ len *= ((txrate==0x14) ? 4 : 8);
+ len -= 76; // CHECKME: is this offset fixed?
+
+ u64 timestamp = *(u64*)&RXBuffer[12 + 24];
+ timestamp += (u64)len;
+
+ USCounter = timestamp;
+ }
+}
+
+void MPClientReplyRX(int client)
+{
+ if (IOPORT(W_PowerState) & 0x0300)
+ return;
+
+ if (!(IOPORT(W_RXCnt) & 0x8000))
+ return;
+
+ if (IOPORT(W_RXBufBegin) == IOPORT(W_RXBufEnd))
+ return;
+
+ int framelen;
+ u8 txrate;
+
+ u8* reply = &MPClientReplies[(client-1)*1024];
+ framelen = *(u16*)&reply[10];
+
+ txrate = reply[8];
+
+ // TODO: what are the maximum crop values?
+ u16 framectl = *(u16*)&reply[12];
+ if (framectl & (1<<14))
+ {
+ framelen -= (IOPORT(W_RXLenCrop) >> 7) & 0x1FE;
+ if (framelen > 24) memmove(&RXBuffer[12+24], &RXBuffer[12+28], framelen);
+ }
+ else
+ framelen -= (IOPORT(W_RXLenCrop) << 1) & 0x1FE;
+
+ if (framelen < 0) framelen = 0;
+
+ // TODO rework RX system so we don't need this (by reading directly into MPClientReplies)
+ memcpy(RXBuffer, reply, 12+framelen);
+
+ *(u16*)&RXBuffer[6] = txrate;
+ *(u16*)&RXBuffer[8] = framelen;
+
+ RXTimestamp = 0;
+ StartRX();
+}
+
+bool CheckRX(int type) // 0=regular 1=MP replies 2=MP host frames
+{
+ if (IOPORT(W_PowerState) & 0x0300)
+ return false;
+
+ if (!(IOPORT(W_RXCnt) & 0x8000))
+ return false;
+
+ if (IOPORT(W_RXBufBegin) == IOPORT(W_RXBufEnd))
+ return false;
+
+ int rxlen;
+ int framelen;
+ u16 framectl;
+ u8 txrate;
+ u64 timestamp;
+
+ for (;;)
+ {
+ timestamp = 0;
+
+ if (type == 0)
+ {
+ rxlen = Platform::MP_RecvPacket(RXBuffer, ×tamp);
+ if (rxlen <= 0)
+ rxlen = WifiAP::RecvPacket(RXBuffer);
+ }
+ else
+ {
+ rxlen = Platform::MP_RecvHostPacket(RXBuffer, ×tamp);
+ if (rxlen < 0)
+ {
+ // host is gone
+ // TODO: make this more resilient
+ IsMPClient = false;
+ }
+ }
+
+ if (rxlen <= 0) return false;
+ if (rxlen < 12+24) continue;
+
+ framelen = *(u16*)&RXBuffer[10];
+ if (framelen != rxlen-12)
+ {
+ printf("bad frame length %d/%d\n", framelen, rxlen-12);
+ continue;
+ }
+
+ framectl = *(u16*)&RXBuffer[12+0];
+ txrate = RXBuffer[8];
+
+ // TODO: what are the maximum crop values?
+ if (framectl & (1<<14))
+ {
+ framelen -= (IOPORT(W_RXLenCrop) >> 7) & 0x1FE;
+ if (framelen > 24) memmove(&RXBuffer[12+24], &RXBuffer[12+28], framelen);
+ }
+ else
+ framelen -= (IOPORT(W_RXLenCrop) << 1) & 0x1FE;
+
+ if (framelen < 0) framelen = 0;
+
+ break;
+ }
+
+ WIFI_LOG("wifi: received packet FC:%04X SN:%04X CL:%04X RXT:%d CMT:%d\n",
+ framectl, *(u16*)&RXBuffer[12+4+6+6+6], *(u16*)&RXBuffer[12+4+6+6+6+2+2], framelen*4, IOPORT(W_CmdReplyTime));
+
+ *(u16*)&RXBuffer[6] = txrate;
+ *(u16*)&RXBuffer[8] = framelen;
+
+ bool macgood = (RXBuffer[12 + 4] & 0x01) || MACEqual(&RXBuffer[12 + 4], (u8*)&IOPORT(W_MACAddr0));
+
+ if (((framectl & 0x00FF) == 0x0010) && timestamp && macgood)
+ {
+ // if receiving an association response: get the sync value from the host
+
+ u16 aid = *(u16*)&RXBuffer[12+24+4];
+
+ if (aid)
+ {
+ printf("[CLIENT %01X] host sync=%016llX\n", aid&0xF, timestamp);
+
+ IsMPClient = true;
+ USTimestamp = timestamp;
+ NextSync = RXTimestamp + (framelen * (txrate==0x14 ? 4:8));
+ }
+
+ RXTimestamp = 0;
+ StartRX();
+ }
+ else if (((framectl & 0x00FF) == 0x00C0) && timestamp && macgood && IsMPClient)
+ {
+ IsMPClient = false;
+ NextSync = 0;
+
+ RXTimestamp = 0;
+ StartRX();
+ }
+ else if (macgood && IsMPClient)
+ {
+ // if we are being a MP client, we need to delay this frame until we reach the
+ // timestamp it came with
+ // we also need to determine how far we can run after having received this frame
+
+ RXTimestamp = timestamp;
+ if (RXTimestamp < USTimestamp) RXTimestamp = USTimestamp;
+ NextSync = RXTimestamp + (framelen * (txrate==0x14 ? 4:8));
+
+ if (MACEqual(&RXBuffer[12 + 4], MPCmdMAC))
+ {
+ u16 clienttime = *(u16*)&RXBuffer[12+24];
+ u16 clientmask = *(u16*)&RXBuffer[12+26];
+
+ // include the MP reply time window
+ NextSync += 112 + ((clienttime + 10) * NumClients(clientmask));
+ }
+ }
+ else
+ {
+ // otherwise, just start receiving this frame now
+
+ RXTimestamp = 0;
+ StartRX();
+ }
+
return true;
}
@@ -942,7 +1568,7 @@ void MSTimer()
{
if (IOPORT(W_USCompareCnt))
{
- if (USCounter == USCompare)
+ if ((USCounter & ~0x3FF) == USCompare)
{
BlockBeaconIRQ14 = false;
SetIRQ14(0);
@@ -964,16 +1590,34 @@ void MSTimer()
void USTimer(u32 param)
{
- WifiAP::USTimer();
+ USTimestamp += kTimerInterval;
+
+ if (IsMPClient && (!ComStatus))
+ {
+ if (RXTimestamp && (USTimestamp >= RXTimestamp))
+ {
+ RXTimestamp = 0;
+ StartRX();
+ }
+
+ if (USTimestamp >= NextSync)
+ {
+ // TODO: not do this every tick if it fails to receive a frame!
+ CheckRX(2);
+ }
+ }
+
+ if (!(USTimestamp & 0x3FF & kTimeCheckMask))
+ WifiAP::MSTimer();
bool switchOffPowerSaving = false;
if (USUntilPowerOn < 0)
{
- USUntilPowerOn++;
+ USUntilPowerOn += kTimerInterval;
- switchOffPowerSaving = USUntilPowerOn == 0 && (IOPORT(W_PowerUnk) & 0x0001 || ForcePowerOn);
+ switchOffPowerSaving = (USUntilPowerOn >= 0) && (IOPORT(W_PowerUnk) & 0x0001 || ForcePowerOn);
}
- if (USUntilPowerOn == 0 && (IOPORT(W_PowerState) & 0x0002 || switchOffPowerSaving))
+ if ((USUntilPowerOn >= 0) && (IOPORT(W_PowerState) & 0x0002 || switchOffPowerSaving))
{
IOPORT(W_PowerState) = 0;
IOPORT(W_RFPins) = 1;
@@ -983,35 +1627,50 @@ void USTimer(u32 param)
if (IOPORT(W_USCountCnt))
{
- USCounter++;
+ USCounter += kTimerInterval;
u32 uspart = (USCounter & 0x3FF);
if (IOPORT(W_USCompareCnt))
{
u32 beaconus = (IOPORT(W_BeaconCount1) << 10) | (0x3FF - uspart);
- if (beaconus == IOPORT(W_PreBeacon)) SetIRQ15();
+ if ((beaconus & kTimeCheckMask) == (IOPORT(W_PreBeacon) & kTimeCheckMask))
+ SetIRQ15();
}
- if (!uspart) MSTimer();
+ if (!(uspart & kTimeCheckMask))
+ MSTimer();
}
if (IOPORT(W_CmdCountCnt) & 0x0001)
{
if (CmdCounter > 0)
{
- CmdCounter--;
+ if (CmdCounter < kTimerInterval)
+ CmdCounter = 0;
+ else
+ CmdCounter -= kTimerInterval;
}
}
if (IOPORT(W_ContentFree) != 0)
- IOPORT(W_ContentFree)--;
-
- if (!(IOPORT(W_PowerState) & 0x300))
{
- if (ComStatus == 0)
+ if (IOPORT(W_ContentFree) < kTimerInterval)
+ IOPORT(W_ContentFree) = 0;
+ else
+ IOPORT(W_ContentFree) -= kTimerInterval;
+ }
+
+ if (ComStatus == 0)
+ {
+ u16 txbusy = IOPORT(W_TXBusy);
+ if (txbusy)
{
- u16 txbusy = IOPORT(W_TXBusy);
- if (txbusy)
+ if (IOPORT(W_PowerState) & 0x0300)
+ {
+ ComStatus = 0;
+ TXCurSlot = -1;
+ }
+ else
{
ComStatus = 0x2;
if (txbusy & 0x0080) TXCurSlot = 5;
@@ -1021,105 +1680,94 @@ void USTimer(u32 param)
else if (txbusy & 0x0002) TXCurSlot = 1;
else if (txbusy & 0x0001) TXCurSlot = 0;
}
+ }
+ else
+ {
+ if ((!IsMPClient) || (USTimestamp > NextSync))
+ {
+ if ((!(RXCounter & 0x1FF & kTimeCheckMask)) && (!ComStatus))
+ {
+ CheckRX(0);
+ }
+ }
+
+ RXCounter += kTimerInterval;
+ }
+ }
+
+ if (ComStatus & 0x2)
+ {
+ bool finished = ProcessTX(&TXSlots[TXCurSlot], TXCurSlot);
+ if (finished)
+ {
+ if (IOPORT(W_PowerState) & 0x0300)
+ {
+ IOPORT(W_TXBusy) = 0;
+ SetStatus(9);
+ }
+
+ // transfer finished, see if there's another slot to do
+ // checkme: priority order of beacon/reply
+ // TODO: for CMD, check CMDCOUNT
+ u16 txbusy = IOPORT(W_TXBusy);
+ if (txbusy & 0x0080) TXCurSlot = 5;
+ else if (txbusy & 0x0010) TXCurSlot = 4;
+ else if (txbusy & 0x0008) TXCurSlot = 3;
+ else if (txbusy & 0x0004) TXCurSlot = 2;
+ else if (txbusy & 0x0002) TXCurSlot = 1;
+ else if (txbusy & 0x0001) TXCurSlot = 0;
else
{
- if ((!(RXCounter & 0x1FF)))
- {
- if (CheckRX(false))
- ComStatus = 0x1;
- }
-
- RXCounter++;
+ TXCurSlot = -1;
+ ComStatus = 0;
+ RXCounter = 0;
}
}
-
- if (ComStatus & 0x2)
+ }
+ if (ComStatus & 0x1)
+ {
+ RXTime -= kTimerInterval;
+ if (!(RXTime & RXHalfwordTimeMask))
{
- bool finished = ProcessTX(&TXSlots[TXCurSlot], TXCurSlot);
- if (finished)
+ u16 addr = IOPORT(W_RXTXAddr) << 1;
+ if (addr < 0x1FFF) *(u16*)&RAM[addr] = *(u16*)&RXBuffer[RXBufferPtr];
+
+ IncrementRXAddr(addr);
+ IOPORT(W_RXTXAddr) = addr >> 1;
+ RXBufferPtr += 2;
+
+ if (RXTime <= 0) // finished receiving
{
- // transfer finished, see if there's another slot to do
- // checkme: priority order of beacon/reply
- // TODO: for CMD, check CMDCOUNT
- u16 txbusy = IOPORT(W_TXBusy);
- if (txbusy & 0x0080) TXCurSlot = 5;
- else if (txbusy & 0x0010) TXCurSlot = 4;
- else if (txbusy & 0x0008) TXCurSlot = 3;
- else if (txbusy & 0x0004) TXCurSlot = 2;
- else if (txbusy & 0x0002) TXCurSlot = 1;
- else if (txbusy & 0x0001) TXCurSlot = 0;
- else
- {
- TXCurSlot = -1;
- ComStatus = 0;
- RXCounter = 0;
- }
+ FinishRX();
}
- }
- if (ComStatus & 0x1)
- {
- RXTime--;
- if (!(RXTime & RXHalfwordTimeMask))
+ else if (addr == (IOPORT(W_RXBufReadCursor) << 1))
{
- u16 addr = IOPORT(W_RXTXAddr) << 1;
- if (addr < 0x1FFF) *(u16*)&RAM[addr] = *(u16*)&RXBuffer[RXBufferPtr];
+ // TODO: properly check the crossing of the read cursor
+ // (for example, if it is outside of the RX buffer)
- IncrementRXAddr(addr);
- RXBufferPtr += 2;
-
- if (RXTime == 0) // finished receiving
+ printf("wifi: RX buffer full (buf=%04X/%04X rd=%04X wr=%04X rxtx=%04X power=%04X com=%d rxcnt=%04X filter=%04X/%04X frame=%04X/%04X len=%d)\n",
+ (IOPORT(W_RXBufBegin)>>1)&0xFFF, (IOPORT(W_RXBufEnd)>>1)&0xFFF,
+ IOPORT(W_RXBufReadCursor), IOPORT(W_RXBufWriteCursor),
+ IOPORT(W_RXTXAddr), IOPORT(W_PowerState), ComStatus,
+ IOPORT(W_RXCnt), IOPORT(W_RXFilter), IOPORT(W_RXFilter2),
+ *(u16*)&RXBuffer[0], *(u16*)&RXBuffer[12], *(u16*)&RXBuffer[8]);
+ RXTime = 0;
+ SetStatus(1);
+ if (TXCurSlot == 0xFFFFFFFF)
{
- if (addr & 0x2) IncrementRXAddr(addr);
-
- // copy the RX header
- u16 headeraddr = IOPORT(W_RXBufWriteCursor) << 1;
- *(u16*)&RAM[headeraddr] = *(u16*)&RXBuffer[0]; IncrementRXAddr(headeraddr);
- *(u16*)&RAM[headeraddr] = *(u16*)&RXBuffer[2]; IncrementRXAddr(headeraddr, 4);
- *(u16*)&RAM[headeraddr] = *(u16*)&RXBuffer[6]; IncrementRXAddr(headeraddr);
- *(u16*)&RAM[headeraddr] = *(u16*)&RXBuffer[8]; IncrementRXAddr(headeraddr);
- *(u16*)&RAM[headeraddr] = *(u16*)&RXBuffer[10];
-
- IOPORT(W_RXBufWriteCursor) = (addr & ~0x3) >> 1;
-
- SetIRQ(0);
- SetStatus(1);
-
- WIFI_LOG("wifi: finished receiving packet %04X\n", *(u16*)&RXBuffer[12]);
-
ComStatus &= ~0x1;
RXCounter = 0;
-
- if ((RXBuffer[0] & 0x0F) == 0x0C)
- {
- u16 clientmask = *(u16*)&RXBuffer[0xC + 26];
- if (IOPORT(W_AIDLow) && (RXBuffer[0xC + 4] & 0x01) && (clientmask & (1 << IOPORT(W_AIDLow))))
- {
- SendMPReply(*(u16*)&RXBuffer[0xC + 24], *(u16*)&RXBuffer[0xC + 26]);
- }
- }
}
-
- if (addr == (IOPORT(W_RXBufReadCursor) << 1))
+ // TODO: proper error management
+ if ((!ComStatus) && (IOPORT(W_PowerState) & 0x0300))
{
- printf("wifi: RX buffer full\n");
- RXTime = 0;
- SetStatus(1);
- if (TXCurSlot == 0xFFFFFFFF)
- {
- ComStatus &= ~0x1;
- RXCounter = 0;
- }
- // TODO: proper error management
+ SetStatus(9);
}
-
- IOPORT(W_RXTXAddr) = addr >> 1;
}
}
}
- // TODO: make it more accurate, eventually
- // in the DS, the wifi system has its own 22MHz clock and doesn't use the system clock
- NDS::ScheduleEvent(NDS::Event_Wifi, true, 33, USTimer, 0);
+ ScheduleTimer(false);
}
@@ -1157,15 +1805,13 @@ void RFTransfer_Type3()
}
-// TODO: wifi waitstates
-
u16 Read(u32 addr)
-{//printf("WIFI READ %08X\n", addr);
+{
if (addr >= 0x04810000)
return 0;
addr &= 0x7FFE;
- //printf("WIFI: read %08X\n", addr);
+
if (addr >= 0x4000 && addr < 0x6000)
{
return *(u16*)&RAM[addr & 0x1FFE];
@@ -1213,7 +1859,6 @@ u16 Read(u32 addr)
if (activeread)
{
u32 rdaddr = IOPORT(W_RXBufReadAddr);
-
u16 ret = *(u16*)&RAM[rdaddr];
rdaddr += 2;
@@ -1243,6 +1888,20 @@ u16 Read(u32 addr)
case W_TXBusy:
return IOPORT(W_TXBusy) & 0x001F; // no bit for MP replies. odd
+
+ case W_CMDStat0:
+ case W_CMDStat1:
+ case W_CMDStat2:
+ case W_CMDStat3:
+ case W_CMDStat4:
+ case W_CMDStat5:
+ case W_CMDStat6:
+ case W_CMDStat7:
+ {
+ u16 ret = IOPORT(addr&0xFFF);
+ IOPORT(addr&0xFFF) = 0;
+ return ret;
+ }
}
//printf("WIFI: read %08X\n", addr);
@@ -1250,12 +1909,12 @@ u16 Read(u32 addr)
}
void Write(u32 addr, u16 val)
-{//printf("WIFI WRITE %08X %04X\n", addr, val);
+{
if (addr >= 0x04810000)
return;
addr &= 0x7FFE;
- //printf("WIFI: write %08X %04X\n", addr, val);
+
if (addr >= 0x4000 && addr < 0x6000)
{
*(u16*)&RAM[addr & 0x1FFE] = val;
@@ -1290,9 +1949,7 @@ void Write(u32 addr, u16 val)
{
//printf("mode reset shutdown %08x\n", NDS::ARM7->R[15]);
IOPORT(0x27C) = 0x000A;
- IOPORT(W_RFPins) = 0x0004;
- IOPORT(W_RFStatus) = 9;
- IOPORT(W_PowerState) |= 0x200;
+ PowerDown();
}
if (val & 0x2000)
@@ -1353,6 +2010,13 @@ void Write(u32 addr, u16 val)
printf("wifi: force-setting IF %04X\n", val);
return;
+ case W_AIDLow:
+ IOPORT(W_AIDLow) = val & 0x000F;
+ return;
+ case W_AIDFull:
+ IOPORT(W_AIDFull) = val & 0x07FF;
+ return;
+
case W_PowerState:
//printf("writing power state %x %08x\n", val, NDS::ARM7->R[15]);
IOPORT(W_PowerState) |= val & 0x0002;
@@ -1376,6 +2040,7 @@ void Write(u32 addr, u16 val)
return;
case W_PowerForce:
//if ((val&0x8001)==0x8000) printf("WIFI: forcing power %04X\n", val);
+
val &= 0x8001;
//printf("writing power force %x %08x\n", val, NDS::ARM7->R[15]);
if (val == 0x8001)
@@ -1384,8 +2049,7 @@ void Write(u32 addr, u16 val)
IOPORT(0x034) = 0x0002;
IOPORT(W_PowerState) = 0x0200;
IOPORT(W_TXReqRead) = 0;
- IOPORT(W_RFPins) = 0x0046;
- IOPORT(W_RFStatus) = 9;
+ PowerDown();
}
if (val == 1 && IOPORT(W_PowerState) & 0x0002)
{
@@ -1403,29 +2067,9 @@ void Write(u32 addr, u16 val)
}
break;
case W_PowerUS:
- // schedule timer event when the clock is enabled
- // TODO: check whether this resets USCOUNT (and also which other events can reset it)
- if ((IOPORT(W_PowerUS) & 0x0001) && !(val & 0x0001))
- {
- printf("WIFI ON\n");
- NDS::ScheduleEvent(NDS::Event_Wifi, false, 33, USTimer, 0);
- if (!MPInited)
- {
- Platform::MP_Init();
- MPInited = true;
- }
- if (!LANInited)
- {
- Platform::LAN_Init();
- LANInited = true;
- }
- }
- else if (!(IOPORT(W_PowerUS) & 0x0001) && (val & 0x0001))
- {
- printf("WIFI OFF\n");
- NDS::CancelEvent(NDS::Event_Wifi);
- }
- break;
+ IOPORT(W_PowerUS) = val & 0x0003;
+ UpdatePowerOn();
+ return;
case W_PowerUnk:
val &= 0x0003;
//printf("writing power unk %x\n", val);
@@ -1486,6 +2130,10 @@ void Write(u32 addr, u16 val)
IOPORT(W_TXSlotReply2) = IOPORT(W_TXSlotReply1);
IOPORT(W_TXSlotReply1) = 0;
}
+ if (val & 0x8000)
+ {
+ FireTX();
+ }
val &= 0xFF0E;
if (val & 0x7FFF) printf("wifi: unknown RXCNT bits set %04X\n", val);
break;
@@ -1570,6 +2218,7 @@ void Write(u32 addr, u16 val)
case W_TXSlotCmd:
// checkme: is it possible to cancel a queued transfer that hasn't started yet
// by clearing bit15 here?
+ // TODO: "W_TXBUF_CMD.Bit15 can be set ONLY while W_CMD_COUNT is non-zero."
IOPORT(addr&0xFFF) = val;
FireTX();
return;
@@ -1597,13 +2246,12 @@ void Write(u32 addr, u16 val)
case 0x214:
case 0x268:
return;
-
+
default:
//printf("WIFI unk: write %08X %04X\n", addr, val);
break;
}
- //printf("WIFI: write %08X %04X\n", addr, val);
IOPORT(addr&0xFFF) = val;
}
diff --git a/src/Wifi.h b/src/Wifi.h
index 2e156738..b9594f45 100644
--- a/src/Wifi.h
+++ b/src/Wifi.h
@@ -93,6 +93,7 @@ enum
W_CmdTotalTime = 0x0C0,
W_CmdReplyTime = 0x0C4,
W_RXFilter = 0x0D0,
+ W_RXLenCrop = 0x0DA,
W_RXFilter2 = 0x0E0,
W_USCountCnt = 0x0E8,
@@ -136,12 +137,43 @@ enum
W_TXErrorCount = 0x1C0,
W_RXCount = 0x1C4,
+ W_CMDStat0 = 0x1D0,
+ W_CMDStat1 = 0x1D2,
+ W_CMDStat2 = 0x1D4,
+ W_CMDStat3 = 0x1D6,
+ W_CMDStat4 = 0x1D8,
+ W_CMDStat5 = 0x1DA,
+ W_CMDStat6 = 0x1DC,
+ W_CMDStat7 = 0x1DE,
+
W_TXSeqNo = 0x210,
W_RFStatus = 0x214,
W_IFSet = 0x21C,
W_RXTXAddr = 0x268,
};
+enum
+{
+ Event_RXCheck = 0,
+ Event_IRQ15,
+ Event_MSTimer,
+ Event_RFWakeup,
+ Event_RX,
+ Event_TX,
+ Event_MPClientSync,
+ Event_RF,
+ Event_BB,
+
+ Event_MAX
+};
+
+struct SchedEvent
+{
+ void (*Func)(u32 param);
+ u64 Timestamp;
+ u32 Param;
+};
+
extern bool MPInited;
@@ -151,7 +183,7 @@ void DeInit();
void Reset();
void DoSavestate(Savestate* file);
-void StartTX_Beacon();
+void SetPowerCnt(u32 val);
void USTimer(u32 param);
diff --git a/src/WifiAP.cpp b/src/WifiAP.cpp
index e083c74d..53516396 100644
--- a/src/WifiAP.cpp
+++ b/src/WifiAP.cpp
@@ -91,7 +91,7 @@ void DeInit()
void Reset()
{
// random starting point for the counter
- USCounter = 0x428888017ULL;
+ USCounter = 0x428888000ULL;
SeqNo = 0x0120;
BeaconDue = false;
@@ -115,18 +115,6 @@ bool MACIsBroadcast(u8* a)
}
-void USTimer()
-{
- USCounter++;
-
- u32 chk = (u32)USCounter;
- if (!(chk & 0x1FFFF))
- {
- // send beacon every 128ms
- BeaconDue = true;
- }
-}
-
void MSTimer()
{
USCounter += 0x400;
diff --git a/src/WifiAP.h b/src/WifiAP.h
index 187f16bd..e5ca1ed7 100644
--- a/src/WifiAP.h
+++ b/src/WifiAP.h
@@ -19,6 +19,8 @@
#ifndef WIFIAP_H
#define WIFIAP_H
+#include "types.h"
+
namespace WifiAP
{
@@ -31,7 +33,6 @@ bool Init();
void DeInit();
void Reset();
-void USTimer();
void MSTimer();
// packet format: 12-byte TX header + original 802.11 frame
diff --git a/src/frontend/qt_sdl/AudioSettingsDialog.cpp b/src/frontend/qt_sdl/AudioSettingsDialog.cpp
index a9b3ade7..4beefaf3 100644
--- a/src/frontend/qt_sdl/AudioSettingsDialog.cpp
+++ b/src/frontend/qt_sdl/AudioSettingsDialog.cpp
@@ -67,6 +67,20 @@ AudioSettingsDialog::AudioSettingsDialog(QWidget* parent) : QDialog(parent), ui(
bool iswav = (Config::MicInputType == 3);
ui->txtMicWavPath->setEnabled(iswav);
ui->btnMicWavBrowse->setEnabled(iswav);
+
+ int inst = Platform::InstanceID();
+ if (inst > 0)
+ {
+ ui->lblInstanceNum->setText(QString("Configuring settings for instance %1").arg(inst+1));
+ ui->cbInterpolation->setEnabled(false);
+ ui->cbBitrate->setEnabled(false);
+ for (QAbstractButton* btn : grpMicMode->buttons())
+ btn->setEnabled(false);
+ ui->txtMicWavPath->setEnabled(false);
+ ui->btnMicWavBrowse->setEnabled(false);
+ }
+ else
+ ui->lblInstanceNum->hide();
}
AudioSettingsDialog::~AudioSettingsDialog()
diff --git a/src/frontend/qt_sdl/AudioSettingsDialog.ui b/src/frontend/qt_sdl/AudioSettingsDialog.ui
index d7cfadd6..8fc38d92 100644
--- a/src/frontend/qt_sdl/AudioSettingsDialog.ui
+++ b/src/frontend/qt_sdl/AudioSettingsDialog.ui
@@ -7,7 +7,7 @@
0
0
482
- 256
+ 301
@@ -23,6 +23,13 @@
QLayout::SetFixedSize
+ -
+
+
+ Configuring settings for instance X
+
+
+
-
@@ -76,7 +83,7 @@
-
- <html><head/><body><p>The bitrate of audio playback. If set to "Automatic" this will be 10-bit for DS mode and 16-bit for DSi mode.</p></body></html>
+ <html><head/><body><p>The bitrate of audio playback. If set to "Automatic" this will be 10-bit for DS mode and 16-bit for DSi mode.</p></body></html>
diff --git a/src/frontend/qt_sdl/CMakeLists.txt b/src/frontend/qt_sdl/CMakeLists.txt
index d211dd6d..c9ba47fe 100644
--- a/src/frontend/qt_sdl/CMakeLists.txt
+++ b/src/frontend/qt_sdl/CMakeLists.txt
@@ -1,10 +1,12 @@
-project(qt_sdl)
+include(CMakeDependentOption)
-SET(SOURCES_QT_SDL
+include(FixInterfaceIncludes)
+
+set(SOURCES_QT_SDL
main.cpp
main_shaders.h
CheatsDialog.cpp
- Config.cpp
+ Config.cpp
EmuSettingsDialog.cpp
PowerManagement/PowerManagementDialog.cpp
PowerManagement/resources/battery.qrc
@@ -16,6 +18,7 @@ SET(SOURCES_QT_SDL
AudioSettingsDialog.cpp
FirmwareSettingsDialog.cpp
PathSettingsDialog.cpp
+ MPSettingsDialog.cpp
WifiSettingsDialog.cpp
InterfaceSettingsDialog.cpp
ROMInfoDialog.cpp
@@ -24,13 +27,13 @@ SET(SOURCES_QT_SDL
Input.cpp
LAN_PCap.cpp
LAN_Socket.cpp
+ LocalMP.cpp
OSD.cpp
OSD_shaders.h
font.h
Platform.cpp
QPathInput.h
- ROMManager.cpp
- SaveManager.cpp
+
CameraManager.cpp
ArchiveUtil.h
@@ -42,7 +45,7 @@ SET(SOURCES_QT_SDL
../mic_blow.h
${CMAKE_SOURCE_DIR}/res/melon.qrc
-)
+ )
if (APPLE)
option(USE_QT6 "Build using Qt 6 instead of 5" ON)
@@ -56,29 +59,11 @@ if (WIN32)
endif()
if (USE_QT6)
- if (BUILD_STATIC AND QT6_STATIC_DIR)
- set(QT6_STATIC_BASE ${QT6_STATIC_DIR}/lib/cmake/Qt6)
- set(Qt6_DIR ${QT6_STATIC_BASE})
- set(Qt6Core_DIR ${QT6_STATIC_BASE}Core)
- set(Qt6Gui_DIR ${QT6_STATIC_BASE}Gui)
- set(Qt6Widgets_DIR ${QT6_STATIC_BASE}Widgets)
- set(Qt6Network_DIR ${QT6_STATIC_BASE}Network)
set(Qt6Multimedia_DIR ${QT6_STATIC_BASE}Multimedia)
- set(Qt6OpenGL_DIR ${QT6_STATIC_BASE}OpenGL)
- set(Qt6OpenGLWidgets_DIR ${QT6_STATIC_BASE}OpenGLWidgets)
- endif()
find_package(Qt6 COMPONENTS Core Gui Widgets Network Multimedia OpenGL OpenGLWidgets REQUIRED)
set(QT_LINK_LIBS Qt6::Core Qt6::Gui Qt6::Widgets Qt6::Network Qt6::Multimedia Qt6::OpenGL Qt6::OpenGLWidgets)
else()
- if (BUILD_STATIC AND QT5_STATIC_DIR)
- set(QT5_STATIC_BASE ${QT5_STATIC_DIR}/lib/cmake/Qt5)
- set(Qt5_DIR ${QT5_STATIC_BASE})
- set(Qt5Core_DIR ${QT5_STATIC_BASE}Core)
- set(Qt5Gui_DIR ${QT5_STATIC_BASE}Gui)
- set(Qt5Widgets_DIR ${QT5_STATIC_BASE}Widgets)
- set(Qt5Network_DIR ${QT5_STATIC_BASE}Network)
set(Qt5Multimedia_DIR ${QT5_STATIC_BASE}Multimedia)
- endif()
find_package(Qt5 COMPONENTS Core Gui Widgets Network Multimedia REQUIRED)
set(QT_LINK_LIBS Qt5::Core Qt5::Gui Qt5::Widgets Qt5::Network Qt5::Multimedia)
endif()
@@ -87,66 +72,53 @@ set(CMAKE_AUTOMOC ON)
set(CMAKE_AUTOUIC ON)
set(CMAKE_AUTORCC ON)
-find_package(Threads REQUIRED)
-find_package(PkgConfig REQUIRED)
-find_package(Iconv REQUIRED)
-pkg_check_modules(SDL2 REQUIRED sdl2)
-pkg_check_modules(SLIRP REQUIRED slirp)
-pkg_check_modules(LIBARCHIVE REQUIRED libarchive)
-add_compile_definitions(ARCHIVE_SUPPORT_ENABLED)
-
-if (WIN32 AND (CMAKE_BUILD_TYPE STREQUAL Release))
- add_executable(melonDS WIN32 ${SOURCES_QT_SDL})
-else()
- add_executable(melonDS ${SOURCES_QT_SDL})
+if (BUILD_STATIC)
+ list(APPEND PKG_CONFIG_EXECUTABLE "--static")
endif()
-target_link_libraries(melonDS ${CMAKE_THREAD_LIBS_INIT})
+find_package(Threads REQUIRED)
+find_package(PkgConfig REQUIRED)
+pkg_check_modules(SDL2 REQUIRED IMPORTED_TARGET sdl2)
+pkg_check_modules(Slirp REQUIRED IMPORTED_TARGET slirp)
+pkg_check_modules(LibArchive REQUIRED IMPORTED_TARGET libarchive)
-target_include_directories(melonDS PRIVATE ${SDL2_INCLUDE_DIRS} ${SDL2_PREFIX}/include ${SLIRP_INCLUDE_DIRS} ${LIBARCHIVE_INCLUDE_DIRS})
-target_link_directories(melonDS PRIVATE ${SDL2_LIBRARY_DIRS} ${SLIRP_LIBRARY_DIRS})
-target_link_directories(melonDS PRIVATE ${LIBARCHIVE_LIBRARY_DIRS})
+fix_interface_includes(PkgConfig::SDL2 PkgConfig::Slirp PkgConfig::LibArchive)
+
+add_compile_definitions(ARCHIVE_SUPPORT_ENABLED)
+
+add_executable(melonDS ${SOURCES_QT_SDL})
+
+if (BUILD_STATIC)
+ qt_import_plugins(melonDS INCLUDE Qt::QSvgPlugin)
+ target_link_options(melonDS PRIVATE -static)
+endif()
target_include_directories(melonDS PUBLIC "${CMAKE_CURRENT_SOURCE_DIR}")
target_include_directories(melonDS PUBLIC "${CMAKE_CURRENT_SOURCE_DIR}/..")
target_include_directories(melonDS PUBLIC "${CMAKE_CURRENT_SOURCE_DIR}/../..")
-target_link_libraries(melonDS core)
-
-if (BUILD_STATIC)
- target_link_libraries(melonDS -static ${SDL2_STATIC_LIBRARIES} ${SLIRP_STATIC_LIBRARIES} ${LIBARCHIVE_STATIC_LIBRARIES})
- qt_import_plugins(melonDS INCLUDE Qt::QSvgPlugin)
-else()
- target_link_libraries(melonDS ${SDL2_LIBRARIES} ${SLIRP_LIBRARIES} ${LIBARCHIVE_LIBRARIES})
-endif()
-
-if (NOT Iconv_IS_BUILT_IN)
- target_link_libraries(melonDS ${Iconv_LIBRARIES})
-endif()
+target_link_libraries(melonDS PRIVATE core)
+target_link_libraries(melonDS PRIVATE PkgConfig::SDL2 PkgConfig::Slirp PkgConfig::LibArchive)
+target_link_libraries(melonDS PRIVATE ${QT_LINK_LIBS} ${CMAKE_DL_LIBS})
if (UNIX)
option(PORTABLE "Make a portable build that looks for its configuration in the current directory" OFF)
- target_link_libraries(melonDS ${QT_LINK_LIBS})
- if (CMAKE_SYSTEM_NAME STREQUAL "Linux")
- target_link_libraries(melonDS dl)
- endif()
elseif (WIN32)
option(PORTABLE "Make a portable build that looks for its configuration in the current directory" ON)
+
configure_file("${CMAKE_SOURCE_DIR}/res/melon.rc.in" "${CMAKE_SOURCE_DIR}/melon.rc")
target_sources(melonDS PUBLIC "${CMAKE_SOURCE_DIR}/melon.rc")
- target_link_libraries(melonDS comctl32 d2d1 dwrite uxtheme ws2_32 iphlpapi gdi32)
- if (BUILD_STATIC)
- target_link_libraries(melonDS imm32 winmm version setupapi -static z zstd ${QT_LINK_LIBS})
- else()
- target_link_libraries(melonDS ${QT_LINK_LIBS})
- endif()
+ target_link_libraries(melonDS PRIVATE ws2_32 iphlpapi)
+ set_target_properties(melonDS PROPERTIES LINK_FLAGS_DEBUG "-mconsole")
endif()
if (PORTABLE)
- add_definitions(-DPORTABLE)
+ target_compile_definitions(melonDS PRIVATE PORTABLE)
endif()
if (APPLE)
+ target_sources(melonDS PRIVATE sem_timedwait.cpp)
+
# Copy icon into the bundle
set(RESOURCE_FILES "${CMAKE_SOURCE_DIR}/res/melon.icns")
target_sources(melonDS PUBLIC "${RESOURCE_FILES}")
@@ -156,7 +128,7 @@ if (APPLE)
MACOSX_BUNDLE_INFO_PLIST ${CMAKE_SOURCE_DIR}/res/melon.plist.in
OUTPUT_NAME melonDS
RESOURCE "${RESOURCE_FILES}")
-
+
option(MACOS_BUNDLE_LIBS "Bundle libraries with the app on macOS" OFF)
option(MACOS_BUILD_DMG "Build DMG image of the macOS application bundle" OFF)
@@ -174,8 +146,8 @@ endif()
if (UNIX AND NOT APPLE)
foreach(SIZE 16 32 48 64 128 256)
install(FILES ${CMAKE_SOURCE_DIR}/res/icon/melon_${SIZE}x${SIZE}.png
- DESTINATION ${CMAKE_INSTALL_PREFIX}/share/icons/hicolor/${SIZE}x${SIZE}/apps
- RENAME net.kuribo64.melonDS.png)
+ DESTINATION ${CMAKE_INSTALL_PREFIX}/share/icons/hicolor/${SIZE}x${SIZE}/apps
+ RENAME net.kuribo64.melonDS.png)
endforeach()
install(FILES ${CMAKE_SOURCE_DIR}/res/net.kuribo64.melonDS.desktop DESTINATION ${CMAKE_INSTALL_PREFIX}/share/applications)
diff --git a/src/frontend/qt_sdl/Config.cpp b/src/frontend/qt_sdl/Config.cpp
index ac04b5da..7f11dd9f 100644
--- a/src/frontend/qt_sdl/Config.cpp
+++ b/src/frontend/qt_sdl/Config.cpp
@@ -106,9 +106,10 @@ int FirmwareBirthdayDay;
int FirmwareFavouriteColour;
std::string FirmwareMessage;
std::string FirmwareMAC;
-bool RandomizeMAC;
-bool SocketBindAnyAddr;
+int MPAudioMode;
+int MPRecvTimeout;
+
std::string LANDevice;
bool DirectLAN;
@@ -143,211 +144,206 @@ CameraConfig Camera[2];
const char* kConfigFile = "melonDS.ini";
+const char* kUniqueConfigFile = "melonDS.%d.ini";
ConfigEntry ConfigFile[] =
{
- {"Key_A", 0, &KeyMapping[0], -1},
- {"Key_B", 0, &KeyMapping[1], -1},
- {"Key_Select", 0, &KeyMapping[2], -1},
- {"Key_Start", 0, &KeyMapping[3], -1},
- {"Key_Right", 0, &KeyMapping[4], -1},
- {"Key_Left", 0, &KeyMapping[5], -1},
- {"Key_Up", 0, &KeyMapping[6], -1},
- {"Key_Down", 0, &KeyMapping[7], -1},
- {"Key_R", 0, &KeyMapping[8], -1},
- {"Key_L", 0, &KeyMapping[9], -1},
- {"Key_X", 0, &KeyMapping[10], -1},
- {"Key_Y", 0, &KeyMapping[11], -1},
+ {"Key_A", 0, &KeyMapping[0], -1, true},
+ {"Key_B", 0, &KeyMapping[1], -1, true},
+ {"Key_Select", 0, &KeyMapping[2], -1, true},
+ {"Key_Start", 0, &KeyMapping[3], -1, true},
+ {"Key_Right", 0, &KeyMapping[4], -1, true},
+ {"Key_Left", 0, &KeyMapping[5], -1, true},
+ {"Key_Up", 0, &KeyMapping[6], -1, true},
+ {"Key_Down", 0, &KeyMapping[7], -1, true},
+ {"Key_R", 0, &KeyMapping[8], -1, true},
+ {"Key_L", 0, &KeyMapping[9], -1, true},
+ {"Key_X", 0, &KeyMapping[10], -1, true},
+ {"Key_Y", 0, &KeyMapping[11], -1, true},
- {"Joy_A", 0, &JoyMapping[0], -1},
- {"Joy_B", 0, &JoyMapping[1], -1},
- {"Joy_Select", 0, &JoyMapping[2], -1},
- {"Joy_Start", 0, &JoyMapping[3], -1},
- {"Joy_Right", 0, &JoyMapping[4], -1},
- {"Joy_Left", 0, &JoyMapping[5], -1},
- {"Joy_Up", 0, &JoyMapping[6], -1},
- {"Joy_Down", 0, &JoyMapping[7], -1},
- {"Joy_R", 0, &JoyMapping[8], -1},
- {"Joy_L", 0, &JoyMapping[9], -1},
- {"Joy_X", 0, &JoyMapping[10], -1},
- {"Joy_Y", 0, &JoyMapping[11], -1},
+ {"Joy_A", 0, &JoyMapping[0], -1, true},
+ {"Joy_B", 0, &JoyMapping[1], -1, true},
+ {"Joy_Select", 0, &JoyMapping[2], -1, true},
+ {"Joy_Start", 0, &JoyMapping[3], -1, true},
+ {"Joy_Right", 0, &JoyMapping[4], -1, true},
+ {"Joy_Left", 0, &JoyMapping[5], -1, true},
+ {"Joy_Up", 0, &JoyMapping[6], -1, true},
+ {"Joy_Down", 0, &JoyMapping[7], -1, true},
+ {"Joy_R", 0, &JoyMapping[8], -1, true},
+ {"Joy_L", 0, &JoyMapping[9], -1, true},
+ {"Joy_X", 0, &JoyMapping[10], -1, true},
+ {"Joy_Y", 0, &JoyMapping[11], -1, true},
- {"HKKey_Lid", 0, &HKKeyMapping[HK_Lid], -1},
- {"HKKey_Mic", 0, &HKKeyMapping[HK_Mic], -1},
- {"HKKey_Pause", 0, &HKKeyMapping[HK_Pause], -1},
- {"HKKey_Reset", 0, &HKKeyMapping[HK_Reset], -1},
- {"HKKey_FastForward", 0, &HKKeyMapping[HK_FastForward], -1},
- {"HKKey_FastForwardToggle", 0, &HKKeyMapping[HK_FastForwardToggle], -1},
- {"HKKey_FullscreenToggle", 0, &HKKeyMapping[HK_FullscreenToggle], -1},
- {"HKKey_SwapScreens", 0, &HKKeyMapping[HK_SwapScreens], -1},
- {"HKKey_SolarSensorDecrease", 0, &HKKeyMapping[HK_SolarSensorDecrease], -1},
- {"HKKey_SolarSensorIncrease", 0, &HKKeyMapping[HK_SolarSensorIncrease], -1},
- {"HKKey_FrameStep", 0, &HKKeyMapping[HK_FrameStep], -1},
+ {"HKKey_Lid", 0, &HKKeyMapping[HK_Lid], -1, true},
+ {"HKKey_Mic", 0, &HKKeyMapping[HK_Mic], -1, true},
+ {"HKKey_Pause", 0, &HKKeyMapping[HK_Pause], -1, true},
+ {"HKKey_Reset", 0, &HKKeyMapping[HK_Reset], -1, true},
+ {"HKKey_FastForward", 0, &HKKeyMapping[HK_FastForward], -1, true},
+ {"HKKey_FastForwardToggle", 0, &HKKeyMapping[HK_FastForwardToggle], -1, true},
+ {"HKKey_FullscreenToggle", 0, &HKKeyMapping[HK_FullscreenToggle], -1, true},
+ {"HKKey_SwapScreens", 0, &HKKeyMapping[HK_SwapScreens], -1, true},
+ {"HKKey_SolarSensorDecrease", 0, &HKKeyMapping[HK_SolarSensorDecrease], -1, true},
+ {"HKKey_SolarSensorIncrease", 0, &HKKeyMapping[HK_SolarSensorIncrease], -1, true},
+ {"HKKey_FrameStep", 0, &HKKeyMapping[HK_FrameStep], -1, true},
- {"HKJoy_Lid", 0, &HKJoyMapping[HK_Lid], -1},
- {"HKJoy_Mic", 0, &HKJoyMapping[HK_Mic], -1},
- {"HKJoy_Pause", 0, &HKJoyMapping[HK_Pause], -1},
- {"HKJoy_Reset", 0, &HKJoyMapping[HK_Reset], -1},
- {"HKJoy_FastForward", 0, &HKJoyMapping[HK_FastForward], -1},
- {"HKJoy_FastForwardToggle", 0, &HKJoyMapping[HK_FastForwardToggle], -1},
- {"HKJoy_FullscreenToggle", 0, &HKJoyMapping[HK_FullscreenToggle], -1},
- {"HKJoy_SwapScreens", 0, &HKJoyMapping[HK_SwapScreens], -1},
- {"HKJoy_SolarSensorDecrease", 0, &HKJoyMapping[HK_SolarSensorDecrease], -1},
- {"HKJoy_SolarSensorIncrease", 0, &HKJoyMapping[HK_SolarSensorIncrease], -1},
- {"HKJoy_FrameStep", 0, &HKJoyMapping[HK_FrameStep], -1},
+ {"HKJoy_Lid", 0, &HKJoyMapping[HK_Lid], -1, true},
+ {"HKJoy_Mic", 0, &HKJoyMapping[HK_Mic], -1, true},
+ {"HKJoy_Pause", 0, &HKJoyMapping[HK_Pause], -1, true},
+ {"HKJoy_Reset", 0, &HKJoyMapping[HK_Reset], -1, true},
+ {"HKJoy_FastForward", 0, &HKJoyMapping[HK_FastForward], -1, true},
+ {"HKJoy_FastForwardToggle", 0, &HKJoyMapping[HK_FastForwardToggle], -1, true},
+ {"HKJoy_FullscreenToggle", 0, &HKJoyMapping[HK_FullscreenToggle], -1, true},
+ {"HKJoy_SwapScreens", 0, &HKJoyMapping[HK_SwapScreens], -1, true},
+ {"HKJoy_SolarSensorDecrease", 0, &HKJoyMapping[HK_SolarSensorDecrease], -1, true},
+ {"HKJoy_SolarSensorIncrease", 0, &HKJoyMapping[HK_SolarSensorIncrease], -1, true},
+ {"HKJoy_FrameStep", 0, &HKJoyMapping[HK_FrameStep], -1, true},
- {"JoystickID", 0, &JoystickID, 0},
+ {"JoystickID", 0, &JoystickID, 0, true},
- {"WindowWidth", 0, &WindowWidth, 256},
- {"WindowHeight", 0, &WindowHeight, 384},
- {"WindowMax", 1, &WindowMaximized, false},
+ {"WindowWidth", 0, &WindowWidth, 256, true},
+ {"WindowHeight", 0, &WindowHeight, 384, true},
+ {"WindowMax", 1, &WindowMaximized, false, true},
- {"ScreenRotation", 0, &ScreenRotation, 0},
- {"ScreenGap", 0, &ScreenGap, 0},
- {"ScreenLayout", 0, &ScreenLayout, 0},
- {"ScreenSwap", 1, &ScreenSwap, false},
- {"ScreenSizing", 0, &ScreenSizing, 0},
- {"IntegerScaling", 1, &IntegerScaling, false},
- {"ScreenAspectTop",0, &ScreenAspectTop,0},
- {"ScreenAspectBot",0, &ScreenAspectBot,0},
- {"ScreenFilter", 1, &ScreenFilter, true},
+ {"ScreenRotation", 0, &ScreenRotation, 0, true},
+ {"ScreenGap", 0, &ScreenGap, 0, true},
+ {"ScreenLayout", 0, &ScreenLayout, 0, true},
+ {"ScreenSwap", 1, &ScreenSwap, false, true},
+ {"ScreenSizing", 0, &ScreenSizing, 0, true},
+ {"IntegerScaling", 1, &IntegerScaling, false, true},
+ {"ScreenAspectTop",0, &ScreenAspectTop,0, true},
+ {"ScreenAspectBot",0, &ScreenAspectBot,0, true},
+ {"ScreenFilter", 1, &ScreenFilter, true, true},
- {"ScreenUseGL", 1, &ScreenUseGL, false},
- {"ScreenVSync", 1, &ScreenVSync, false},
- {"ScreenVSyncInterval", 0, &ScreenVSyncInterval, 1},
+ {"ScreenUseGL", 1, &ScreenUseGL, false, false},
+ {"ScreenVSync", 1, &ScreenVSync, false, false},
+ {"ScreenVSyncInterval", 0, &ScreenVSyncInterval, 1, false},
- {"3DRenderer", 0, &_3DRenderer, 0},
- {"Threaded3D", 1, &Threaded3D, true},
+ {"3DRenderer", 0, &_3DRenderer, 0, false},
+ {"Threaded3D", 1, &Threaded3D, true, false},
- {"GL_ScaleFactor", 0, &GL_ScaleFactor, 1},
- {"GL_BetterPolygons", 1, &GL_BetterPolygons, false},
+ {"GL_ScaleFactor", 0, &GL_ScaleFactor, 1, false},
+ {"GL_BetterPolygons", 1, &GL_BetterPolygons, false, false},
- {"LimitFPS", 1, &LimitFPS, true},
+ {"LimitFPS", 1, &LimitFPS, true, false},
{"AudioSync", 1, &AudioSync, false},
- {"ShowOSD", 1, &ShowOSD, true},
+ {"ShowOSD", 1, &ShowOSD, true, false},
- {"ConsoleType", 0, &ConsoleType, 0},
- {"DirectBoot", 1, &DirectBoot, true},
+ {"ConsoleType", 0, &ConsoleType, 0, false},
+ {"DirectBoot", 1, &DirectBoot, true, false},
#ifdef JIT_ENABLED
- {"JIT_Enable", 1, &JIT_Enable, false},
- {"JIT_MaxBlockSize", 0, &JIT_MaxBlockSize, 32},
- {"JIT_BranchOptimisations", 1, &JIT_BranchOptimisations, true},
- {"JIT_LiteralOptimisations", 1, &JIT_LiteralOptimisations, true},
+ {"JIT_Enable", 1, &JIT_Enable, false, false},
+ {"JIT_MaxBlockSize", 0, &JIT_MaxBlockSize, 32, false},
+ {"JIT_BranchOptimisations", 1, &JIT_BranchOptimisations, true, false},
+ {"JIT_LiteralOptimisations", 1, &JIT_LiteralOptimisations, true, false},
#ifdef __APPLE__
- {"JIT_FastMemory", 1, &JIT_FastMemory, false},
+ {"JIT_FastMemory", 1, &JIT_FastMemory, false, false},
#else
- {"JIT_FastMemory", 1, &JIT_FastMemory, true},
+ {"JIT_FastMemory", 1, &JIT_FastMemory, true, false},
#endif
#endif
- {"ExternalBIOSEnable", 1, &ExternalBIOSEnable, false},
+ {"ExternalBIOSEnable", 1, &ExternalBIOSEnable, false, false},
- {"BIOS9Path", 2, &BIOS9Path, (std::string)""},
- {"BIOS7Path", 2, &BIOS7Path, (std::string)""},
- {"FirmwarePath", 2, &FirmwarePath, (std::string)""},
+ {"BIOS9Path", 2, &BIOS9Path, (std::string)"", false},
+ {"BIOS7Path", 2, &BIOS7Path, (std::string)"", false},
+ {"FirmwarePath", 2, &FirmwarePath, (std::string)"", false},
- {"DSiBIOS9Path", 2, &DSiBIOS9Path, (std::string)""},
- {"DSiBIOS7Path", 2, &DSiBIOS7Path, (std::string)""},
- {"DSiFirmwarePath", 2, &DSiFirmwarePath, (std::string)""},
- {"DSiNANDPath", 2, &DSiNANDPath, (std::string)""},
+ {"DSiBIOS9Path", 2, &DSiBIOS9Path, (std::string)"", false},
+ {"DSiBIOS7Path", 2, &DSiBIOS7Path, (std::string)"", false},
+ {"DSiFirmwarePath", 2, &DSiFirmwarePath, (std::string)"", false},
+ {"DSiNANDPath", 2, &DSiNANDPath, (std::string)"", false},
- {"DLDIEnable", 1, &DLDIEnable, false},
- {"DLDISDPath", 2, &DLDISDPath, (std::string)"dldi.bin"},
- {"DLDISize", 0, &DLDISize, 0},
- {"DLDIReadOnly", 1, &DLDIReadOnly, false},
- {"DLDIFolderSync", 1, &DLDIFolderSync, false},
- {"DLDIFolderPath", 2, &DLDIFolderPath, (std::string)""},
+ {"DLDIEnable", 1, &DLDIEnable, false, false},
+ {"DLDISDPath", 2, &DLDISDPath, (std::string)"dldi.bin", false},
+ {"DLDISize", 0, &DLDISize, 0, false},
+ {"DLDIReadOnly", 1, &DLDIReadOnly, false, false},
+ {"DLDIFolderSync", 1, &DLDIFolderSync, false, false},
+ {"DLDIFolderPath", 2, &DLDIFolderPath, (std::string)"", false},
- {"DSiSDEnable", 1, &DSiSDEnable, false},
- {"DSiSDPath", 2, &DSiSDPath, (std::string)"dsisd.bin"},
- {"DSiSDSize", 0, &DSiSDSize, 0},
- {"DSiSDReadOnly", 1, &DSiSDReadOnly, false},
- {"DSiSDFolderSync", 1, &DSiSDFolderSync, false},
- {"DSiSDFolderPath", 2, &DSiSDFolderPath, (std::string)""},
+ {"DSiSDEnable", 1, &DSiSDEnable, false, false},
+ {"DSiSDPath", 2, &DSiSDPath, (std::string)"dsisd.bin", false},
+ {"DSiSDSize", 0, &DSiSDSize, 0, false},
+ {"DSiSDReadOnly", 1, &DSiSDReadOnly, false, false},
+ {"DSiSDFolderSync", 1, &DSiSDFolderSync, false, false},
+ {"DSiSDFolderPath", 2, &DSiSDFolderPath, (std::string)"", false},
- {"FirmwareOverrideSettings", 1, &FirmwareOverrideSettings, false},
- {"FirmwareUsername", 2, &FirmwareUsername, (std::string)"melonDS"},
- {"FirmwareLanguage", 0, &FirmwareLanguage, 1},
- {"FirmwareBirthdayMonth", 0, &FirmwareBirthdayMonth, 1},
- {"FirmwareBirthdayDay", 0, &FirmwareBirthdayDay, 1},
- {"FirmwareFavouriteColour", 0, &FirmwareFavouriteColour, 0},
- {"FirmwareMessage", 2, &FirmwareMessage, (std::string)""},
- {"FirmwareMAC", 2, &FirmwareMAC, (std::string)""},
- {"RandomizeMAC", 1, &RandomizeMAC, false},
+ {"FirmwareOverrideSettings", 1, &FirmwareOverrideSettings, false, true},
+ {"FirmwareUsername", 2, &FirmwareUsername, (std::string)"melonDS", true},
+ {"FirmwareLanguage", 0, &FirmwareLanguage, 1, true},
+ {"FirmwareBirthdayMonth", 0, &FirmwareBirthdayMonth, 1, true},
+ {"FirmwareBirthdayDay", 0, &FirmwareBirthdayDay, 1, true},
+ {"FirmwareFavouriteColour", 0, &FirmwareFavouriteColour, 0, true},
+ {"FirmwareMessage", 2, &FirmwareMessage, (std::string)"", true},
+ {"FirmwareMAC", 2, &FirmwareMAC, (std::string)"", true},
- {"SockBindAnyAddr", 1, &SocketBindAnyAddr, false},
- {"LANDevice", 2, &LANDevice, (std::string)""},
- {"DirectLAN", 1, &DirectLAN, false},
+ {"MPAudioMode", 0, &MPAudioMode, 1, false},
+ {"MPRecvTimeout", 0, &MPRecvTimeout, 25, false},
- {"SavStaRelocSRAM", 1, &SavestateRelocSRAM, false},
+ {"LANDevice", 2, &LANDevice, (std::string)"", false},
+ {"DirectLAN", 1, &DirectLAN, false, false},
- {"AudioInterp", 0, &AudioInterp, 0},
- {"AudioBitrate", 0, &AudioBitrate, 0},
- {"AudioVolume", 0, &AudioVolume, 256},
- {"MicInputType", 0, &MicInputType, 1},
- {"MicWavPath", 2, &MicWavPath, (std::string)""},
+ {"SavStaRelocSRAM", 1, &SavestateRelocSRAM, false, false},
- {"LastROMFolder", 2, &LastROMFolder, (std::string)""},
+ {"AudioInterp", 0, &AudioInterp, 0, false},
+ {"AudioBitrate", 0, &AudioBitrate, 0, false},
+ {"AudioVolume", 0, &AudioVolume, 256, true},
+ {"MicInputType", 0, &MicInputType, 1, false},
+ {"MicWavPath", 2, &MicWavPath, (std::string)"", false},
- {"RecentROM_0", 2, &RecentROMList[0], (std::string)""},
- {"RecentROM_1", 2, &RecentROMList[1], (std::string)""},
- {"RecentROM_2", 2, &RecentROMList[2], (std::string)""},
- {"RecentROM_3", 2, &RecentROMList[3], (std::string)""},
- {"RecentROM_4", 2, &RecentROMList[4], (std::string)""},
- {"RecentROM_5", 2, &RecentROMList[5], (std::string)""},
- {"RecentROM_6", 2, &RecentROMList[6], (std::string)""},
- {"RecentROM_7", 2, &RecentROMList[7], (std::string)""},
- {"RecentROM_8", 2, &RecentROMList[8], (std::string)""},
- {"RecentROM_9", 2, &RecentROMList[9], (std::string)""},
+ {"LastROMFolder", 2, &LastROMFolder, (std::string)"", true},
- {"SaveFilePath", 2, &SaveFilePath, (std::string)""},
- {"SavestatePath", 2, &SavestatePath, (std::string)""},
- {"CheatFilePath", 2, &CheatFilePath, (std::string)""},
+ {"RecentROM_0", 2, &RecentROMList[0], (std::string)"", true},
+ {"RecentROM_1", 2, &RecentROMList[1], (std::string)"", true},
+ {"RecentROM_2", 2, &RecentROMList[2], (std::string)"", true},
+ {"RecentROM_3", 2, &RecentROMList[3], (std::string)"", true},
+ {"RecentROM_4", 2, &RecentROMList[4], (std::string)"", true},
+ {"RecentROM_5", 2, &RecentROMList[5], (std::string)"", true},
+ {"RecentROM_6", 2, &RecentROMList[6], (std::string)"", true},
+ {"RecentROM_7", 2, &RecentROMList[7], (std::string)"", true},
+ {"RecentROM_8", 2, &RecentROMList[8], (std::string)"", true},
+ {"RecentROM_9", 2, &RecentROMList[9], (std::string)"", true},
- {"EnableCheats", 1, &EnableCheats, false},
+ {"SaveFilePath", 2, &SaveFilePath, (std::string)"", true},
+ {"SavestatePath", 2, &SavestatePath, (std::string)"", true},
+ {"CheatFilePath", 2, &CheatFilePath, (std::string)"", true},
- {"MouseHide", 1, &MouseHide, false},
- {"MouseHideSeconds", 0, &MouseHideSeconds, 5},
- {"PauseLostFocus", 1, &PauseLostFocus, false},
+ {"EnableCheats", 1, &EnableCheats, false, true},
- {"DSBatteryLevelOkay", 1, &DSBatteryLevelOkay, true},
- {"DSiBatteryLevel", 0, &DSiBatteryLevel, 0xF},
- {"DSiBatteryCharging", 1, &DSiBatteryCharging, true},
+ {"MouseHide", 1, &MouseHide, false, false},
+ {"MouseHideSeconds", 0, &MouseHideSeconds, 5, false},
+ {"PauseLostFocus", 1, &PauseLostFocus, false, false},
+ {"DSBatteryLevelOkay", 1, &DSBatteryLevelOkay, true, true},
+ {"DSiBatteryLevel", 0, &DSiBatteryLevel, 0xF, true},
+ {"DSiBatteryCharging", 1, &DSiBatteryCharging, true, true},
// TODO!!
// we need a more elegant way to deal with this
- {"Camera0_InputType", 0, &Camera[0].InputType, 0},
- {"Camera0_ImagePath", 2, &Camera[0].ImagePath, (std::string)""},
- {"Camera0_CamDeviceName", 2, &Camera[0].CamDeviceName, (std::string)""},
- {"Camera0_XFlip", 1, &Camera[0].XFlip, false},
- {"Camera1_InputType", 0, &Camera[1].InputType, 0},
- {"Camera1_ImagePath", 2, &Camera[1].ImagePath, (std::string)""},
- {"Camera1_CamDeviceName", 2, &Camera[1].CamDeviceName, (std::string)""},
- {"Camera1_XFlip", 1, &Camera[1].XFlip, false},
+ {"Camera0_InputType", 0, &Camera[0].InputType, 0, false},
+ {"Camera0_ImagePath", 2, &Camera[0].ImagePath, (std::string)"", false},
+ {"Camera0_CamDeviceName", 2, &Camera[0].CamDeviceName, (std::string)"", false},
+ {"Camera0_XFlip", 1, &Camera[0].XFlip, false, false},
+ {"Camera1_InputType", 0, &Camera[1].InputType, 0, false},
+ {"Camera1_ImagePath", 2, &Camera[1].ImagePath, (std::string)"", false},
+ {"Camera1_CamDeviceName", 2, &Camera[1].CamDeviceName, (std::string)"", false},
+ {"Camera1_XFlip", 1, &Camera[1].XFlip, false, false},
- {"", -1, nullptr, 0}
+ {"", -1, nullptr, 0, false}
};
-void Load()
+void LoadFile(int inst)
{
- ConfigEntry* entry = &ConfigFile[0];
- for (;;)
+ FILE* f;
+ if (inst > 0)
{
- if (!entry->Value) break;
-
- switch (entry->Type)
- {
- case 0: *(int*)entry->Value = std::get(entry->Default); break;
- case 1: *(bool*)entry->Value = std::get(entry->Default); break;
- case 2: *(std::string*)entry->Value = std::get(entry->Default); break;
- }
-
- entry++;
+ char name[100] = {0};
+ snprintf(name, 99, kUniqueConfigFile, inst+1);
+ f = Platform::OpenLocalFile(name, "r");
}
+ else
+ f = Platform::OpenLocalFile(kConfigFile, "r");
- FILE* f = Platform::OpenLocalFile(kConfigFile, "r");
if (!f) return;
char linebuf[1024];
@@ -362,13 +358,13 @@ void Load()
entryname[31] = '\0';
if (ret < 2) continue;
- ConfigEntry* entry = &ConfigFile[0];
- for (;;)
+ for (ConfigEntry* entry = &ConfigFile[0]; entry->Value; entry++)
{
- if (!entry->Value) break;
-
if (!strncmp(entry->Name, entryname, 32))
{
+ if ((inst > 0) && (!entry->InstanceUnique))
+ break;
+
switch (entry->Type)
{
case 0: *(int*)entry->Value = strtol(entryval, NULL, 10); break;
@@ -378,23 +374,52 @@ void Load()
break;
}
-
- entry++;
}
}
fclose(f);
}
+void Load()
+{
+
+ for (ConfigEntry* entry = &ConfigFile[0]; entry->Value; entry++)
+ {
+ switch (entry->Type)
+ {
+ case 0: *(int*)entry->Value = std::get(entry->Default); break;
+ case 1: *(bool*)entry->Value = std::get(entry->Default); break;
+ case 2: *(std::string*)entry->Value = std::get(entry->Default); break;
+ }
+ }
+
+ LoadFile(0);
+
+ int inst = Platform::InstanceID();
+ if (inst > 0)
+ LoadFile(inst);
+}
+
void Save()
{
- FILE* f = Platform::OpenLocalFile(kConfigFile, "w");
+ int inst = Platform::InstanceID();
+
+ FILE* f;
+ if (inst > 0)
+ {
+ char name[100] = {0};
+ snprintf(name, 99, kUniqueConfigFile, inst+1);
+ f = Platform::OpenLocalFile(name, "w");
+ }
+ else
+ f = Platform::OpenLocalFile(kConfigFile, "w");
+
if (!f) return;
- ConfigEntry* entry = &ConfigFile[0];
- for (;;)
+ for (ConfigEntry* entry = &ConfigFile[0]; entry->Value; entry++)
{
- if (!entry->Value) break;
+ if ((inst > 0) && (!entry->InstanceUnique))
+ continue;
switch (entry->Type)
{
@@ -402,8 +427,6 @@ void Save()
case 1: fprintf(f, "%s=%d\r\n", entry->Name, *(bool*)entry->Value ? 1:0); break;
case 2: fprintf(f, "%s=%s\r\n", entry->Name, (*(std::string*)entry->Value).c_str()); break;
}
-
- entry++;
}
fclose(f);
diff --git a/src/frontend/qt_sdl/Config.h b/src/frontend/qt_sdl/Config.h
index 7a0b0ca0..6ccae5f4 100644
--- a/src/frontend/qt_sdl/Config.h
+++ b/src/frontend/qt_sdl/Config.h
@@ -58,6 +58,7 @@ struct ConfigEntry
int Type; // 0=int 1=bool 2=string
void* Value; // pointer to the value variable
std::variant Default;
+ bool InstanceUnique; // whether the setting can exist individually for each instance in multiplayer
};
struct CameraConfig
@@ -149,9 +150,10 @@ extern int FirmwareBirthdayDay;
extern int FirmwareFavouriteColour;
extern std::string FirmwareMessage;
extern std::string FirmwareMAC;
-extern bool RandomizeMAC;
-extern bool SocketBindAnyAddr;
+extern int MPAudioMode;
+extern int MPRecvTimeout;
+
extern std::string LANDevice;
extern bool DirectLAN;
diff --git a/src/frontend/qt_sdl/FirmwareSettingsDialog.cpp b/src/frontend/qt_sdl/FirmwareSettingsDialog.cpp
index 754cc8a5..ffca5676 100644
--- a/src/frontend/qt_sdl/FirmwareSettingsDialog.cpp
+++ b/src/frontend/qt_sdl/FirmwareSettingsDialog.cpp
@@ -18,6 +18,7 @@
#include
+#include "Platform.h"
#include "Config.h"
#include "FirmwareSettingsDialog.h"
@@ -64,10 +65,14 @@ FirmwareSettingsDialog::FirmwareSettingsDialog(QWidget* parent) : QDialog(parent
ui->overrideFirmwareBox->setChecked(Config::FirmwareOverrideSettings);
ui->txtMAC->setText(QString::fromStdString(Config::FirmwareMAC));
- ui->cbRandomizeMAC->setChecked(Config::RandomizeMAC);
on_overrideFirmwareBox_toggled();
- on_cbRandomizeMAC_toggled();
+
+ int inst = Platform::InstanceID();
+ if (inst > 0)
+ ui->lblInstanceNum->setText(QString("Configuring settings for instance %1").arg(inst+1));
+ else
+ ui->lblInstanceNum->hide();
}
FirmwareSettingsDialog::~FirmwareSettingsDialog()
@@ -135,7 +140,6 @@ void FirmwareSettingsDialog::done(int r)
std::string newMessage = ui->messageEdit->text().toStdString();
std::string newMAC = ui->txtMAC->text().toStdString();
- bool newRandomizeMAC = ui->cbRandomizeMAC->isChecked();
if ( newOverride != Config::FirmwareOverrideSettings
|| newName != Config::FirmwareUsername
@@ -144,8 +148,7 @@ void FirmwareSettingsDialog::done(int r)
|| newBirthdayDay != Config::FirmwareBirthdayDay
|| newBirthdayMonth != Config::FirmwareBirthdayMonth
|| newMessage != Config::FirmwareMessage
- || newMAC != Config::FirmwareMAC
- || newRandomizeMAC != Config::RandomizeMAC)
+ || newMAC != Config::FirmwareMAC)
{
if (RunningSomething
&& QMessageBox::warning(this, "Reset necessary to apply changes",
@@ -163,7 +166,6 @@ void FirmwareSettingsDialog::done(int r)
Config::FirmwareMessage = newMessage;
Config::FirmwareMAC = newMAC;
- Config::RandomizeMAC = newRandomizeMAC;
Config::Save();
@@ -210,9 +212,3 @@ void FirmwareSettingsDialog::on_overrideFirmwareBox_toggled()
ui->grpUserSettings->setDisabled(disable);
ui->grpWifiSettings->setDisabled(disable);
}
-
-void FirmwareSettingsDialog::on_cbRandomizeMAC_toggled()
-{
- bool disable = ui->cbRandomizeMAC->isChecked();
- ui->txtMAC->setDisabled(disable);
-}
diff --git a/src/frontend/qt_sdl/FirmwareSettingsDialog.h b/src/frontend/qt_sdl/FirmwareSettingsDialog.h
index 97bf5c07..b3695e2f 100644
--- a/src/frontend/qt_sdl/FirmwareSettingsDialog.h
+++ b/src/frontend/qt_sdl/FirmwareSettingsDialog.h
@@ -124,7 +124,6 @@ private slots:
void on_cbxBirthdayMonth_currentIndexChanged(int idx);
void on_overrideFirmwareBox_toggled();
- void on_cbRandomizeMAC_toggled();
private:
bool verifyMAC();
diff --git a/src/frontend/qt_sdl/FirmwareSettingsDialog.ui b/src/frontend/qt_sdl/FirmwareSettingsDialog.ui
index a97689cf..37146296 100644
--- a/src/frontend/qt_sdl/FirmwareSettingsDialog.ui
+++ b/src/frontend/qt_sdl/FirmwareSettingsDialog.ui
@@ -7,7 +7,7 @@
0
0
511
- 342
+ 357
@@ -23,6 +23,13 @@
QLayout::SetFixedSize
+ -
+
+
+ Configuring settings for instance X
+
+
+
-
@@ -144,9 +151,9 @@
-
-
+
- Randomize
+ (leave empty to use default MAC)
diff --git a/src/frontend/qt_sdl/InputConfig/InputConfigDialog.cpp b/src/frontend/qt_sdl/InputConfig/InputConfigDialog.cpp
index 697e983a..92a01867 100644
--- a/src/frontend/qt_sdl/InputConfig/InputConfigDialog.cpp
+++ b/src/frontend/qt_sdl/InputConfig/InputConfigDialog.cpp
@@ -24,6 +24,7 @@
#include
#include "types.h"
+#include "Platform.h"
#include "Config.h"
#include "MapButton.h"
@@ -123,6 +124,12 @@ InputConfigDialog::InputConfigDialog(QWidget* parent) : QDialog(parent), ui(new
}
setupKeypadPage();
+
+ int inst = Platform::InstanceID();
+ if (inst > 0)
+ ui->lblInstanceNum->setText(QString("Configuring mappings for instance %1").arg(inst+1));
+ else
+ ui->lblInstanceNum->hide();
}
InputConfigDialog::~InputConfigDialog()
diff --git a/src/frontend/qt_sdl/InputConfig/InputConfigDialog.ui b/src/frontend/qt_sdl/InputConfig/InputConfigDialog.ui
index 15cb683d..0db61b15 100644
--- a/src/frontend/qt_sdl/InputConfig/InputConfigDialog.ui
+++ b/src/frontend/qt_sdl/InputConfig/InputConfigDialog.ui
@@ -7,7 +7,7 @@
0
0
770
- 719
+ 678
@@ -20,7 +20,7 @@
QLayout::SetFixedSize
- -
+
-
Qt::Horizontal
@@ -30,49 +30,7 @@
- -
-
-
- 0
-
-
- 0
-
-
- 0
-
-
- 0
-
-
-
-
-
-
- 0
- 0
-
-
-
- Joystick:
-
-
-
- -
-
-
-
- 0
- 0
-
-
-
- <html><head/><body><p>Selects which joystick will be used for joystick input, if any is present.</p></body></html>
-
-
-
-
-
- -
+
-
0
@@ -167,7 +125,7 @@
- 76
+ 70
0
@@ -258,7 +216,7 @@
- 76
+ 70
0
@@ -384,7 +342,7 @@
- 76
+ 70
0
@@ -464,7 +422,7 @@
- 76
+ 70
0
@@ -523,7 +481,7 @@
- 76
+ 70
0
@@ -615,7 +573,7 @@
- 76
+ 70
0
@@ -698,7 +656,7 @@
- 76
+ 70
0
@@ -757,7 +715,7 @@
- 76
+ 70
0
@@ -882,7 +840,7 @@
- 76
+ 70
0
@@ -962,7 +920,7 @@
- 76
+ 70
0
@@ -1021,7 +979,7 @@
- 76
+ 70
0
@@ -1113,7 +1071,7 @@
- 76
+ 70
0
@@ -1289,7 +1247,7 @@
- 76
+ 70
0
@@ -1441,7 +1399,7 @@
- 76
+ 70
0
@@ -1521,7 +1479,7 @@
- 76
+ 70
0
@@ -1580,7 +1538,7 @@
- 76
+ 70
0
@@ -1672,7 +1630,7 @@
- 76
+ 70
0
@@ -1814,7 +1772,7 @@
- 76
+ 70
0
@@ -1894,7 +1852,7 @@
- 76
+ 70
0
@@ -1953,7 +1911,7 @@
- 76
+ 70
0
@@ -2045,7 +2003,7 @@
- 76
+ 70
0
@@ -2128,7 +2086,7 @@
- 76
+ 70
0
@@ -2187,7 +2145,7 @@
- 76
+ 70
0
@@ -2251,7 +2209,7 @@
- 76
+ 70
0
@@ -2321,6 +2279,55 @@
+ -
+
+
+ 0
+
+
+ 0
+
+
+ 0
+
+
+ 0
+
+
-
+
+
+
+ 0
+ 0
+
+
+
+ Joystick:
+
+
+
+ -
+
+
+
+ 0
+ 0
+
+
+
+ <html><head/><body><p>Selects which joystick will be used for joystick input, if any is present.</p></body></html>
+
+
+
+
+
+ -
+
+
+ Configuring mappings for instance X
+
+
+
diff --git a/src/frontend/qt_sdl/InputConfig/MapButton.h b/src/frontend/qt_sdl/InputConfig/MapButton.h
index ca210f9f..afefed79 100644
--- a/src/frontend/qt_sdl/InputConfig/MapButton.h
+++ b/src/frontend/qt_sdl/InputConfig/MapButton.h
@@ -163,6 +163,7 @@ public:
setCheckable(true);
setText(mappingText());
+ setFocusPolicy(Qt::StrongFocus); //Fixes binding keys in macOS
connect(this, &JoyMapButton::clicked, this, &JoyMapButton::onClick);
@@ -245,19 +246,21 @@ protected:
Sint16 axisval = SDL_JoystickGetAxis(joy, i);
int diff = abs(axisval - axesRest[i]);
- if (axesRest[i] < -16384 && axisval >= 0)
+ if (diff >= 16384)
{
- *mapping = (oldmap & 0xFFFF) | 0x10000 | (2 << 20) | (i << 24);
- click();
- return;
- }
- else if (diff > 16384)
- {
- int axistype;
- if (axisval > 0) axistype = 0;
- else axistype = 1;
+ if (axesRest[i] < -16384) // Trigger
+ {
+ *mapping = (oldmap & 0xFFFF) | 0x10000 | (2 << 20) | (i << 24);
+ }
+ else // Analog stick
+ {
+ int axistype;
+ if (axisval > 0) axistype = 0;
+ else axistype = 1;
+
+ *mapping = (oldmap & 0xFFFF) | 0x10000 | (axistype << 20) | (i << 24);
+ }
- *mapping = (oldmap & 0xFFFF) | 0x10000 | (axistype << 20) | (i << 24);
click();
return;
}
@@ -352,4 +355,4 @@ private:
int axesRest[16];
};
-#endif // MAPBUTTON_H
\ No newline at end of file
+#endif // MAPBUTTON_H
diff --git a/src/frontend/qt_sdl/LAN_PCap.cpp b/src/frontend/qt_sdl/LAN_PCap.cpp
index 57223eb8..86c218f1 100644
--- a/src/frontend/qt_sdl/LAN_PCap.cpp
+++ b/src/frontend/qt_sdl/LAN_PCap.cpp
@@ -114,6 +114,12 @@ bool TryLoadPCap(void* lib)
bool Init(bool open_adapter)
{
+ PCapAdapter = NULL;
+ PacketLen = 0;
+ RXNum = 0;
+
+ NumAdapters = 0;
+
// TODO: how to deal with cases where an adapter is unplugged or changes config??
if (!PCapLib)
{
@@ -142,12 +148,6 @@ bool Init(bool open_adapter)
}
}
- PCapAdapter = NULL;
- PacketLen = 0;
- RXNum = 0;
-
- NumAdapters = 0;
-
char errbuf[PCAP_ERRBUF_SIZE];
int ret;
diff --git a/src/frontend/qt_sdl/LocalMP.cpp b/src/frontend/qt_sdl/LocalMP.cpp
new file mode 100644
index 00000000..fb7ef7ad
--- /dev/null
+++ b/src/frontend/qt_sdl/LocalMP.cpp
@@ -0,0 +1,634 @@
+/*
+ Copyright 2016-2022 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
+#include
+#include
+
+#ifdef __WIN32__
+ #include
+#else
+ #include
+ #include
+ #include
+ #ifdef __APPLE__
+ #include "sem_timedwait.h"
+ #endif
+#endif
+
+#include
+#include
+
+#include "Config.h"
+#include "LocalMP.h"
+
+
+namespace LocalMP
+{
+
+u32 MPUniqueID;
+u8 PacketBuffer[2048];
+
+struct MPQueueHeader
+{
+ u16 NumInstances;
+ u16 InstanceBitmask; // bitmask of all instances present
+ u16 ConnectedBitmask; // bitmask of which instances are ready to send/receive packets
+ u32 PacketWriteOffset;
+ u32 ReplyWriteOffset;
+ u16 MPHostInstanceID; // instance ID from which the last CMD frame was sent
+ u16 MPReplyBitmask; // bitmask of which clients replied in time
+};
+
+struct MPPacketHeader
+{
+ u32 Magic;
+ u32 SenderID;
+ u32 Type; // 0=regular 1=CMD 2=reply 3=ack
+ u32 Length;
+ u64 Timestamp;
+};
+
+struct MPSync
+{
+ u32 Magic;
+ u32 SenderID;
+ u16 ClientMask;
+ u16 Type;
+ u64 Timestamp;
+};
+
+QSharedMemory* MPQueue;
+int InstanceID;
+u32 PacketReadOffset;
+u32 ReplyReadOffset;
+
+const u32 kQueueSize = 0x20000;
+const u32 kMaxFrameSize = 0x800;
+const u32 kPacketStart = sizeof(MPQueueHeader);
+const u32 kReplyStart = kQueueSize / 2;
+const u32 kPacketEnd = kReplyStart;
+const u32 kReplyEnd = kQueueSize;
+
+int RecvTimeout;
+
+int LastHostID;
+
+
+// we need to come up with our own abstraction layer for named semaphores
+// because QSystemSemaphore doesn't support waiting with a timeout
+// and, as such, is unsuitable to our needs
+
+#ifdef __WIN32__
+
+bool SemInited[32];
+HANDLE SemPool[32];
+
+void SemPoolInit()
+{
+ for (int i = 0; i < 32; i++)
+ {
+ SemPool[i] = INVALID_HANDLE_VALUE;
+ SemInited[i] = false;
+ }
+}
+
+void SemDeinit(int num);
+
+void SemPoolDeinit()
+{
+ for (int i = 0; i < 32; i++)
+ SemDeinit(i);
+}
+
+bool SemInit(int num)
+{
+ if (SemInited[num])
+ return true;
+
+ char semname[64];
+ sprintf(semname, "Local\\melonNIFI_Sem%02d", num);
+
+ HANDLE sem = CreateSemaphore(nullptr, 0, 64, semname);
+ SemPool[num] = sem;
+ SemInited[num] = true;
+ return sem != INVALID_HANDLE_VALUE;
+}
+
+void SemDeinit(int num)
+{
+ if (SemPool[num] != INVALID_HANDLE_VALUE)
+ {
+ CloseHandle(SemPool[num]);
+ SemPool[num] = INVALID_HANDLE_VALUE;
+ }
+
+ SemInited[num] = false;
+}
+
+bool SemPost(int num)
+{
+ SemInit(num);
+ return ReleaseSemaphore(SemPool[num], 1, nullptr) != 0;
+}
+
+bool SemWait(int num, int timeout)
+{
+ return WaitForSingleObject(SemPool[num], timeout) == WAIT_OBJECT_0;
+}
+
+void SemReset(int num)
+{
+ while (WaitForSingleObject(SemPool[num], 0) == WAIT_OBJECT_0);
+}
+
+#else
+
+bool SemInited[32];
+sem_t* SemPool[32];
+
+void SemPoolInit()
+{
+ for (int i = 0; i < 32; i++)
+ {
+ SemPool[i] = SEM_FAILED;
+ SemInited[i] = false;
+ }
+}
+
+void SemDeinit(int num);
+
+void SemPoolDeinit()
+{
+ for (int i = 0; i < 32; i++)
+ SemDeinit(i);
+}
+
+bool SemInit(int num)
+{
+ if (SemInited[num])
+ return true;
+
+ char semname[64];
+ sprintf(semname, "/melonNIFI_Sem%02d", num);
+
+ sem_t* sem = sem_open(semname, O_CREAT, 0644, 0);
+ SemPool[num] = sem;
+ SemInited[num] = true;
+ return sem != SEM_FAILED;
+}
+
+void SemDeinit(int num)
+{
+ if (SemPool[num] != SEM_FAILED)
+ {
+ sem_close(SemPool[num]);
+ SemPool[num] = SEM_FAILED;
+ }
+
+ SemInited[num] = false;
+}
+
+bool SemPost(int num)
+{
+ SemInit(num);
+ return sem_post(SemPool[num]) == 0;
+}
+
+bool SemWait(int num, int timeout)
+{
+ if (!timeout)
+ return sem_trywait(SemPool[num]) == 0;
+
+ struct timespec ts;
+ clock_gettime(CLOCK_REALTIME, &ts);
+ ts.tv_nsec += timeout * 1000000;
+ long sec = ts.tv_nsec / 1000000000;
+ ts.tv_nsec -= sec * 1000000000;
+ ts.tv_sec += sec;
+
+ return sem_timedwait(SemPool[num], &ts) == 0;
+}
+
+void SemReset(int num)
+{
+ while (sem_trywait(SemPool[num]) == 0);
+}
+
+#endif
+
+
+bool Init()
+{
+ MPQueue = new QSharedMemory("melonNIFI");
+
+ if (!MPQueue->attach())
+ {
+ printf("MP sharedmem doesn't exist. creating\n");
+ if (!MPQueue->create(kQueueSize))
+ {
+ printf("MP sharedmem create failed :(\n");
+ return false;
+ }
+
+ MPQueue->lock();
+ memset(MPQueue->data(), 0, MPQueue->size());
+ MPQueueHeader* header = (MPQueueHeader*)MPQueue->data();
+ header->PacketWriteOffset = kPacketStart;
+ header->ReplyWriteOffset = kReplyStart;
+ MPQueue->unlock();
+ }
+
+ MPQueue->lock();
+ MPQueueHeader* header = (MPQueueHeader*)MPQueue->data();
+
+ u16 mask = header->InstanceBitmask;
+ for (int i = 0; i < 16; i++)
+ {
+ if (!(mask & (1<InstanceBitmask |= (1<ConnectedBitmask |= (1 << i);
+ break;
+ }
+ }
+ header->NumInstances++;
+
+ PacketReadOffset = header->PacketWriteOffset;
+ ReplyReadOffset = header->ReplyWriteOffset;
+
+ MPQueue->unlock();
+
+ // prepare semaphores
+ // semaphores 0-15: regular frames; semaphore I is posted when instance I needs to process a new frame
+ // semaphores 16-31: MP replies; semaphore I is posted when instance I needs to process a new MP reply
+
+ SemPoolInit();
+ SemInit(InstanceID);
+ SemInit(16+InstanceID);
+
+ LastHostID = -1;
+
+ printf("MP comm init OK, instance ID %d\n", InstanceID);
+
+ RecvTimeout = 25;
+
+ return true;
+}
+
+void DeInit()
+{
+ MPQueue->lock();
+ MPQueueHeader* header = (MPQueueHeader*)MPQueue->data();
+ header->ConnectedBitmask &= ~(1 << InstanceID);
+ header->InstanceBitmask &= ~(1 << InstanceID);
+ header->NumInstances--;
+ MPQueue->unlock();
+
+ SemPoolDeinit();
+
+ MPQueue->detach();
+ delete MPQueue;
+}
+
+void SetRecvTimeout(int timeout)
+{
+ RecvTimeout = timeout;
+}
+
+void Begin()
+{
+ MPQueue->lock();
+ MPQueueHeader* header = (MPQueueHeader*)MPQueue->data();
+ PacketReadOffset = header->PacketWriteOffset;
+ ReplyReadOffset = header->ReplyWriteOffset;
+ SemReset(InstanceID);
+ SemReset(16+InstanceID);
+ header->ConnectedBitmask |= (1 << InstanceID);
+ MPQueue->unlock();
+}
+
+void End()
+{
+ MPQueue->lock();
+ MPQueueHeader* header = (MPQueueHeader*)MPQueue->data();
+ //SemReset(InstanceID);
+ //SemReset(16+InstanceID);
+ header->ConnectedBitmask &= ~(1 << InstanceID);
+ MPQueue->unlock();
+}
+
+void FIFORead(int fifo, void* buf, int len)
+{
+ u8* data = (u8*)MPQueue->data();
+
+ u32 offset, start, end;
+ if (fifo == 0)
+ {
+ offset = PacketReadOffset;
+ start = kPacketStart;
+ end = kPacketEnd;
+ }
+ else
+ {
+ offset = ReplyReadOffset;
+ start = kReplyStart;
+ end = kReplyEnd;
+ }
+
+ if ((offset + len) >= end)
+ {
+ u32 part1 = end - offset;
+ memcpy(buf, &data[offset], part1);
+ memcpy(&((u8*)buf)[part1], &data[start], len - part1);
+ offset = start + len - part1;
+ }
+ else
+ {
+ memcpy(buf, &data[offset], len);
+ offset += len;
+ }
+
+ if (fifo == 0) PacketReadOffset = offset;
+ else ReplyReadOffset = offset;
+}
+
+void FIFOWrite(int fifo, void* buf, int len)
+{
+ u8* data = (u8*)MPQueue->data();
+ MPQueueHeader* header = (MPQueueHeader*)&data[0];
+
+ u32 offset, start, end;
+ if (fifo == 0)
+ {
+ offset = header->PacketWriteOffset;
+ start = kPacketStart;
+ end = kPacketEnd;
+ }
+ else
+ {
+ offset = header->ReplyWriteOffset;
+ start = kReplyStart;
+ end = kReplyEnd;
+ }
+
+ if ((offset + len) >= end)
+ {
+ u32 part1 = end - offset;
+ memcpy(&data[offset], buf, part1);
+ memcpy(&data[start], &((u8*)buf)[part1], len - part1);
+ offset = start + len - part1;
+ }
+ else
+ {
+ memcpy(&data[offset], buf, len);
+ offset += len;
+ }
+
+ if (fifo == 0) header->PacketWriteOffset = offset;
+ else header->ReplyWriteOffset = offset;
+}
+
+int SendPacketGeneric(u32 type, u8* packet, int len, u64 timestamp)
+{
+ MPQueue->lock();
+ u8* data = (u8*)MPQueue->data();
+ MPQueueHeader* header = (MPQueueHeader*)&data[0];
+
+ u16 mask = header->ConnectedBitmask;
+
+ // TODO: check if the FIFO is full!
+
+ MPPacketHeader pktheader;
+ pktheader.Magic = 0x4946494E;
+ pktheader.SenderID = InstanceID;
+ pktheader.Type = type;
+ pktheader.Length = len;
+ pktheader.Timestamp = timestamp;
+
+ type &= 0xFFFF;
+ int nfifo = (type == 2) ? 1 : 0;
+ FIFOWrite(nfifo, &pktheader, sizeof(pktheader));
+ if (len)
+ FIFOWrite(nfifo, packet, len);
+
+ if (type == 1)
+ {
+ // NOTE: this is not guarded against, say, multiple multiplay games happening on the same machine
+ // we would need to pass the packet's SenderID through the wifi module for that
+ header->MPHostInstanceID = InstanceID;
+ header->MPReplyBitmask = 0;
+ ReplyReadOffset = header->ReplyWriteOffset;
+ SemReset(16 + InstanceID);
+ }
+ else if (type == 2)
+ {
+ header->MPReplyBitmask |= (1 << InstanceID);
+ }
+
+ MPQueue->unlock();
+
+ if (type == 2)
+ {
+ SemPost(16 + header->MPHostInstanceID);
+ }
+ else
+ {
+ for (int i = 0; i < 16; i++)
+ {
+ if (mask & (1<lock();
+ u8* data = (u8*)MPQueue->data();
+ MPQueueHeader* header = (MPQueueHeader*)&data[0];
+
+ MPPacketHeader pktheader;
+ FIFORead(0, &pktheader, sizeof(pktheader));
+
+ if (pktheader.Magic != 0x4946494E)
+ {
+ printf("PACKET FIFO OVERFLOW\n");
+ PacketReadOffset = header->PacketWriteOffset;
+ SemReset(InstanceID);
+ MPQueue->unlock();
+ return 0;
+ }
+
+ if (pktheader.SenderID == InstanceID)
+ {
+ // skip this packet
+ PacketReadOffset += pktheader.Length;
+ if (PacketReadOffset >= kPacketEnd)
+ PacketReadOffset += kPacketStart - kPacketEnd;
+
+ MPQueue->unlock();
+ continue;
+ }
+
+ if (pktheader.Length)
+ {
+ FIFORead(0, packet, pktheader.Length);
+
+ if (pktheader.Type == 1)
+ LastHostID = pktheader.SenderID;
+ }
+
+ if (timestamp) *timestamp = pktheader.Timestamp;
+ MPQueue->unlock();
+ return pktheader.Length;
+ }
+}
+
+int SendPacket(u8* packet, int len, u64 timestamp)
+{
+ return SendPacketGeneric(0, packet, len, timestamp);
+}
+
+int RecvPacket(u8* packet, u64* timestamp)
+{
+ return RecvPacketGeneric(packet, false, timestamp);
+}
+
+
+int SendCmd(u8* packet, int len, u64 timestamp)
+{
+ return SendPacketGeneric(1, packet, len, timestamp);
+}
+
+int SendReply(u8* packet, int len, u64 timestamp, u16 aid)
+{
+ return SendPacketGeneric(2 | (aid<<16), packet, len, timestamp);
+}
+
+int SendAck(u8* packet, int len, u64 timestamp)
+{
+ return SendPacketGeneric(3, packet, len, timestamp);
+}
+
+int RecvHostPacket(u8* packet, u64* timestamp)
+{
+ if (LastHostID != -1)
+ {
+ // check if the host is still connected
+
+ MPQueue->lock();
+ u8* data = (u8*)MPQueue->data();
+ MPQueueHeader* header = (MPQueueHeader*)&data[0];
+ u16 curinstmask = header->ConnectedBitmask;
+ MPQueue->unlock();
+
+ if (!(curinstmask & (1 << LastHostID)))
+ return -1;
+ }
+
+ return RecvPacketGeneric(packet, true, timestamp);
+}
+
+u16 RecvReplies(u8* packets, u64 timestamp, u16 aidmask)
+{
+ u16 ret = 0;
+ u16 myinstmask = (1 << InstanceID);
+ u16 curinstmask;
+
+ {
+ MPQueue->lock();
+ u8* data = (u8*)MPQueue->data();
+ MPQueueHeader* header = (MPQueueHeader*)&data[0];
+ curinstmask = header->ConnectedBitmask;
+ MPQueue->unlock();
+ }
+
+ // if all clients have left: return early
+ if ((myinstmask & curinstmask) == curinstmask)
+ return 0;
+
+ for (;;)
+ {
+ if (!SemWait(16+InstanceID, RecvTimeout))
+ {
+ // no more replies available
+ return ret;
+ }
+
+ MPQueue->lock();
+ u8* data = (u8*)MPQueue->data();
+ MPQueueHeader* header = (MPQueueHeader*)&data[0];
+
+ MPPacketHeader pktheader;
+ FIFORead(1, &pktheader, sizeof(pktheader));
+
+ if (pktheader.Magic != 0x4946494E)
+ {
+ printf("REPLY FIFO OVERFLOW\n");
+ ReplyReadOffset = header->ReplyWriteOffset;
+ SemReset(16+InstanceID);
+ MPQueue->unlock();
+ return 0;
+ }
+
+ if ((pktheader.SenderID == InstanceID) || // packet we sent out (shouldn't happen, but hey)
+ (pktheader.Timestamp < (timestamp - 32))) // stale packet
+ {
+ // skip this packet
+ ReplyReadOffset += pktheader.Length;
+ if (ReplyReadOffset >= kReplyEnd)
+ ReplyReadOffset += kReplyStart - kReplyEnd;
+
+ MPQueue->unlock();
+ continue;
+ }
+
+ if (pktheader.Length)
+ {
+ u32 aid = (pktheader.Type >> 16);
+ FIFORead(1, &packets[(aid-1)*1024], pktheader.Length);
+ ret |= (1 << aid);
+ }
+
+ myinstmask |= (1 << pktheader.SenderID);
+ if (((myinstmask & curinstmask) == curinstmask) ||
+ ((ret & aidmask) == aidmask))
+ {
+ // all the clients have sent their reply
+
+ MPQueue->unlock();
+ return ret;
+ }
+
+ MPQueue->unlock();
+ }
+}
+
+}
+
diff --git a/src/frontend/qt_sdl/LocalMP.h b/src/frontend/qt_sdl/LocalMP.h
new file mode 100644
index 00000000..51dfcb93
--- /dev/null
+++ b/src/frontend/qt_sdl/LocalMP.h
@@ -0,0 +1,45 @@
+/*
+ Copyright 2016-2022 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 LOCALMP_H
+#define LOCALMP_H
+
+#include "types.h"
+
+namespace LocalMP
+{
+
+bool Init();
+void DeInit();
+
+void SetRecvTimeout(int timeout);
+
+void Begin();
+void End();
+
+int SendPacket(u8* data, int len, u64 timestamp);
+int RecvPacket(u8* data, u64* timestamp);
+int SendCmd(u8* data, int len, u64 timestamp);
+int SendReply(u8* data, int len, u64 timestamp, u16 aid);
+int SendAck(u8* data, int len, u64 timestamp);
+int RecvHostPacket(u8* data, u64* timestamp);
+u16 RecvReplies(u8* data, u64 timestamp, u16 aidmask);
+
+}
+
+#endif // LOCALMP_H
diff --git a/src/frontend/qt_sdl/MPSettingsDialog.cpp b/src/frontend/qt_sdl/MPSettingsDialog.cpp
new file mode 100644
index 00000000..e3114220
--- /dev/null
+++ b/src/frontend/qt_sdl/MPSettingsDialog.cpp
@@ -0,0 +1,73 @@
+/*
+ Copyright 2016-2022 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
+#include
+
+#include "types.h"
+#include "Platform.h"
+#include "Config.h"
+
+#include "LAN_Socket.h"
+#include "LAN_PCap.h"
+#include "Wifi.h"
+
+#include "MPSettingsDialog.h"
+#include "ui_MPSettingsDialog.h"
+
+
+MPSettingsDialog* MPSettingsDialog::currentDlg = nullptr;
+
+extern bool RunningSomething;
+
+
+MPSettingsDialog::MPSettingsDialog(QWidget* parent) : QDialog(parent), ui(new Ui::MPSettingsDialog)
+{
+ ui->setupUi(this);
+ setAttribute(Qt::WA_DeleteOnClose);
+
+ grpAudioMode = new QButtonGroup(this);
+ grpAudioMode->addButton(ui->rbAudioAll, 0);
+ grpAudioMode->addButton(ui->rbAudioOneOnly, 1);
+ grpAudioMode->addButton(ui->rbAudioActiveOnly, 2);
+ grpAudioMode->button(Config::MPAudioMode)->setChecked(true);
+
+ ui->sbReceiveTimeout->setValue(Config::MPRecvTimeout);
+}
+
+MPSettingsDialog::~MPSettingsDialog()
+{
+ delete ui;
+}
+
+void MPSettingsDialog::done(int r)
+{
+ if (r == QDialog::Accepted)
+ {
+ Config::MPAudioMode = grpAudioMode->checkedId();
+ Config::MPRecvTimeout = ui->sbReceiveTimeout->value();
+
+ Config::Save();
+ }
+
+ QDialog::done(r);
+
+ closeDlg();
+}
+
+//
diff --git a/src/frontend/qt_sdl/MPSettingsDialog.h b/src/frontend/qt_sdl/MPSettingsDialog.h
new file mode 100644
index 00000000..fe917e89
--- /dev/null
+++ b/src/frontend/qt_sdl/MPSettingsDialog.h
@@ -0,0 +1,65 @@
+/*
+ Copyright 2016-2022 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 MPSETTINGSDIALOG_H
+#define MPSETTINGSDIALOG_H
+
+#include
+#include
+
+namespace Ui { class MPSettingsDialog; }
+class MPSettingsDialog;
+
+class MPSettingsDialog : public QDialog
+{
+ Q_OBJECT
+
+public:
+ explicit MPSettingsDialog(QWidget* parent);
+ ~MPSettingsDialog();
+
+ static MPSettingsDialog* currentDlg;
+ static MPSettingsDialog* openDlg(QWidget* parent)
+ {
+ if (currentDlg)
+ {
+ currentDlg->activateWindow();
+ return currentDlg;
+ }
+
+ currentDlg = new MPSettingsDialog(parent);
+ currentDlg->open();
+ return currentDlg;
+ }
+ static void closeDlg()
+ {
+ currentDlg = nullptr;
+ }
+
+private slots:
+ void done(int r);
+
+ //
+
+private:
+ Ui::MPSettingsDialog* ui;
+
+ QButtonGroup* grpAudioMode;
+};
+
+#endif // MPSETTINGSDIALOG_H
diff --git a/src/frontend/qt_sdl/MPSettingsDialog.ui b/src/frontend/qt_sdl/MPSettingsDialog.ui
new file mode 100644
index 00000000..bce0fc94
--- /dev/null
+++ b/src/frontend/qt_sdl/MPSettingsDialog.ui
@@ -0,0 +1,142 @@
+
+
+ MPSettingsDialog
+
+
+
+ 0
+ 0
+ 466
+ 202
+
+
+
+ Multiplayer settings - melonDS
+
+
+ -
+
+
+ Audio output
+
+
+
-
+
+
+ Instance 1 only
+
+
+
+ -
+
+
+ All instances
+
+
+
+ -
+
+
+ Active instance only
+
+
+
+
+
+
+ -
+
+
+ Network
+
+
+
-
+
+
+
+ 0
+ 0
+
+
+
+
+ 50
+ 0
+
+
+
+ 1000
+
+
+
+ -
+
+
+ Data reception timeout:
+
+
+
+ -
+
+
+
+ 1
+ 0
+
+
+
+ milliseconds
+
+
+
+
+
+
+ -
+
+
+ Qt::Horizontal
+
+
+ QDialogButtonBox::Cancel|QDialogButtonBox::Ok
+
+
+
+
+
+
+
+
+ buttonBox
+ accepted()
+ MPSettingsDialog
+ accept()
+
+
+ 248
+ 254
+
+
+ 157
+ 274
+
+
+
+
+ buttonBox
+ rejected()
+ MPSettingsDialog
+ reject()
+
+
+ 316
+ 260
+
+
+ 286
+ 274
+
+
+
+
+
diff --git a/src/frontend/qt_sdl/OSD.cpp b/src/frontend/qt_sdl/OSD.cpp
index 68b1b783..6f060a97 100644
--- a/src/frontend/qt_sdl/OSD.cpp
+++ b/src/frontend/qt_sdl/OSD.cpp
@@ -146,7 +146,7 @@ void LayoutText(const char* text, u32* width, u32* height, int* breaks)
u32 w = 0;
u32 h = 14;
u32 totalw = 0;
- u32 maxw = mainWindow->panel->width() - (kOSDMargin*2);
+ u32 maxw = mainWindow->panelWidget->width() - (kOSDMargin*2);
int lastbreak = -1;
int numbrk = 0;
u16* ptr;
@@ -236,7 +236,7 @@ void RenderText(u32 color, const char* text, Item* item)
memset(item->Bitmap, 0, w*h*sizeof(u32));
u32 x = 0, y = 1;
- u32 maxw = mainWindow->panel->width() - (kOSDMargin*2);
+ u32 maxw = mainWindow->panelWidget->width() - (kOSDMargin*2);
int curline = 0;
u16* ptr;
diff --git a/src/frontend/qt_sdl/PathSettingsDialog.cpp b/src/frontend/qt_sdl/PathSettingsDialog.cpp
index 7fa517d3..286032e9 100644
--- a/src/frontend/qt_sdl/PathSettingsDialog.cpp
+++ b/src/frontend/qt_sdl/PathSettingsDialog.cpp
@@ -22,6 +22,7 @@
#include "types.h"
#include "Config.h"
+#include "Platform.h"
#include "PathSettingsDialog.h"
#include "ui_PathSettingsDialog.h"
@@ -43,6 +44,12 @@ PathSettingsDialog::PathSettingsDialog(QWidget* parent) : QDialog(parent), ui(ne
ui->txtSaveFilePath->setText(QString::fromStdString(Config::SaveFilePath));
ui->txtSavestatePath->setText(QString::fromStdString(Config::SavestatePath));
ui->txtCheatFilePath->setText(QString::fromStdString(Config::CheatFilePath));
+
+ int inst = Platform::InstanceID();
+ if (inst > 0)
+ ui->lblInstanceNum->setText(QString("Configuring paths for instance %1").arg(inst+1));
+ else
+ ui->lblInstanceNum->hide();
}
PathSettingsDialog::~PathSettingsDialog()
diff --git a/src/frontend/qt_sdl/PathSettingsDialog.ui b/src/frontend/qt_sdl/PathSettingsDialog.ui
index 95f5acc9..295b1c44 100644
--- a/src/frontend/qt_sdl/PathSettingsDialog.ui
+++ b/src/frontend/qt_sdl/PathSettingsDialog.ui
@@ -7,49 +7,63 @@
0
0
439
- 166
+ 185
Path settings - melonDS
- -
-
-
- true
+
-
+
+
+ Cheat files path:
- -
+
-
Browse...
- -
+
-
Savestates path:
+ -
+
+
+
+
+
+
-
+
+
+ true
+
+
+
+ -
+
+
+ Browse...
+
+
+
+ -
true
- -
-
-
- Leave a path blank to use the current ROM's path.
-
-
-
- -
+
-
Qt::Horizontal
@@ -59,35 +73,14 @@
- -
-
-
- Cheat files path:
-
-
-
- -
-
-
- Browse...
-
-
-
- -
+
-
Browse...
- -
-
-
- true
-
-
-
- -
+
-
Save files path:
@@ -95,9 +88,23 @@
-
-
+
-
+ Leave a path blank to use the current ROM's path.
+
+
+
+ -
+
+
+ true
+
+
+
+ -
+
+
+ Configuring paths for instance X
diff --git a/src/frontend/qt_sdl/Platform.cpp b/src/frontend/qt_sdl/Platform.cpp
index 2a3dde55..14e4f587 100644
--- a/src/frontend/qt_sdl/Platform.cpp
+++ b/src/frontend/qt_sdl/Platform.cpp
@@ -20,28 +20,7 @@
#include
#include
-#ifdef __WIN32__
- #define NTDDI_VERSION 0x06000000 // GROSS FUCKING HACK
- #include
- #include
- //#include // FUCK THAT SHIT
- #include
- #include
- #include
- #define dup _dup
- #define socket_t SOCKET
- #define sockaddr_t SOCKADDR
-#else
- #include
- #include
- #include
- #include
-
- #define socket_t int
- #define sockaddr_t struct sockaddr
- #define closesocket close
-#endif
-
+#include
#include
#include
#include
@@ -49,6 +28,7 @@
#include
#include
#include
+#include
#include "Platform.h"
#include "Config.h"
@@ -56,11 +36,7 @@
#include "CameraManager.h"
#include "LAN_Socket.h"
#include "LAN_PCap.h"
-#include
-
-#ifndef INVALID_SOCKET
- #define INVALID_SOCKET (socket_t)-1
-#endif
+#include "LocalMP.h"
std::string EmuDirectory;
@@ -73,11 +49,63 @@ extern CameraManager* camManager[2];
namespace Platform
{
-socket_t MPSocket;
-sockaddr_t MPSendAddr;
-u8 PacketBuffer[2048];
+QSharedMemory* IPCBuffer = nullptr;
+int IPCInstanceID;
-#define NIFI_VER 1
+void IPCInit()
+{
+ IPCInstanceID = 0;
+
+ IPCBuffer = new QSharedMemory("melonIPC");
+
+ if (!IPCBuffer->attach())
+ {
+ printf("IPC sharedmem doesn't exist. creating\n");
+ if (!IPCBuffer->create(1024))
+ {
+ printf("IPC sharedmem create failed :(\n");
+ delete IPCBuffer;
+ IPCBuffer = nullptr;
+ return;
+ }
+
+ IPCBuffer->lock();
+ memset(IPCBuffer->data(), 0, IPCBuffer->size());
+ IPCBuffer->unlock();
+ }
+
+ IPCBuffer->lock();
+ u8* data = (u8*)IPCBuffer->data();
+ u16 mask = *(u16*)&data[0];
+ for (int i = 0; i < 16; i++)
+ {
+ if (!(mask & (1<unlock();
+
+ printf("IPC: instance ID %d\n", IPCInstanceID);
+}
+
+void IPCDeInit()
+{
+ if (IPCBuffer)
+ {
+ IPCBuffer->lock();
+ u8* data = (u8*)IPCBuffer->data();
+ *(u16*)&data[0] &= ~(1<unlock();
+
+ IPCBuffer->detach();
+ delete IPCBuffer;
+ }
+
+ IPCBuffer = nullptr;
+}
void Init(int argc, char** argv)
@@ -113,10 +141,13 @@ void Init(int argc, char** argv)
confdir = config.absolutePath() + "/melonDS/";
EmuDirectory = confdir.toStdString();
#endif
+
+ IPCInit();
}
void DeInit()
{
+ IPCDeInit();
}
@@ -126,6 +157,22 @@ void StopEmu()
}
+int InstanceID()
+{
+ return IPCInstanceID;
+}
+
+std::string InstanceFileSuffix()
+{
+ int inst = IPCInstanceID;
+ if (inst == 0) return "";
+
+ char suffix[16] = {0};
+ snprintf(suffix, 15, ".%d", inst+1);
+ return suffix;
+}
+
+
int GetConfigInt(ConfigEntry entry)
{
const int imgsizes[] = {0, 256, 512, 1024, 2048, 4096};
@@ -172,7 +219,6 @@ bool GetConfigBool(ConfigEntry entry)
case DSiSD_ReadOnly: return Config::DSiSDReadOnly != 0;
case DSiSD_FolderSync: return Config::DSiSDFolderSync != 0;
- case Firm_RandomizeMAC: return Config::RandomizeMAC != 0;
case Firm_OverrideSettings: return Config::FirmwareOverrideSettings != 0;
}
@@ -394,146 +440,60 @@ void WriteGBASave(const u8* savedata, u32 savelen, u32 writeoffset, u32 writelen
}
+
bool MP_Init()
{
- int opt_true = 1;
- int res;
-
-#ifdef __WIN32__
- WSADATA wsadata;
- if (WSAStartup(MAKEWORD(2, 2), &wsadata) != 0)
- {
- return false;
- }
-#endif // __WIN32__
-
- MPSocket = socket(AF_INET, SOCK_DGRAM, 0);
- if (MPSocket < 0)
- {
- return false;
- }
-
- res = setsockopt(MPSocket, SOL_SOCKET, SO_REUSEADDR, (const char*)&opt_true, sizeof(int));
- if (res < 0)
- {
- closesocket(MPSocket);
- MPSocket = INVALID_SOCKET;
- return false;
- }
-
-#if defined(BSD) || defined(__APPLE__)
- res = setsockopt(MPSocket, SOL_SOCKET, SO_REUSEPORT, (const char*)&opt_true, sizeof(int));
- if (res < 0)
- {
- closesocket(MPSocket);
- MPSocket = INVALID_SOCKET;
- return false;
- }
-#endif
-
- sockaddr_t saddr;
- saddr.sa_family = AF_INET;
- *(u32*)&saddr.sa_data[2] = htonl(Config::SocketBindAnyAddr ? INADDR_ANY : INADDR_LOOPBACK);
- *(u16*)&saddr.sa_data[0] = htons(7064);
- res = bind(MPSocket, &saddr, sizeof(sockaddr_t));
- if (res < 0)
- {
- closesocket(MPSocket);
- MPSocket = INVALID_SOCKET;
- return false;
- }
-
- res = setsockopt(MPSocket, SOL_SOCKET, SO_BROADCAST, (const char*)&opt_true, sizeof(int));
- if (res < 0)
- {
- closesocket(MPSocket);
- MPSocket = INVALID_SOCKET;
- return false;
- }
-
- MPSendAddr.sa_family = AF_INET;
- *(u32*)&MPSendAddr.sa_data[2] = htonl(INADDR_BROADCAST);
- *(u16*)&MPSendAddr.sa_data[0] = htons(7064);
-
- return true;
+ return LocalMP::Init();
}
void MP_DeInit()
{
- if (MPSocket >= 0)
- closesocket(MPSocket);
-
-#ifdef __WIN32__
- WSACleanup();
-#endif // __WIN32__
+ return LocalMP::DeInit();
}
-int MP_SendPacket(u8* data, int len)
+void MP_Begin()
{
- if (MPSocket < 0)
- return 0;
-
- if (len > 2048-8)
- {
- printf("MP_SendPacket: error: packet too long (%d)\n", len);
- return 0;
- }
-
- *(u32*)&PacketBuffer[0] = htonl(0x4946494E); // NIFI
- PacketBuffer[4] = NIFI_VER;
- PacketBuffer[5] = 0;
- *(u16*)&PacketBuffer[6] = htons(len);
- memcpy(&PacketBuffer[8], data, len);
-
- int slen = sendto(MPSocket, (const char*)PacketBuffer, len+8, 0, &MPSendAddr, sizeof(sockaddr_t));
- if (slen < 8) return 0;
- return slen - 8;
+ return LocalMP::Begin();
}
-int MP_RecvPacket(u8* data, bool block)
+void MP_End()
{
- if (MPSocket < 0)
- return 0;
+ return LocalMP::End();
+}
- fd_set fd;
- struct timeval tv;
+int MP_SendPacket(u8* data, int len, u64 timestamp)
+{
+ return LocalMP::SendPacket(data, len, timestamp);
+}
- FD_ZERO(&fd);
- FD_SET(MPSocket, &fd);
- tv.tv_sec = 0;
- tv.tv_usec = block ? 5000 : 0;
+int MP_RecvPacket(u8* data, u64* timestamp)
+{
+ return LocalMP::RecvPacket(data, timestamp);
+}
- if (!select(MPSocket+1, &fd, 0, 0, &tv))
- {
- return 0;
- }
+int MP_SendCmd(u8* data, int len, u64 timestamp)
+{
+ return LocalMP::SendCmd(data, len, timestamp);
+}
- sockaddr_t fromAddr;
- socklen_t fromLen = sizeof(sockaddr_t);
- int rlen = recvfrom(MPSocket, (char*)PacketBuffer, 2048, 0, &fromAddr, &fromLen);
- if (rlen < 8+24)
- {
- return 0;
- }
- rlen -= 8;
+int MP_SendReply(u8* data, int len, u64 timestamp, u16 aid)
+{
+ return LocalMP::SendReply(data, len, timestamp, aid);
+}
- if (ntohl(*(u32*)&PacketBuffer[0]) != 0x4946494E)
- {
- return 0;
- }
+int MP_SendAck(u8* data, int len, u64 timestamp)
+{
+ return LocalMP::SendAck(data, len, timestamp);
+}
- if (PacketBuffer[4] != NIFI_VER)
- {
- return 0;
- }
+int MP_RecvHostPacket(u8* data, u64* timestamp)
+{
+ return LocalMP::RecvHostPacket(data, timestamp);
+}
- if (ntohs(*(u16*)&PacketBuffer[6]) != rlen)
- {
- return 0;
- }
-
- memcpy(data, &PacketBuffer[8], rlen);
- return rlen;
+u16 MP_RecvReplies(u8* data, u64 timestamp, u16 aidmask)
+{
+ return LocalMP::RecvReplies(data, timestamp, aidmask);
}
diff --git a/src/frontend/qt_sdl/PowerManagement/PowerManagementDialog.cpp b/src/frontend/qt_sdl/PowerManagement/PowerManagementDialog.cpp
index 499c176a..89f74e5f 100644
--- a/src/frontend/qt_sdl/PowerManagement/PowerManagementDialog.cpp
+++ b/src/frontend/qt_sdl/PowerManagement/PowerManagementDialog.cpp
@@ -23,6 +23,7 @@
#include "DSi_I2C.h"
#include "NDS.h"
#include "Config.h"
+#include "Platform.h"
#include "types.h"
@@ -65,6 +66,12 @@ PowerManagementDialog::PowerManagementDialog(QWidget* parent) : QDialog(parent),
}
ui->sliderDSiBatteryLevel->setValue(dsiBatterySliderPos);
+ int inst = Platform::InstanceID();
+ if (inst > 0)
+ ui->lblInstanceNum->setText(QString("Setting battery levels for instance %1").arg(inst+1));
+ else
+ ui->lblInstanceNum->hide();
+
inited = true;
}
diff --git a/src/frontend/qt_sdl/PowerManagement/PowerManagementDialog.ui b/src/frontend/qt_sdl/PowerManagement/PowerManagementDialog.ui
index e0e7c6e8..77af2254 100644
--- a/src/frontend/qt_sdl/PowerManagement/PowerManagementDialog.ui
+++ b/src/frontend/qt_sdl/PowerManagement/PowerManagementDialog.ui
@@ -7,7 +7,7 @@
0
0
562
- 279
+ 288
@@ -23,37 +23,7 @@
QLayout::SetFixedSize
- -
-
-
- DS Battery
-
-
-
-
-
-
- Low
-
-
-
- -
-
-
- Battery Level
-
-
-
- -
-
-
- Okay
-
-
-
-
-
-
- -
+
-
Qt::Horizontal
@@ -63,7 +33,7 @@
- -
+
-
DSi Battery
@@ -219,6 +189,49 @@
+ -
+
+
+ DS Battery
+
+
+
-
+
+
+ Low
+
+
+
+ -
+
+
+ Battery Level
+
+
+
+ -
+
+
+ Okay
+
+
+
+
+
+
+ -
+
+
+
+ 0
+ 0
+
+
+
+ Configuring settings for instance X
+
+
+
diff --git a/src/frontend/qt_sdl/ROMInfoDialog.cpp b/src/frontend/qt_sdl/ROMInfoDialog.cpp
index 0a71dc8b..e82ec4be 100644
--- a/src/frontend/qt_sdl/ROMInfoDialog.cpp
+++ b/src/frontend/qt_sdl/ROMInfoDialog.cpp
@@ -75,12 +75,12 @@ ROMInfoDialog::ROMInfoDialog(QWidget* parent) : QDialog(parent), ui(new Ui::ROMI
ui->iconTitle->setText(QString::fromUtf16(NDSCart::Banner.EnglishTitle));
- ui->japaneseTitle->setText(QString::fromUtf16(NDSCart::Banner.JapaneseTitle, 128));
- ui->englishTitle->setText(QString::fromUtf16(NDSCart::Banner.EnglishTitle, 128));
- ui->frenchTitle->setText(QString::fromUtf16(NDSCart::Banner.FrenchTitle, 128));
- ui->germanTitle->setText(QString::fromUtf16(NDSCart::Banner.GermanTitle, 128));
- ui->italianTitle->setText(QString::fromUtf16(NDSCart::Banner.ItalianTitle, 128));
- ui->spanishTitle->setText(QString::fromUtf16(NDSCart::Banner.SpanishTitle, 128));
+ ui->japaneseTitle->setText(QString::fromUtf16(NDSCart::Banner.JapaneseTitle));
+ ui->englishTitle->setText(QString::fromUtf16(NDSCart::Banner.EnglishTitle));
+ ui->frenchTitle->setText(QString::fromUtf16(NDSCart::Banner.FrenchTitle));
+ ui->germanTitle->setText(QString::fromUtf16(NDSCart::Banner.GermanTitle));
+ ui->italianTitle->setText(QString::fromUtf16(NDSCart::Banner.ItalianTitle));
+ ui->spanishTitle->setText(QString::fromUtf16(NDSCart::Banner.SpanishTitle));
if (NDSCart::Banner.Version > 1)
ui->chineseTitle->setText(QString::fromUtf16(NDSCart::Banner.ChineseTitle));
diff --git a/src/frontend/qt_sdl/ROMInfoDialog.ui b/src/frontend/qt_sdl/ROMInfoDialog.ui
index 0c65cab3..1c9d844b 100644
--- a/src/frontend/qt_sdl/ROMInfoDialog.ui
+++ b/src/frontend/qt_sdl/ROMInfoDialog.ui
@@ -6,8 +6,8 @@
0
0
- 427
- 434
+ 559
+ 532
@@ -22,12 +22,6 @@
-
-
-
- 0
- 0
-
-
Titles
@@ -350,12 +344,6 @@
-
-
-
- 0
- 0
-
-
Filesystem
@@ -441,12 +429,6 @@
-
-
-
- 0
- 0
-
-
General info
@@ -668,7 +650,7 @@
- -
+
-
@@ -742,43 +724,11 @@
- -
-
-
- Qt::Horizontal
-
-
-
- 55
- 20
-
-
-
-
- -
+
-
Qt::Horizontal
-
-
- 40
- 20
-
-
-
-
- -
-
-
- Qt::Horizontal
-
-
-
- 40
- 20
-
-
-
@@ -788,6 +738,13 @@
+ -
+
+
+ Qt::Horizontal
+
+
+
diff --git a/src/frontend/qt_sdl/ROMManager.cpp b/src/frontend/qt_sdl/ROMManager.cpp
index 304862eb..716a4543 100644
--- a/src/frontend/qt_sdl/ROMManager.cpp
+++ b/src/frontend/qt_sdl/ROMManager.cpp
@@ -326,6 +326,7 @@ bool LoadState(std::string filename)
std::string savefile = filename.substr(LastSep(filename)+1);
savefile = GetAssetPath(false, Config::SaveFilePath, ".sav", savefile);
+ savefile += Platform::InstanceFileSuffix();
NDSSave->SetPath(savefile, true);
}
@@ -350,6 +351,7 @@ bool SaveState(std::string filename)
{
std::string savefile = filename.substr(LastSep(filename)+1);
savefile = GetAssetPath(false, Config::SaveFilePath, ".sav", savefile);
+ savefile += Platform::InstanceFileSuffix();
NDSSave->SetPath(savefile, false);
}
@@ -432,6 +434,7 @@ void Reset()
{
std::string oldsave = NDSSave->GetPath();
std::string newsave = GetAssetPath(false, Config::SaveFilePath, ".sav");
+ newsave += Platform::InstanceFileSuffix();
if (oldsave != newsave)
NDSSave->SetPath(newsave, false);
}
@@ -440,6 +443,7 @@ void Reset()
{
std::string oldsave = GBASave->GetPath();
std::string newsave = GetAssetPath(true, Config::SaveFilePath, ".sav");
+ newsave += Platform::InstanceFileSuffix();
if (oldsave != newsave)
GBASave->SetPath(newsave, false);
}
@@ -562,7 +566,11 @@ bool LoadROM(QStringList filepath, bool reset)
u8* savedata = nullptr;
std::string savname = GetAssetPath(false, Config::SaveFilePath, ".sav");
+ std::string origsav = savname;
+ savname += Platform::InstanceFileSuffix();
+
FILE* sav = Platform::OpenFile(savname, "rb", true);
+ if (!sav) sav = Platform::OpenFile(origsav, "rb", true);
if (sav)
{
fseek(sav, 0, SEEK_END);
@@ -711,7 +719,11 @@ bool LoadGBAROM(QStringList filepath)
u8* savedata = nullptr;
std::string savname = GetAssetPath(true, Config::SaveFilePath, ".sav");
+ std::string origsav = savname;
+ savname += Platform::InstanceFileSuffix();
+
FILE* sav = Platform::OpenFile(savname, "rb", true);
+ if (!sav) sav = Platform::OpenFile(origsav, "rb", true);
if (sav)
{
fseek(sav, 0, SEEK_END);
diff --git a/src/frontend/qt_sdl/TitleManagerDialog.cpp b/src/frontend/qt_sdl/TitleManagerDialog.cpp
index a4c9cfd0..8087ee65 100644
--- a/src/frontend/qt_sdl/TitleManagerDialog.cpp
+++ b/src/frontend/qt_sdl/TitleManagerDialog.cpp
@@ -31,7 +31,7 @@
#include "ui_TitleImportDialog.h"
-FILE* TitleManagerDialog::curNAND = nullptr;
+bool TitleManagerDialog::NANDInited = false;
TitleManagerDialog* TitleManagerDialog::currentDlg = nullptr;
extern std::string EmuDirectory;
@@ -136,6 +136,8 @@ void TitleManagerDialog::createTitleItem(u32 category, u32 titleid)
bool TitleManagerDialog::openNAND()
{
+ NANDInited = false;
+
FILE* bios7i = Platform::OpenLocalFile(Config::DSiBIOS7Path, "rb");
if (!bios7i)
return false;
@@ -145,28 +147,21 @@ bool TitleManagerDialog::openNAND()
fread(es_keyY, 16, 1, bios7i);
fclose(bios7i);
- curNAND = Platform::OpenLocalFile(Config::DSiNANDPath, "r+b");
- if (!curNAND)
- return false;
-
- if (!DSi_NAND::Init(curNAND, es_keyY))
+ if (!DSi_NAND::Init(es_keyY))
{
- fclose(curNAND);
- curNAND = nullptr;
return false;
}
+ NANDInited = true;
return true;
}
void TitleManagerDialog::closeNAND()
{
- if (curNAND)
+ if (NANDInited)
{
DSi_NAND::DeInit();
-
- fclose(curNAND);
- curNAND = nullptr;
+ NANDInited = false;
}
}
diff --git a/src/frontend/qt_sdl/TitleManagerDialog.h b/src/frontend/qt_sdl/TitleManagerDialog.h
index 201e5e8b..cba70470 100644
--- a/src/frontend/qt_sdl/TitleManagerDialog.h
+++ b/src/frontend/qt_sdl/TitleManagerDialog.h
@@ -45,7 +45,7 @@ public:
explicit TitleManagerDialog(QWidget* parent);
~TitleManagerDialog();
- static FILE* curNAND;
+ static bool NANDInited;
static bool openNAND();
static void closeNAND();
diff --git a/src/frontend/qt_sdl/WifiSettingsDialog.cpp b/src/frontend/qt_sdl/WifiSettingsDialog.cpp
index 19cece6f..9bf265e9 100644
--- a/src/frontend/qt_sdl/WifiSettingsDialog.cpp
+++ b/src/frontend/qt_sdl/WifiSettingsDialog.cpp
@@ -50,12 +50,12 @@ WifiSettingsDialog::WifiSettingsDialog(QWidget* parent) : QDialog(parent), ui(ne
ui->setupUi(this);
setAttribute(Qt::WA_DeleteOnClose);
- LAN_Socket::Init();
haspcap = LAN_PCap::Init(false);
ui->rbDirectMode->setText("Direct mode (requires " PCAP_NAME " and ethernet connection)");
- ui->cbBindAnyAddr->setChecked(Config::SocketBindAnyAddr);
+ ui->lblAdapterMAC->setText("(none)");
+ ui->lblAdapterIP->setText("(none)");
int sel = 0;
for (int i = 0; i < LAN_PCap::NumAdapters; i++)
@@ -88,7 +88,6 @@ void WifiSettingsDialog::done(int r)
if (r == QDialog::Accepted)
{
- Config::SocketBindAnyAddr = ui->cbBindAnyAddr->isChecked();
Config::DirectLAN = ui->rbDirectMode->isChecked();
int sel = ui->cbxDirectAdapter->currentIndex();
diff --git a/src/frontend/qt_sdl/WifiSettingsDialog.ui b/src/frontend/qt_sdl/WifiSettingsDialog.ui
index 08970595..444e1d5f 100644
--- a/src/frontend/qt_sdl/WifiSettingsDialog.ui
+++ b/src/frontend/qt_sdl/WifiSettingsDialog.ui
@@ -7,7 +7,7 @@
0
0
572
- 273
+ 217
@@ -26,92 +26,10 @@
-
- Local
+ Network mode
-
-
-
- <html><head/><body><p>Enabling this allows (theoretically) playing local multiplayer games over a local network. It may or may not help make for a better connection in general.</p></body></html>
-
-
- Bind socket to any address
-
-
-
-
-
-
- -
-
-
- Online
-
-
-
-
-
-
- Direct mode settings
-
-
-
-
-
-
- Network adapter:
-
-
-
- -
-
-
-
- 0
- 0
-
-
-
-
- 300
- 0
-
-
-
- <html><head/><body><p>Selects the network adapter through which to route network traffic under direct mode.</p></body></html>
-
-
-
- -
-
-
- MAC address:
-
-
-
- -
-
-
- [PLACEHOLDER]
-
-
-
- -
-
-
- IP address:
-
-
-
- -
-
-
- [PLACEHOLDER]
-
-
-
-
-
-
- -
<html><head/><body><p>Indirect mode uses libslirp. It requires no extra setup and is easy to use.</p></body></html>
@@ -121,7 +39,7 @@
- -
+
-
<html><head/><body><p>Direct mode directly routes network traffic to the host network. It is the most reliable, but requires an ethernet connection.</p><p><br/></p><p>Non-direct mode uses a layer of emulation to get around this, but is more prone to problems.</p></body></html>
@@ -134,6 +52,69 @@
+ -
+
+
+ Direct mode settings
+
+
+
-
+
+
+ Network adapter:
+
+
+
+ -
+
+
+
+ 0
+ 0
+
+
+
+
+ 300
+ 0
+
+
+
+ <html><head/><body><p>Selects the network adapter through which to route network traffic under direct mode.</p></body></html>
+
+
+
+ -
+
+
+ MAC address:
+
+
+
+ -
+
+
+ [PLACEHOLDER]
+
+
+
+ -
+
+
+ IP address:
+
+
+
+ -
+
+
+ [PLACEHOLDER]
+
+
+
+
+
+
-
diff --git a/src/frontend/qt_sdl/main.cpp b/src/frontend/qt_sdl/main.cpp
index 213a09da..22edab11 100644
--- a/src/frontend/qt_sdl/main.cpp
+++ b/src/frontend/qt_sdl/main.cpp
@@ -25,6 +25,7 @@
#include
#include
+#include
#include
#include
#include
@@ -58,6 +59,7 @@
#include "AudioSettingsDialog.h"
#include "FirmwareSettingsDialog.h"
#include "PathSettingsDialog.h"
+#include "MPSettingsDialog.h"
#include "WifiSettingsDialog.h"
#include "InterfaceSettingsDialog.h"
#include "ROMInfoDialog.h"
@@ -78,6 +80,7 @@
#include "SPU.h"
#include "Wifi.h"
#include "Platform.h"
+#include "LocalMP.h"
#include "Config.h"
#include "Savestate.h"
@@ -103,6 +106,7 @@ bool videoSettingsDirty;
SDL_AudioDeviceID audioDevice;
int audioFreq;
+bool audioMuted;
SDL_cond* audioSync;
SDL_mutex* audioSyncLock;
@@ -115,6 +119,15 @@ s16* micWavBuffer;
CameraManager* camManager[2];
+const struct { int id; float ratio; const char* label; } aspectRatios[] =
+{
+ { 0, 1, "4:3 (native)" },
+ { 4, (5.f / 3) / (4.f / 3), "5:3 (3DS)"},
+ { 1, (16.f / 9) / (4.f / 3), "16:9" },
+ { 2, (21.f / 9) / (4.f / 3), "21:9" },
+ { 3, 0, "window" }
+};
+
void micCallback(void* data, Uint8* stream, int len);
@@ -134,7 +147,7 @@ void audioCallback(void* data, Uint8* stream, int len)
SDL_CondSignal(audioSync);
SDL_UnlockMutex(audioSyncLock);
- if (num_in < 1)
+ if ((num_in < 1) || audioMuted)
{
memset(stream, 0, len*sizeof(s16)*2);
return;
@@ -154,6 +167,23 @@ void audioCallback(void* data, Uint8* stream, int len)
Frontend::AudioOut_Resample(buf_in, num_in, (s16*)stream, len, Config::AudioVolume);
}
+void audioMute()
+{
+ int inst = Platform::InstanceID();
+ audioMuted = false;
+
+ switch (Config::MPAudioMode)
+ {
+ case 1: // only instance 1
+ if (inst > 0) audioMuted = true;
+ break;
+
+ case 2: // only currently focused instance
+ if (!mainWindow->isActiveWindow()) audioMuted = true;
+ break;
+ }
+}
+
void micOpen()
{
@@ -326,7 +356,7 @@ EmuThread::EmuThread(QObject* parent) : QThread(parent)
EmuPause = 0;
RunningSomething = false;
- connect(this, SIGNAL(windowUpdate()), mainWindow->panel, SLOT(repaint()));
+ connect(this, SIGNAL(windowUpdate()), mainWindow->panelWidget, SLOT(repaint()));
connect(this, SIGNAL(windowTitleChange(QString)), mainWindow, SLOT(onTitleUpdate(QString)));
connect(this, SIGNAL(windowEmuStart()), mainWindow, SLOT(onEmuStart()));
connect(this, SIGNAL(windowEmuStop()), mainWindow, SLOT(onEmuStop()));
@@ -334,7 +364,7 @@ EmuThread::EmuThread(QObject* parent) : QThread(parent)
connect(this, SIGNAL(windowEmuReset()), mainWindow->actReset, SLOT(trigger()));
connect(this, SIGNAL(windowEmuFrameStep()), mainWindow->actFrameStep, SLOT(trigger()));
connect(this, SIGNAL(windowLimitFPSChange()), mainWindow->actLimitFramerate, SLOT(trigger()));
- connect(this, SIGNAL(screenLayoutChange()), mainWindow->panel, SLOT(onScreenLayoutChanged()));
+ connect(this, SIGNAL(screenLayoutChange()), mainWindow->panelWidget, SLOT(onScreenLayoutChanged()));
connect(this, SIGNAL(windowFullscreenToggle()), mainWindow, SLOT(onFullscreenToggled()));
connect(this, SIGNAL(swapScreensToggle()), mainWindow->actScreenSwap, SLOT(trigger()));
@@ -683,7 +713,11 @@ printf("PROULON\n");
if (winUpdateFreq < 1)
winUpdateFreq = 1;
- sprintf(melontitle, "[%d/%.0f] melonDS " MELONDS_VERSION, fps, fpstarget);
+ int inst = Platform::InstanceID();
+ if (inst == 0)
+ sprintf(melontitle, "[%d/%.0f] melonDS " MELONDS_VERSION, fps, fpstarget);
+ else
+ sprintf(melontitle, "[%d/%.0f] melonDS (%d)", fps, fpstarget, inst+1);
changeWindowTitle(melontitle);
}
}
@@ -698,7 +732,11 @@ printf("PROULON\n");
EmuStatus = EmuRunning;
- sprintf(melontitle, "melonDS " MELONDS_VERSION);
+ int inst = Platform::InstanceID();
+ if (inst == 0)
+ sprintf(melontitle, "melonDS " MELONDS_VERSION);
+ else
+ sprintf(melontitle, "melonDS (%d)", inst+1);
changeWindowTitle(melontitle);
SDL_Delay(75);
@@ -789,19 +827,39 @@ bool EmuThread::emuIsActive()
return (RunningSomething == 1);
}
+ScreenHandler::ScreenHandler(QWidget* widget)
+{
+ widget->setMouseTracking(true);
+ widget->setAttribute(Qt::WA_AcceptTouchEvents);
+ QTimer* mouseTimer = setupMouseTimer();
+ widget->connect(mouseTimer, &QTimer::timeout, [=] { if (Config::MouseHide) widget->setCursor(Qt::BlankCursor);});
+}
+
+ScreenHandler::~ScreenHandler()
+{
+ mouseTimer->stop();
+}
void ScreenHandler::screenSetupLayout(int w, int h)
{
int sizing = Config::ScreenSizing;
if (sizing == 3) sizing = autoScreenSizing;
- float aspectRatios[] =
+ float aspectTop, aspectBot;
+
+ for (auto ratio : aspectRatios)
{
- 1.f,
- (16.f/9)/(4.f/3),
- (21.f/9)/(4.f/3),
- ((float)w/h)/(4.f/3)
- };
+ if (ratio.id == Config::ScreenAspectTop)
+ aspectTop = ratio.ratio;
+ if (ratio.id == Config::ScreenAspectBot)
+ aspectBot = ratio.ratio;
+ }
+
+ if (aspectTop == 0)
+ aspectTop = (float) w / h;
+
+ if (aspectBot == 0)
+ aspectBot = (float) w / h;
Frontend::SetupScreenLayout(w, h,
Config::ScreenLayout,
@@ -810,8 +868,8 @@ void ScreenHandler::screenSetupLayout(int w, int h)
Config::ScreenGap,
Config::IntegerScaling != 0,
Config::ScreenSwap != 0,
- aspectRatios[Config::ScreenAspectTop],
- aspectRatios[Config::ScreenAspectBot]);
+ aspectTop,
+ aspectBot);
numScreens = Frontend::GetScreenTransforms(screenMatrix[0], screenKind);
}
@@ -819,11 +877,16 @@ void ScreenHandler::screenSetupLayout(int w, int h)
QSize ScreenHandler::screenGetMinSize(int factor = 1)
{
bool isHori = (Config::ScreenRotation == 1 || Config::ScreenRotation == 3);
- int gap = Config::ScreenGap;
+ int gap = Config::ScreenGap * factor;
int w = 256 * factor;
int h = 192 * factor;
+ if (Config::ScreenSizing == 4 || Config::ScreenSizing == 5)
+ {
+ return QSize(w, h);
+ }
+
if (Config::ScreenLayout == 0) // natural
{
if (isHori)
@@ -959,7 +1022,7 @@ void ScreenHandler::screenHandleTouch(QTouchEvent* event)
void ScreenHandler::showCursor()
{
- mainWindow->panel->setCursor(Qt::ArrowCursor);
+ mainWindow->panelWidget->setCursor(Qt::ArrowCursor);
mouseTimer->start();
}
@@ -973,7 +1036,7 @@ QTimer* ScreenHandler::setupMouseTimer()
return mouseTimer;
}
-ScreenPanelNative::ScreenPanelNative(QWidget* parent) : QWidget(parent)
+ScreenPanelNative::ScreenPanelNative(QWidget* parent) : QWidget(parent), ScreenHandler(this)
{
screen[0] = QImage(256, 192, QImage::Format_RGB32);
screen[1] = QImage(256, 192, QImage::Format_RGB32);
@@ -981,17 +1044,12 @@ ScreenPanelNative::ScreenPanelNative(QWidget* parent) : QWidget(parent)
screenTrans[0].reset();
screenTrans[1].reset();
- touching = false;
-
- setAttribute(Qt::WA_AcceptTouchEvents);
-
OSD::Init(nullptr);
}
ScreenPanelNative::~ScreenPanelNative()
{
OSD::DeInit(nullptr);
- mouseTimer->stop();
}
void ScreenPanelNative::setupScreenLayout()
@@ -1090,17 +1148,11 @@ void ScreenPanelNative::onScreenLayoutChanged()
}
-ScreenPanelGL::ScreenPanelGL(QWidget* parent) : QOpenGLWidget(parent)
-{
- touching = false;
-
- setAttribute(Qt::WA_AcceptTouchEvents);
-}
+ScreenPanelGL::ScreenPanelGL(QWidget* parent) : QOpenGLWidget(parent), ScreenHandler(this)
+{}
ScreenPanelGL::~ScreenPanelGL()
{
- mouseTimer->stop();
-
makeCurrent();
OSD::DeInit(this);
@@ -1356,6 +1408,9 @@ MainWindow::MainWindow(QWidget* parent) : QMainWindow(parent)
setWindowTitle("melonDS " MELONDS_VERSION);
setAttribute(Qt::WA_DeleteOnClose);
setAcceptDrops(true);
+ setFocusPolicy(Qt::ClickFocus);
+
+ int inst = Platform::InstanceID();
QMenuBar* menubar = new QMenuBar();
{
@@ -1488,19 +1543,30 @@ MainWindow::MainWindow(QWidget* parent) : QMainWindow(parent)
actEnableCheats->setCheckable(true);
connect(actEnableCheats, &QAction::triggered, this, &MainWindow::onEnableCheats);
- actSetupCheats = menu->addAction("Setup cheat codes");
- actSetupCheats->setMenuRole(QAction::NoRole);
- connect(actSetupCheats, &QAction::triggered, this, &MainWindow::onSetupCheats);
+ //if (inst == 0)
+ {
+ actSetupCheats = menu->addAction("Setup cheat codes");
+ actSetupCheats->setMenuRole(QAction::NoRole);
+ connect(actSetupCheats, &QAction::triggered, this, &MainWindow::onSetupCheats);
- menu->addSeparator();
- actROMInfo = menu->addAction("ROM info");
- connect(actROMInfo, &QAction::triggered, this, &MainWindow::onROMInfo);
+ menu->addSeparator();
+ actROMInfo = menu->addAction("ROM info");
+ connect(actROMInfo, &QAction::triggered, this, &MainWindow::onROMInfo);
- actRAMInfo = menu->addAction("RAM search");
- connect(actRAMInfo, &QAction::triggered, this, &MainWindow::onRAMInfo);
+ actRAMInfo = menu->addAction("RAM search");
+ connect(actRAMInfo, &QAction::triggered, this, &MainWindow::onRAMInfo);
- actTitleManager = menu->addAction("Manage DSi titles");
- connect(actTitleManager, &QAction::triggered, this, &MainWindow::onOpenTitleManager);
+ actTitleManager = menu->addAction("Manage DSi titles");
+ connect(actTitleManager, &QAction::triggered, this, &MainWindow::onOpenTitleManager);
+ }
+
+ {
+ menu->addSeparator();
+ QMenu* submenu = menu->addMenu("Multiplayer");
+
+ actMPNewInstance = submenu->addAction("Launch new instance");
+ connect(actMPNewInstance, &QAction::triggered, this, &MainWindow::onMPNewInstance);
+ }
}
{
QMenu* menu = menubar->addMenu("Config");
@@ -1509,7 +1575,7 @@ MainWindow::MainWindow(QWidget* parent) : QMainWindow(parent)
connect(actEmuSettings, &QAction::triggered, this, &MainWindow::onOpenEmuSettings);
#ifdef __APPLE__
- QAction* actPreferences = menu->addAction("Preferences...");
+ actPreferences = menu->addAction("Preferences...");
connect(actPreferences, &QAction::triggered, this, &MainWindow::onOpenEmuSettings);
actPreferences->setMenuRole(QAction::PreferencesRole);
#endif
@@ -1526,15 +1592,18 @@ MainWindow::MainWindow(QWidget* parent) : QMainWindow(parent)
actAudioSettings = menu->addAction("Audio settings");
connect(actAudioSettings, &QAction::triggered, this, &MainWindow::onOpenAudioSettings);
+ actMPSettings = menu->addAction("Multiplayer settings");
+ connect(actMPSettings, &QAction::triggered, this, &MainWindow::onOpenMPSettings);
+
actWifiSettings = menu->addAction("Wifi settings");
connect(actWifiSettings, &QAction::triggered, this, &MainWindow::onOpenWifiSettings);
- actInterfaceSettings = menu->addAction("Interface settings");
- connect(actInterfaceSettings, &QAction::triggered, this, &MainWindow::onOpenInterfaceSettings);
-
actFirmwareSettings = menu->addAction("Firmware settings");
connect(actFirmwareSettings, &QAction::triggered, this, &MainWindow::onOpenFirmwareSettings);
+ actInterfaceSettings = menu->addAction("Interface settings");
+ connect(actInterfaceSettings, &QAction::triggered, this, &MainWindow::onOpenInterfaceSettings);
+
actPathSettings = menu->addAction("Path settings");
connect(actPathSettings, &QAction::triggered, this, &MainWindow::onOpenPathSettings);
@@ -1638,34 +1707,34 @@ MainWindow::MainWindow(QWidget* parent) : QMainWindow(parent)
{
QMenu* submenu = menu->addMenu("Aspect ratio");
grpScreenAspectTop = new QActionGroup(submenu);
-
- const char* aspectRatiosTop[] = {"Top 4:3 (native)", "Top 16:9", "Top 21:9", "Top window"};
-
- for (int i = 0; i < 4; i++)
- {
- actScreenAspectTop[i] = submenu->addAction(QString(aspectRatiosTop[i]));
- actScreenAspectTop[i]->setActionGroup(grpScreenAspectTop);
- actScreenAspectTop[i]->setData(QVariant(i));
- actScreenAspectTop[i]->setCheckable(true);
- }
-
- connect(grpScreenAspectTop, &QActionGroup::triggered, this, &MainWindow::onChangeScreenAspectTop);
-
- submenu->addSeparator();
-
grpScreenAspectBot = new QActionGroup(submenu);
+ actScreenAspectTop = new QAction*[sizeof(aspectRatios) / sizeof(aspectRatios[0])];
+ actScreenAspectBot = new QAction*[sizeof(aspectRatios) / sizeof(aspectRatios[0])];
- const char* aspectRatiosBot[] = {"Bottom 4:3 (native)", "Bottom 16:9", "Bottom 21:9", "Bottom window"};
-
- for (int i = 0; i < 4; i++)
+ for (int i = 0; i < 2; i++)
{
- actScreenAspectBot[i] = submenu->addAction(QString(aspectRatiosBot[i]));
- actScreenAspectBot[i]->setActionGroup(grpScreenAspectBot);
- actScreenAspectBot[i]->setData(QVariant(i));
- actScreenAspectBot[i]->setCheckable(true);
- }
+ QActionGroup* group = grpScreenAspectTop;
+ QAction** actions = actScreenAspectTop;
- connect(grpScreenAspectBot, &QActionGroup::triggered, this, &MainWindow::onChangeScreenAspectBot);
+ if (i == 1)
+ {
+ group = grpScreenAspectBot;
+ submenu->addSeparator();
+ actions = actScreenAspectBot;
+ }
+
+ for (int j = 0; j < sizeof(aspectRatios) / sizeof(aspectRatios[0]); j++)
+ {
+ auto ratio = aspectRatios[j];
+ QString label = QString("%1 %2").arg(i ? "Bottom" : "Top", ratio.label);
+ actions[j] = submenu->addAction(label);
+ actions[j]->setActionGroup(group);
+ actions[j]->setData(QVariant(ratio.id));
+ actions[j]->setCheckable(true);
+ }
+
+ connect(group, &QActionGroup::triggered, this, &MainWindow::onChangeScreenAspect);
+ }
}
actScreenFiltering = menu->addAction("Screen filtering");
@@ -1690,6 +1759,9 @@ MainWindow::MainWindow(QWidget* parent) : QMainWindow(parent)
resize(Config::WindowWidth, Config::WindowHeight);
+ if (Config::FirmwareUsername == "Arisotura")
+ actMPNewInstance->setText("Fart");
+
#ifdef Q_OS_MAC
QPoint screenCenter = screen()->availableGeometry().center();
QRect frameGeo = frameGeometry();
@@ -1756,8 +1828,13 @@ MainWindow::MainWindow(QWidget* parent) : QMainWindow(parent)
actScreenSwap->setChecked(Config::ScreenSwap);
- actScreenAspectTop[Config::ScreenAspectTop]->setChecked(true);
- actScreenAspectBot[Config::ScreenAspectBot]->setChecked(true);
+ for (int i = 0; i < sizeof(aspectRatios) / sizeof(aspectRatios[0]); i++)
+ {
+ if (Config::ScreenAspectTop == aspectRatios[i].id)
+ actScreenAspectTop[i]->setChecked(true);
+ if (Config::ScreenAspectBot == aspectRatios[i].id)
+ actScreenAspectBot[i]->setChecked(true);
+ }
actScreenFiltering->setChecked(Config::ScreenFilter);
actShowOSD->setChecked(Config::ShowOSD);
@@ -1765,11 +1842,18 @@ MainWindow::MainWindow(QWidget* parent) : QMainWindow(parent)
actLimitFramerate->setChecked(Config::LimitFPS);
actAudioSync->setChecked(Config::AudioSync);
+ if (inst > 0)
+ {
+ actEmuSettings->setEnabled(false);
+ actVideoSettings->setEnabled(false);
+ actMPSettings->setEnabled(false);
+ actWifiSettings->setEnabled(false);
+ actInterfaceSettings->setEnabled(false);
-
- const QList cameras = QCameraInfo::availableCameras();
- for (const QCameraInfo &cameraInfo : cameras)
- printf("CAMERAFAZIL: %s\n", cameraInfo.deviceName().toStdString().c_str());
+#ifdef __APPLE__
+ actPreferences->setEnabled(false);
+#endif // __APPLE__
+ }
}
MainWindow::~MainWindow()
@@ -1780,17 +1864,13 @@ void MainWindow::createScreenPanel()
{
hasOGL = (Config::ScreenUseGL != 0) || (Config::_3DRenderer != 0);
- QTimer* mouseTimer;
-
if (hasOGL)
{
- panelGL = new ScreenPanelGL(this);
+ ScreenPanelGL* panelGL = new ScreenPanelGL(this);
panelGL->show();
panel = panelGL;
- panelGL->setMouseTracking(true);
- mouseTimer = panelGL->setupMouseTimer();
- connect(mouseTimer, &QTimer::timeout, [=] { if (Config::MouseHide) panelGL->setCursor(Qt::BlankCursor);});
+ panelWidget = panelGL;
if (!panelGL->isValid())
hasOGL = false;
@@ -1807,17 +1887,14 @@ void MainWindow::createScreenPanel()
if (!hasOGL)
{
- panelNative = new ScreenPanelNative(this);
+ ScreenPanelNative* panelNative = new ScreenPanelNative(this);
panel = panelNative;
- panel->show();
-
- panelNative->setMouseTracking(true);
- mouseTimer = panelNative->setupMouseTimer();
- connect(mouseTimer, &QTimer::timeout, [=] { if (Config::MouseHide) panelNative->setCursor(Qt::BlankCursor);});
+ panelWidget = panelNative;
+ panelWidget->show();
}
- setCentralWidget(panel);
+ setCentralWidget(panelWidget);
- connect(this, SIGNAL(screenLayoutChange()), panel, SLOT(onScreenLayoutChanged()));
+ connect(this, SIGNAL(screenLayoutChange()), panelWidget, SLOT(onScreenLayoutChanged()));
emit screenLayoutChange();
}
@@ -1825,7 +1902,7 @@ QOpenGLContext* MainWindow::getOGLContext()
{
if (!hasOGL) return nullptr;
- QOpenGLWidget* glpanel = (QOpenGLWidget*)panel;
+ QOpenGLWidget* glpanel = dynamic_cast(panel);
return glpanel->context();
}
@@ -1967,6 +2044,16 @@ void MainWindow::dropEvent(QDropEvent* event)
}
}
+void MainWindow::focusInEvent(QFocusEvent* event)
+{
+ audioMute();
+}
+
+void MainWindow::focusOutEvent(QFocusEvent* event)
+{
+ audioMute();
+}
+
void MainWindow::onAppStateChanged(Qt::ApplicationState state)
{
if (state == Qt::ApplicationInactive)
@@ -2639,6 +2726,23 @@ void MainWindow::onOpenTitleManager()
TitleManagerDialog* dlg = TitleManagerDialog::openDlg(this);
}
+void MainWindow::onMPNewInstance()
+{
+ //QProcess::startDetached(QApplication::applicationFilePath());
+ QProcess newinst;
+ newinst.setProgram(QApplication::applicationFilePath());
+ newinst.setArguments(QApplication::arguments().mid(1, QApplication::arguments().length()-1));
+
+#ifdef __WIN32__
+ newinst.setCreateProcessArgumentsModifier([] (QProcess::CreateProcessArguments *args)
+ {
+ args->flags |= CREATE_NEW_CONSOLE;
+ });
+#endif
+
+ newinst.startDetached();
+}
+
void MainWindow::onOpenEmuSettings()
{
emuThread->emuPause();
@@ -2779,6 +2883,22 @@ void MainWindow::onAudioSettingsFinished(int res)
micOpen();
}
+void MainWindow::onOpenMPSettings()
+{
+ emuThread->emuPause();
+
+ MPSettingsDialog* dlg = MPSettingsDialog::openDlg(this);
+ connect(dlg, &MPSettingsDialog::finished, this, &MainWindow::onMPSettingsFinished);
+}
+
+void MainWindow::onMPSettingsFinished(int res)
+{
+ audioMute();
+ LocalMP::SetRecvTimeout(Config::MPRecvTimeout);
+
+ emuThread->emuUnpause();
+}
+
void MainWindow::onOpenWifiSettings()
{
emuThread->emuPause();
@@ -2789,12 +2909,6 @@ void MainWindow::onOpenWifiSettings()
void MainWindow::onWifiSettingsFinished(int res)
{
- if (Wifi::MPInited)
- {
- Platform::MP_DeInit();
- Platform::MP_Init();
- }
-
Platform::LAN_DeInit();
Platform::LAN_Init();
@@ -2814,10 +2928,7 @@ void MainWindow::onOpenInterfaceSettings()
void MainWindow::onUpdateMouseTimer()
{
- if (hasOGL)
- panelGL->mouseTimer->setInterval(Config::MouseHideSeconds*1000);
- else
- panelNative->mouseTimer->setInterval(Config::MouseHideSeconds*1000);
+ panel->mouseTimer->setInterval(Config::MouseHideSeconds*1000);
}
void MainWindow::onInterfaceSettingsFinished(int res)
@@ -2833,8 +2944,8 @@ void MainWindow::onChangeSavestateSRAMReloc(bool checked)
void MainWindow::onChangeScreenSize()
{
int factor = ((QAction*)sender())->data().toInt();
- QSize diff = size() - panel->size();
- resize(dynamic_cast(panel)->screenGetMinSize(factor) + diff);
+ QSize diff = size() - panelWidget->size();
+ resize(panel->screenGetMinSize(factor) + diff);
}
void MainWindow::onChangeScreenRotation(QAction* act)
@@ -2892,18 +3003,19 @@ void MainWindow::onChangeScreenSizing(QAction* act)
emit screenLayoutChange();
}
-void MainWindow::onChangeScreenAspectTop(QAction* act)
+void MainWindow::onChangeScreenAspect(QAction* act)
{
int aspect = act->data().toInt();
- Config::ScreenAspectTop = aspect;
+ QActionGroup* group = act->actionGroup();
- emit screenLayoutChange();
-}
-
-void MainWindow::onChangeScreenAspectBot(QAction* act)
-{
- int aspect = act->data().toInt();
- Config::ScreenAspectBot = aspect;
+ if (group == grpScreenAspectTop)
+ {
+ Config::ScreenAspectTop = aspect;
+ }
+ else
+ {
+ Config::ScreenAspectBot = aspect;
+ }
emit screenLayoutChange();
}
@@ -3006,16 +3118,10 @@ void MainWindow::onUpdateVideoSettings(bool glchange)
emuThread->emuPause();
if (hasOGL)
- {
emuThread->deinitOpenGL();
- delete panelGL;
- }
- else
- {
- delete panelNative;
- }
+ delete panel;
createScreenPanel();
- connect(emuThread, SIGNAL(windowUpdate()), panel, SLOT(repaint()));
+ connect(emuThread, SIGNAL(windowUpdate()), panelWidget, SLOT(repaint()));
if (hasOGL) emuThread->initOpenGL();
}
@@ -3121,6 +3227,7 @@ int main(int argc, char** argv)
format.setSwapInterval(0);
QSurfaceFormat::setDefaultFormat(format);
+ audioMuted = false;
audioSync = SDL_CreateCond();
audioSyncLock = SDL_CreateMutex();
@@ -3176,6 +3283,8 @@ int main(int argc, char** argv)
emuThread->start();
emuThread->emuPause();
+ audioMute();
+
QObject::connect(&melon, &QApplication::applicationStateChanged, mainWindow, &MainWindow::onAppStateChanged);
if (argc > 1)
@@ -3221,7 +3330,7 @@ int CALLBACK WinMain(HINSTANCE hinst, HINSTANCE hprev, LPSTR cmdline, int cmdsho
{
int argc = 0;
wchar_t** argv_w = CommandLineToArgvW(GetCommandLineW(), &argc);
- char* nullarg = "";
+ char nullarg[] = {'\0'};
char** argv = new char*[argc];
for (int i = 0; i < argc; i++)
@@ -3236,7 +3345,8 @@ int CALLBACK WinMain(HINSTANCE hinst, HINSTANCE hprev, LPSTR cmdline, int cmdsho
if (argv_w) LocalFree(argv_w);
- /*if (AttachConsole(ATTACH_PARENT_PROCESS))
+ //if (AttachConsole(ATTACH_PARENT_PROCESS))
+ /*if (AllocConsole())
{
freopen("CONOUT$", "w", stdout);
freopen("CONOUT$", "w", stderr);
diff --git a/src/frontend/qt_sdl/main.h b/src/frontend/qt_sdl/main.h
index 2c3078c3..515ddd77 100644
--- a/src/frontend/qt_sdl/main.h
+++ b/src/frontend/qt_sdl/main.h
@@ -101,7 +101,8 @@ class ScreenHandler
Q_GADGET
public:
- virtual ~ScreenHandler() {}
+ ScreenHandler(QWidget* widget);
+ virtual ~ScreenHandler();
QTimer* setupMouseTimer();
void updateMouseTimer();
QTimer* mouseTimer;
@@ -121,7 +122,7 @@ protected:
int screenKind[Frontend::MaxScreenTransforms];
int numScreens;
- bool touching;
+ bool touching = false;
void showCursor();
};
@@ -133,7 +134,7 @@ class ScreenPanelNative : public QWidget, public ScreenHandler
public:
explicit ScreenPanelNative(QWidget* parent);
- ~ScreenPanelNative();
+ virtual ~ScreenPanelNative();
protected:
void paintEvent(QPaintEvent* event) override;
@@ -163,7 +164,7 @@ class ScreenPanelGL : public QOpenGLWidget, public ScreenHandler, protected QOpe
public:
explicit ScreenPanelGL(QWidget* parent);
- ~ScreenPanelGL();
+ virtual ~ScreenPanelGL();
protected:
void initializeGL() override;
@@ -225,6 +226,9 @@ protected:
void dragEnterEvent(QDragEnterEvent* event) override;
void dropEvent(QDropEvent* event) override;
+ void focusInEvent(QFocusEvent* event) override;
+ void focusOutEvent(QFocusEvent* event) override;
+
signals:
void screenLayoutChange();
@@ -254,6 +258,7 @@ private slots:
void onROMInfo();
void onRAMInfo();
void onOpenTitleManager();
+ void onMPNewInstance();
void onOpenEmuSettings();
void onEmuSettingsDialogFinished(int res);
@@ -267,6 +272,8 @@ private slots:
void onOpenPathSettings();
void onUpdateAudioSettings();
void onAudioSettingsFinished(int res);
+ void onOpenMPSettings();
+ void onMPSettingsFinished(int res);
void onOpenWifiSettings();
void onWifiSettingsFinished(int res);
void onFirmwareSettingsFinished(int res);
@@ -281,8 +288,7 @@ private slots:
void onChangeScreenLayout(QAction* act);
void onChangeScreenSwap(bool checked);
void onChangeScreenSizing(QAction* act);
- void onChangeScreenAspectTop(QAction* act);
- void onChangeScreenAspectBot(QAction* act);
+ void onChangeScreenAspect(QAction* act);
void onChangeIntegerScaling(bool checked);
void onChangeScreenFiltering(bool checked);
void onChangeShowOSD(bool checked);
@@ -318,9 +324,8 @@ private:
bool oldMax;
public:
- QWidget* panel;
- ScreenPanelGL* panelGL;
- ScreenPanelNative* panelNative;
+ ScreenHandler* panel;
+ QWidget* panelWidget;
QAction* actOpenROM;
QAction* actBootFirmware;
@@ -346,13 +351,18 @@ public:
QAction* actROMInfo;
QAction* actRAMInfo;
QAction* actTitleManager;
+ QAction* actMPNewInstance;
QAction* actEmuSettings;
+#ifdef __APPLE__
+ QAction* actPreferences;
+#endif
QAction* actPowerManagement;
QAction* actInputConfig;
QAction* actVideoSettings;
QAction* actCameraSettings;
QAction* actAudioSettings;
+ QAction* actMPSettings;
QAction* actWifiSettings;
QAction* actFirmwareSettings;
QAction* actPathSettings;
@@ -370,9 +380,9 @@ public:
QAction* actScreenSizing[6];
QAction* actIntegerScaling;
QActionGroup* grpScreenAspectTop;
- QAction* actScreenAspectTop[4];
+ QAction** actScreenAspectTop;
QActionGroup* grpScreenAspectBot;
- QAction* actScreenAspectBot[4];
+ QAction** actScreenAspectBot;
QAction* actScreenFiltering;
QAction* actShowOSD;
QAction* actLimitFramerate;
diff --git a/src/frontend/qt_sdl/sem_timedwait.cpp b/src/frontend/qt_sdl/sem_timedwait.cpp
new file mode 100644
index 00000000..38b3c16f
--- /dev/null
+++ b/src/frontend/qt_sdl/sem_timedwait.cpp
@@ -0,0 +1,488 @@
+/*
+ * s e m _ t i m e d w a i t
+ *
+ * Function:
+ * Implements a version of sem_timedwait().
+ *
+ * Description:
+ * Not all systems implement sem_timedwait(), which is a version of
+ * sem_wait() with a timeout. Mac OS X is one example, at least up to
+ * and including version 10.6 (Leopard). If such a function is needed,
+ * this code provides a reasonable implementation, which I think is
+ * compatible with the standard version, although possibly less
+ * efficient. It works by creating a thread that interrupts a normal
+ * sem_wait() call after the specified timeout.
+ *
+ * Call:
+ *
+ * The Linux man pages say:
+ *
+ * #include
+ *
+ * int sem_timedwait(sem_t *sem, const struct timespec *abs_timeout);
+ *
+ * sem_timedwait() is the same as sem_wait(), except that abs_timeout
+ * specifies a limit on the amount of time that the call should block if
+ * the decrement cannot be immediately performed. The abs_timeout argument
+ * points to a structure that specifies an absolute timeout in seconds and
+ * nanoseconds since the Epoch (00:00:00, 1 January 1970). This structure
+ * is defined as follows:
+ *
+ * struct timespec {
+ * time_t tv_sec; Seconds
+ * long tv_nsec; Nanoseconds [0 .. 999999999]
+ * };
+ *
+ * If the timeout has already expired by the time of the call, and the
+ * semaphore could not be locked immediately, then sem_timedwait() fails
+ * with a timeout error (errno set to ETIMEDOUT).
+ * If the operation can be performed immediately, then sem_timedwait()
+ * never fails with a timeout error, regardless of the value of abs_timeout.
+ * Furthermore, the validity of abs_timeout is not checked in this case.
+ *
+ * Limitations:
+ *
+ * The mechanism used involves sending a SIGUSR2 signal to the thread
+ * calling sem_timedwait(). The handler for this signal is set to a null
+ * routine which does nothing, and with any flags for the signal
+ * (eg SA_RESTART) cleared. Note that this effective disabling of the
+ * SIGUSR2 signal is a side-effect of using this routine, and means it
+ * may not be a completely transparent plug-in replacement for a
+ * 'normal' sig_timedwait() call. Since OS X does not declare the
+ * sem_timedwait() call in its standard include files, the relevant
+ * declaration (shown above in the man pages extract) will probably have
+ * to be added to any code that uses this.
+ *
+ * Compiling:
+ * This compiles and runs cleanly on OS X (10.6) with gcc with the
+ * -Wall -ansi -pedantic flags. On Linux, using -ansi causes a sweep of
+ * compiler complaints about the timespec structure, but it compiles
+ * and works fine with just -Wall -pedantic. (Since Linux provides
+ * sem_timedwait() anyway, this really isn't needed on Linux.) However,
+ * since Linux provides sem_timedwait anyway, the sem_timedwait()
+ * code in this file is only compiled on OS X, and is a null on other
+ * systems.
+ *
+ * Testing:
+ * This file contains a test program that exercises the sem_timedwait
+ * code. It is compiled if the pre-processor variable TEST is defined.
+ * For more details, see the comments for the test routine at the end
+ * of the file.
+ *
+ * Author: Keith Shortridge, AAO.
+ *
+ * History:
+ * 8th Sep 2009. Original version. KS.
+ * 24th Sep 2009. Added test that the calling thread still exists before
+ * trying to set the timed-out flag. KS.
+ * 2nd Oct 2009. No longer restores the original SIGUSR2 signal handler.
+ * See comments in the body of the code for more details.
+ * Prototypes for now discontinued internal routines removed.
+ * 12th Aug 2010. Added the cleanup handler, so that this code no longer
+ * leaks resources if the calling thread is cancelled. KS.
+ * 21st Sep 2011. Added copyright notice below. Modified header comments
+ * to describe the use of SIGUSR2 more accurately in the
+ * light of the 2/10/09 change above. Now undefs DEBUG
+ * before defining it, to avoid any possible clash. KS.
+ * 14th Feb 2012. Tidied out a number of TABs that had got into the
+ * code. KS.
+ * 6th May 2013. Copyright notice modified to one based on the MIT licence,
+ * which is more permissive than the previous notice. KS.
+ *
+ * Copyright (c) Australian Astronomical Observatory (AAO), (2013).
+ * Permission is hereby granted, free of charge, to any person obtaining a
+ * copy of this software and associated documentation files (the "Software"),
+ * to deal in the Software without restriction, including without limitation
+ * the rights to use, copy, modify, merge, publish, distribute, sublicense,
+ * and/or sell copies of the Software, and to permit persons to whom the
+ * Software is furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
+ * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
+ * IN THE SOFTWARE.
+ */
+
+#ifdef __APPLE__
+
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+
+#include "sem_timedwait.h"
+
+/* Some useful definitions - TRUE, FALSE, and DEBUG */
+
+#undef TRUE
+#define TRUE 1
+#undef FALSE
+#define FALSE 0
+#undef DEBUG
+#define DEBUG printf
+
+/* A structure of type timeoutDetails is passed to the thread used to
+ * implement the timeout.
+ */
+
+typedef struct {
+ struct timespec delay; /* Specifies the delay, relative to now */
+ pthread_t callingThread; /* The thread doing the sem_wait call */
+ volatile short *timedOutShort; /* Address of a flag set to indicate that
+ * the timeout was triggered. */
+} timeoutDetails;
+
+/* A structure of type cleanupDetails is passed to the thread cleanup
+ * routine which is called at the end of the routine or if the thread calling
+ * it is cancelled.
+ */
+
+typedef struct {
+ pthread_t *threadIdAddr; /* Address of the variable that holds
+ * the Id of the timeout thread. */
+ struct sigaction *sigHandlerAddr; /* Address of the old signal action
+ * handler. */
+ volatile short *timedOutShort; /* Address of a flag set to indicate that
+ * the timeout was triggered. */
+} cleanupDetails;
+
+/* Forward declarations of internal routines */
+
+static void* timeoutThreadMain (void* passedPtr);
+static int triggerSignal (int Signal, pthread_t Thread);
+static void ignoreSignal (int Signal);
+static void timeoutThreadCleanup (void* passedPtr);
+
+/* -------------------------------------------------------------------------- */
+/*
+ * s e m _ t i m e d w a i t
+ *
+ * This is the main code for the sem_timedwait() implementation.
+ */
+
+int sem_timedwait (
+ sem_t *sem,
+ const struct timespec *abs_timeout)
+{
+ int result = 0; /* Code returned by this routine 0 or -1 */
+
+ /* "Under no circumstances shall the function fail if the semaphore
+ * can be locked immediately". So we try to get it quickly to see if we
+ * can avoid all the timeout overheads.
+ */
+
+ if (sem_trywait(sem) == 0) {
+
+ /* Yes, got it immediately. */
+
+ result = 0;
+
+ } else {
+
+ /* No, we've got to do it with a sem_wait() call and a thread to run
+ * the timeout. First, work out the time from now to the specified
+ * timeout, which we will pass to the timeout thread in a way that can
+ * be used to pass to nanosleep(). So we need this in seconds and
+ * nanoseconds. Along the way, we check for an invalid passed time,
+ * and for one that's already expired.
+ */
+
+ if ((abs_timeout->tv_nsec < 0) || (abs_timeout->tv_nsec > 1000000000)) {
+
+ /* Passed time is invalid */
+
+ result = -1;
+ errno = EINVAL;
+
+ } else {
+
+ struct timeval currentTime; /* Time now */
+ long secsToWait,nsecsToWait; /* Seconds and nsec to delay */
+ gettimeofday (¤tTime,NULL);
+ secsToWait = abs_timeout->tv_sec - currentTime.tv_sec;
+ nsecsToWait = (abs_timeout->tv_nsec - (currentTime.tv_usec * 1000));
+ while (nsecsToWait < 0) {
+ nsecsToWait += 1000000000;
+ secsToWait--;
+ }
+ if ((secsToWait < 0) || ((secsToWait == 0) && (nsecsToWait < 0))) {
+
+ /* Time has passed. Report an immediate timeout. */
+
+ result = -1;
+ errno = ETIMEDOUT;
+
+ } else {
+
+ /* We're going to have to do a sem_wait() with a timeout thread.
+ * The thread will wait the specified time, then will issue a
+ * SIGUSR2 signal that will interrupt the sem_wait() call.
+ * We pass the thread the id of the current thread, the delay,
+ * and the address of a flag to set on a timeout, so we can
+ * distinguish an interrupt caused by the timeout thread from
+ * one caused by some other signal.
+ */
+
+ volatile short timedOut; /* Flag to set on timeout */
+ timeoutDetails details; /* All the stuff the thread must know */
+ struct sigaction oldSignalAction; /* Current signal setting */
+ pthread_t timeoutThread; /* Id of timeout thread */
+ cleanupDetails cleaningDetails; /* What the cleanup routine needs */
+ int oldCancelState; /* Previous cancellation state */
+ int ignoreCancelState; /* Used in call, but ignored */
+ int createStatus; /* Status of pthread_create() call */
+
+ /* If the current thread is cancelled (and CML does do this)
+ * we don't want to leave our timer thread running - if we've
+ * started the thread we want to make sure we join it in order
+ * to release its resources. So we set a cleanup handler to
+ * do this. We pass it the address of the structure that will
+ * hold all it needs to know. While we set all this up,
+ * we prevent ourselves being cancelled, so all this data is
+ * coherent.
+ */
+
+ pthread_setcancelstate (PTHREAD_CANCEL_DISABLE,&oldCancelState);
+ timeoutThread = (pthread_t) 0;
+ cleaningDetails.timedOutShort = &timedOut;
+ cleaningDetails.threadIdAddr = &timeoutThread;
+ cleaningDetails.sigHandlerAddr = &oldSignalAction;
+ pthread_cleanup_push (timeoutThreadCleanup,&cleaningDetails);
+
+ /* Set up the details for the thread. Clear the timeout flag,
+ * record the current SIGUSR2 action settings so we can restore
+ * them later.
+ */
+
+ details.delay.tv_sec = secsToWait;
+ details.delay.tv_nsec = nsecsToWait;
+ details.callingThread = pthread_self();
+ details.timedOutShort = &timedOut;
+ timedOut = FALSE;
+ sigaction (SIGUSR2,NULL,&oldSignalAction);
+
+ /* Start up the timeout thread. Once we've done that, we can
+ * restore the previous cancellation state.
+ */
+
+ createStatus = pthread_create(&timeoutThread,NULL,
+ timeoutThreadMain, (void*)&details);
+ pthread_setcancelstate (oldCancelState,&ignoreCancelState);
+
+ if (createStatus < 0) {
+
+ /* Failed to create thread. errno will already be set properly */
+
+ result = -1;
+
+ } else {
+
+ /* Thread created OK. This is where we wait for the semaphore.
+ */
+
+ if (sem_wait(sem) == 0) {
+
+ /* Got the semaphore OK. We return zero, and all's well. */
+
+ result = 0;
+
+ } else {
+
+ /* If we got a -1 error from sem_wait(), it may be because
+ * it was interrupted by a timeout, or failed for some
+ * other reason. We check for the expected timeout
+ * condition, which is an 'interrupted' status and the
+ * timeout flag set by the timeout thread. We report that as
+ * a timeout error. Anything else is some other error and
+ * errno is already set properly.
+ */
+
+ result = -1;
+ if (errno == EINTR) {
+ if (timedOut) errno = ETIMEDOUT;
+ }
+ }
+
+ }
+
+ /* The cleanup routine - timeoutThreadCleanup() - packages up
+ * any tidying up that is needed, including joining with the
+ * timer thread. This will be called if the current thread is
+ * cancelled, but we need it to happen anyway, so we set the
+ * execute flag true here as we remove it from the list of
+ * cleanup routines to be called. So normally, this line amounts
+ * to calling timeoutThreadCleanup().
+ */
+
+ pthread_cleanup_pop (TRUE);
+ }
+ }
+ }
+ return (result);
+}
+
+/* -------------------------------------------------------------------------- */
+/*
+ * t i m e o u t T h r e a d C l e a n u p
+ *
+ * This internal routine tidies up at the end of a sem_timedwait() call.
+ * It is set as a cleanup routine for the current thread (not the timer
+ * thread) so it is executed even if the thread is cancelled. This is
+ * important, as we need to tidy up the timeout thread. If we took the
+ * semaphore (in other words, if we didn't timeout) then the timer thread
+ * will still be running, sitting in its nanosleep() call, and we need
+ * to cancel it. If the timer thread did signal a timeout then it will
+ * now be closing down. In either case, we need to join it (using a call
+ * to pthread_join()) or its resources will never be released.
+ * The single argument is a pointer to a cleanupDetails structure that has
+ * all the routine needs to know.
+ */
+
+static void timeoutThreadCleanup (void* passedPtr)
+{
+ /* Get what we need from the structure we've been passed. */
+
+ cleanupDetails *detailsPtr = (cleanupDetails*) passedPtr;
+ short timedOut = *(detailsPtr->timedOutShort);
+ pthread_t timeoutThread = *(detailsPtr->threadIdAddr);
+
+ /* If we created the thread, stop it - doesn't matter if it's no longer
+ * running, pthread_cancel can handle that. We make sure we wait for it
+ * to complete, because it is this pthread_join() call that releases any
+ * memory the thread may have allocated. Note that cancelling a thread is
+ * generally not a good idea, because of the difficulty of cleaning up
+ * after it, but this is a very simple thread that does nothing but call
+ * nanosleep(), and that we can cancel quite happily.
+ */
+
+ if (!timedOut) pthread_cancel(timeoutThread);
+ pthread_join(timeoutThread,NULL);
+
+ /* The code originally restored the old action handler, which generally
+ * was the default handler that caused the task to exit. Just occasionally,
+ * there seem to be cases where the signal is still queued and ready to
+ * trigger even though the thread that presumably sent it off just before
+ * it was cancelled has finished. I had thought that once we'd joined
+ * that thread, we could be sure of not seeing the signal, but that seems
+ * not to be the case, and so restoring a handler that will allow the task
+ * to crash is not a good idea, and so the line below has been commented
+ * out.
+ *
+ * sigaction (SIGUSR2,detailsPtr->sigHandlerAddr,NULL);
+ */
+}
+
+/* -------------------------------------------------------------------------- */
+/*
+ * t i m e o u t T h r e a d M a i n
+ *
+ * This internal routine is the main code for the timeout thread.
+ * The single argument is a pointer to a timeoutDetails structure that has
+ * all the thread needs to know - thread to signal, delay time, and the
+ * address of a flag to set if it triggers a timeout.
+ */
+
+static void* timeoutThreadMain (void* passedPtr)
+{
+ void* Return = (void*) 0;
+
+ /* We grab all the data held in the calling thread right now. In some
+ * cases, we find that the calling thread has vanished and released
+ * its memory, including the details structure, by the time the timeout
+ * expires, and then we get an access violation when we try to set the
+ * 'timed out' flag.
+ */
+
+ timeoutDetails details = *((timeoutDetails*) passedPtr);
+ struct timespec requestedDelay = details.delay;
+
+ /* We do a nanosleep() for the specified delay, and then trigger a
+ * timeout. Note that we allow for the case where the nanosleep() is
+ * interrupted, and restart it for the remaining time. If the
+ * thread that is doing the sem_wait() call gets the semaphore, it
+ * will cancel this thread, which is fine as we aren't doing anything
+ * other than a sleep and a signal.
+ */
+
+ for (;;) {
+ struct timespec remainingDelay;
+ if (nanosleep (&requestedDelay,&remainingDelay) == 0) {
+ break;
+ } else if (errno == EINTR) {
+ requestedDelay = remainingDelay;
+ } else {
+ Return = (void*) errno;
+ break;
+ }
+ }
+
+ /* We've completed the delay without being cancelled, so we now trigger
+ * the timeout by sending a signal to the calling thread. And that's it,
+ * although we set the timeout flag first to indicate that it was us
+ * that interrupted the sem_wait() call. One precaution: before we
+ * try to set the timed-out flag, make sure the calling thread still
+ * exists - this may not be the case if things are closing down a bit
+ * messily. We check this quickly using a zero test signal.
+ */
+
+ if (pthread_kill(details.callingThread,0) == 0) {
+ *(details.timedOutShort) = TRUE;
+ if (triggerSignal (SIGUSR2,details.callingThread) < 0) {
+ Return = (void*) errno;
+ }
+ }
+
+ return Return;
+}
+
+/* -------------------------------------------------------------------------- */
+/*
+ * t r i g g e r S i g n a l
+ *
+ * This is a general purpose routine that sends a specified signal to
+ * a specified thread, setting up a signal handler that does nothing,
+ * and then giving the signal. The only effect will be to interrupt any
+ * operation that is currently blocking - in this case, we expect this to
+ * be a sem_wait() call.
+ */
+
+static int triggerSignal (int Signal, pthread_t Thread)
+{
+ int Result = 0;
+ struct sigaction SignalDetails;
+ SignalDetails.sa_handler = ignoreSignal;
+ SignalDetails.sa_flags = 0;
+ (void) sigemptyset(&SignalDetails.sa_mask);
+ if ((Result = sigaction(Signal,&SignalDetails,NULL)) == 0) {
+ Result = pthread_kill(Thread,Signal);
+ }
+ return Result;
+}
+
+/* -------------------------------------------------------------------------- */
+/*
+ * i g n o r e S i g n a l
+ *
+ * And this is the signal handler that does nothing. (It clears its argument,
+ * but this has no effect and prevents a compiler warning about an unused
+ * argument.)
+ */
+
+static void ignoreSignal (int Signal) {
+ Signal = 0;
+}
+
+#endif
diff --git a/src/frontend/qt_sdl/sem_timedwait.h b/src/frontend/qt_sdl/sem_timedwait.h
new file mode 100644
index 00000000..42ae201e
--- /dev/null
+++ b/src/frontend/qt_sdl/sem_timedwait.h
@@ -0,0 +1,8 @@
+#ifndef __SEM_TIMEDWAIT_H
+#define __SEM_TIMEDWAIT_H
+
+#ifdef __APPLE__
+int sem_timedwait(sem_t *sem, const struct timespec *abs_timeout);
+#endif
+
+#endif
diff --git a/src/melonDLDI.h b/src/melonDLDI.h
index 351bc158..261f798b 100644
--- a/src/melonDLDI.h
+++ b/src/melonDLDI.h
@@ -19,6 +19,8 @@
#ifndef MELONDLDI_H
#define MELONDLDI_H
+#include "types.h"
+
const u8 melonDLDI[] =
{
0xED, 0xA5, 0x8D, 0xBF, 0x20, 0x43, 0x68, 0x69, 0x73, 0x68, 0x6D, 0x00, 0x01, 0x09, 0x00, 0x00,
diff --git a/src/teakra/CMakeLists.txt b/src/teakra/CMakeLists.txt
index 046f8c45..e4ecb1af 100644
--- a/src/teakra/CMakeLists.txt
+++ b/src/teakra/CMakeLists.txt
@@ -58,6 +58,7 @@ else()
-pedantic
-pedantic-errors
-Wfatal-errors
+ -Wno-error=maybe-uninitialized
-Wno-missing-braces
-Wno-unused-parameter)
diff --git a/src/teakra/externals/catch/catch.hpp b/src/teakra/externals/catch/catch.hpp
index b1b2411d..71c863ab 100644
--- a/src/teakra/externals/catch/catch.hpp
+++ b/src/teakra/externals/catch/catch.hpp
@@ -1,9 +1,9 @@
/*
- * Catch v2.5.0
- * Generated: 2018-11-26 20:46:12.165372
+ * Catch v2.13.8
+ * Generated: 2022-01-03 21:20:09.589503
* ----------------------------------------------------------
* This file has been merged from multiple headers. Please don't edit it directly
- * Copyright (c) 2018 Two Blue Cubes Ltd. All rights reserved.
+ * Copyright (c) 2022 Two Blue Cubes Ltd. All rights reserved.
*
* Distributed under the Boost Software License, Version 1.0. (See accompanying
* file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)
@@ -14,8 +14,8 @@
#define CATCH_VERSION_MAJOR 2
-#define CATCH_VERSION_MINOR 5
-#define CATCH_VERSION_PATCH 0
+#define CATCH_VERSION_MINOR 13
+#define CATCH_VERSION_PATCH 8
#ifdef __clang__
# pragma clang system_header
@@ -36,10 +36,11 @@
# pragma clang diagnostic ignored "-Wcovered-switch-default"
# endif
#elif defined __GNUC__
- // GCC likes to warn on REQUIREs, and we cannot suppress them
- // locally because g++'s support for _Pragma is lacking in older,
- // still supported, versions
-# pragma GCC diagnostic ignored "-Wparentheses"
+ // Because REQUIREs trigger GCC's -Wparentheses, and because still
+ // supported version of g++ have only buggy support for _Pragmas,
+ // Wparentheses have to be suppressed globally.
+# pragma GCC diagnostic ignored "-Wparentheses" // See #674 for details
+
# pragma GCC diagnostic push
# pragma GCC diagnostic ignored "-Wunused-variable"
# pragma GCC diagnostic ignored "-Wpadded"
@@ -65,13 +66,16 @@
#if !defined(CATCH_CONFIG_IMPL_ONLY)
// start catch_platform.h
+// See e.g.:
+// https://opensource.apple.com/source/CarbonHeaders/CarbonHeaders-18.1/TargetConditionals.h.auto.html
#ifdef __APPLE__
-# include
-# if TARGET_OS_OSX == 1
-# define CATCH_PLATFORM_MAC
-# elif TARGET_OS_IPHONE == 1
-# define CATCH_PLATFORM_IPHONE
-# endif
+# include
+# if (defined(TARGET_OS_OSX) && TARGET_OS_OSX == 1) || \
+ (defined(TARGET_OS_MAC) && TARGET_OS_MAC == 1)
+# define CATCH_PLATFORM_MAC
+# elif (defined(TARGET_OS_IPHONE) && TARGET_OS_IPHONE == 1)
+# define CATCH_PLATFORM_IPHONE
+# endif
#elif defined(linux) || defined(__linux) || defined(__linux__)
# define CATCH_PLATFORM_LINUX
@@ -131,30 +135,51 @@ namespace Catch {
#endif
-#if defined(CATCH_CPP17_OR_GREATER)
-# define CATCH_INTERNAL_CONFIG_CPP17_UNCAUGHT_EXCEPTIONS
+// Only GCC compiler should be used in this block, so other compilers trying to
+// mask themselves as GCC should be ignored.
+#if defined(__GNUC__) && !defined(__clang__) && !defined(__ICC) && !defined(__CUDACC__) && !defined(__LCC__)
+# define CATCH_INTERNAL_START_WARNINGS_SUPPRESSION _Pragma( "GCC diagnostic push" )
+# define CATCH_INTERNAL_STOP_WARNINGS_SUPPRESSION _Pragma( "GCC diagnostic pop" )
+
+# define CATCH_INTERNAL_IGNORE_BUT_WARN(...) (void)__builtin_constant_p(__VA_ARGS__)
+
#endif
-#ifdef __clang__
+#if defined(__clang__)
-# define CATCH_INTERNAL_SUPPRESS_GLOBALS_WARNINGS \
- _Pragma( "clang diagnostic push" ) \
- _Pragma( "clang diagnostic ignored \"-Wexit-time-destructors\"" ) \
- _Pragma( "clang diagnostic ignored \"-Wglobal-constructors\"")
-# define CATCH_INTERNAL_UNSUPPRESS_GLOBALS_WARNINGS \
- _Pragma( "clang diagnostic pop" )
+# define CATCH_INTERNAL_START_WARNINGS_SUPPRESSION _Pragma( "clang diagnostic push" )
+# define CATCH_INTERNAL_STOP_WARNINGS_SUPPRESSION _Pragma( "clang diagnostic pop" )
-# define CATCH_INTERNAL_SUPPRESS_PARENTHESES_WARNINGS \
- _Pragma( "clang diagnostic push" ) \
- _Pragma( "clang diagnostic ignored \"-Wparentheses\"" )
-# define CATCH_INTERNAL_UNSUPPRESS_PARENTHESES_WARNINGS \
- _Pragma( "clang diagnostic pop" )
+// As of this writing, IBM XL's implementation of __builtin_constant_p has a bug
+// which results in calls to destructors being emitted for each temporary,
+// without a matching initialization. In practice, this can result in something
+// like `std::string::~string` being called on an uninitialized value.
+//
+// For example, this code will likely segfault under IBM XL:
+// ```
+// REQUIRE(std::string("12") + "34" == "1234")
+// ```
+//
+// Therefore, `CATCH_INTERNAL_IGNORE_BUT_WARN` is not implemented.
+# if !defined(__ibmxl__) && !defined(__CUDACC__)
+# define CATCH_INTERNAL_IGNORE_BUT_WARN(...) (void)__builtin_constant_p(__VA_ARGS__) /* NOLINT(cppcoreguidelines-pro-type-vararg, hicpp-vararg) */
+# endif
-# define CATCH_INTERNAL_SUPPRESS_UNUSED_WARNINGS \
- _Pragma( "clang diagnostic push" ) \
- _Pragma( "clang diagnostic ignored \"-Wunused-variable\"" )
-# define CATCH_INTERNAL_UNSUPPRESS_UNUSED_WARNINGS \
- _Pragma( "clang diagnostic pop" )
+# define CATCH_INTERNAL_SUPPRESS_GLOBALS_WARNINGS \
+ _Pragma( "clang diagnostic ignored \"-Wexit-time-destructors\"" ) \
+ _Pragma( "clang diagnostic ignored \"-Wglobal-constructors\"")
+
+# define CATCH_INTERNAL_SUPPRESS_PARENTHESES_WARNINGS \
+ _Pragma( "clang diagnostic ignored \"-Wparentheses\"" )
+
+# define CATCH_INTERNAL_SUPPRESS_UNUSED_WARNINGS \
+ _Pragma( "clang diagnostic ignored \"-Wunused-variable\"" )
+
+# define CATCH_INTERNAL_SUPPRESS_ZERO_VARIADIC_WARNINGS \
+ _Pragma( "clang diagnostic ignored \"-Wgnu-zero-variadic-macro-arguments\"" )
+
+# define CATCH_INTERNAL_SUPPRESS_UNUSED_TEMPLATE_WARNINGS \
+ _Pragma( "clang diagnostic ignored \"-Wunused-template\"" )
#endif // __clang__
@@ -179,6 +204,7 @@ namespace Catch {
// Android somehow still does not support std::to_string
#if defined(__ANDROID__)
# define CATCH_INTERNAL_CONFIG_NO_CPP11_TO_STRING
+# define CATCH_INTERNAL_CONFIG_ANDROID_LOGWRITE
#endif
////////////////////////////////////////////////////////////////////////////////
@@ -203,20 +229,16 @@ namespace Catch {
// some versions of cygwin (most) do not support std::to_string. Use the libstd check.
// https://gcc.gnu.org/onlinedocs/gcc-4.8.2/libstdc++/api/a01053_source.html line 2812-2813
# if !((__cplusplus >= 201103L) && defined(_GLIBCXX_USE_C99) \
- && !defined(_GLIBCXX_HAVE_BROKEN_VSWPRINTF))
+ && !defined(_GLIBCXX_HAVE_BROKEN_VSWPRINTF))
-# define CATCH_INTERNAL_CONFIG_NO_CPP11_TO_STRING
+# define CATCH_INTERNAL_CONFIG_NO_CPP11_TO_STRING
# endif
#endif // __CYGWIN__
////////////////////////////////////////////////////////////////////////////////
// Visual C++
-#ifdef _MSC_VER
-
-# if _MSC_VER >= 1900 // Visual Studio 2015 or newer
-# define CATCH_INTERNAL_CONFIG_CPP17_UNCAUGHT_EXCEPTIONS
-# endif
+#if defined(_MSC_VER)
// Universal Windows platform does not support SEH
// Or console colours (or console at all...)
@@ -226,13 +248,25 @@ namespace Catch {
# define CATCH_INTERNAL_CONFIG_WINDOWS_SEH
# endif
+# if !defined(__clang__) // Handle Clang masquerading for msvc
+
// MSVC traditional preprocessor needs some workaround for __VA_ARGS__
// _MSVC_TRADITIONAL == 0 means new conformant preprocessor
// _MSVC_TRADITIONAL == 1 means old traditional non-conformant preprocessor
-# if !defined(_MSVC_TRADITIONAL) || (defined(_MSVC_TRADITIONAL) && _MSVC_TRADITIONAL)
-# define CATCH_INTERNAL_CONFIG_TRADITIONAL_MSVC_PREPROCESSOR
-# endif
+# if !defined(_MSVC_TRADITIONAL) || (defined(_MSVC_TRADITIONAL) && _MSVC_TRADITIONAL)
+# define CATCH_INTERNAL_CONFIG_TRADITIONAL_MSVC_PREPROCESSOR
+# endif // MSVC_TRADITIONAL
+// Only do this if we're not using clang on Windows, which uses `diagnostic push` & `diagnostic pop`
+# define CATCH_INTERNAL_START_WARNINGS_SUPPRESSION __pragma( warning(push) )
+# define CATCH_INTERNAL_STOP_WARNINGS_SUPPRESSION __pragma( warning(pop) )
+# endif // __clang__
+
+#endif // _MSC_VER
+
+#if defined(_REENTRANT) || defined(_MSC_VER)
+// Enable async processing, as -pthread is specified or no additional linking is required
+# define CATCH_INTERNAL_CONFIG_USE_ASYNC
#endif // _MSC_VER
////////////////////////////////////////////////////////////////////////////////
@@ -265,30 +299,56 @@ namespace Catch {
#endif
////////////////////////////////////////////////////////////////////////////////
-// Check if string_view is available and usable
-// The check is split apart to work around v140 (VS2015) preprocessor issue...
-#if defined(__has_include)
-#if __has_include() && defined(CATCH_CPP17_OR_GREATER)
-# define CATCH_INTERNAL_CONFIG_CPP17_STRING_VIEW
-#endif
+
+// RTX is a special version of Windows that is real time.
+// This means that it is detected as Windows, but does not provide
+// the same set of capabilities as real Windows does.
+#if defined(UNDER_RTSS) || defined(RTX64_BUILD)
+ #define CATCH_INTERNAL_CONFIG_NO_WINDOWS_SEH
+ #define CATCH_INTERNAL_CONFIG_NO_ASYNC
+ #define CATCH_CONFIG_COLOUR_NONE
#endif
-////////////////////////////////////////////////////////////////////////////////
-// Check if variant is available and usable
+#if !defined(_GLIBCXX_USE_C99_MATH_TR1)
+#define CATCH_INTERNAL_CONFIG_GLOBAL_NEXTAFTER
+#endif
+
+// Various stdlib support checks that require __has_include
#if defined(__has_include)
-# if __has_include() && defined(CATCH_CPP17_OR_GREATER)
-# if defined(__clang__) && (__clang_major__ < 8)
- // work around clang bug with libstdc++ https://bugs.llvm.org/show_bug.cgi?id=31852
- // fix should be in clang 8, workaround in libstdc++ 8.2
-# include
-# if defined(__GLIBCXX__) && defined(_GLIBCXX_RELEASE) && (_GLIBCXX_RELEASE < 9)
-# define CATCH_CONFIG_NO_CPP17_VARIANT
-# else
-# define CATCH_INTERNAL_CONFIG_CPP17_VARIANT
-# endif // defined(__GLIBCXX__) && defined(_GLIBCXX_RELEASE) && (_GLIBCXX_RELEASE < 9)
-# endif // defined(__clang__) && (__clang_major__ < 8)
-# endif // __has_include() && defined(CATCH_CPP17_OR_GREATER)
-#endif // __has_include
+ // Check if string_view is available and usable
+ #if __has_include() && defined(CATCH_CPP17_OR_GREATER)
+ # define CATCH_INTERNAL_CONFIG_CPP17_STRING_VIEW
+ #endif
+
+ // Check if optional is available and usable
+ # if __has_include() && defined(CATCH_CPP17_OR_GREATER)
+ # define CATCH_INTERNAL_CONFIG_CPP17_OPTIONAL
+ # endif // __has_include() && defined(CATCH_CPP17_OR_GREATER)
+
+ // Check if byte is available and usable
+ # if __has_include() && defined(CATCH_CPP17_OR_GREATER)
+ # include
+ # if defined(__cpp_lib_byte) && (__cpp_lib_byte > 0)
+ # define CATCH_INTERNAL_CONFIG_CPP17_BYTE
+ # endif
+ # endif // __has_include() && defined(CATCH_CPP17_OR_GREATER)
+
+ // Check if variant is available and usable
+ # if __has_include() && defined(CATCH_CPP17_OR_GREATER)
+ # if defined(__clang__) && (__clang_major__ < 8)
+ // work around clang bug with libstdc++ https://bugs.llvm.org/show_bug.cgi?id=31852
+ // fix should be in clang 8, workaround in libstdc++ 8.2
+ # include
+ # if defined(__GLIBCXX__) && defined(_GLIBCXX_RELEASE) && (_GLIBCXX_RELEASE < 9)
+ # define CATCH_CONFIG_NO_CPP17_VARIANT
+ # else
+ # define CATCH_INTERNAL_CONFIG_CPP17_VARIANT
+ # endif // defined(__GLIBCXX__) && defined(_GLIBCXX_RELEASE) && (_GLIBCXX_RELEASE < 9)
+ # else
+ # define CATCH_INTERNAL_CONFIG_CPP17_VARIANT
+ # endif // defined(__clang__) && (__clang_major__ < 8)
+ # endif // __has_include() && defined(CATCH_CPP17_OR_GREATER)
+#endif // defined(__has_include)
#if defined(CATCH_INTERNAL_CONFIG_COUNTER) && !defined(CATCH_CONFIG_NO_COUNTER) && !defined(CATCH_CONFIG_COUNTER)
# define CATCH_CONFIG_COUNTER
@@ -309,8 +369,8 @@ namespace Catch {
# define CATCH_CONFIG_CPP11_TO_STRING
#endif
-#if defined(CATCH_INTERNAL_CONFIG_CPP17_UNCAUGHT_EXCEPTIONS) && !defined(CATCH_CONFIG_NO_CPP17_UNCAUGHT_EXCEPTIONS) && !defined(CATCH_CONFIG_CPP17_UNCAUGHT_EXCEPTIONS)
-# define CATCH_CONFIG_CPP17_UNCAUGHT_EXCEPTIONS
+#if defined(CATCH_INTERNAL_CONFIG_CPP17_OPTIONAL) && !defined(CATCH_CONFIG_NO_CPP17_OPTIONAL) && !defined(CATCH_CONFIG_CPP17_OPTIONAL)
+# define CATCH_CONFIG_CPP17_OPTIONAL
#endif
#if defined(CATCH_INTERNAL_CONFIG_CPP17_STRING_VIEW) && !defined(CATCH_CONFIG_NO_CPP17_STRING_VIEW) && !defined(CATCH_CONFIG_CPP17_STRING_VIEW)
@@ -321,6 +381,10 @@ namespace Catch {
# define CATCH_CONFIG_CPP17_VARIANT
#endif
+#if defined(CATCH_INTERNAL_CONFIG_CPP17_BYTE) && !defined(CATCH_CONFIG_NO_CPP17_BYTE) && !defined(CATCH_CONFIG_CPP17_BYTE)
+# define CATCH_CONFIG_CPP17_BYTE
+#endif
+
#if defined(CATCH_CONFIG_EXPERIMENTAL_REDIRECT)
# define CATCH_INTERNAL_CONFIG_NEW_CAPTURE
#endif
@@ -337,17 +401,53 @@ namespace Catch {
# define CATCH_CONFIG_POLYFILL_ISNAN
#endif
+#if defined(CATCH_INTERNAL_CONFIG_USE_ASYNC) && !defined(CATCH_INTERNAL_CONFIG_NO_ASYNC) && !defined(CATCH_CONFIG_NO_USE_ASYNC) && !defined(CATCH_CONFIG_USE_ASYNC)
+# define CATCH_CONFIG_USE_ASYNC
+#endif
+
+#if defined(CATCH_INTERNAL_CONFIG_ANDROID_LOGWRITE) && !defined(CATCH_CONFIG_NO_ANDROID_LOGWRITE) && !defined(CATCH_CONFIG_ANDROID_LOGWRITE)
+# define CATCH_CONFIG_ANDROID_LOGWRITE
+#endif
+
+#if defined(CATCH_INTERNAL_CONFIG_GLOBAL_NEXTAFTER) && !defined(CATCH_CONFIG_NO_GLOBAL_NEXTAFTER) && !defined(CATCH_CONFIG_GLOBAL_NEXTAFTER)
+# define CATCH_CONFIG_GLOBAL_NEXTAFTER
+#endif
+
+// Even if we do not think the compiler has that warning, we still have
+// to provide a macro that can be used by the code.
+#if !defined(CATCH_INTERNAL_START_WARNINGS_SUPPRESSION)
+# define CATCH_INTERNAL_START_WARNINGS_SUPPRESSION
+#endif
+#if !defined(CATCH_INTERNAL_STOP_WARNINGS_SUPPRESSION)
+# define CATCH_INTERNAL_STOP_WARNINGS_SUPPRESSION
+#endif
#if !defined(CATCH_INTERNAL_SUPPRESS_PARENTHESES_WARNINGS)
# define CATCH_INTERNAL_SUPPRESS_PARENTHESES_WARNINGS
-# define CATCH_INTERNAL_UNSUPPRESS_PARENTHESES_WARNINGS
#endif
#if !defined(CATCH_INTERNAL_SUPPRESS_GLOBALS_WARNINGS)
# define CATCH_INTERNAL_SUPPRESS_GLOBALS_WARNINGS
-# define CATCH_INTERNAL_UNSUPPRESS_GLOBALS_WARNINGS
#endif
#if !defined(CATCH_INTERNAL_SUPPRESS_UNUSED_WARNINGS)
# define CATCH_INTERNAL_SUPPRESS_UNUSED_WARNINGS
-# define CATCH_INTERNAL_UNSUPPRESS_UNUSED_WARNINGS
+#endif
+#if !defined(CATCH_INTERNAL_SUPPRESS_ZERO_VARIADIC_WARNINGS)
+# define CATCH_INTERNAL_SUPPRESS_ZERO_VARIADIC_WARNINGS
+#endif
+
+// The goal of this macro is to avoid evaluation of the arguments, but
+// still have the compiler warn on problems inside...
+#if !defined(CATCH_INTERNAL_IGNORE_BUT_WARN)
+# define CATCH_INTERNAL_IGNORE_BUT_WARN(...)
+#endif
+
+#if defined(__APPLE__) && defined(__apple_build_version__) && (__clang_major__ < 10)
+# undef CATCH_INTERNAL_SUPPRESS_UNUSED_TEMPLATE_WARNINGS
+#elif defined(__clang__) && (__clang_major__ < 5)
+# undef CATCH_INTERNAL_SUPPRESS_UNUSED_TEMPLATE_WARNINGS
+#endif
+
+#if !defined(CATCH_INTERNAL_SUPPRESS_UNUSED_TEMPLATE_WARNINGS)
+# define CATCH_INTERNAL_SUPPRESS_UNUSED_TEMPLATE_WARNINGS
#endif
#if defined(CATCH_CONFIG_DISABLE_EXCEPTIONS)
@@ -407,12 +507,12 @@ namespace Catch {
line( _line )
{}
- SourceLineInfo( SourceLineInfo const& other ) = default;
- SourceLineInfo( SourceLineInfo && ) = default;
- SourceLineInfo& operator = ( SourceLineInfo const& ) = default;
- SourceLineInfo& operator = ( SourceLineInfo && ) = default;
+ SourceLineInfo( SourceLineInfo const& other ) = default;
+ SourceLineInfo& operator = ( SourceLineInfo const& ) = default;
+ SourceLineInfo( SourceLineInfo&& ) noexcept = default;
+ SourceLineInfo& operator = ( SourceLineInfo&& ) noexcept = default;
- bool empty() const noexcept;
+ bool empty() const noexcept { return file[0] == '\0'; }
bool operator == ( SourceLineInfo const& other ) const noexcept;
bool operator < ( SourceLineInfo const& other ) const noexcept;
@@ -453,9 +553,10 @@ namespace Catch {
} // end namespace Catch
#define CATCH_REGISTER_TAG_ALIAS( alias, spec ) \
+ CATCH_INTERNAL_START_WARNINGS_SUPPRESSION \
CATCH_INTERNAL_SUPPRESS_GLOBALS_WARNINGS \
namespace{ Catch::RegistrarForTagAliases INTERNAL_CATCH_UNIQUE_NAME( AutoRegisterTagAlias )( alias, spec, CATCH_INTERNAL_LINEINFO ); } \
- CATCH_INTERNAL_UNSUPPRESS_GLOBALS_WARNINGS
+ CATCH_INTERNAL_STOP_WARNINGS_SUPPRESSION
// end catch_tag_alias_autoregistrar.h
// start catch_test_registry.h
@@ -463,7 +564,6 @@ namespace Catch {
// start catch_interfaces_testcase.h
#include
-#include
namespace Catch {
@@ -474,8 +574,6 @@ namespace Catch {
virtual ~ITestInvoker();
};
- using ITestCasePtr = std::shared_ptr;
-
class TestCase;
struct IConfig;
@@ -485,6 +583,7 @@ namespace Catch {
virtual std::vector const& getAllTestsSorted( IConfig const& config ) const = 0;
};
+ bool isThrowSafe( TestCase const& testCase, IConfig const& config );
bool matchTest( TestCase const& testCase, TestSpec const& testSpec, IConfig const& config );
std::vector filterTests( std::vector const& testCases, TestSpec const& testSpec, IConfig const& config );
std::vector const& getAllTestCasesSorted( IConfig const& config );
@@ -497,55 +596,30 @@ namespace Catch {
#include
#include
#include
+#include
namespace Catch {
- class StringData;
-
/// A non-owning string class (similar to the forthcoming std::string_view)
/// Note that, because a StringRef may be a substring of another string,
- /// it may not be null terminated. c_str() must return a null terminated
- /// string, however, and so the StringRef will internally take ownership
- /// (taking a copy), if necessary. In theory this ownership is not externally
- /// visible - but it does mean (substring) StringRefs should not be shared between
- /// threads.
+ /// it may not be null terminated.
class StringRef {
public:
using size_type = std::size_t;
+ using const_iterator = const char*;
private:
- friend struct StringRefTestAccess;
-
- char const* m_start;
- size_type m_size;
-
- char* m_data = nullptr;
-
- void takeOwnership();
-
static constexpr char const* const s_empty = "";
- public: // construction/ assignment
- StringRef() noexcept
- : StringRef( s_empty, 0 )
- {}
+ char const* m_start = s_empty;
+ size_type m_size = 0;
- StringRef( StringRef const& other ) noexcept
- : m_start( other.m_start ),
- m_size( other.m_size )
- {}
-
- StringRef( StringRef&& other ) noexcept
- : m_start( other.m_start ),
- m_size( other.m_size ),
- m_data( other.m_data )
- {
- other.m_data = nullptr;
- }
+ public: // construction
+ constexpr StringRef() noexcept = default;
StringRef( char const* rawChars ) noexcept;
- StringRef( char const* rawChars, size_type size ) noexcept
+ constexpr StringRef( char const* rawChars, size_type size ) noexcept
: m_start( rawChars ),
m_size( size )
{}
@@ -555,99 +629,64 @@ namespace Catch {
m_size( stdString.size() )
{}
- ~StringRef() noexcept {
- delete[] m_data;
+ explicit operator std::string() const {
+ return std::string(m_start, m_size);
}
- auto operator = ( StringRef const &other ) noexcept -> StringRef& {
- delete[] m_data;
- m_data = nullptr;
- m_start = other.m_start;
- m_size = other.m_size;
- return *this;
- }
-
- operator std::string() const;
-
- void swap( StringRef& other ) noexcept;
-
public: // operators
auto operator == ( StringRef const& other ) const noexcept -> bool;
- auto operator != ( StringRef const& other ) const noexcept -> bool;
+ auto operator != (StringRef const& other) const noexcept -> bool {
+ return !(*this == other);
+ }
- auto operator[] ( size_type index ) const noexcept -> char;
+ auto operator[] ( size_type index ) const noexcept -> char {
+ assert(index < m_size);
+ return m_start[index];
+ }
public: // named queries
- auto empty() const noexcept -> bool {
+ constexpr auto empty() const noexcept -> bool {
return m_size == 0;
}
- auto size() const noexcept -> size_type {
+ constexpr auto size() const noexcept -> size_type {
return m_size;
}
- auto numberOfCharacters() const noexcept -> size_type;
+ // Returns the current start pointer. If the StringRef is not
+ // null-terminated, throws std::domain_exception
auto c_str() const -> char const*;
public: // substrings and searches
- auto substr( size_type start, size_type size ) const noexcept -> StringRef;
+ // Returns a substring of [start, start + length).
+ // If start + length > size(), then the substring is [start, size()).
+ // If start > size(), then the substring is empty.
+ auto substr( size_type start, size_type length ) const noexcept -> StringRef;
- // Returns the current start pointer.
- // Note that the pointer can change when if the StringRef is a substring
- auto currentData() const noexcept -> char const*;
+ // Returns the current start pointer. May not be null-terminated.
+ auto data() const noexcept -> char const*;
- private: // ownership queries - may not be consistent between calls
- auto isOwned() const noexcept -> bool;
- auto isSubstring() const noexcept -> bool;
+ constexpr auto isNullTerminated() const noexcept -> bool {
+ return m_start[m_size] == '\0';
+ }
+
+ public: // iterators
+ constexpr const_iterator begin() const { return m_start; }
+ constexpr const_iterator end() const { return m_start + m_size; }
};
- auto operator + ( StringRef const& lhs, StringRef const& rhs ) -> std::string;
- auto operator + ( StringRef const& lhs, char const* rhs ) -> std::string;
- auto operator + ( char const* lhs, StringRef const& rhs ) -> std::string;
-
auto operator += ( std::string& lhs, StringRef const& sr ) -> std::string&;
auto operator << ( std::ostream& os, StringRef const& sr ) -> std::ostream&;
- inline auto operator "" _sr( char const* rawChars, std::size_t size ) noexcept -> StringRef {
+ constexpr auto operator "" _sr( char const* rawChars, std::size_t size ) noexcept -> StringRef {
return StringRef( rawChars, size );
}
-
} // namespace Catch
-inline auto operator "" _catch_sr( char const* rawChars, std::size_t size ) noexcept -> Catch::StringRef {
+constexpr auto operator "" _catch_sr( char const* rawChars, std::size_t size ) noexcept -> Catch::StringRef {
return Catch::StringRef( rawChars, size );
}
// end catch_stringref.h
-// start catch_type_traits.hpp
-
-
-namespace Catch{
-
-#ifdef CATCH_CPP17_OR_GREATER
- template
- inline constexpr auto is_unique = std::true_type{};
-
- template
- inline constexpr auto is_unique = std::bool_constant<
- (!std::is_same_v && ...) && is_unique
- >{};
-#else
-
-template
-struct is_unique : std::true_type{};
-
-template
-struct is_unique : std::integral_constant
-::value
- && is_unique::value
- && is_unique::value
->{};
-
-#endif
-}
-
-// end catch_type_traits.hpp
// start catch_preprocessor.hpp
@@ -699,21 +738,224 @@ struct is_unique : std::integral_constant
#define INTERNAL_CATCH_EXPAND2(...) INTERNAL_CATCH_NO## __VA_ARGS__
#define INTERNAL_CATCH_DEF(...) INTERNAL_CATCH_DEF __VA_ARGS__
#define INTERNAL_CATCH_NOINTERNAL_CATCH_DEF
+#define INTERNAL_CATCH_STRINGIZE(...) INTERNAL_CATCH_STRINGIZE2(__VA_ARGS__)
+#ifndef CATCH_CONFIG_TRADITIONAL_MSVC_PREPROCESSOR
+#define INTERNAL_CATCH_STRINGIZE2(...) #__VA_ARGS__
+#define INTERNAL_CATCH_STRINGIZE_WITHOUT_PARENS(param) INTERNAL_CATCH_STRINGIZE(INTERNAL_CATCH_REMOVE_PARENS(param))
+#else
+// MSVC is adding extra space and needs another indirection to expand INTERNAL_CATCH_NOINTERNAL_CATCH_DEF
+#define INTERNAL_CATCH_STRINGIZE2(...) INTERNAL_CATCH_STRINGIZE3(__VA_ARGS__)
+#define INTERNAL_CATCH_STRINGIZE3(...) #__VA_ARGS__
+#define INTERNAL_CATCH_STRINGIZE_WITHOUT_PARENS(param) (INTERNAL_CATCH_STRINGIZE(INTERNAL_CATCH_REMOVE_PARENS(param)) + 1)
+#endif
+
+#define INTERNAL_CATCH_MAKE_NAMESPACE2(...) ns_##__VA_ARGS__
+#define INTERNAL_CATCH_MAKE_NAMESPACE(name) INTERNAL_CATCH_MAKE_NAMESPACE2(name)
#define INTERNAL_CATCH_REMOVE_PARENS(...) INTERNAL_CATCH_EXPAND1(INTERNAL_CATCH_DEF __VA_ARGS__)
-#define INTERNAL_CATCH_TEMPLATE_UNIQUE_NAME2(Name, ...) INTERNAL_CATCH_TEMPLATE_UNIQUE_NAME3(Name, __VA_ARGS__)
#ifndef CATCH_CONFIG_TRADITIONAL_MSVC_PREPROCESSOR
-#define INTERNAL_CATCH_TEMPLATE_UNIQUE_NAME3(Name,...) Name " - " #__VA_ARGS__
-#define INTERNAL_CATCH_TEMPLATE_UNIQUE_NAME(Name,...) INTERNAL_CATCH_TEMPLATE_UNIQUE_NAME2(Name, INTERNAL_CATCH_REMOVE_PARENS(__VA_ARGS__))
+#define INTERNAL_CATCH_MAKE_TYPE_LIST2(...) decltype(get_wrapper())
+#define INTERNAL_CATCH_MAKE_TYPE_LIST(...) INTERNAL_CATCH_MAKE_TYPE_LIST2(INTERNAL_CATCH_REMOVE_PARENS(__VA_ARGS__))
#else
-// MSVC is adding extra space and needs more calls to properly remove ()
-#define INTERNAL_CATCH_TEMPLATE_UNIQUE_NAME3(Name,...) Name " -" #__VA_ARGS__
-#define INTERNAL_CATCH_TEMPLATE_UNIQUE_NAME1(Name, ...) INTERNAL_CATCH_TEMPLATE_UNIQUE_NAME2(Name, __VA_ARGS__)
-#define INTERNAL_CATCH_TEMPLATE_UNIQUE_NAME(Name, ...) INTERNAL_CATCH_TEMPLATE_UNIQUE_NAME1(Name, INTERNAL_CATCH_EXPAND_VARGS(INTERNAL_CATCH_REMOVE_PARENS(__VA_ARGS__)))
+#define INTERNAL_CATCH_MAKE_TYPE_LIST2(...) INTERNAL_CATCH_EXPAND_VARGS(decltype(get_wrapper()))
+#define INTERNAL_CATCH_MAKE_TYPE_LIST(...) INTERNAL_CATCH_EXPAND_VARGS(INTERNAL_CATCH_MAKE_TYPE_LIST2(INTERNAL_CATCH_REMOVE_PARENS(__VA_ARGS__)))
+#endif
+
+#define INTERNAL_CATCH_MAKE_TYPE_LISTS_FROM_TYPES(...)\
+ CATCH_REC_LIST(INTERNAL_CATCH_MAKE_TYPE_LIST,__VA_ARGS__)
+
+#define INTERNAL_CATCH_REMOVE_PARENS_1_ARG(_0) INTERNAL_CATCH_REMOVE_PARENS(_0)
+#define INTERNAL_CATCH_REMOVE_PARENS_2_ARG(_0, _1) INTERNAL_CATCH_REMOVE_PARENS(_0), INTERNAL_CATCH_REMOVE_PARENS_1_ARG(_1)
+#define INTERNAL_CATCH_REMOVE_PARENS_3_ARG(_0, _1, _2) INTERNAL_CATCH_REMOVE_PARENS(_0), INTERNAL_CATCH_REMOVE_PARENS_2_ARG(_1, _2)
+#define INTERNAL_CATCH_REMOVE_PARENS_4_ARG(_0, _1, _2, _3) INTERNAL_CATCH_REMOVE_PARENS(_0), INTERNAL_CATCH_REMOVE_PARENS_3_ARG(_1, _2, _3)
+#define INTERNAL_CATCH_REMOVE_PARENS_5_ARG(_0, _1, _2, _3, _4) INTERNAL_CATCH_REMOVE_PARENS(_0), INTERNAL_CATCH_REMOVE_PARENS_4_ARG(_1, _2, _3, _4)
+#define INTERNAL_CATCH_REMOVE_PARENS_6_ARG(_0, _1, _2, _3, _4, _5) INTERNAL_CATCH_REMOVE_PARENS(_0), INTERNAL_CATCH_REMOVE_PARENS_5_ARG(_1, _2, _3, _4, _5)
+#define INTERNAL_CATCH_REMOVE_PARENS_7_ARG(_0, _1, _2, _3, _4, _5, _6) INTERNAL_CATCH_REMOVE_PARENS(_0), INTERNAL_CATCH_REMOVE_PARENS_6_ARG(_1, _2, _3, _4, _5, _6)
+#define INTERNAL_CATCH_REMOVE_PARENS_8_ARG(_0, _1, _2, _3, _4, _5, _6, _7) INTERNAL_CATCH_REMOVE_PARENS(_0), INTERNAL_CATCH_REMOVE_PARENS_7_ARG(_1, _2, _3, _4, _5, _6, _7)
+#define INTERNAL_CATCH_REMOVE_PARENS_9_ARG(_0, _1, _2, _3, _4, _5, _6, _7, _8) INTERNAL_CATCH_REMOVE_PARENS(_0), INTERNAL_CATCH_REMOVE_PARENS_8_ARG(_1, _2, _3, _4, _5, _6, _7, _8)
+#define INTERNAL_CATCH_REMOVE_PARENS_10_ARG(_0, _1, _2, _3, _4, _5, _6, _7, _8, _9) INTERNAL_CATCH_REMOVE_PARENS(_0), INTERNAL_CATCH_REMOVE_PARENS_9_ARG(_1, _2, _3, _4, _5, _6, _7, _8, _9)
+#define INTERNAL_CATCH_REMOVE_PARENS_11_ARG(_0, _1, _2, _3, _4, _5, _6, _7, _8, _9, _10) INTERNAL_CATCH_REMOVE_PARENS(_0), INTERNAL_CATCH_REMOVE_PARENS_10_ARG(_1, _2, _3, _4, _5, _6, _7, _8, _9, _10)
+
+#define INTERNAL_CATCH_VA_NARGS_IMPL(_0, _1, _2, _3, _4, _5, _6, _7, _8, _9, _10, N, ...) N
+
+#define INTERNAL_CATCH_TYPE_GEN\
+ template struct TypeList {};\
+ template\
+ constexpr auto get_wrapper() noexcept -> TypeList { return {}; }\
+ template class...> struct TemplateTypeList{};\
+ template class...Cs>\
+ constexpr auto get_wrapper() noexcept -> TemplateTypeList { return {}; }\
+ template\
+ struct append;\
+ template\
+ struct rewrap;\
+ template class, typename...>\
+ struct create;\
+ template class, typename>\
+ struct convert;\
+ \
+ template \
+ struct append { using type = T; };\
+ template< template class L1, typename...E1, template class L2, typename...E2, typename...Rest>\
+ struct append, L2, Rest...> { using type = typename append, Rest...>::type; };\
+ template< template class L1, typename...E1, typename...Rest>\
+ struct append, TypeList, Rest...> { using type = L1; };\
+ \
+ template< template class Container, template class List, typename...elems>\
+ struct rewrap, List> { using type = TypeList>; };\
+ template< template class Container, template class List, class...Elems, typename...Elements>\
+ struct rewrap, List, Elements...> { using type = typename append>, typename rewrap, Elements...>::type>::type; };\
+ \
+ template class Final, template< typename...> class...Containers, typename...Types>\
+ struct create, TypeList> { using type = typename append, typename rewrap, Types...>::type...>::type; };\
+ template class Final, template class List, typename...Ts>\
+ struct convert> { using type = typename append,TypeList...>::type; };
+
+#define INTERNAL_CATCH_NTTP_1(signature, ...)\
+ template struct Nttp{};\
+ template\
+ constexpr auto get_wrapper() noexcept -> Nttp<__VA_ARGS__> { return {}; } \
+ template class...> struct NttpTemplateTypeList{};\
+ template class...Cs>\
+ constexpr auto get_wrapper() noexcept -> NttpTemplateTypeList { return {}; } \
+ \
+ template< template class Container, template class List, INTERNAL_CATCH_REMOVE_PARENS(signature)>\
+ struct rewrap, List<__VA_ARGS__>> { using type = TypeList>; };\
+ template< template class Container, template class List, INTERNAL_CATCH_REMOVE_PARENS(signature), typename...Elements>\
+ struct rewrap, List<__VA_ARGS__>, Elements...> { using type = typename append>, typename rewrap, Elements...>::type>::type; };\
+ template class Final, template class...Containers, typename...Types>\
+ struct create, TypeList> { using type = typename append, typename rewrap, Types...>::type...>::type; };
+
+#define INTERNAL_CATCH_DECLARE_SIG_TEST0(TestName)
+#define INTERNAL_CATCH_DECLARE_SIG_TEST1(TestName, signature)\
+ template\
+ static void TestName()
+#define INTERNAL_CATCH_DECLARE_SIG_TEST_X(TestName, signature, ...)\
+ template\
+ static void TestName()
+
+#define INTERNAL_CATCH_DEFINE_SIG_TEST0(TestName)
+#define INTERNAL_CATCH_DEFINE_SIG_TEST1(TestName, signature)\
+ template\
+ static void TestName()
+#define INTERNAL_CATCH_DEFINE_SIG_TEST_X(TestName, signature,...)\
+ template\
+ static void TestName()
+
+#define INTERNAL_CATCH_NTTP_REGISTER0(TestFunc, signature)\
+ template\
+ void reg_test(TypeList, Catch::NameAndTags nameAndTags)\
+ {\
+ Catch::AutoReg( Catch::makeTestInvoker(&TestFunc), CATCH_INTERNAL_LINEINFO, Catch::StringRef(), nameAndTags);\
+ }
+
+#define INTERNAL_CATCH_NTTP_REGISTER(TestFunc, signature, ...)\
+ template\
+ void reg_test(Nttp<__VA_ARGS__>, Catch::NameAndTags nameAndTags)\
+ {\
+ Catch::AutoReg( Catch::makeTestInvoker(&TestFunc<__VA_ARGS__>), CATCH_INTERNAL_LINEINFO, Catch::StringRef(), nameAndTags);\
+ }
+
+#define INTERNAL_CATCH_NTTP_REGISTER_METHOD0(TestName, signature, ...)\
+ template\
+ void reg_test(TypeList, Catch::StringRef className, Catch::NameAndTags nameAndTags)\
+ {\
+ Catch::AutoReg( Catch::makeTestInvoker(&TestName::test), CATCH_INTERNAL_LINEINFO, className, nameAndTags);\
+ }
+
+#define INTERNAL_CATCH_NTTP_REGISTER_METHOD(TestName, signature, ...)\
+ template\
+ void reg_test(Nttp<__VA_ARGS__>, Catch::StringRef className, Catch::NameAndTags nameAndTags)\
+ {\
+ Catch::AutoReg( Catch::makeTestInvoker(&TestName<__VA_ARGS__>::test), CATCH_INTERNAL_LINEINFO, className, nameAndTags);\
+ }
+
+#define INTERNAL_CATCH_DECLARE_SIG_TEST_METHOD0(TestName, ClassName)
+#define INTERNAL_CATCH_DECLARE_SIG_TEST_METHOD1(TestName, ClassName, signature)\
+ template \
+ struct TestName : INTERNAL_CATCH_REMOVE_PARENS(ClassName) { \
+ void test();\
+ }
+
+#define INTERNAL_CATCH_DECLARE_SIG_TEST_METHOD_X(TestName, ClassName, signature, ...)\
+ template \
+ struct TestName : INTERNAL_CATCH_REMOVE_PARENS(ClassName)<__VA_ARGS__> { \
+ void test();\
+ }
+
+#define INTERNAL_CATCH_DEFINE_SIG_TEST_METHOD0(TestName)
+#define INTERNAL_CATCH_DEFINE_SIG_TEST_METHOD1(TestName, signature)\
+ template \
+ void INTERNAL_CATCH_MAKE_NAMESPACE(TestName)::TestName::test()
+#define INTERNAL_CATCH_DEFINE_SIG_TEST_METHOD_X(TestName, signature, ...)\
+ template \
+ void INTERNAL_CATCH_MAKE_NAMESPACE(TestName)::TestName<__VA_ARGS__>::test()
+
+#ifndef CATCH_CONFIG_TRADITIONAL_MSVC_PREPROCESSOR
+#define INTERNAL_CATCH_NTTP_0
+#define INTERNAL_CATCH_NTTP_GEN(...) INTERNAL_CATCH_VA_NARGS_IMPL(__VA_ARGS__, INTERNAL_CATCH_NTTP_1(__VA_ARGS__), INTERNAL_CATCH_NTTP_1(__VA_ARGS__), INTERNAL_CATCH_NTTP_1(__VA_ARGS__), INTERNAL_CATCH_NTTP_1(__VA_ARGS__), INTERNAL_CATCH_NTTP_1(__VA_ARGS__), INTERNAL_CATCH_NTTP_1( __VA_ARGS__), INTERNAL_CATCH_NTTP_1( __VA_ARGS__), INTERNAL_CATCH_NTTP_1( __VA_ARGS__), INTERNAL_CATCH_NTTP_1( __VA_ARGS__),INTERNAL_CATCH_NTTP_1( __VA_ARGS__), INTERNAL_CATCH_NTTP_0)
+#define INTERNAL_CATCH_DEFINE_SIG_TEST_METHOD(TestName, ...) INTERNAL_CATCH_VA_NARGS_IMPL( "dummy", __VA_ARGS__, INTERNAL_CATCH_DEFINE_SIG_TEST_METHOD_X,INTERNAL_CATCH_DEFINE_SIG_TEST_METHOD_X, INTERNAL_CATCH_DEFINE_SIG_TEST_METHOD_X, INTERNAL_CATCH_DEFINE_SIG_TEST_METHOD_X, INTERNAL_CATCH_DEFINE_SIG_TEST_METHOD_X, INTERNAL_CATCH_DEFINE_SIG_TEST_METHOD_X, INTERNAL_CATCH_DEFINE_SIG_TEST_METHOD_X,INTERNAL_CATCH_DEFINE_SIG_TEST_METHOD_X,INTERNAL_CATCH_DEFINE_SIG_TEST_METHOD_X, INTERNAL_CATCH_DEFINE_SIG_TEST_METHOD1, INTERNAL_CATCH_DEFINE_SIG_TEST_METHOD0)(TestName, __VA_ARGS__)
+#define INTERNAL_CATCH_DECLARE_SIG_TEST_METHOD(TestName, ClassName, ...) INTERNAL_CATCH_VA_NARGS_IMPL( "dummy", __VA_ARGS__, INTERNAL_CATCH_DECLARE_SIG_TEST_METHOD_X,INTERNAL_CATCH_DECLARE_SIG_TEST_METHOD_X, INTERNAL_CATCH_DECLARE_SIG_TEST_METHOD_X, INTERNAL_CATCH_DECLARE_SIG_TEST_METHOD_X, INTERNAL_CATCH_DECLARE_SIG_TEST_METHOD_X, INTERNAL_CATCH_DECLARE_SIG_TEST_METHOD_X, INTERNAL_CATCH_DECLARE_SIG_TEST_METHOD_X,INTERNAL_CATCH_DECLARE_SIG_TEST_METHOD_X,INTERNAL_CATCH_DECLARE_SIG_TEST_METHOD_X, INTERNAL_CATCH_DECLARE_SIG_TEST_METHOD1, INTERNAL_CATCH_DECLARE_SIG_TEST_METHOD0)(TestName, ClassName, __VA_ARGS__)
+#define INTERNAL_CATCH_NTTP_REG_METHOD_GEN(TestName, ...) INTERNAL_CATCH_VA_NARGS_IMPL( "dummy", __VA_ARGS__, INTERNAL_CATCH_NTTP_REGISTER_METHOD, INTERNAL_CATCH_NTTP_REGISTER_METHOD, INTERNAL_CATCH_NTTP_REGISTER_METHOD, INTERNAL_CATCH_NTTP_REGISTER_METHOD, INTERNAL_CATCH_NTTP_REGISTER_METHOD, INTERNAL_CATCH_NTTP_REGISTER_METHOD, INTERNAL_CATCH_NTTP_REGISTER_METHOD, INTERNAL_CATCH_NTTP_REGISTER_METHOD, INTERNAL_CATCH_NTTP_REGISTER_METHOD, INTERNAL_CATCH_NTTP_REGISTER_METHOD0, INTERNAL_CATCH_NTTP_REGISTER_METHOD0)(TestName, __VA_ARGS__)
+#define INTERNAL_CATCH_NTTP_REG_GEN(TestFunc, ...) INTERNAL_CATCH_VA_NARGS_IMPL( "dummy", __VA_ARGS__, INTERNAL_CATCH_NTTP_REGISTER, INTERNAL_CATCH_NTTP_REGISTER, INTERNAL_CATCH_NTTP_REGISTER, INTERNAL_CATCH_NTTP_REGISTER, INTERNAL_CATCH_NTTP_REGISTER, INTERNAL_CATCH_NTTP_REGISTER, INTERNAL_CATCH_NTTP_REGISTER, INTERNAL_CATCH_NTTP_REGISTER, INTERNAL_CATCH_NTTP_REGISTER, INTERNAL_CATCH_NTTP_REGISTER0, INTERNAL_CATCH_NTTP_REGISTER0)(TestFunc, __VA_ARGS__)
+#define INTERNAL_CATCH_DEFINE_SIG_TEST(TestName, ...) INTERNAL_CATCH_VA_NARGS_IMPL( "dummy", __VA_ARGS__, INTERNAL_CATCH_DEFINE_SIG_TEST_X, INTERNAL_CATCH_DEFINE_SIG_TEST_X, INTERNAL_CATCH_DEFINE_SIG_TEST_X, INTERNAL_CATCH_DEFINE_SIG_TEST_X, INTERNAL_CATCH_DEFINE_SIG_TEST_X, INTERNAL_CATCH_DEFINE_SIG_TEST_X, INTERNAL_CATCH_DEFINE_SIG_TEST_X, INTERNAL_CATCH_DEFINE_SIG_TEST_X,INTERNAL_CATCH_DEFINE_SIG_TEST_X,INTERNAL_CATCH_DEFINE_SIG_TEST1, INTERNAL_CATCH_DEFINE_SIG_TEST0)(TestName, __VA_ARGS__)
+#define INTERNAL_CATCH_DECLARE_SIG_TEST(TestName, ...) INTERNAL_CATCH_VA_NARGS_IMPL( "dummy", __VA_ARGS__, INTERNAL_CATCH_DECLARE_SIG_TEST_X,INTERNAL_CATCH_DECLARE_SIG_TEST_X, INTERNAL_CATCH_DECLARE_SIG_TEST_X, INTERNAL_CATCH_DECLARE_SIG_TEST_X, INTERNAL_CATCH_DECLARE_SIG_TEST_X, INTERNAL_CATCH_DECLARE_SIG_TEST_X, INTERNAL_CATCH_DEFINE_SIG_TEST_X,INTERNAL_CATCH_DECLARE_SIG_TEST_X,INTERNAL_CATCH_DECLARE_SIG_TEST_X, INTERNAL_CATCH_DECLARE_SIG_TEST1, INTERNAL_CATCH_DECLARE_SIG_TEST0)(TestName, __VA_ARGS__)
+#define INTERNAL_CATCH_REMOVE_PARENS_GEN(...) INTERNAL_CATCH_VA_NARGS_IMPL(__VA_ARGS__, INTERNAL_CATCH_REMOVE_PARENS_11_ARG,INTERNAL_CATCH_REMOVE_PARENS_10_ARG,INTERNAL_CATCH_REMOVE_PARENS_9_ARG,INTERNAL_CATCH_REMOVE_PARENS_8_ARG,INTERNAL_CATCH_REMOVE_PARENS_7_ARG,INTERNAL_CATCH_REMOVE_PARENS_6_ARG,INTERNAL_CATCH_REMOVE_PARENS_5_ARG,INTERNAL_CATCH_REMOVE_PARENS_4_ARG,INTERNAL_CATCH_REMOVE_PARENS_3_ARG,INTERNAL_CATCH_REMOVE_PARENS_2_ARG,INTERNAL_CATCH_REMOVE_PARENS_1_ARG)(__VA_ARGS__)
+#else
+#define INTERNAL_CATCH_NTTP_0(signature)
+#define INTERNAL_CATCH_NTTP_GEN(...) INTERNAL_CATCH_EXPAND_VARGS(INTERNAL_CATCH_VA_NARGS_IMPL(__VA_ARGS__, INTERNAL_CATCH_NTTP_1, INTERNAL_CATCH_NTTP_1, INTERNAL_CATCH_NTTP_1, INTERNAL_CATCH_NTTP_1, INTERNAL_CATCH_NTTP_1, INTERNAL_CATCH_NTTP_1, INTERNAL_CATCH_NTTP_1, INTERNAL_CATCH_NTTP_1, INTERNAL_CATCH_NTTP_1,INTERNAL_CATCH_NTTP_1, INTERNAL_CATCH_NTTP_0)( __VA_ARGS__))
+#define INTERNAL_CATCH_DEFINE_SIG_TEST_METHOD(TestName, ...) INTERNAL_CATCH_EXPAND_VARGS(INTERNAL_CATCH_VA_NARGS_IMPL( "dummy", __VA_ARGS__, INTERNAL_CATCH_DEFINE_SIG_TEST_METHOD_X,INTERNAL_CATCH_DEFINE_SIG_TEST_METHOD_X, INTERNAL_CATCH_DEFINE_SIG_TEST_METHOD_X, INTERNAL_CATCH_DEFINE_SIG_TEST_METHOD_X, INTERNAL_CATCH_DEFINE_SIG_TEST_METHOD_X, INTERNAL_CATCH_DEFINE_SIG_TEST_METHOD_X, INTERNAL_CATCH_DEFINE_SIG_TEST_METHOD_X,INTERNAL_CATCH_DEFINE_SIG_TEST_METHOD_X,INTERNAL_CATCH_DEFINE_SIG_TEST_METHOD_X, INTERNAL_CATCH_DEFINE_SIG_TEST_METHOD1, INTERNAL_CATCH_DEFINE_SIG_TEST_METHOD0)(TestName, __VA_ARGS__))
+#define INTERNAL_CATCH_DECLARE_SIG_TEST_METHOD(TestName, ClassName, ...) INTERNAL_CATCH_EXPAND_VARGS(INTERNAL_CATCH_VA_NARGS_IMPL( "dummy", __VA_ARGS__, INTERNAL_CATCH_DECLARE_SIG_TEST_METHOD_X,INTERNAL_CATCH_DECLARE_SIG_TEST_METHOD_X, INTERNAL_CATCH_DECLARE_SIG_TEST_METHOD_X, INTERNAL_CATCH_DECLARE_SIG_TEST_METHOD_X, INTERNAL_CATCH_DECLARE_SIG_TEST_METHOD_X, INTERNAL_CATCH_DECLARE_SIG_TEST_METHOD_X, INTERNAL_CATCH_DECLARE_SIG_TEST_METHOD_X,INTERNAL_CATCH_DECLARE_SIG_TEST_METHOD_X,INTERNAL_CATCH_DECLARE_SIG_TEST_METHOD_X, INTERNAL_CATCH_DECLARE_SIG_TEST_METHOD1, INTERNAL_CATCH_DECLARE_SIG_TEST_METHOD0)(TestName, ClassName, __VA_ARGS__))
+#define INTERNAL_CATCH_NTTP_REG_METHOD_GEN(TestName, ...) INTERNAL_CATCH_EXPAND_VARGS(INTERNAL_CATCH_VA_NARGS_IMPL( "dummy", __VA_ARGS__, INTERNAL_CATCH_NTTP_REGISTER_METHOD, INTERNAL_CATCH_NTTP_REGISTER_METHOD, INTERNAL_CATCH_NTTP_REGISTER_METHOD, INTERNAL_CATCH_NTTP_REGISTER_METHOD, INTERNAL_CATCH_NTTP_REGISTER_METHOD, INTERNAL_CATCH_NTTP_REGISTER_METHOD, INTERNAL_CATCH_NTTP_REGISTER_METHOD, INTERNAL_CATCH_NTTP_REGISTER_METHOD, INTERNAL_CATCH_NTTP_REGISTER_METHOD, INTERNAL_CATCH_NTTP_REGISTER_METHOD0, INTERNAL_CATCH_NTTP_REGISTER_METHOD0)(TestName, __VA_ARGS__))
+#define INTERNAL_CATCH_NTTP_REG_GEN(TestFunc, ...) INTERNAL_CATCH_EXPAND_VARGS(INTERNAL_CATCH_VA_NARGS_IMPL( "dummy", __VA_ARGS__, INTERNAL_CATCH_NTTP_REGISTER, INTERNAL_CATCH_NTTP_REGISTER, INTERNAL_CATCH_NTTP_REGISTER, INTERNAL_CATCH_NTTP_REGISTER, INTERNAL_CATCH_NTTP_REGISTER, INTERNAL_CATCH_NTTP_REGISTER, INTERNAL_CATCH_NTTP_REGISTER, INTERNAL_CATCH_NTTP_REGISTER, INTERNAL_CATCH_NTTP_REGISTER, INTERNAL_CATCH_NTTP_REGISTER0, INTERNAL_CATCH_NTTP_REGISTER0)(TestFunc, __VA_ARGS__))
+#define INTERNAL_CATCH_DEFINE_SIG_TEST(TestName, ...) INTERNAL_CATCH_EXPAND_VARGS(INTERNAL_CATCH_VA_NARGS_IMPL( "dummy", __VA_ARGS__, INTERNAL_CATCH_DEFINE_SIG_TEST_X, INTERNAL_CATCH_DEFINE_SIG_TEST_X, INTERNAL_CATCH_DEFINE_SIG_TEST_X, INTERNAL_CATCH_DEFINE_SIG_TEST_X, INTERNAL_CATCH_DEFINE_SIG_TEST_X, INTERNAL_CATCH_DEFINE_SIG_TEST_X, INTERNAL_CATCH_DEFINE_SIG_TEST_X, INTERNAL_CATCH_DEFINE_SIG_TEST_X,INTERNAL_CATCH_DEFINE_SIG_TEST_X,INTERNAL_CATCH_DEFINE_SIG_TEST1, INTERNAL_CATCH_DEFINE_SIG_TEST0)(TestName, __VA_ARGS__))
+#define INTERNAL_CATCH_DECLARE_SIG_TEST(TestName, ...) INTERNAL_CATCH_EXPAND_VARGS(INTERNAL_CATCH_VA_NARGS_IMPL( "dummy", __VA_ARGS__, INTERNAL_CATCH_DECLARE_SIG_TEST_X,INTERNAL_CATCH_DECLARE_SIG_TEST_X, INTERNAL_CATCH_DECLARE_SIG_TEST_X, INTERNAL_CATCH_DECLARE_SIG_TEST_X, INTERNAL_CATCH_DECLARE_SIG_TEST_X, INTERNAL_CATCH_DECLARE_SIG_TEST_X, INTERNAL_CATCH_DEFINE_SIG_TEST_X,INTERNAL_CATCH_DECLARE_SIG_TEST_X,INTERNAL_CATCH_DECLARE_SIG_TEST_X, INTERNAL_CATCH_DECLARE_SIG_TEST1, INTERNAL_CATCH_DECLARE_SIG_TEST0)(TestName, __VA_ARGS__))
+#define INTERNAL_CATCH_REMOVE_PARENS_GEN(...) INTERNAL_CATCH_EXPAND_VARGS(INTERNAL_CATCH_VA_NARGS_IMPL(__VA_ARGS__, INTERNAL_CATCH_REMOVE_PARENS_11_ARG,INTERNAL_CATCH_REMOVE_PARENS_10_ARG,INTERNAL_CATCH_REMOVE_PARENS_9_ARG,INTERNAL_CATCH_REMOVE_PARENS_8_ARG,INTERNAL_CATCH_REMOVE_PARENS_7_ARG,INTERNAL_CATCH_REMOVE_PARENS_6_ARG,INTERNAL_CATCH_REMOVE_PARENS_5_ARG,INTERNAL_CATCH_REMOVE_PARENS_4_ARG,INTERNAL_CATCH_REMOVE_PARENS_3_ARG,INTERNAL_CATCH_REMOVE_PARENS_2_ARG,INTERNAL_CATCH_REMOVE_PARENS_1_ARG)(__VA_ARGS__))
#endif
// end catch_preprocessor.hpp
+// start catch_meta.hpp
+
+
+#include
+
+namespace Catch {
+ template
+ struct always_false : std::false_type {};
+
+ template struct true_given : std::true_type {};
+ struct is_callable_tester {
+ template
+ true_given()(std::declval()...))> static test(int);
+ template
+ std::false_type static test(...);
+ };
+
+ template
+ struct is_callable;
+
+ template
+ struct is_callable : decltype(is_callable_tester::test(0)) {};
+
+#if defined(__cpp_lib_is_invocable) && __cpp_lib_is_invocable >= 201703
+ // std::result_of is deprecated in C++17 and removed in C++20. Hence, it is
+ // replaced with std::invoke_result here.
+ template
+ using FunctionReturnType = std::remove_reference_t>>;
+#else
+ // Keep ::type here because we still support C++11
+ template
+ using FunctionReturnType = typename std::remove_reference::type>::type>::type;
+#endif
+
+} // namespace Catch
+
+namespace mpl_{
+ struct na;
+}
+
+// end catch_meta.hpp
namespace Catch {
template
@@ -758,38 +1000,70 @@ struct AutoReg : NonCopyable {
}; \
} \
void TestName::test()
- #define INTERNAL_CATCH_TEMPLATE_TEST_CASE_NO_REGISTRATION( TestName, ... ) \
- template \
- static void TestName()
- #define INTERNAL_CATCH_TEMPLATE_TEST_CASE_METHOD_NO_REGISTRATION( TestName, ClassName, ... ) \
+ #define INTERNAL_CATCH_TEMPLATE_TEST_CASE_NO_REGISTRATION_2( TestName, TestFunc, Name, Tags, Signature, ... ) \
+ INTERNAL_CATCH_DEFINE_SIG_TEST(TestFunc, INTERNAL_CATCH_REMOVE_PARENS(Signature))
+ #define INTERNAL_CATCH_TEMPLATE_TEST_CASE_METHOD_NO_REGISTRATION_2( TestNameClass, TestName, ClassName, Name, Tags, Signature, ... ) \
namespace{ \
- template \
- struct TestName : INTERNAL_CATCH_REMOVE_PARENS(ClassName ) { \
- void test(); \
- }; \
+ namespace INTERNAL_CATCH_MAKE_NAMESPACE(TestName) { \
+ INTERNAL_CATCH_DECLARE_SIG_TEST_METHOD(TestName, ClassName, INTERNAL_CATCH_REMOVE_PARENS(Signature));\
} \
- template \
- void TestName::test()
+ } \
+ INTERNAL_CATCH_DEFINE_SIG_TEST_METHOD(TestName, INTERNAL_CATCH_REMOVE_PARENS(Signature))
+
+ #ifndef CATCH_CONFIG_TRADITIONAL_MSVC_PREPROCESSOR
+ #define INTERNAL_CATCH_TEMPLATE_TEST_CASE_NO_REGISTRATION(Name, Tags, ...) \
+ INTERNAL_CATCH_TEMPLATE_TEST_CASE_NO_REGISTRATION_2( INTERNAL_CATCH_UNIQUE_NAME( C_A_T_C_H_T_E_M_P_L_A_T_E_T_E_S_T_ ), INTERNAL_CATCH_UNIQUE_NAME( C_A_T_C_H_T_E_M_P_L_A_T_E_T_E_S_T_F_U_N_C_ ), Name, Tags, typename TestType, __VA_ARGS__ )
+ #else
+ #define INTERNAL_CATCH_TEMPLATE_TEST_CASE_NO_REGISTRATION(Name, Tags, ...) \
+ INTERNAL_CATCH_EXPAND_VARGS( INTERNAL_CATCH_TEMPLATE_TEST_CASE_NO_REGISTRATION_2( INTERNAL_CATCH_UNIQUE_NAME( C_A_T_C_H_T_E_M_P_L_A_T_E_T_E_S_T_ ), INTERNAL_CATCH_UNIQUE_NAME( C_A_T_C_H_T_E_M_P_L_A_T_E_T_E_S_T_F_U_N_C_ ), Name, Tags, typename TestType, __VA_ARGS__ ) )
+ #endif
+
+ #ifndef CATCH_CONFIG_TRADITIONAL_MSVC_PREPROCESSOR
+ #define INTERNAL_CATCH_TEMPLATE_TEST_CASE_SIG_NO_REGISTRATION(Name, Tags, Signature, ...) \
+ INTERNAL_CATCH_TEMPLATE_TEST_CASE_NO_REGISTRATION_2( INTERNAL_CATCH_UNIQUE_NAME( C_A_T_C_H_T_E_M_P_L_A_T_E_T_E_S_T_ ), INTERNAL_CATCH_UNIQUE_NAME( C_A_T_C_H_T_E_M_P_L_A_T_E_T_E_S_T_F_U_N_C_ ), Name, Tags, Signature, __VA_ARGS__ )
+ #else
+ #define INTERNAL_CATCH_TEMPLATE_TEST_CASE_SIG_NO_REGISTRATION(Name, Tags, Signature, ...) \
+ INTERNAL_CATCH_EXPAND_VARGS( INTERNAL_CATCH_TEMPLATE_TEST_CASE_NO_REGISTRATION_2( INTERNAL_CATCH_UNIQUE_NAME( C_A_T_C_H_T_E_M_P_L_A_T_E_T_E_S_T_ ), INTERNAL_CATCH_UNIQUE_NAME( C_A_T_C_H_T_E_M_P_L_A_T_E_T_E_S_T_F_U_N_C_ ), Name, Tags, Signature, __VA_ARGS__ ) )
+ #endif
+
+ #ifndef CATCH_CONFIG_TRADITIONAL_MSVC_PREPROCESSOR
+ #define INTERNAL_CATCH_TEMPLATE_TEST_CASE_METHOD_NO_REGISTRATION( ClassName, Name, Tags,... ) \
+ INTERNAL_CATCH_TEMPLATE_TEST_CASE_METHOD_NO_REGISTRATION_2( INTERNAL_CATCH_UNIQUE_NAME( C_A_T_C_H_T_E_M_P_L_A_T_E_T_E_S_T_C_L_A_S_S_ ), INTERNAL_CATCH_UNIQUE_NAME( C_A_T_C_H_T_E_M_P_L_A_T_E_T_E_S_T_ ) , ClassName, Name, Tags, typename T, __VA_ARGS__ )
+ #else
+ #define INTERNAL_CATCH_TEMPLATE_TEST_CASE_METHOD_NO_REGISTRATION( ClassName, Name, Tags,... ) \
+ INTERNAL_CATCH_EXPAND_VARGS( INTERNAL_CATCH_TEMPLATE_TEST_CASE_METHOD_NO_REGISTRATION_2( INTERNAL_CATCH_UNIQUE_NAME( C_A_T_C_H_T_E_M_P_L_A_T_E_T_E_S_T_C_L_A_S_S_ ), INTERNAL_CATCH_UNIQUE_NAME( C_A_T_C_H_T_E_M_P_L_A_T_E_T_E_S_T_ ) , ClassName, Name, Tags, typename T, __VA_ARGS__ ) )
+ #endif
+
+ #ifndef CATCH_CONFIG_TRADITIONAL_MSVC_PREPROCESSOR
+ #define INTERNAL_CATCH_TEMPLATE_TEST_CASE_METHOD_SIG_NO_REGISTRATION( ClassName, Name, Tags, Signature, ... ) \
+ INTERNAL_CATCH_TEMPLATE_TEST_CASE_METHOD_NO_REGISTRATION_2( INTERNAL_CATCH_UNIQUE_NAME( C_A_T_C_H_T_E_M_P_L_A_T_E_T_E_S_T_C_L_A_S_S_ ), INTERNAL_CATCH_UNIQUE_NAME( C_A_T_C_H_T_E_M_P_L_A_T_E_T_E_S_T_ ) , ClassName, Name, Tags, Signature, __VA_ARGS__ )
+ #else
+ #define INTERNAL_CATCH_TEMPLATE_TEST_CASE_METHOD_SIG_NO_REGISTRATION( ClassName, Name, Tags, Signature, ... ) \
+ INTERNAL_CATCH_EXPAND_VARGS( INTERNAL_CATCH_TEMPLATE_TEST_CASE_METHOD_NO_REGISTRATION_2( INTERNAL_CATCH_UNIQUE_NAME( C_A_T_C_H_T_E_M_P_L_A_T_E_T_E_S_T_C_L_A_S_S_ ), INTERNAL_CATCH_UNIQUE_NAME( C_A_T_C_H_T_E_M_P_L_A_T_E_T_E_S_T_ ) , ClassName, Name, Tags, Signature, __VA_ARGS__ ) )
+ #endif
#endif
///////////////////////////////////////////////////////////////////////////////
#define INTERNAL_CATCH_TESTCASE2( TestName, ... ) \
static void TestName(); \
+ CATCH_INTERNAL_START_WARNINGS_SUPPRESSION \
CATCH_INTERNAL_SUPPRESS_GLOBALS_WARNINGS \
namespace{ Catch::AutoReg INTERNAL_CATCH_UNIQUE_NAME( autoRegistrar )( Catch::makeTestInvoker( &TestName ), CATCH_INTERNAL_LINEINFO, Catch::StringRef(), Catch::NameAndTags{ __VA_ARGS__ } ); } /* NOLINT */ \
- CATCH_INTERNAL_UNSUPPRESS_GLOBALS_WARNINGS \
+ CATCH_INTERNAL_STOP_WARNINGS_SUPPRESSION \
static void TestName()
#define INTERNAL_CATCH_TESTCASE( ... ) \
- INTERNAL_CATCH_TESTCASE2( INTERNAL_CATCH_UNIQUE_NAME( ____C_A_T_C_H____T_E_S_T____ ), __VA_ARGS__ )
+ INTERNAL_CATCH_TESTCASE2( INTERNAL_CATCH_UNIQUE_NAME( C_A_T_C_H_T_E_S_T_ ), __VA_ARGS__ )
///////////////////////////////////////////////////////////////////////////////
#define INTERNAL_CATCH_METHOD_AS_TEST_CASE( QualifiedMethod, ... ) \
+ CATCH_INTERNAL_START_WARNINGS_SUPPRESSION \
CATCH_INTERNAL_SUPPRESS_GLOBALS_WARNINGS \
namespace{ Catch::AutoReg INTERNAL_CATCH_UNIQUE_NAME( autoRegistrar )( Catch::makeTestInvoker( &QualifiedMethod ), CATCH_INTERNAL_LINEINFO, "&" #QualifiedMethod, Catch::NameAndTags{ __VA_ARGS__ } ); } /* NOLINT */ \
- CATCH_INTERNAL_UNSUPPRESS_GLOBALS_WARNINGS
+ CATCH_INTERNAL_STOP_WARNINGS_SUPPRESSION
///////////////////////////////////////////////////////////////////////////////
#define INTERNAL_CATCH_TEST_CASE_METHOD2( TestName, ClassName, ... )\
+ CATCH_INTERNAL_START_WARNINGS_SUPPRESSION \
CATCH_INTERNAL_SUPPRESS_GLOBALS_WARNINGS \
namespace{ \
struct TestName : INTERNAL_CATCH_REMOVE_PARENS(ClassName) { \
@@ -797,88 +1071,273 @@ struct AutoReg : NonCopyable {
}; \
Catch::AutoReg INTERNAL_CATCH_UNIQUE_NAME( autoRegistrar ) ( Catch::makeTestInvoker( &TestName::test ), CATCH_INTERNAL_LINEINFO, #ClassName, Catch::NameAndTags{ __VA_ARGS__ } ); /* NOLINT */ \
} \
- CATCH_INTERNAL_UNSUPPRESS_GLOBALS_WARNINGS \
+ CATCH_INTERNAL_STOP_WARNINGS_SUPPRESSION \
void TestName::test()
#define INTERNAL_CATCH_TEST_CASE_METHOD( ClassName, ... ) \
- INTERNAL_CATCH_TEST_CASE_METHOD2( INTERNAL_CATCH_UNIQUE_NAME( ____C_A_T_C_H____T_E_S_T____ ), ClassName, __VA_ARGS__ )
+ INTERNAL_CATCH_TEST_CASE_METHOD2( INTERNAL_CATCH_UNIQUE_NAME( C_A_T_C_H_T_E_S_T_ ), ClassName, __VA_ARGS__ )
///////////////////////////////////////////////////////////////////////////////
#define INTERNAL_CATCH_REGISTER_TESTCASE( Function, ... ) \
+ CATCH_INTERNAL_START_WARNINGS_SUPPRESSION \
CATCH_INTERNAL_SUPPRESS_GLOBALS_WARNINGS \
Catch::AutoReg INTERNAL_CATCH_UNIQUE_NAME( autoRegistrar )( Catch::makeTestInvoker( Function ), CATCH_INTERNAL_LINEINFO, Catch::StringRef(), Catch::NameAndTags{ __VA_ARGS__ } ); /* NOLINT */ \
- CATCH_INTERNAL_UNSUPPRESS_GLOBALS_WARNINGS
+ CATCH_INTERNAL_STOP_WARNINGS_SUPPRESSION
///////////////////////////////////////////////////////////////////////////////
- #define INTERNAL_CATCH_TEMPLATE_TEST_CASE_2(TestName, TestFunc, Name, Tags, ... )\
+ #define INTERNAL_CATCH_TEMPLATE_TEST_CASE_2(TestName, TestFunc, Name, Tags, Signature, ... )\
+ CATCH_INTERNAL_START_WARNINGS_SUPPRESSION \
CATCH_INTERNAL_SUPPRESS_GLOBALS_WARNINGS \
- template \
- static void TestFunc();\
+ CATCH_INTERNAL_SUPPRESS_ZERO_VARIADIC_WARNINGS \
+ CATCH_INTERNAL_SUPPRESS_UNUSED_TEMPLATE_WARNINGS \
+ INTERNAL_CATCH_DECLARE_SIG_TEST(TestFunc, INTERNAL_CATCH_REMOVE_PARENS(Signature));\
namespace {\
+ namespace INTERNAL_CATCH_MAKE_NAMESPACE(TestName){\
+ INTERNAL_CATCH_TYPE_GEN\
+ INTERNAL_CATCH_NTTP_GEN(INTERNAL_CATCH_REMOVE_PARENS(Signature))\
+ INTERNAL_CATCH_NTTP_REG_GEN(TestFunc,INTERNAL_CATCH_REMOVE_PARENS(Signature))\
template \
struct TestName{\
- template \
- TestName(Ts...names){\
- CATCH_INTERNAL_CHECK_UNIQUE_TYPES(CATCH_REC_LIST(INTERNAL_CATCH_REMOVE_PARENS, __VA_ARGS__)) \
+ TestName(){\
+ int index = 0; \
+ constexpr char const* tmpl_types[] = {CATCH_REC_LIST(INTERNAL_CATCH_STRINGIZE_WITHOUT_PARENS, __VA_ARGS__)};\
using expander = int[];\
- (void)expander{(Catch::AutoReg( Catch::makeTestInvoker( &TestFunc ), CATCH_INTERNAL_LINEINFO, Catch::StringRef(), Catch::NameAndTags{ names, Tags } ), 0)... };/* NOLINT */ \
+ (void)expander{(reg_test(Types{}, Catch::NameAndTags{ Name " - " + std::string(tmpl_types[index]), Tags } ), index++)... };/* NOLINT */ \
}\
};\
- INTERNAL_CATCH_TEMPLATE_REGISTRY_INITIATE(TestName, Name, __VA_ARGS__) \
+ static int INTERNAL_CATCH_UNIQUE_NAME( globalRegistrar ) = [](){\
+ TestName();\
+ return 0;\
+ }();\
}\
- CATCH_INTERNAL_UNSUPPRESS_GLOBALS_WARNINGS \
- template \
- static void TestFunc()
-
-#if defined(CATCH_CPP17_OR_GREATER)
-#define CATCH_INTERNAL_CHECK_UNIQUE_TYPES(...) static_assert(Catch::is_unique<__VA_ARGS__>,"Duplicate type detected in declaration of template test case");
-#else
-#define CATCH_INTERNAL_CHECK_UNIQUE_TYPES(...) static_assert(Catch::is_unique<__VA_ARGS__>::value,"Duplicate type detected in declaration of template test case");
-#endif
+ }\
+ CATCH_INTERNAL_STOP_WARNINGS_SUPPRESSION \
+ INTERNAL_CATCH_DEFINE_SIG_TEST(TestFunc,INTERNAL_CATCH_REMOVE_PARENS(Signature))
#ifndef CATCH_CONFIG_TRADITIONAL_MSVC_PREPROCESSOR
#define INTERNAL_CATCH_TEMPLATE_TEST_CASE(Name, Tags, ...) \
- INTERNAL_CATCH_TEMPLATE_TEST_CASE_2( INTERNAL_CATCH_UNIQUE_NAME( ____C_A_T_C_H____T_E_M_P_L_A_T_E____T_E_S_T____ ), INTERNAL_CATCH_UNIQUE_NAME( ____C_A_T_C_H____T_E_M_P_L_A_T_E____T_E_S_T____F_U_N_C____ ), Name, Tags, __VA_ARGS__ )
+ INTERNAL_CATCH_TEMPLATE_TEST_CASE_2( INTERNAL_CATCH_UNIQUE_NAME( C_A_T_C_H_T_E_M_P_L_A_T_E_T_E_S_T_ ), INTERNAL_CATCH_UNIQUE_NAME( C_A_T_C_H_T_E_M_P_L_A_T_E_T_E_S_T_F_U_N_C_ ), Name, Tags, typename TestType, __VA_ARGS__ )
#else
#define INTERNAL_CATCH_TEMPLATE_TEST_CASE(Name, Tags, ...) \
- INTERNAL_CATCH_EXPAND_VARGS( INTERNAL_CATCH_TEMPLATE_TEST_CASE_2( INTERNAL_CATCH_UNIQUE_NAME( ____C_A_T_C_H____T_E_M_P_L_A_T_E____T_E_S_T____ ), INTERNAL_CATCH_UNIQUE_NAME( ____C_A_T_C_H____T_E_M_P_L_A_T_E____T_E_S_T____F_U_N_C____ ), Name, Tags, __VA_ARGS__ ) )
+ INTERNAL_CATCH_EXPAND_VARGS( INTERNAL_CATCH_TEMPLATE_TEST_CASE_2( INTERNAL_CATCH_UNIQUE_NAME( C_A_T_C_H_T_E_M_P_L_A_T_E_T_E_S_T_ ), INTERNAL_CATCH_UNIQUE_NAME( C_A_T_C_H_T_E_M_P_L_A_T_E_T_E_S_T_F_U_N_C_ ), Name, Tags, typename TestType, __VA_ARGS__ ) )
#endif
- #define INTERNAL_CATCH_TEMPLATE_REGISTRY_INITIATE(TestName, Name, ...)\
- static int INTERNAL_CATCH_UNIQUE_NAME( globalRegistrar ) = [](){\
- TestName(CATCH_REC_LIST_UD(INTERNAL_CATCH_TEMPLATE_UNIQUE_NAME,Name, __VA_ARGS__));\
- return 0;\
- }();
+#ifndef CATCH_CONFIG_TRADITIONAL_MSVC_PREPROCESSOR
+ #define INTERNAL_CATCH_TEMPLATE_TEST_CASE_SIG(Name, Tags, Signature, ...) \
+ INTERNAL_CATCH_TEMPLATE_TEST_CASE_2( INTERNAL_CATCH_UNIQUE_NAME( C_A_T_C_H_T_E_M_P_L_A_T_E_T_E_S_T_ ), INTERNAL_CATCH_UNIQUE_NAME( C_A_T_C_H_T_E_M_P_L_A_T_E_T_E_S_T_F_U_N_C_ ), Name, Tags, Signature, __VA_ARGS__ )
+#else
+ #define INTERNAL_CATCH_TEMPLATE_TEST_CASE_SIG(Name, Tags, Signature, ...) \
+ INTERNAL_CATCH_EXPAND_VARGS( INTERNAL_CATCH_TEMPLATE_TEST_CASE_2( INTERNAL_CATCH_UNIQUE_NAME( C_A_T_C_H_T_E_M_P_L_A_T_E_T_E_S_T_ ), INTERNAL_CATCH_UNIQUE_NAME( C_A_T_C_H_T_E_M_P_L_A_T_E_T_E_S_T_F_U_N_C_ ), Name, Tags, Signature, __VA_ARGS__ ) )
+#endif
- #define INTERNAL_CATCH_TEMPLATE_TEST_CASE_METHOD_2( TestNameClass, TestName, ClassName, Name, Tags, ... ) \
+ #define INTERNAL_CATCH_TEMPLATE_PRODUCT_TEST_CASE2(TestName, TestFuncName, Name, Tags, Signature, TmplTypes, TypesList) \
+ CATCH_INTERNAL_START_WARNINGS_SUPPRESSION \
+ CATCH_INTERNAL_SUPPRESS_GLOBALS_WARNINGS \
+ CATCH_INTERNAL_SUPPRESS_ZERO_VARIADIC_WARNINGS \
+ CATCH_INTERNAL_SUPPRESS_UNUSED_TEMPLATE_WARNINGS \
+ template static void TestFuncName(); \
+ namespace {\
+ namespace INTERNAL_CATCH_MAKE_NAMESPACE(TestName) { \
+ INTERNAL_CATCH_TYPE_GEN \
+ INTERNAL_CATCH_NTTP_GEN(INTERNAL_CATCH_REMOVE_PARENS(Signature)) \
+ template \
+ struct TestName { \
+ void reg_tests() { \
+ int index = 0; \
+ using expander = int[]; \
+ constexpr char const* tmpl_types[] = {CATCH_REC_LIST(INTERNAL_CATCH_STRINGIZE_WITHOUT_PARENS, INTERNAL_CATCH_REMOVE_PARENS(TmplTypes))};\
+ constexpr char const* types_list[] = {CATCH_REC_LIST(INTERNAL_CATCH_STRINGIZE_WITHOUT_PARENS, INTERNAL_CATCH_REMOVE_PARENS(TypesList))};\
+ constexpr auto num_types = sizeof(types_list) / sizeof(types_list[0]);\
+ (void)expander{(Catch::AutoReg( Catch::makeTestInvoker( &TestFuncName ), CATCH_INTERNAL_LINEINFO, Catch::StringRef(), Catch::NameAndTags{ Name " - " + std::string(tmpl_types[index / num_types]) + "<" + std::string(types_list[index % num_types]) + ">", Tags } ), index++)... };/* NOLINT */\
+ } \
+ }; \
+ static int INTERNAL_CATCH_UNIQUE_NAME( globalRegistrar ) = [](){ \
+ using TestInit = typename create()), TypeList>::type; \
+ TestInit t; \
+ t.reg_tests(); \
+ return 0; \
+ }(); \
+ } \
+ } \
+ CATCH_INTERNAL_STOP_WARNINGS_SUPPRESSION \
+ template \
+ static void TestFuncName()
+
+#ifndef CATCH_CONFIG_TRADITIONAL_MSVC_PREPROCESSOR
+ #define INTERNAL_CATCH_TEMPLATE_PRODUCT_TEST_CASE(Name, Tags, ...)\
+ INTERNAL_CATCH_TEMPLATE_PRODUCT_TEST_CASE2(INTERNAL_CATCH_UNIQUE_NAME( C_A_T_C_H_T_E_M_P_L_A_T_E_T_E_S_T_ ), INTERNAL_CATCH_UNIQUE_NAME( C_A_T_C_H_T_E_M_P_L_A_T_E_T_E_S_T_F_U_N_C_ ), Name, Tags, typename T,__VA_ARGS__)
+#else
+ #define INTERNAL_CATCH_TEMPLATE_PRODUCT_TEST_CASE(Name, Tags, ...)\
+ INTERNAL_CATCH_EXPAND_VARGS( INTERNAL_CATCH_TEMPLATE_PRODUCT_TEST_CASE2( INTERNAL_CATCH_UNIQUE_NAME( C_A_T_C_H_T_E_M_P_L_A_T_E_T_E_S_T_ ), INTERNAL_CATCH_UNIQUE_NAME( C_A_T_C_H_T_E_M_P_L_A_T_E_T_E_S_T_F_U_N_C_ ), Name, Tags, typename T, __VA_ARGS__ ) )
+#endif
+
+#ifndef CATCH_CONFIG_TRADITIONAL_MSVC_PREPROCESSOR
+ #define INTERNAL_CATCH_TEMPLATE_PRODUCT_TEST_CASE_SIG(Name, Tags, Signature, ...)\
+ INTERNAL_CATCH_TEMPLATE_PRODUCT_TEST_CASE2(INTERNAL_CATCH_UNIQUE_NAME( C_A_T_C_H_T_E_M_P_L_A_T_E_T_E_S_T_ ), INTERNAL_CATCH_UNIQUE_NAME( C_A_T_C_H_T_E_M_P_L_A_T_E_T_E_S_T_F_U_N_C_ ), Name, Tags, Signature, __VA_ARGS__)
+#else
+ #define INTERNAL_CATCH_TEMPLATE_PRODUCT_TEST_CASE_SIG(Name, Tags, Signature, ...)\
+ INTERNAL_CATCH_EXPAND_VARGS( INTERNAL_CATCH_TEMPLATE_PRODUCT_TEST_CASE2( INTERNAL_CATCH_UNIQUE_NAME( C_A_T_C_H_T_E_M_P_L_A_T_E_T_E_S_T_ ), INTERNAL_CATCH_UNIQUE_NAME( C_A_T_C_H_T_E_M_P_L_A_T_E_T_E_S_T_F_U_N_C_ ), Name, Tags, Signature, __VA_ARGS__ ) )
+#endif
+
+ #define INTERNAL_CATCH_TEMPLATE_LIST_TEST_CASE_2(TestName, TestFunc, Name, Tags, TmplList)\
+ CATCH_INTERNAL_START_WARNINGS_SUPPRESSION \
CATCH_INTERNAL_SUPPRESS_GLOBALS_WARNINGS \
- namespace{ \
- template \
+ CATCH_INTERNAL_SUPPRESS_UNUSED_TEMPLATE_WARNINGS \
+ template static void TestFunc(); \
+ namespace {\
+ namespace INTERNAL_CATCH_MAKE_NAMESPACE(TestName){\
+ INTERNAL_CATCH_TYPE_GEN\
+ template \
+ struct TestName { \
+ void reg_tests() { \
+ int index = 0; \
+ using expander = int[]; \
+ (void)expander{(Catch::AutoReg( Catch::makeTestInvoker( &TestFunc ), CATCH_INTERNAL_LINEINFO, Catch::StringRef(), Catch::NameAndTags{ Name " - " + std::string(INTERNAL_CATCH_STRINGIZE(TmplList)) + " - " + std::to_string(index), Tags } ), index++)... };/* NOLINT */\
+ } \
+ };\
+ static int INTERNAL_CATCH_UNIQUE_NAME( globalRegistrar ) = [](){ \
+ using TestInit = typename convert::type; \
+ TestInit t; \
+ t.reg_tests(); \
+ return 0; \
+ }(); \
+ }}\
+ CATCH_INTERNAL_STOP_WARNINGS_SUPPRESSION \
+ template \
+ static void TestFunc()
+
+ #define INTERNAL_CATCH_TEMPLATE_LIST_TEST_CASE(Name, Tags, TmplList) \
+ INTERNAL_CATCH_TEMPLATE_LIST_TEST_CASE_2( INTERNAL_CATCH_UNIQUE_NAME( C_A_T_C_H_T_E_M_P_L_A_T_E_T_E_S_T_ ), INTERNAL_CATCH_UNIQUE_NAME( C_A_T_C_H_T_E_M_P_L_A_T_E_T_E_S_T_F_U_N_C_ ), Name, Tags, TmplList )
+
+ #define INTERNAL_CATCH_TEMPLATE_TEST_CASE_METHOD_2( TestNameClass, TestName, ClassName, Name, Tags, Signature, ... ) \
+ CATCH_INTERNAL_START_WARNINGS_SUPPRESSION \
+ CATCH_INTERNAL_SUPPRESS_GLOBALS_WARNINGS \
+ CATCH_INTERNAL_SUPPRESS_ZERO_VARIADIC_WARNINGS \
+ CATCH_INTERNAL_SUPPRESS_UNUSED_TEMPLATE_WARNINGS \
+ namespace {\
+ namespace INTERNAL_CATCH_MAKE_NAMESPACE(TestName){ \
+ INTERNAL_CATCH_TYPE_GEN\
+ INTERNAL_CATCH_NTTP_GEN(INTERNAL_CATCH_REMOVE_PARENS(Signature))\
+ INTERNAL_CATCH_DECLARE_SIG_TEST_METHOD(TestName, ClassName, INTERNAL_CATCH_REMOVE_PARENS(Signature));\
+ INTERNAL_CATCH_NTTP_REG_METHOD_GEN(TestName, INTERNAL_CATCH_REMOVE_PARENS(Signature))\
+ template \
+ struct TestNameClass{\
+ TestNameClass(){\
+ int index = 0; \
+ constexpr char const* tmpl_types[] = {CATCH_REC_LIST(INTERNAL_CATCH_STRINGIZE_WITHOUT_PARENS, __VA_ARGS__)};\
+ using expander = int[];\
+ (void)expander{(reg_test(Types{}, #ClassName, Catch::NameAndTags{ Name " - " + std::string(tmpl_types[index]), Tags } ), index++)... };/* NOLINT */ \
+ }\
+ };\
+ static int INTERNAL_CATCH_UNIQUE_NAME( globalRegistrar ) = [](){\
+ TestNameClass();\
+ return 0;\
+ }();\
+ }\
+ }\
+ CATCH_INTERNAL_STOP_WARNINGS_SUPPRESSION \
+ INTERNAL_CATCH_DEFINE_SIG_TEST_METHOD(TestName, INTERNAL_CATCH_REMOVE_PARENS(Signature))
+
+#ifndef CATCH_CONFIG_TRADITIONAL_MSVC_PREPROCESSOR
+ #define INTERNAL_CATCH_TEMPLATE_TEST_CASE_METHOD( ClassName, Name, Tags,... ) \
+ INTERNAL_CATCH_TEMPLATE_TEST_CASE_METHOD_2( INTERNAL_CATCH_UNIQUE_NAME( C_A_T_C_H_T_E_M_P_L_A_T_E_T_E_S_T_C_L_A_S_S_ ), INTERNAL_CATCH_UNIQUE_NAME( C_A_T_C_H_T_E_M_P_L_A_T_E_T_E_S_T_ ) , ClassName, Name, Tags, typename T, __VA_ARGS__ )
+#else
+ #define INTERNAL_CATCH_TEMPLATE_TEST_CASE_METHOD( ClassName, Name, Tags,... ) \
+ INTERNAL_CATCH_EXPAND_VARGS( INTERNAL_CATCH_TEMPLATE_TEST_CASE_METHOD_2( INTERNAL_CATCH_UNIQUE_NAME( C_A_T_C_H_T_E_M_P_L_A_T_E_T_E_S_T_C_L_A_S_S_ ), INTERNAL_CATCH_UNIQUE_NAME( C_A_T_C_H_T_E_M_P_L_A_T_E_T_E_S_T_ ) , ClassName, Name, Tags, typename T, __VA_ARGS__ ) )
+#endif
+
+#ifndef CATCH_CONFIG_TRADITIONAL_MSVC_PREPROCESSOR
+ #define INTERNAL_CATCH_TEMPLATE_TEST_CASE_METHOD_SIG( ClassName, Name, Tags, Signature, ... ) \
+ INTERNAL_CATCH_TEMPLATE_TEST_CASE_METHOD_2( INTERNAL_CATCH_UNIQUE_NAME( C_A_T_C_H_T_E_M_P_L_A_T_E_T_E_S_T_C_L_A_S_S_ ), INTERNAL_CATCH_UNIQUE_NAME( C_A_T_C_H_T_E_M_P_L_A_T_E_T_E_S_T_ ) , ClassName, Name, Tags, Signature, __VA_ARGS__ )
+#else
+ #define INTERNAL_CATCH_TEMPLATE_TEST_CASE_METHOD_SIG( ClassName, Name, Tags, Signature, ... ) \
+ INTERNAL_CATCH_EXPAND_VARGS( INTERNAL_CATCH_TEMPLATE_TEST_CASE_METHOD_2( INTERNAL_CATCH_UNIQUE_NAME( C_A_T_C_H_T_E_M_P_L_A_T_E_T_E_S_T_C_L_A_S_S_ ), INTERNAL_CATCH_UNIQUE_NAME( C_A_T_C_H_T_E_M_P_L_A_T_E_T_E_S_T_ ) , ClassName, Name, Tags, Signature, __VA_ARGS__ ) )
+#endif
+
+ #define INTERNAL_CATCH_TEMPLATE_PRODUCT_TEST_CASE_METHOD_2(TestNameClass, TestName, ClassName, Name, Tags, Signature, TmplTypes, TypesList)\
+ CATCH_INTERNAL_START_WARNINGS_SUPPRESSION \
+ CATCH_INTERNAL_SUPPRESS_GLOBALS_WARNINGS \
+ CATCH_INTERNAL_SUPPRESS_ZERO_VARIADIC_WARNINGS \
+ CATCH_INTERNAL_SUPPRESS_UNUSED_TEMPLATE_WARNINGS \
+ template \
struct TestName : INTERNAL_CATCH_REMOVE_PARENS(ClassName ) { \
void test();\
};\
- template \
+ namespace {\
+ namespace INTERNAL_CATCH_MAKE_NAMESPACE(TestNameClass) {\
+ INTERNAL_CATCH_TYPE_GEN \
+ INTERNAL_CATCH_NTTP_GEN(INTERNAL_CATCH_REMOVE_PARENS(Signature))\
+ template\
struct TestNameClass{\
- template \
- TestNameClass(Ts...names){\
- CATCH_INTERNAL_CHECK_UNIQUE_TYPES(CATCH_REC_LIST(INTERNAL_CATCH_REMOVE_PARENS, __VA_ARGS__)) \
+ void reg_tests(){\
+ int index = 0;\
using expander = int[];\
- (void)expander{(Catch::AutoReg( Catch::makeTestInvoker( &TestName::test ), CATCH_INTERNAL_LINEINFO, #ClassName, Catch::NameAndTags{ names, Tags } ), 0)... };/* NOLINT */ \
+ constexpr char const* tmpl_types[] = {CATCH_REC_LIST(INTERNAL_CATCH_STRINGIZE_WITHOUT_PARENS, INTERNAL_CATCH_REMOVE_PARENS(TmplTypes))};\
+ constexpr char const* types_list[] = {CATCH_REC_LIST(INTERNAL_CATCH_STRINGIZE_WITHOUT_PARENS, INTERNAL_CATCH_REMOVE_PARENS(TypesList))};\
+ constexpr auto num_types = sizeof(types_list) / sizeof(types_list[0]);\
+ (void)expander{(Catch::AutoReg( Catch::makeTestInvoker( &TestName::test ), CATCH_INTERNAL_LINEINFO, #ClassName, Catch::NameAndTags{ Name " - " + std::string(tmpl_types[index / num_types]) + "<" + std::string(types_list[index % num_types]) + ">", Tags } ), index++)... };/* NOLINT */ \
}\
};\
- INTERNAL_CATCH_TEMPLATE_REGISTRY_INITIATE(TestNameClass, Name, __VA_ARGS__)\
+ static int INTERNAL_CATCH_UNIQUE_NAME( globalRegistrar ) = [](){\
+ using TestInit = typename create()), TypeList>::type;\
+ TestInit t;\
+ t.reg_tests();\
+ return 0;\
+ }(); \
}\
- CATCH_INTERNAL_UNSUPPRESS_GLOBALS_WARNINGS\
+ }\
+ CATCH_INTERNAL_STOP_WARNINGS_SUPPRESSION \
template \
void TestName::test()
#ifndef CATCH_CONFIG_TRADITIONAL_MSVC_PREPROCESSOR
- #define INTERNAL_CATCH_TEMPLATE_TEST_CASE_METHOD( ClassName, Name, Tags,... ) \
- INTERNAL_CATCH_TEMPLATE_TEST_CASE_METHOD_2( INTERNAL_CATCH_UNIQUE_NAME( ____C_A_T_C_H____T_E_M_P_L_A_T_E____T_E_S_T____C_L_A_S_S____ ), INTERNAL_CATCH_UNIQUE_NAME( ____C_A_T_C_H____T_E_M_P_L_A_T_E____T_E_S_T____ ) , ClassName, Name, Tags, __VA_ARGS__ )
+ #define INTERNAL_CATCH_TEMPLATE_PRODUCT_TEST_CASE_METHOD( ClassName, Name, Tags, ... )\
+ INTERNAL_CATCH_TEMPLATE_PRODUCT_TEST_CASE_METHOD_2( INTERNAL_CATCH_UNIQUE_NAME( C_A_T_C_H_T_E_M_P_L_A_T_E_T_E_S_T_ ), INTERNAL_CATCH_UNIQUE_NAME( C_A_T_C_H_T_E_M_P_L_A_T_E_T_E_S_T_F_U_N_C_ ), ClassName, Name, Tags, typename T, __VA_ARGS__ )
#else
- #define INTERNAL_CATCH_TEMPLATE_TEST_CASE_METHOD( ClassName, Name, Tags,... ) \
- INTERNAL_CATCH_EXPAND_VARGS( INTERNAL_CATCH_TEMPLATE_TEST_CASE_METHOD_2( INTERNAL_CATCH_UNIQUE_NAME( ____C_A_T_C_H____T_E_M_P_L_A_T_E____T_E_S_T____C_L_A_S_S____ ), INTERNAL_CATCH_UNIQUE_NAME( ____C_A_T_C_H____T_E_M_P_L_A_T_E____T_E_S_T____ ) , ClassName, Name, Tags, __VA_ARGS__ ) )
+ #define INTERNAL_CATCH_TEMPLATE_PRODUCT_TEST_CASE_METHOD( ClassName, Name, Tags, ... )\
+ INTERNAL_CATCH_EXPAND_VARGS( INTERNAL_CATCH_TEMPLATE_PRODUCT_TEST_CASE_METHOD_2( INTERNAL_CATCH_UNIQUE_NAME( C_A_T_C_H_T_E_M_P_L_A_T_E_T_E_S_T_ ), INTERNAL_CATCH_UNIQUE_NAME( C_A_T_C_H_T_E_M_P_L_A_T_E_T_E_S_T_F_U_N_C_ ), ClassName, Name, Tags, typename T,__VA_ARGS__ ) )
#endif
+#ifndef CATCH_CONFIG_TRADITIONAL_MSVC_PREPROCESSOR
+ #define INTERNAL_CATCH_TEMPLATE_PRODUCT_TEST_CASE_METHOD_SIG( ClassName, Name, Tags, Signature, ... )\
+ INTERNAL_CATCH_TEMPLATE_PRODUCT_TEST_CASE_METHOD_2( INTERNAL_CATCH_UNIQUE_NAME( C_A_T_C_H_T_E_M_P_L_A_T_E_T_E_S_T_ ), INTERNAL_CATCH_UNIQUE_NAME( C_A_T_C_H_T_E_M_P_L_A_T_E_T_E_S_T_F_U_N_C_ ), ClassName, Name, Tags, Signature, __VA_ARGS__ )
+#else
+ #define INTERNAL_CATCH_TEMPLATE_PRODUCT_TEST_CASE_METHOD_SIG( ClassName, Name, Tags, Signature, ... )\
+ INTERNAL_CATCH_EXPAND_VARGS( INTERNAL_CATCH_TEMPLATE_PRODUCT_TEST_CASE_METHOD_2( INTERNAL_CATCH_UNIQUE_NAME( C_A_T_C_H_T_E_M_P_L_A_T_E_T_E_S_T_ ), INTERNAL_CATCH_UNIQUE_NAME( C_A_T_C_H_T_E_M_P_L_A_T_E_T_E_S_T_F_U_N_C_ ), ClassName, Name, Tags, Signature,__VA_ARGS__ ) )
+#endif
+
+ #define INTERNAL_CATCH_TEMPLATE_LIST_TEST_CASE_METHOD_2( TestNameClass, TestName, ClassName, Name, Tags, TmplList) \
+ CATCH_INTERNAL_START_WARNINGS_SUPPRESSION \
+ CATCH_INTERNAL_SUPPRESS_GLOBALS_WARNINGS \
+ CATCH_INTERNAL_SUPPRESS_UNUSED_TEMPLATE_WARNINGS \
+ template \
+ struct TestName : INTERNAL_CATCH_REMOVE_PARENS(ClassName ) { \
+ void test();\
+ };\
+ namespace {\
+ namespace INTERNAL_CATCH_MAKE_NAMESPACE(TestName){ \
+ INTERNAL_CATCH_TYPE_GEN\
+ template\
+ struct TestNameClass{\
+ void reg_tests(){\
+ int index = 0;\
+ using expander = int[];\
+ (void)expander{(Catch::AutoReg( Catch::makeTestInvoker( &TestName::test ), CATCH_INTERNAL_LINEINFO, #ClassName, Catch::NameAndTags{ Name " - " + std::string(INTERNAL_CATCH_STRINGIZE(TmplList)) + " - " + std::to_string(index), Tags } ), index++)... };/* NOLINT */ \
+ }\
+ };\
+ static int INTERNAL_CATCH_UNIQUE_NAME( globalRegistrar ) = [](){\
+ using TestInit = typename convert::type;\
+ TestInit t;\
+ t.reg_tests();\
+ return 0;\
+ }(); \
+ }}\
+ CATCH_INTERNAL_STOP_WARNINGS_SUPPRESSION \
+ template \
+ void TestName::test()
+
+#define INTERNAL_CATCH_TEMPLATE_LIST_TEST_CASE_METHOD(ClassName, Name, Tags, TmplList) \
+ INTERNAL_CATCH_TEMPLATE_LIST_TEST_CASE_METHOD_2( INTERNAL_CATCH_UNIQUE_NAME( C_A_T_C_H_T_E_M_P_L_A_T_E_T_E_S_T_ ), INTERNAL_CATCH_UNIQUE_NAME( C_A_T_C_H_T_E_M_P_L_A_T_E_T_E_S_T_F_U_N_C_ ), ClassName, Name, Tags, TmplList )
+
// end catch_test_registry.h
// start catch_capture.hpp
@@ -978,7 +1437,7 @@ namespace Catch {
auto makeStream( StringRef const &filename ) -> IStream const*;
- class ReusableStringStream {
+ class ReusableStringStream : NonCopyable {
std::size_t m_index;
std::ostream* m_oss;
public:
@@ -997,6 +1456,42 @@ namespace Catch {
}
// end catch_stream.h
+// start catch_interfaces_enum_values_registry.h
+
+#include
+
+namespace Catch {
+
+ namespace Detail {
+ struct EnumInfo {
+ StringRef m_name;
+ std::vector> m_values;
+
+ ~EnumInfo();
+
+ StringRef lookup( int value ) const;
+ };
+ } // namespace Detail
+
+ struct IMutableEnumValuesRegistry {
+ virtual ~IMutableEnumValuesRegistry();
+
+ virtual Detail::EnumInfo const& registerEnum( StringRef enumName, StringRef allEnums, std::vector const& values ) = 0;
+
+ template
+ Detail::EnumInfo const& registerEnum( StringRef enumName, StringRef allEnums, std::initializer_list values ) {
+ static_assert(sizeof(int) >= sizeof(E), "Cannot serialize enum to int");
+ std::vector intValues;
+ intValues.reserve( values.size() );
+ for( auto enumValue : values )
+ intValues.push_back( static_cast( enumValue ) );
+ return registerEnum( enumName, allEnums, intValues );
+ }
+ };
+
+} // Catch
+
+// end catch_interfaces_enum_values_registry.h
#ifdef CATCH_CONFIG_CPP17_STRING_VIEW
#include
@@ -1067,9 +1562,9 @@ namespace Catch {
template
class IsStreamInsertable {
- template
+ template
static auto test(int)
- -> decltype(std::declval() << std::declval(), std::true_type());
+ -> decltype(std::declval() << std::declval(), std::true_type());
template
static auto test(...)->std::false_type;
@@ -1231,6 +1726,12 @@ namespace Catch {
}
};
+#if defined(CATCH_CONFIG_CPP17_BYTE)
+ template<>
+ struct StringMaker {
+ static std::string convert(std::byte value);
+ };
+#endif // defined(CATCH_CONFIG_CPP17_BYTE)
template<>
struct StringMaker {
static std::string convert(int value);
@@ -1282,10 +1783,13 @@ namespace Catch {
template<>
struct StringMaker {
static std::string convert(float value);
+ static int precision;
};
+
template<>
struct StringMaker {
static std::string convert(double value);
+ static int precision;
};
template
@@ -1321,8 +1825,8 @@ namespace Catch {
#endif
namespace Detail {
- template
- std::string rangeToString(InputIterator first, InputIterator last) {
+ template
+ std::string rangeToString(InputIterator first, Sentinel last) {
ReusableStringStream rss;
rss << "{ ";
if (first != last) {
@@ -1370,6 +1874,7 @@ namespace Catch {
# define CATCH_CONFIG_ENABLE_TUPLE_STRINGMAKER
# define CATCH_CONFIG_ENABLE_VARIANT_STRINGMAKER
# define CATCH_CONFIG_ENABLE_CHRONO_STRINGMAKER
+# define CATCH_CONFIG_ENABLE_OPTIONAL_STRINGMAKER
#endif
// Separate std::pair specialization
@@ -1391,6 +1896,24 @@ namespace Catch {
}
#endif // CATCH_CONFIG_ENABLE_PAIR_STRINGMAKER
+#if defined(CATCH_CONFIG_ENABLE_OPTIONAL_STRINGMAKER) && defined(CATCH_CONFIG_CPP17_OPTIONAL)
+#include
+namespace Catch {
+ template
+ struct StringMaker > {
+ static std::string convert(const std::optional& optional) {
+ ReusableStringStream rss;
+ if (optional.has_value()) {
+ rss << ::Catch::Detail::stringify(*optional);
+ } else {
+ rss << "{ }";
+ }
+ return rss.str();
+ }
+ };
+}
+#endif // CATCH_CONFIG_ENABLE_OPTIONAL_STRINGMAKER
+
// Separate std::tuple specialization
#if defined(CATCH_CONFIG_ENABLE_TUPLE_STRINGMAKER)
#include
@@ -1461,20 +1984,27 @@ namespace Catch {
#endif // CATCH_CONFIG_ENABLE_VARIANT_STRINGMAKER
namespace Catch {
- struct not_this_one {}; // Tag type for detecting which begin/ end are being selected
-
- // Import begin/ end from std here so they are considered alongside the fallback (...) overloads in this namespace
+ // Import begin/ end from std here
using std::begin;
using std::end;
- not_this_one begin( ... );
- not_this_one end( ... );
+ namespace detail {
+ template
+ struct void_type {
+ using type = void;
+ };
+
+ template
+ struct is_range_impl : std::false_type {
+ };
+
+ template
+ struct is_range_impl()))>::type> : std::true_type {
+ };
+ } // namespace detail
template
- struct is_range {
- static const bool value =
- !std::is_same