mirror of
https://github.com/wheremyfoodat/Panda3DS.git
synced 2025-09-02 15:45:46 +00:00
Merge branch 'master' into open-bp-cpp
This commit is contained in:
commit
6d067d7108
402 changed files with 72132 additions and 27830 deletions
4
.github/workflows/Android_Build.yml
vendored
4
.github/workflows/Android_Build.yml
vendored
|
@ -37,7 +37,7 @@ jobs:
|
|||
${{ runner.os }}-pandroid-x86_64-
|
||||
|
||||
- name: Setup Vulkan SDK
|
||||
uses: humbletim/setup-vulkan-sdk@v1.2.0
|
||||
uses: humbletim/setup-vulkan-sdk@main
|
||||
with:
|
||||
vulkan-query-version: latest
|
||||
vulkan-use-cache: true
|
||||
|
@ -105,7 +105,7 @@ jobs:
|
|||
${{ runner.os }}-pandroid-arm64-
|
||||
|
||||
- name: Setup Vulkan SDK
|
||||
uses: humbletim/setup-vulkan-sdk@v1.2.0
|
||||
uses: humbletim/setup-vulkan-sdk@main
|
||||
with:
|
||||
vulkan-query-version: latest
|
||||
vulkan-use-cache: true
|
||||
|
|
2
.github/workflows/HTTP_Build.yml
vendored
2
.github/workflows/HTTP_Build.yml
vendored
|
@ -30,7 +30,7 @@ jobs:
|
|||
sudo ./llvm.sh 17
|
||||
|
||||
- name: Setup Vulkan SDK
|
||||
uses: humbletim/setup-vulkan-sdk@v1.2.0
|
||||
uses: humbletim/setup-vulkan-sdk@main
|
||||
with:
|
||||
vulkan-query-version: latest
|
||||
vulkan-use-cache: true
|
||||
|
|
41
.github/workflows/Hydra_Build.yml
vendored
41
.github/workflows/Hydra_Build.yml
vendored
|
@ -20,7 +20,7 @@ jobs:
|
|||
run: git submodule update --init --recursive
|
||||
|
||||
- name: Setup Vulkan SDK
|
||||
uses: humbletim/setup-vulkan-sdk@v1.2.0
|
||||
uses: humbletim/setup-vulkan-sdk@main
|
||||
with:
|
||||
vulkan-query-version: latest
|
||||
vulkan-use-cache: true
|
||||
|
@ -63,7 +63,7 @@ jobs:
|
|||
run: git submodule update --init --recursive
|
||||
|
||||
- name: Setup Vulkan SDK
|
||||
uses: humbletim/setup-vulkan-sdk@v1.2.0
|
||||
uses: humbletim/setup-vulkan-sdk@main
|
||||
with:
|
||||
vulkan-query-version: latest
|
||||
vulkan-use-cache: true
|
||||
|
@ -116,7 +116,7 @@ jobs:
|
|||
sudo ./llvm.sh 17
|
||||
|
||||
- name: Setup Vulkan SDK
|
||||
uses: humbletim/setup-vulkan-sdk@v1.2.0
|
||||
uses: humbletim/setup-vulkan-sdk@main
|
||||
with:
|
||||
vulkan-query-version: latest
|
||||
vulkan-use-cache: true
|
||||
|
@ -163,7 +163,7 @@ jobs:
|
|||
sudo apt-get update && sudo apt install libx11-dev libgl1 libglx-mesa0 mesa-common-dev libfuse2 libwayland-dev
|
||||
|
||||
- name: Setup Vulkan SDK
|
||||
uses: humbletim/setup-vulkan-sdk@v1.2.0
|
||||
uses: humbletim/setup-vulkan-sdk@main
|
||||
with:
|
||||
vulkan-query-version: latest
|
||||
vulkan-use-cache: true
|
||||
|
@ -180,3 +180,36 @@ jobs:
|
|||
with:
|
||||
name: Android Hydra core
|
||||
path: '${{github.workspace}}/build/libAlber.so'
|
||||
|
||||
ARM-Libretro:
|
||||
runs-on: ubuntu-24.04-arm
|
||||
|
||||
steps:
|
||||
- uses: actions/checkout@v4
|
||||
- name: Fetch submodules
|
||||
run: git submodule update --init --recursive
|
||||
|
||||
- name: Install misc packages
|
||||
run: |
|
||||
sudo apt-get update && sudo apt install libx11-dev libxext-dev libgl1 libglx-mesa0 mesa-common-dev libfuse2 libwayland-dev
|
||||
|
||||
- name: Install newer Clang
|
||||
run: |
|
||||
wget https://apt.llvm.org/llvm.sh
|
||||
chmod +x ./llvm.sh
|
||||
sudo ./llvm.sh 17
|
||||
|
||||
- name: Configure CMake
|
||||
run: |
|
||||
cmake -B ${{github.workspace}}/build -DCMAKE_BUILD_TYPE=${{env.BUILD_TYPE}} -DCMAKE_C_COMPILER=clang-17 -DCMAKE_CXX_COMPILER=clang++-17 -DENABLE_USER_BUILD=ON -DBUILD_LIBRETRO_CORE=ON -DENABLE_VULKAN=OFF -DCRYPTOPP_OPT_DISABLE_ASM=ON
|
||||
|
||||
- name: Build
|
||||
run: cmake --build ${{github.workspace}}/build --config ${{env.BUILD_TYPE}}
|
||||
|
||||
- name: Upload Libretro core
|
||||
uses: actions/upload-artifact@v4
|
||||
with:
|
||||
name: Linux arm64 Libretro core
|
||||
path: |
|
||||
${{github.workspace}}/build/panda3ds_libretro.so
|
||||
${{github.workspace}}/docs/libretro/panda3ds_libretro.info
|
||||
|
|
2
.github/workflows/Linux_AppImage_Build.yml
vendored
2
.github/workflows/Linux_AppImage_Build.yml
vendored
|
@ -33,7 +33,7 @@ jobs:
|
|||
sudo ./llvm.sh 17
|
||||
|
||||
- name: Setup Vulkan SDK
|
||||
uses: humbletim/setup-vulkan-sdk@v1.2.0
|
||||
uses: humbletim/setup-vulkan-sdk@main
|
||||
with:
|
||||
vulkan-query-version: latest
|
||||
vulkan-use-cache: true
|
||||
|
|
2
.github/workflows/Linux_Build.yml
vendored
2
.github/workflows/Linux_Build.yml
vendored
|
@ -33,7 +33,7 @@ jobs:
|
|||
sudo ./llvm.sh 17
|
||||
|
||||
- name: Setup Vulkan SDK
|
||||
uses: humbletim/setup-vulkan-sdk@v1.2.0
|
||||
uses: humbletim/setup-vulkan-sdk@main
|
||||
with:
|
||||
vulkan-query-version: latest
|
||||
vulkan-use-cache: true
|
||||
|
|
2
.github/workflows/MacOS_Build.yml
vendored
2
.github/workflows/MacOS_Build.yml
vendored
|
@ -25,7 +25,7 @@ jobs:
|
|||
run: git submodule update --init --recursive
|
||||
|
||||
- name: Setup Vulkan SDK
|
||||
uses: humbletim/setup-vulkan-sdk@v1.2.0
|
||||
uses: humbletim/setup-vulkan-sdk@main
|
||||
with:
|
||||
vulkan-query-version: latest
|
||||
vulkan-use-cache: true
|
||||
|
|
6
.github/workflows/Qt_Build.yml
vendored
6
.github/workflows/Qt_Build.yml
vendored
|
@ -26,7 +26,7 @@ jobs:
|
|||
version: 6.2.0
|
||||
|
||||
- name: Setup Vulkan SDK
|
||||
uses: humbletim/setup-vulkan-sdk@v1.2.0
|
||||
uses: humbletim/setup-vulkan-sdk@main
|
||||
with:
|
||||
vulkan-query-version: latest
|
||||
vulkan-use-cache: true
|
||||
|
@ -64,7 +64,7 @@ jobs:
|
|||
run: git submodule update --init --recursive
|
||||
|
||||
- name: Setup Vulkan SDK
|
||||
uses: humbletim/setup-vulkan-sdk@v1.2.0
|
||||
uses: humbletim/setup-vulkan-sdk@main
|
||||
with:
|
||||
vulkan-query-version: latest
|
||||
vulkan-use-cache: true
|
||||
|
@ -162,7 +162,7 @@ jobs:
|
|||
sudo ./llvm.sh 17
|
||||
|
||||
- name: Setup Vulkan SDK
|
||||
uses: humbletim/setup-vulkan-sdk@v1.2.0
|
||||
uses: humbletim/setup-vulkan-sdk@main
|
||||
with:
|
||||
vulkan-query-version: latest
|
||||
vulkan-use-cache: true
|
||||
|
|
2
.github/workflows/Windows_Build.yml
vendored
2
.github/workflows/Windows_Build.yml
vendored
|
@ -24,7 +24,7 @@ jobs:
|
|||
run: git submodule update --init --recursive
|
||||
|
||||
- name: Setup Vulkan SDK
|
||||
uses: humbletim/setup-vulkan-sdk@v1.2.0
|
||||
uses: humbletim/setup-vulkan-sdk@main
|
||||
with:
|
||||
vulkan-query-version: latest
|
||||
vulkan-use-cache: true
|
||||
|
|
39
.github/workflows/iOS_Build.yml
vendored
Normal file
39
.github/workflows/iOS_Build.yml
vendored
Normal file
|
@ -0,0 +1,39 @@
|
|||
name: iOS Simulator Build
|
||||
|
||||
on:
|
||||
push:
|
||||
branches:
|
||||
- master
|
||||
pull_request:
|
||||
|
||||
env:
|
||||
# Customize the CMake build type here (Release, Debug, RelWithDebInfo, etc.)
|
||||
BUILD_TYPE: Release
|
||||
|
||||
jobs:
|
||||
build:
|
||||
# The CMake configure and build commands are platform agnostic and should work equally
|
||||
# well on Windows or Mac. You can convert this to a matrix build if you need
|
||||
# cross-platform coverage.
|
||||
# See: https://docs.github.com/en/free-pro-team@latest/actions/learn-github-actions/managing-complex-workflows#using-a-build-matrix
|
||||
runs-on: macos-latest
|
||||
|
||||
steps:
|
||||
- uses: actions/checkout@v4
|
||||
- name: Fetch submodules
|
||||
run: git submodule update --init --recursive
|
||||
|
||||
- name: Update Xcode
|
||||
uses: maxim-lobanov/setup-xcode@v1
|
||||
with:
|
||||
xcode-version: latest
|
||||
|
||||
- name: Setup Vulkan SDK
|
||||
uses: humbletim/setup-vulkan-sdk@main
|
||||
with:
|
||||
vulkan-query-version: latest
|
||||
vulkan-use-cache: true
|
||||
vulkan-components: Vulkan-Headers, Vulkan-Loader, SPIRV-Tools, Glslang
|
||||
|
||||
- name: Build core and frontend
|
||||
run: cd src/pandios && ./build.sh
|
9
.gitignore
vendored
9
.gitignore
vendored
|
@ -70,3 +70,12 @@ fb.bat
|
|||
|
||||
config.toml
|
||||
CMakeSettings.json
|
||||
|
||||
# IDE files
|
||||
|
||||
# KDevelop files
|
||||
*.kdev4
|
||||
# IDEA/Clion files
|
||||
.idea/
|
||||
# VSC files
|
||||
/.vscode/
|
10
.gitmodules
vendored
10
.gitmodules
vendored
|
@ -1,9 +1,6 @@
|
|||
[submodule "third_party/elfio"]
|
||||
path = third_party/elfio
|
||||
url = https://github.com/serge1/ELFIO
|
||||
[submodule "third_party/SDL2"]
|
||||
path = third_party/SDL2
|
||||
url = https://github.com/libsdl-org/SDL
|
||||
[submodule "third_party/cryptopp/cryptopp"]
|
||||
path = third_party/cryptopp/cryptopp
|
||||
url = https://github.com/weidai11/cryptopp
|
||||
|
@ -82,9 +79,10 @@
|
|||
[submodule "third_party/fdk-aac"]
|
||||
path = third_party/fdk-aac
|
||||
url = https://github.com/Panda3DS-emu/fdk-aac/
|
||||
[submodule "third_party/cryptoppwin"]
|
||||
path = third_party/cryptoppwin
|
||||
url = https://github.com/shadps4-emu/ext-cryptoppwin
|
||||
[submodule "third_party/oaknut"]
|
||||
path = third_party/oaknut
|
||||
url = https://github.com/panda3ds-emu/oaknut
|
||||
[submodule "third_party/SDL2"]
|
||||
path = third_party/SDL2
|
||||
url = https://github.com/libsdl-org/SDL
|
||||
branch = SDL2
|
||||
|
|
|
@ -64,6 +64,14 @@ option(BUILD_HYDRA_CORE "Build a Hydra core" OFF)
|
|||
option(BUILD_LIBRETRO_CORE "Build a Libretro core" OFF)
|
||||
option(ENABLE_RENDERDOC_API "Build with support for Renderdoc's capture API for graphics debugging" ON)
|
||||
option(DISABLE_SSE4 "Build with SSE4 instructions disabled, may reduce performance" OFF)
|
||||
option(USE_LIBRETRO_AUDIO "Enable to use the LR audio device with the LR core. Otherwise our own device is used" OFF)
|
||||
option(IOS_SIMULATOR_BUILD "Compiling for IOS simulator (Set to off if compiling for a real iPhone)" ON)
|
||||
|
||||
# Discord RPC & LuaJIT are currently not supported on iOS
|
||||
if(IOS)
|
||||
set(ENABLE_DISCORD_RPC OFF)
|
||||
set(ENABLE_LUAJIT OFF)
|
||||
endif()
|
||||
|
||||
set(OPENGL_PROFILE ${DEFAULT_OPENGL_PROFILE} CACHE STRING "OpenGL profile to use if OpenGL is enabled. Valid values are 'OpenGL' and 'OpenGLES'.")
|
||||
set_property(CACHE OPENGL_PROFILE PROPERTY STRINGS OpenGL OpenGLES)
|
||||
|
@ -80,6 +88,10 @@ endif()
|
|||
if(BUILD_LIBRETRO_CORE)
|
||||
set(CMAKE_POSITION_INDEPENDENT_CODE ON)
|
||||
add_compile_definitions(__LIBRETRO__)
|
||||
|
||||
if(USE_LIBRETRO_AUDIO)
|
||||
add_compile_definitions(USE_LIBRETRO_AUDIO_DEVICE)
|
||||
endif()
|
||||
endif()
|
||||
|
||||
if(CMAKE_CXX_COMPILER_ID STREQUAL "MSVC" AND ENABLE_USER_BUILD)
|
||||
|
@ -157,7 +169,6 @@ if(ENABLE_DISCORD_RPC AND NOT ANDROID)
|
|||
include_directories(third_party/discord-rpc/include)
|
||||
endif()
|
||||
|
||||
|
||||
if (NOT ANDROID)
|
||||
if (USE_SYSTEM_SDL2)
|
||||
find_package(SDL2 CONFIG REQUIRED)
|
||||
|
@ -303,7 +314,7 @@ else()
|
|||
message(FATAL_ERROR "Currently unsupported CPU architecture")
|
||||
endif()
|
||||
|
||||
add_subdirectory(third_party/teakra EXCLUDE_FROM_ALL)
|
||||
add_subdirectory(third_party/teakra)
|
||||
add_subdirectory(third_party/fdk-aac)
|
||||
|
||||
set(CAPSTONE_ARCHITECTURE_DEFAULT OFF)
|
||||
|
@ -316,8 +327,8 @@ set(SOURCE_FILES src/emulator.cpp src/io_file.cpp src/config.cpp
|
|||
src/core/CPU/cpu_dynarmic.cpp src/core/CPU/dynarmic_cycles.cpp
|
||||
src/core/memory.cpp src/renderer.cpp src/core/renderer_null/renderer_null.cpp
|
||||
src/http_server.cpp src/stb_image_write.c src/core/cheats.cpp src/core/action_replay.cpp
|
||||
src/discord_rpc.cpp src/lua.cpp src/memory_mapped_file.cpp src/miniaudio.cpp src/renderdoc.cpp
|
||||
src/frontend_settings.cpp
|
||||
src/discord_rpc.cpp src/lua.cpp src/memory_mapped_file.cpp src/renderdoc.cpp
|
||||
src/frontend_settings.cpp src/miniaudio/miniaudio.cpp src/core/screen_layout.cpp
|
||||
)
|
||||
set(CRYPTO_SOURCE_FILES src/core/crypto/aes_engine.cpp)
|
||||
set(KERNEL_SOURCE_FILES src/core/kernel/kernel.cpp src/core/kernel/resource_limits.cpp
|
||||
|
@ -335,10 +346,10 @@ set(SERVICE_SOURCE_FILES src/core/services/service_manager.cpp src/core/services
|
|||
src/core/services/frd.cpp src/core/services/nim.cpp src/core/services/mcu/mcu_hwc.cpp
|
||||
src/core/services/y2r.cpp src/core/services/cam.cpp src/core/services/ldr_ro.cpp
|
||||
src/core/services/act.cpp src/core/services/nfc.cpp src/core/services/dlp_srvr.cpp
|
||||
src/core/services/ir_user.cpp src/core/services/http.cpp src/core/services/soc.cpp
|
||||
src/core/services/ir/ir_user.cpp src/core/services/http.cpp src/core/services/soc.cpp
|
||||
src/core/services/ssl.cpp src/core/services/news_u.cpp src/core/services/amiibo_device.cpp
|
||||
src/core/services/csnd.cpp src/core/services/nwm_uds.cpp src/core/services/fonts.cpp
|
||||
src/core/services/ns.cpp
|
||||
src/core/services/ns.cpp src/core/services/ir/circlepad_pro.cpp src/core/services/ir/crc8.cpp
|
||||
)
|
||||
set(PICA_SOURCE_FILES src/core/PICA/gpu.cpp src/core/PICA/regs.cpp src/core/PICA/shader_unit.cpp
|
||||
src/core/PICA/shader_interpreter.cpp src/core/PICA/dynapica/shader_rec.cpp
|
||||
|
@ -386,7 +397,7 @@ set(HEADER_FILES include/emulator.hpp include/helpers.hpp include/termcolor.hpp
|
|||
include/result/result_common.hpp include/result/result_fs.hpp include/result/result_fnd.hpp
|
||||
include/result/result_gsp.hpp include/result/result_kernel.hpp include/result/result_os.hpp
|
||||
include/crypto/aes_engine.hpp include/metaprogramming.hpp include/PICA/pica_vertex.hpp
|
||||
include/config.hpp include/services/ir_user.hpp include/http_server.hpp include/cheats.hpp
|
||||
include/config.hpp include/services/ir/ir_user.hpp include/http_server.hpp include/cheats.hpp
|
||||
include/action_replay.hpp include/renderer_sw/renderer_sw.hpp include/compiler_builtins.hpp
|
||||
include/fs/romfs.hpp include/fs/ivfc.hpp include/discord_rpc.hpp include/services/http.hpp include/result/result_cfg.hpp
|
||||
include/applets/applet.hpp include/applets/mii_selector.hpp include/math_util.hpp include/services/soc.hpp
|
||||
|
@ -402,10 +413,21 @@ set(HEADER_FILES include/emulator.hpp include/helpers.hpp include/termcolor.hpp
|
|||
include/align.hpp include/audio/aac_decoder.hpp include/PICA/pica_simd.hpp include/services/fonts.hpp
|
||||
include/audio/audio_interpolation.hpp include/audio/hle_mixer.hpp include/audio/dsp_simd.hpp
|
||||
include/services/dsp_firmware_db.hpp include/frontend_settings.hpp include/fs/archive_twl_photo.hpp
|
||||
include/fs/archive_twl_sound.hpp include/fs/archive_card_spi.hpp include/services/ns.hpp
|
||||
include/external_haptics_manager.hpp
|
||||
include/fs/archive_twl_sound.hpp include/fs/archive_card_spi.hpp include/services/ns.hpp include/audio/audio_device.hpp
|
||||
include/audio/audio_device_interface.hpp include/audio/libretro_audio_device.hpp include/services/ir/ir_types.hpp
|
||||
include/services/ir/ir_device.hpp include/services/ir/circlepad_pro.hpp include/services/service_intercept.hpp
|
||||
include/screen_layout.hpp include/services/service_map.hpp include/audio/dsp_binary.hpp include/external_haptics_manager.hpp
|
||||
)
|
||||
|
||||
if(IOS)
|
||||
set(SOURCE_FILES ${SOURCE_FILES} src/miniaudio/miniaudio.m)
|
||||
target_compile_definitions(AlberCore PUBLIC "PANDA3DS_IOS=1")
|
||||
|
||||
if (IOS_SIMULATOR_BUILD)
|
||||
target_compile_definitions(AlberCore PUBLIC "PANDA3DS_IOS_SIMULATOR=1")
|
||||
endif()
|
||||
endif()
|
||||
|
||||
cmrc_add_resource_library(
|
||||
resources_console_fonts
|
||||
NAMESPACE ConsoleFonts
|
||||
|
@ -589,14 +611,16 @@ if(ENABLE_METAL AND APPLE)
|
|||
include/renderer_mtl/mtl_common.hpp
|
||||
include/renderer_mtl/pica_to_mtl.hpp
|
||||
include/renderer_mtl/objc_helper.hpp
|
||||
include/renderer_mtl/texture_decoder.hpp
|
||||
)
|
||||
|
||||
set(RENDERER_MTL_SOURCE_FILES src/core/renderer_mtl/metal_cpp_impl.cpp
|
||||
src/core/renderer_mtl/renderer_mtl.cpp
|
||||
src/core/renderer_mtl/mtl_texture.cpp
|
||||
src/core/renderer_mtl/mtl_etc1.cpp
|
||||
src/core/renderer_mtl/mtl_lut_texture.cpp
|
||||
src/core/renderer_mtl/pica_to_mtl.cpp
|
||||
src/core/renderer_mtl/objc_helper.mm
|
||||
src/core/renderer_mtl/texture_decoder.cpp
|
||||
src/host_shaders/metal_shaders.metal
|
||||
src/host_shaders/metal_blit.metal
|
||||
#src/host_shaders/metal_copy_to_lut_texture.metal
|
||||
|
@ -610,15 +634,26 @@ if(ENABLE_METAL AND APPLE)
|
|||
set(SHADER_SOURCE "${CMAKE_SOURCE_DIR}/src/host_shaders/${SHADER}.metal")
|
||||
set(SHADER_IR "${CMAKE_SOURCE_DIR}/src/host_shaders/${SHADER}.ir")
|
||||
set(SHADER_METALLIB "${CMAKE_SOURCE_DIR}/src/host_shaders/${SHADER}.metallib")
|
||||
|
||||
# MacOS, iOS and the iOS simulator all use different compilation options for shaders
|
||||
set(MetalSDK "macosx")
|
||||
if(IOS)
|
||||
if (IOS_SIMULATOR_BUILD)
|
||||
set(MetalSDK "iphonesimulator")
|
||||
else()
|
||||
set(MetalSDK "iphoneos")
|
||||
endif()
|
||||
endif()
|
||||
|
||||
# TODO: only include sources in debug builds
|
||||
add_custom_command(
|
||||
OUTPUT ${SHADER_IR}
|
||||
COMMAND xcrun -sdk macosx metal -gline-tables-only -frecord-sources -o ${SHADER_IR} -c ${SHADER_SOURCE}
|
||||
COMMAND xcrun -sdk ${MetalSDK} metal -gline-tables-only -frecord-sources -o ${SHADER_IR} -c ${SHADER_SOURCE}
|
||||
DEPENDS ${SHADER_SOURCE}
|
||||
VERBATIM)
|
||||
add_custom_command(
|
||||
OUTPUT ${SHADER_METALLIB}
|
||||
COMMAND xcrun -sdk macosx metallib -o ${SHADER_METALLIB} ${SHADER_IR}
|
||||
COMMAND xcrun -sdk ${MetalSDK} metallib -o ${SHADER_METALLIB} ${SHADER_IR}
|
||||
DEPENDS ${SHADER_IR}
|
||||
VERBATIM)
|
||||
set(RENDERER_MTL_HOST_SHADERS_SOURCES ${RENDERER_MTL_HOST_SHADERS_SOURCES} ${SHADER_METALLIB})
|
||||
|
@ -647,7 +682,7 @@ if(ENABLE_METAL AND APPLE)
|
|||
target_compile_definitions(AlberCore PUBLIC "PANDA3DS_ENABLE_METAL=1")
|
||||
target_include_directories(AlberCore PRIVATE third_party/metal-cpp)
|
||||
# TODO: check if all of them are needed
|
||||
target_link_libraries(AlberCore PRIVATE "-framework Metal" "-framework Foundation" "-framework QuartzCore" resources_renderer_mtl)
|
||||
target_link_libraries(AlberCore PUBLIC "-framework Metal" "-framework Foundation" "-framework QuartzCore" resources_renderer_mtl)
|
||||
endif()
|
||||
|
||||
source_group("Header Files\\Core" FILES ${HEADER_FILES})
|
||||
|
@ -656,8 +691,8 @@ set(ALL_SOURCES ${SOURCE_FILES} ${FS_SOURCE_FILES} ${CRYPTO_SOURCE_FILES} ${KERN
|
|||
${AUDIO_SOURCE_FILES} ${HEADER_FILES} ${FRONTEND_HEADER_FILES})
|
||||
target_sources(AlberCore PRIVATE ${ALL_SOURCES})
|
||||
|
||||
target_link_libraries(AlberCore PRIVATE dynarmic glad resources_console_fonts teakra fdk-aac)
|
||||
target_link_libraries(AlberCore PUBLIC glad capstone fmt::fmt)
|
||||
target_link_libraries(AlberCore PRIVATE dynarmic glad resources_console_fonts fdk-aac)
|
||||
target_link_libraries(AlberCore PUBLIC glad capstone fmt::fmt teakra)
|
||||
|
||||
if(ENABLE_DISCORD_RPC AND NOT ANDROID)
|
||||
target_compile_definitions(AlberCore PUBLIC "PANDA3DS_ENABLE_DISCORD_RPC=1")
|
||||
|
@ -703,10 +738,13 @@ if(NOT BUILD_HYDRA_CORE AND NOT BUILD_LIBRETRO_CORE)
|
|||
set(FRONTEND_SOURCE_FILES src/panda_qt/main.cpp src/panda_qt/screen.cpp src/panda_qt/main_window.cpp src/panda_qt/about_window.cpp
|
||||
src/panda_qt/config_window.cpp src/panda_qt/zep.cpp src/panda_qt/text_editor.cpp src/panda_qt/cheats_window.cpp src/panda_qt/mappings.cpp
|
||||
src/panda_qt/patch_window.cpp src/panda_qt/elided_label.cpp src/panda_qt/shader_editor.cpp src/panda_qt/translations.cpp
|
||||
src/panda_qt/thread_debugger.cpp src/panda_qt/cpu_debugger.cpp src/panda_qt/dsp_debugger.cpp src/panda_qt/input_window.cpp
|
||||
)
|
||||
set(FRONTEND_HEADER_FILES include/panda_qt/screen.hpp include/panda_qt/main_window.hpp include/panda_qt/about_window.hpp
|
||||
include/panda_qt/config_window.hpp include/panda_qt/text_editor.hpp include/panda_qt/cheats_window.hpp
|
||||
include/panda_qt/patch_window.hpp include/panda_qt/elided_label.hpp include/panda_qt/shader_editor.hpp
|
||||
include/panda_qt/thread_debugger.hpp include/panda_qt/cpu_debugger.hpp include/panda_qt/dsp_debugger.hpp
|
||||
include/panda_qt/disabled_widget_overlay.hpp include/panda_qt/input_window.hpp
|
||||
)
|
||||
|
||||
source_group("Source Files\\Qt" FILES ${FRONTEND_SOURCE_FILES})
|
||||
|
@ -753,11 +791,15 @@ if(NOT BUILD_HYDRA_CORE AND NOT BUILD_LIBRETRO_CORE)
|
|||
docs/img/rsob_icon.png docs/img/rstarstruck_icon.png docs/img/rpog_icon.png docs/img/rsyn_icon.png
|
||||
docs/img/settings_icon.png docs/img/display_icon.png docs/img/speaker_icon.png
|
||||
docs/img/sparkling_icon.png docs/img/battery_icon.png docs/img/sdcard_icon.png
|
||||
docs/img/rnap_icon.png docs/img/rcow_icon.png docs/img/skyemu_icon.png
|
||||
docs/img/rnap_icon.png docs/img/rcow_icon.png docs/img/skyemu_icon.png docs/img/runpog_icon.png
|
||||
docs/img/gamepad_icon.png
|
||||
)
|
||||
|
||||
# Translation files in Qt's .ts format. Will be converted into binary files and embedded into the executable
|
||||
set(TRANSLATIONS_TS docs/translations/en.ts docs/translations/el.ts docs/translations/es.ts docs/translations/pt_br.ts docs/translations/nl.ts)
|
||||
set(TRANSLATIONS_TS docs/translations/en.ts docs/translations/el.ts docs/translations/es.ts docs/translations/pt_br.ts docs/translations/nl.ts
|
||||
docs/translations/sv.ts
|
||||
)
|
||||
|
||||
set_source_files_properties(${TRANSLATIONS_TS} PROPERTIES OUTPUT_LOCATION "${CMAKE_CURRENT_BINARY_DIR}/translations")
|
||||
qt_add_translation(TRANSLATIONS_QM ${TRANSLATIONS_TS})
|
||||
|
||||
|
@ -782,7 +824,13 @@ if(NOT BUILD_HYDRA_CORE AND NOT BUILD_LIBRETRO_CORE)
|
|||
elseif(BUILD_HYDRA_CORE)
|
||||
target_compile_definitions(AlberCore PRIVATE PANDA3DS_HYDRA_CORE=1)
|
||||
include_directories(third_party/hydra_core/include)
|
||||
add_library(Alber SHARED src/hydra_core.cpp)
|
||||
|
||||
set(SHARED_SOURCE_FILES src/hydra_core.cpp)
|
||||
if(IOS)
|
||||
set(SHARED_SOURCE_FILES ${SHARED_SOURCE_FILES} src/ios_driver.mm)
|
||||
endif()
|
||||
|
||||
add_library(Alber SHARED ${SHARED_SOURCE_FILES})
|
||||
target_link_libraries(Alber PUBLIC AlberCore)
|
||||
elseif(BUILD_LIBRETRO_CORE)
|
||||
include_directories(third_party/libretro/include)
|
||||
|
|
BIN
docs/img/gamepad_icon.png
Normal file
BIN
docs/img/gamepad_icon.png
Normal file
Binary file not shown.
After Width: | Height: | Size: 311 B |
BIN
docs/img/runpog_icon.png
Normal file
BIN
docs/img/runpog_icon.png
Normal file
Binary file not shown.
After Width: | Height: | Size: 16 KiB |
774
docs/translations/sv.ts
Normal file
774
docs/translations/sv.ts
Normal file
|
@ -0,0 +1,774 @@
|
|||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<!DOCTYPE TS>
|
||||
<TS version="2.1" language="sv_SE">
|
||||
<extra-po-header-language>sv</extra-po-header-language>
|
||||
<extra-po-header-language_team></extra-po-header-language_team>
|
||||
<extra-po-header-last_translator>Daniel Nylander <github@danielnylander.se></extra-po-header-last_translator>
|
||||
<extra-po-header-po_revision_date></extra-po-header-po_revision_date>
|
||||
<extra-po-header-pot_creation_date></extra-po-header-pot_creation_date>
|
||||
<extra-po-header-project_id_version></extra-po-header-project_id_version>
|
||||
<extra-po-header-x_generator>Poedit 3.5</extra-po-header-x_generator>
|
||||
<extra-po-headers>Project-Id-Version,POT-Creation-Date,PO-Revision-Date,Last-Translator,Language-Team,Language,MIME-Version,Content-Type,Content-Transfer-Encoding,X-Qt-Contexts,X-Generator</extra-po-headers>
|
||||
<context>
|
||||
<name>AboutWindow</name>
|
||||
<message>
|
||||
<location filename="../../src/panda_qt/about_window.cpp" line="16"/>
|
||||
<source>About Panda3DS</source>
|
||||
<translation>Om Panda3DS</translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="../../src/panda_qt/about_window.cpp" line="35"/>
|
||||
<source>Panda3DS is a free and open source Nintendo 3DS emulator, for Windows, MacOS and Linux</source>
|
||||
<translation>Panda3DS är en Nintendo 3DS-emulator med fri och öppen källkod för Windows, MacOS och Linux</translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="../../src/panda_qt/about_window.cpp" line="36"/>
|
||||
<source>Visit panda3ds.com for help with Panda3DS and links to our official support sites.</source>
|
||||
<translation>Besök panda3ds.com för att få hjälp med Panda3DS och länkar till våra officiella supportwebbplatser.</translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="../../src/panda_qt/about_window.cpp" line="38"/>
|
||||
<source>Panda3DS is developed by volunteers in their spare time. Below is a list of some of these volunteers who've agreed to be listed here, in no particular order.<br>If you think you should be listed here too, please inform us<br><br>- Peach (wheremyfoodat)<br>- noumidev<br>- liuk707<br>- Wunk<br>- marysaka<br>- Sky<br>- merryhime<br>- TGP17<br>- Shadow<br></source>
|
||||
<translation>Panda3DS utvecklas av volontärer på deras fritid. Nedan finns en lista över några av dessa volontärer som har gått med på att listas här, utan någon särskild ordning.<br>Om du tycker att du också borde listas här, informera oss<br><br>- Peach (wheremyfoodat)<br>- noumidev<br>- liuk707<br>- Wunk<br>- marysaka<br>- Sky<br>- merryhime<br>- TGP17<br>- Shadow<br></translation>
|
||||
</message>
|
||||
</context>
|
||||
<context>
|
||||
<name>CheatEditDialog</name>
|
||||
<message>
|
||||
<location filename="../../src/panda_qt/cheats_window.cpp" line="72"/>
|
||||
<source>Edit Cheat</source>
|
||||
<translation>Redigera fusk</translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="../../src/panda_qt/cheats_window.cpp" line="82"/>
|
||||
<source>Cheat name</source>
|
||||
<translation>Fusknamn</translation>
|
||||
</message>
|
||||
</context>
|
||||
<context>
|
||||
<name>CheatEntryWidget</name>
|
||||
<message>
|
||||
<location filename="../../src/panda_qt/cheats_window.cpp" line="34"/>
|
||||
<source>Edit</source>
|
||||
<translation>Redigera</translation>
|
||||
</message>
|
||||
</context>
|
||||
<context>
|
||||
<name>CheatsWindow</name>
|
||||
<message>
|
||||
<location filename="../../src/panda_qt/cheats_window.cpp" line="164"/>
|
||||
<source>Cheats</source>
|
||||
<translation>Fusk</translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="../../src/panda_qt/cheats_window.cpp" line="177"/>
|
||||
<source>Add</source>
|
||||
<translation>Lägg till</translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="../../src/panda_qt/cheats_window.cpp" line="178"/>
|
||||
<source>Remove</source>
|
||||
<translation>Ta bort</translation>
|
||||
</message>
|
||||
</context>
|
||||
<context>
|
||||
<name>ConfigWindow</name>
|
||||
<message>
|
||||
<location filename="../../src/panda_qt/config_window.cpp" line="7"/>
|
||||
<source>Configuration</source>
|
||||
<translation>Konfiguration</translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="../../src/panda_qt/config_window.cpp" line="63"/>
|
||||
<source>Interface Settings</source>
|
||||
<translation>Inställningar för gränssnitt</translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="../../src/panda_qt/config_window.cpp" line="69"/>
|
||||
<source>System</source>
|
||||
<translation>System</translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="../../src/panda_qt/config_window.cpp" line="70"/>
|
||||
<source>Light</source>
|
||||
<translation>Ljus</translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="../../src/panda_qt/config_window.cpp" line="71"/>
|
||||
<source>Dark</source>
|
||||
<translation>Mörk</translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="../../src/panda_qt/config_window.cpp" line="72"/>
|
||||
<source>Greetings Cat</source>
|
||||
<translation>Hälsningskatt</translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="../../src/panda_qt/config_window.cpp" line="73"/>
|
||||
<source>Cream</source>
|
||||
<translation>Grädde</translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="../../src/panda_qt/config_window.cpp" line="81"/>
|
||||
<source>Color theme</source>
|
||||
<translation>Färgtema</translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="../../src/panda_qt/config_window.cpp" line="84"/>
|
||||
<source>Happy panda</source>
|
||||
<translation>Glad panda</translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="../../src/panda_qt/config_window.cpp" line="85"/>
|
||||
<source>Happy panda (colourful)</source>
|
||||
<translation>Glad panda (färgglad)</translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="../../src/panda_qt/config_window.cpp" line="86"/>
|
||||
<source>Sleepy panda</source>
|
||||
<translation>Sömnig panda</translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="../../src/panda_qt/config_window.cpp" line="87"/>
|
||||
<source>Cow panda</source>
|
||||
<translation>Ko-panda</translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="../../src/panda_qt/config_window.cpp" line="88"/>
|
||||
<source>The penguin from SkyEmu</source>
|
||||
<translation>Pingvinen från SkyEmu</translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="../../src/panda_qt/config_window.cpp" line="97"/>
|
||||
<source>Window icon</source>
|
||||
<translation>Fönsterikon</translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="../../src/panda_qt/config_window.cpp" line="100"/>
|
||||
<source>Language</source>
|
||||
<translation>Språk</translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="../../src/panda_qt/config_window.cpp" line="102"/>
|
||||
<source>Show version on window title</source>
|
||||
<translation>Visa version på fönstertitel</translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="../../src/panda_qt/config_window.cpp" line="15"/>
|
||||
<location filename="../../src/panda_qt/config_window.cpp" line="109"/>
|
||||
<source>Alber v%1</source>
|
||||
<translation>Alber v%1</translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="../../src/panda_qt/config_window.cpp" line="109"/>
|
||||
<source>Alber</source>
|
||||
<translation>Alber</translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="../../src/panda_qt/config_window.cpp" line="114"/>
|
||||
<source>Remember window position</source>
|
||||
<translation>Kom ihåg fönstrets position</translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="../../src/panda_qt/config_window.cpp" line="119"/>
|
||||
<source>General Settings</source>
|
||||
<translation>Allmänna inställningar</translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="../../src/panda_qt/config_window.cpp" line="130"/>
|
||||
<source>Browse...</source>
|
||||
<translation>Bläddra...</translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="../../src/panda_qt/config_window.cpp" line="134"/>
|
||||
<source>Select Directory</source>
|
||||
<translation>Välj katalog</translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="../../src/panda_qt/config_window.cpp" line="146"/>
|
||||
<source>Default ROMs path</source>
|
||||
<translation>Standardsökväg för ROMar</translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="../../src/panda_qt/config_window.cpp" line="148"/>
|
||||
<source>Enable Discord RPC</source>
|
||||
<translation>Aktivera Discord RPC</translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="../../src/panda_qt/config_window.cpp" line="152"/>
|
||||
<source>Use portable build</source>
|
||||
<translation>Använd portabelt bygge</translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="../../src/panda_qt/config_window.cpp" line="156"/>
|
||||
<source>Print version in console output</source>
|
||||
<translation>Skriv ut versionen i konsolutmatningen</translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="../../src/panda_qt/config_window.cpp" line="161"/>
|
||||
<source>Graphics Settings</source>
|
||||
<translation>Grafikinställningar</translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="../../src/panda_qt/config_window.cpp" line="167"/>
|
||||
<location filename="../../src/panda_qt/config_window.cpp" line="221"/>
|
||||
<source>Null</source>
|
||||
<translation>Null</translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="../../src/panda_qt/config_window.cpp" line="168"/>
|
||||
<source>OpenGL</source>
|
||||
<translation>OpenGL</translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="../../src/panda_qt/config_window.cpp" line="169"/>
|
||||
<source>Vulkan</source>
|
||||
<translation>Vulkan</translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="../../src/panda_qt/config_window.cpp" line="175"/>
|
||||
<source>GPU renderer</source>
|
||||
<translation>GPU-rendering</translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="../../src/panda_qt/config_window.cpp" line="177"/>
|
||||
<source>Enable Renderdoc</source>
|
||||
<translation>Aktivera Renderdoc</translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="../../src/panda_qt/config_window.cpp" line="181"/>
|
||||
<source>Enable shader JIT</source>
|
||||
<translation>Aktivera shader JIT</translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="../../src/panda_qt/config_window.cpp" line="185"/>
|
||||
<source>Enable VSync</source>
|
||||
<translation>Aktivera VSync</translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="../../src/panda_qt/config_window.cpp" line="189"/>
|
||||
<source>Use ubershaders (No stutter, maybe slower)</source>
|
||||
<translation>Använda ubershaders (inga hackningar, kanske långsammare)</translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="../../src/panda_qt/config_window.cpp" line="193"/>
|
||||
<source>Accurate shader multiplication</source>
|
||||
<translation>Korrekt multiplicering av shaders</translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="../../src/panda_qt/config_window.cpp" line="197"/>
|
||||
<source>Accelerate shaders</source>
|
||||
<translation>Snabbare shaders</translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="../../src/panda_qt/config_window.cpp" line="201"/>
|
||||
<source>Force shadergen when rendering lights</source>
|
||||
<translation>Tvinga fram shadergen vid rendering av ljus</translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="../../src/panda_qt/config_window.cpp" line="212"/>
|
||||
<source>Light threshold for forcing shadergen</source>
|
||||
<translation>Ljuströskel för att tvinga shadergen</translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="../../src/panda_qt/config_window.cpp" line="215"/>
|
||||
<source>Audio Settings</source>
|
||||
<translation>Ljudinställningar</translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="../../src/panda_qt/config_window.cpp" line="222"/>
|
||||
<source>LLE</source>
|
||||
<translation>LLE</translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="../../src/panda_qt/config_window.cpp" line="223"/>
|
||||
<source>HLE</source>
|
||||
<translation>HLE</translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="../../src/panda_qt/config_window.cpp" line="229"/>
|
||||
<source>DSP emulation</source>
|
||||
<translation>DSP-emulering</translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="../../src/panda_qt/config_window.cpp" line="231"/>
|
||||
<source>Enable audio</source>
|
||||
<translation>Aktivera ljud</translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="../../src/panda_qt/config_window.cpp" line="235"/>
|
||||
<source>Enable AAC audio</source>
|
||||
<translation>Aktivera AAC-ljud</translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="../../src/panda_qt/config_window.cpp" line="239"/>
|
||||
<source>Print DSP firmware</source>
|
||||
<translation>Skriv ut firmware för DSP</translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="../../src/panda_qt/config_window.cpp" line="243"/>
|
||||
<source>Mute audio device</source>
|
||||
<translation>Stäng av ljudet på audioenheten</translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="../../src/panda_qt/config_window.cpp" line="248"/>
|
||||
<source>Cubic</source>
|
||||
<translation>Kubisk</translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="../../src/panda_qt/config_window.cpp" line="249"/>
|
||||
<source>Linear</source>
|
||||
<translation>Linjär</translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="../../src/panda_qt/config_window.cpp" line="255"/>
|
||||
<source>Volume curve</source>
|
||||
<translation>Volymkurva</translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="../../src/panda_qt/config_window.cpp" line="273"/>
|
||||
<source>Audio device volume</source>
|
||||
<translation>Ljudenhetens volym</translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="../../src/panda_qt/config_window.cpp" line="276"/>
|
||||
<source>Battery Settings</source>
|
||||
<translation>Batteriinställningar</translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="../../src/panda_qt/config_window.cpp" line="288"/>
|
||||
<source>Battery percentage</source>
|
||||
<translation>Batteriprocent</translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="../../src/panda_qt/config_window.cpp" line="290"/>
|
||||
<source>Charger plugged</source>
|
||||
<translation>Laddaren är ansluten</translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="../../src/panda_qt/config_window.cpp" line="295"/>
|
||||
<source>SD Card Settings</source>
|
||||
<translation>Inställningar för SD-kort</translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="../../src/panda_qt/config_window.cpp" line="300"/>
|
||||
<source>Enable virtual SD card</source>
|
||||
<translation>Aktivera virtuellt SD-kort</translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="../../src/panda_qt/config_window.cpp" line="304"/>
|
||||
<source>Write protect virtual SD card</source>
|
||||
<translation>Skrivskydd för virtuellt SD-kort</translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="../../src/panda_qt/config_window.cpp" line="309"/>
|
||||
<source>Interface</source>
|
||||
<translation>Gränssnitt</translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="../../src/panda_qt/config_window.cpp" line="309"/>
|
||||
<source>User Interface settings</source>
|
||||
<translation>Inställningar för användargränssnitt</translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="../../src/panda_qt/config_window.cpp" line="310"/>
|
||||
<source>General</source>
|
||||
<translation>Allmänt</translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="../../src/panda_qt/config_window.cpp" line="310"/>
|
||||
<source>General emulator settings</source>
|
||||
<translation>Allmänna inställningar för emulatorn</translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="../../src/panda_qt/config_window.cpp" line="311"/>
|
||||
<source>Graphics</source>
|
||||
<translation>Grafik</translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="../../src/panda_qt/config_window.cpp" line="311"/>
|
||||
<source>Graphics emulation and output settings</source>
|
||||
<translation>Inställningar för grafikemulering och utdata</translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="../../src/panda_qt/config_window.cpp" line="312"/>
|
||||
<source>Audio</source>
|
||||
<translation>Ljud</translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="../../src/panda_qt/config_window.cpp" line="312"/>
|
||||
<source>Audio emulation and output settings</source>
|
||||
<translation>Inställningar för ljudemulering och utdata</translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="../../src/panda_qt/config_window.cpp" line="313"/>
|
||||
<source>Battery</source>
|
||||
<translation>Batteri</translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="../../src/panda_qt/config_window.cpp" line="313"/>
|
||||
<source>Battery emulation settings</source>
|
||||
<translation>Inställningar för batteriemulering</translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="../../src/panda_qt/config_window.cpp" line="314"/>
|
||||
<source>SD Card</source>
|
||||
<translation>SD-kort</translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="../../src/panda_qt/config_window.cpp" line="314"/>
|
||||
<source>SD Card emulation settings</source>
|
||||
<translation>Inställningar för emulering av SD-kort</translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="../../src/panda_qt/translations.cpp" line="75"/>
|
||||
<source>Language change successful</source>
|
||||
<translation>Språkändringen lyckades</translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="../../src/panda_qt/translations.cpp" line="76"/>
|
||||
<source>Restart Panda3DS for the new language to be used.</source>
|
||||
<translation>Starta om Panda3DS för att det nya språket ska kunna användas.</translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="../../src/panda_qt/translations.cpp" line="82"/>
|
||||
<source>Language change failed</source>
|
||||
<translation>Språkändringen misslyckades</translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="../../src/panda_qt/translations.cpp" line="83"/>
|
||||
<source>The language you selected is not included in Panda3DS. If you're seeing this, someone messed up the language UI code...</source>
|
||||
<translation>Det språk du valde ingår inte i Panda3DS. Om du ser detta, har någon rört till koden för språkgränssnittet...</translation>
|
||||
</message>
|
||||
</context>
|
||||
<context>
|
||||
<name>MainWindow</name>
|
||||
<message>
|
||||
<location filename="../../src/panda_qt/main_window.cpp" line="20"/>
|
||||
<source>Alber</source>
|
||||
<translation>Alber</translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="../../src/panda_qt/main_window.cpp" line="38"/>
|
||||
<source>File</source>
|
||||
<translation>Arkiv</translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="../../src/panda_qt/main_window.cpp" line="39"/>
|
||||
<source>Emulation</source>
|
||||
<translation>Emulering</translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="../../src/panda_qt/main_window.cpp" line="40"/>
|
||||
<source>Tools</source>
|
||||
<translation>Verktyg</translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="../../src/panda_qt/main_window.cpp" line="41"/>
|
||||
<source>About</source>
|
||||
<translation>Om</translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="../../src/panda_qt/main_window.cpp" line="44"/>
|
||||
<source>Load game</source>
|
||||
<translation>Läs in spel</translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="../../src/panda_qt/main_window.cpp" line="45"/>
|
||||
<source>Load Lua script</source>
|
||||
<translation>Läs in Lua-skript</translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="../../src/panda_qt/main_window.cpp" line="46"/>
|
||||
<source>Open Panda3DS folder</source>
|
||||
<translation>Öppna Panda3DS-mappen</translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="../../src/panda_qt/main_window.cpp" line="55"/>
|
||||
<source>Pause</source>
|
||||
<translation>Pausa</translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="../../src/panda_qt/main_window.cpp" line="56"/>
|
||||
<source>Resume</source>
|
||||
<translation>Återuppta</translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="../../src/panda_qt/main_window.cpp" line="57"/>
|
||||
<source>Reset</source>
|
||||
<translation>Starta om</translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="../../src/panda_qt/main_window.cpp" line="58"/>
|
||||
<source>Configure</source>
|
||||
<translation>Konfigurera</translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="../../src/panda_qt/main_window.cpp" line="64"/>
|
||||
<source>Dump RomFS</source>
|
||||
<translation>Dumpa RomFS</translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="../../src/panda_qt/main_window.cpp" line="65"/>
|
||||
<source>Open Lua Editor</source>
|
||||
<translation>Öppna Lua-redigeraren</translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="../../src/panda_qt/main_window.cpp" line="66"/>
|
||||
<source>Open Cheats Editor</source>
|
||||
<translation>Öppna fuskredigeraren</translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="../../src/panda_qt/main_window.cpp" line="67"/>
|
||||
<source>Open Patch Window</source>
|
||||
<translation>Öppna patchfönstret</translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="../../src/panda_qt/main_window.cpp" line="68"/>
|
||||
<source>Open Shader Editor</source>
|
||||
<translation>Öppna shader-redigeraren</translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="../../src/panda_qt/main_window.cpp" line="69"/>
|
||||
<source>Dump loaded DSP firmware</source>
|
||||
<translation>Dumpa inläst DSP-firmware</translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="../../src/panda_qt/main_window.cpp" line="78"/>
|
||||
<source>About Panda3DS</source>
|
||||
<translation>Om Panda3DS</translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="../../src/panda_qt/main_window.cpp" line="195"/>
|
||||
<source>Select 3DS ROM to load</source>
|
||||
<translation>Välj 3DS ROM att läsa in</translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="../../src/panda_qt/main_window.cpp" line="196"/>
|
||||
<source>Nintendo 3DS ROMs (*.3ds *.cci *.cxi *.app *.ncch *.3dsx *.elf *.axf)</source>
|
||||
<translation>Nintendo 3DS ROM (*.3ds *.cci *.cxi *.app *.ncch *.3dsx *.elf *.axf)</translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="../../src/panda_qt/main_window.cpp" line="209"/>
|
||||
<source>Select Lua script to load</source>
|
||||
<translation>Välj Lua-skript som ska läsas in</translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="../../src/panda_qt/main_window.cpp" line="209"/>
|
||||
<source>Lua scripts (*.lua *.txt)</source>
|
||||
<translation>Lua-skript (*.lua *.txt)</translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="../../src/panda_qt/main_window.cpp" line="272"/>
|
||||
<source>Select folder to dump RomFS files to</source>
|
||||
<translation>Välj mapp för att dumpa RomFS-filer till</translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="../../src/panda_qt/main_window.cpp" line="288"/>
|
||||
<source>Invalid format for RomFS dumping</source>
|
||||
<translation>Ogiltigt format för RomFS-dumpning</translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="../../src/panda_qt/main_window.cpp" line="289"/>
|
||||
<source>The currently loaded app is not in a format that supports RomFS</source>
|
||||
<translation>Den aktuella appen är inte i ett format som stöder RomFS</translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="../../src/panda_qt/main_window.cpp" line="292"/>
|
||||
<location filename="../../src/panda_qt/main_window.cpp" line="323"/>
|
||||
<location filename="../../src/panda_qt/main_window.cpp" line="336"/>
|
||||
<source>OK</source>
|
||||
<translation>Ok</translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="../../src/panda_qt/main_window.cpp" line="299"/>
|
||||
<source>No RomFS found</source>
|
||||
<translation>Ingen RomFS hittades</translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="../../src/panda_qt/main_window.cpp" line="299"/>
|
||||
<source>No RomFS partition was found in the loaded app</source>
|
||||
<translation>Ingen RomFS-partition hittades i den inlästa appen</translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="../../src/panda_qt/main_window.cpp" line="305"/>
|
||||
<source>Select file</source>
|
||||
<translation>Välj fil</translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="../../src/panda_qt/main_window.cpp" line="305"/>
|
||||
<source>DSP firmware file (*.cdc)</source>
|
||||
<translation>DSP firmware-fil (*.cdc)</translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="../../src/panda_qt/main_window.cpp" line="320"/>
|
||||
<source>No DSP firmware loaded</source>
|
||||
<translation>Ingen firmware för DSP inläst</translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="../../src/panda_qt/main_window.cpp" line="320"/>
|
||||
<source>The currently loaded app has not uploaded a firmware to the DSP</source>
|
||||
<translation>Den aktuella appen har inte skickat upp någon firmware till DSP:n</translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="../../src/panda_qt/main_window.cpp" line="331"/>
|
||||
<source>Failed to open output file</source>
|
||||
<translation>Misslyckades med att öppna utdatafilen</translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="../../src/panda_qt/main_window.cpp" line="332"/>
|
||||
<source>The currently loaded DSP firmware could not be written to the selected file. Please make sure you have permission to access this file</source>
|
||||
<translation>Den aktuella DSP-firmware som lästes in kunde inte skrivas till den valda filen. Kontrollera att du har behörighet att komma åt den här filen</translation>
|
||||
</message>
|
||||
</context>
|
||||
<context>
|
||||
<name>PatchWindow</name>
|
||||
<message>
|
||||
<location filename="../../src/panda_qt/patch_window.cpp" line="15"/>
|
||||
<source>ROM patcher</source>
|
||||
<translation>ROM-patchare</translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="../../src/panda_qt/patch_window.cpp" line="23"/>
|
||||
<source>Select input file</source>
|
||||
<translation>Välj inmatningsfil</translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="../../src/panda_qt/patch_window.cpp" line="24"/>
|
||||
<location filename="../../src/panda_qt/patch_window.cpp" line="36"/>
|
||||
<source>Select</source>
|
||||
<translation>Välj</translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="../../src/panda_qt/patch_window.cpp" line="35"/>
|
||||
<location filename="../../src/panda_qt/patch_window.cpp" line="63"/>
|
||||
<source>Select patch file</source>
|
||||
<translation>Välj patchfil</translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="../../src/panda_qt/patch_window.cpp" line="47"/>
|
||||
<source>Apply patch</source>
|
||||
<translation>Applicera patch</translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="../../src/panda_qt/patch_window.cpp" line="56"/>
|
||||
<source>Select file to patch</source>
|
||||
<translation>Välj fil som ska patchas</translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="../../src/panda_qt/patch_window.cpp" line="56"/>
|
||||
<location filename="../../src/panda_qt/patch_window.cpp" line="80"/>
|
||||
<source>All files (*.*)</source>
|
||||
<translation>Alla filer (*.*)</translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="../../src/panda_qt/patch_window.cpp" line="63"/>
|
||||
<source>Patch files (*.ips *.ups *.bps)</source>
|
||||
<translation>Patch-filer (*.ips *.ups *.bps)</translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="../../src/panda_qt/patch_window.cpp" line="71"/>
|
||||
<source>Paths not provided correctly</source>
|
||||
<translation>Sökvägar anges inte korrekt</translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="../../src/panda_qt/patch_window.cpp" line="71"/>
|
||||
<source>Please provide paths for both the input file and the patch file</source>
|
||||
<translation>Ange sökvägar för både indatafilen och patchfilen</translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="../../src/panda_qt/patch_window.cpp" line="80"/>
|
||||
<source>Select file</source>
|
||||
<translation>Välj fil</translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="../../src/panda_qt/patch_window.cpp" line="84"/>
|
||||
<source>No output path</source>
|
||||
<translation>Ingen sökväg för utmatning</translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="../../src/panda_qt/patch_window.cpp" line="84"/>
|
||||
<source>No path was provided for the output file, no patching was done</source>
|
||||
<translation>Ingen sökväg angavs för utdatafilen, ingen patchning gjordes</translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="../../src/panda_qt/patch_window.cpp" line="99"/>
|
||||
<source>Unknown patch format</source>
|
||||
<translation>Okänt patchformat</translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="../../src/panda_qt/patch_window.cpp" line="99"/>
|
||||
<source>Unknown format for patch file. Currently IPS, UPS and BPS are supported</source>
|
||||
<translation>Okänt format för patchfil. För närvarande stöds IPS, UPS och BPS</translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="../../src/panda_qt/patch_window.cpp" line="108"/>
|
||||
<source>Failed to open input files</source>
|
||||
<translation>Misslyckades med att öppna indatafiler</translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="../../src/panda_qt/patch_window.cpp" line="108"/>
|
||||
<source>Make sure they're in a directory Panda3DS has access to</source>
|
||||
<translation>Se till att de finns i en katalog som Panda3DS har tillgång till</translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="../../src/panda_qt/patch_window.cpp" line="135"/>
|
||||
<source>Patching Success</source>
|
||||
<translation>Patchning lyckades</translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="../../src/panda_qt/patch_window.cpp" line="135"/>
|
||||
<source>Your file was patched successfully.</source>
|
||||
<translation>Din fil patchades.</translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="../../src/panda_qt/patch_window.cpp" line="141"/>
|
||||
<source>Checksum mismatch</source>
|
||||
<translation>Kontrollsumman stämmer inte överens</translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="../../src/panda_qt/patch_window.cpp" line="142"/>
|
||||
<source>Patch was applied successfully but a checksum mismatch was detected. The input or output files might not be correct</source>
|
||||
<translation>Patchen applicerades men en avvikelse i kontrollsumman upptäcktes. Inmatnings- eller utdatafilerna kanske inte är korrekta</translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="../../src/panda_qt/patch_window.cpp" line="146"/>
|
||||
<source>Patching error</source>
|
||||
<translation>Fel vid patchning</translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="../../src/panda_qt/patch_window.cpp" line="146"/>
|
||||
<source>An error occured while patching</source>
|
||||
<translation>Ett fel uppstod vid patchning</translation>
|
||||
</message>
|
||||
</context>
|
||||
<context>
|
||||
<name>PatchWindow::PatchWindow</name>
|
||||
<message>
|
||||
<location filename="../../src/panda_qt/patch_window.cpp" line="153"/>
|
||||
<source>OK</source>
|
||||
<translation>Ok</translation>
|
||||
</message>
|
||||
</context>
|
||||
<context>
|
||||
<name>ShaderEditorWindow</name>
|
||||
<message>
|
||||
<location filename="../../src/panda_qt/shader_editor.cpp" line="26"/>
|
||||
<source>Reload shader</source>
|
||||
<translation>Läs om shader</translation>
|
||||
</message>
|
||||
</context>
|
||||
<context>
|
||||
<name>TextEditorWindow</name>
|
||||
<message>
|
||||
<location filename="../../src/panda_qt/text_editor.cpp" line="12"/>
|
||||
<source>Lua Editor</source>
|
||||
<translation>Lua-redigerare</translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="../../src/panda_qt/text_editor.cpp" line="27"/>
|
||||
<source>Load script</source>
|
||||
<translation>Läs in skript</translation>
|
||||
</message>
|
||||
</context>
|
||||
</TS>
|
|
@ -29,11 +29,11 @@ class ShaderEmitter : public Xbyak::CodeGenerator {
|
|||
std::vector<u32> returnPCs;
|
||||
|
||||
// Vector value of (-0.0, -0.0, -0.0, -0.0) for negating vectors via pxor
|
||||
Label negateVector;
|
||||
Xbyak::Label negateVector;
|
||||
// Vector value of (1.0, 1.0, 1.0, 1.0) for SLT(i)/SGE(i)
|
||||
Label onesVector;
|
||||
Xbyak::Label onesVector;
|
||||
// Vector value of (0xFF, 0xFF, 0xFF, 0) for setting the w component to 0 in DP3
|
||||
Label dp3Vector;
|
||||
Xbyak::Label dp3Vector;
|
||||
|
||||
u32 recompilerPC = 0; // PC the recompiler is currently recompiling @
|
||||
u32 loopLevel = 0; // The current loop nesting level (0 = not in a loop)
|
||||
|
@ -47,7 +47,7 @@ class ShaderEmitter : public Xbyak::CodeGenerator {
|
|||
bool codeHasExp2 = false;
|
||||
// Whether to compile this shader using accurate, safe, non-IEEE multiplication (slow) or faster but less accurate mul
|
||||
bool useSafeMUL = false;
|
||||
|
||||
|
||||
Xbyak::Label log2Func, exp2Func;
|
||||
Xbyak::Label emitLog2Func();
|
||||
Xbyak::Label emitExp2Func();
|
||||
|
@ -72,8 +72,8 @@ class ShaderEmitter : public Xbyak::CodeGenerator {
|
|||
|
||||
// Load register with number "srcReg" indexed by index "idx" into the xmm register "reg"
|
||||
template <int sourceIndex>
|
||||
void loadRegister(Xmm dest, const PICAShader& shader, u32 src, u32 idx, u32 operandDescriptor);
|
||||
void storeRegister(Xmm source, const PICAShader& shader, u32 dest, u32 operandDescriptor);
|
||||
void loadRegister(Xbyak::Xmm dest, const PICAShader& shader, u32 src, u32 idx, u32 operandDescriptor);
|
||||
void storeRegister(Xbyak::Xmm source, const PICAShader& shader, u32 dest, u32 operandDescriptor);
|
||||
|
||||
const vec4f& getSourceRef(const PICAShader& shader, u32 src);
|
||||
const vec4f& getDestRef(const PICAShader& shader, u32 dest);
|
||||
|
|
|
@ -2,39 +2,37 @@
|
|||
|
||||
#ifdef PANDA3DS_X64_HOST
|
||||
#include "xbyak/xbyak.h"
|
||||
using namespace Xbyak;
|
||||
using namespace Xbyak::util;
|
||||
|
||||
#if defined(WIN32) || defined(_WIN32) || defined(__WIN32) && !defined(__CYGWIN__)
|
||||
#define PANDA3DS_MS_ABI
|
||||
constexpr Reg32 arg1 = ecx; // register where first arg is stored
|
||||
constexpr Reg32 arg2 = edx; // register where second arg is stored
|
||||
constexpr Reg32 arg3 = r8d; // register where third arg is stored
|
||||
constexpr Reg32 arg4 = r9d; // register where fourth arg is stored
|
||||
constexpr Xbyak::Reg32 arg1 = Xbyak::util::ecx; // register where first arg is stored
|
||||
constexpr Xbyak::Reg32 arg2 = Xbyak::util::edx; // register where second arg is stored
|
||||
constexpr Xbyak::Reg32 arg3 = Xbyak::util::r8d; // register where third arg is stored
|
||||
constexpr Xbyak::Reg32 arg4 = Xbyak::util::r9d; // register where fourth arg is stored
|
||||
|
||||
// Similar for floating point and vector arguemnts.
|
||||
constexpr Xmm arg1f = xmm0;
|
||||
constexpr Xmm arg2f = xmm1;
|
||||
constexpr Xmm arg3f = xmm2;
|
||||
constexpr Xmm arg4f = xmm3;
|
||||
constexpr Xbyak::Xmm arg1f = Xbyak::util::xmm0;
|
||||
constexpr Xbyak::Xmm arg2f = Xbyak::util::xmm1;
|
||||
constexpr Xbyak::Xmm arg3f = Xbyak::util::xmm2;
|
||||
constexpr Xbyak::Xmm arg4f = Xbyak::util::xmm3;
|
||||
|
||||
constexpr bool isWindows() { return true; }
|
||||
|
||||
#else // System V calling convention
|
||||
#define PANDA3DS_SYSV_ABI
|
||||
constexpr Reg32 arg1 = edi;
|
||||
constexpr Reg32 arg2 = esi;
|
||||
constexpr Reg32 arg3 = edx;
|
||||
constexpr Reg32 arg4 = ecx;
|
||||
constexpr Xbyak::Reg32 arg1 = Xbyak::util::edi;
|
||||
constexpr Xbyak::Reg32 arg2 = Xbyak::util::esi;
|
||||
constexpr Xbyak::Reg32 arg3 = Xbyak::util::edx;
|
||||
constexpr Xbyak::Reg32 arg4 = Xbyak::util::ecx;
|
||||
|
||||
constexpr Xmm arg1f = xmm0;
|
||||
constexpr Xmm arg2f = xmm1;
|
||||
constexpr Xmm arg3f = xmm2;
|
||||
constexpr Xmm arg4f = xmm3;
|
||||
constexpr Xmm arg5f = xmm4;
|
||||
constexpr Xmm arg6f = xmm5;
|
||||
constexpr Xmm arg7f = xmm6;
|
||||
constexpr Xmm arg8f = xmm7;
|
||||
constexpr Xbyak::Xmm arg1f = Xbyak::util::xmm0;
|
||||
constexpr Xbyak::Xmm arg2f = Xbyak::util::xmm1;
|
||||
constexpr Xbyak::Xmm arg3f = Xbyak::util::xmm2;
|
||||
constexpr Xbyak::Xmm arg4f = Xbyak::util::xmm3;
|
||||
constexpr Xbyak::Xmm arg5f = Xbyak::util::xmm4;
|
||||
constexpr Xbyak::Xmm arg6f = Xbyak::util::xmm5;
|
||||
constexpr Xbyak::Xmm arg7f = Xbyak::util::xmm6;
|
||||
constexpr Xbyak::Xmm arg8f = Xbyak::util::xmm7;
|
||||
|
||||
constexpr bool isWindows() { return false; }
|
||||
#endif
|
||||
|
|
|
@ -6,156 +6,125 @@
|
|||
|
||||
#include <cmath>
|
||||
#include <cstring>
|
||||
|
||||
#include "helpers.hpp"
|
||||
|
||||
namespace Floats {
|
||||
/**
|
||||
* Template class for converting arbitrary Pica float types to IEEE 754 32-bit single-precision
|
||||
* floating point.
|
||||
*
|
||||
* When decoding, format is as follows:
|
||||
* - The first `M` bits are the mantissa
|
||||
* - The next `E` bits are the exponent
|
||||
* - The last bit is the sign bit
|
||||
*
|
||||
* @todo Verify on HW if this conversion is sufficiently accurate.
|
||||
*/
|
||||
template <unsigned M, unsigned E>
|
||||
struct Float {
|
||||
public:
|
||||
static Float<M, E> fromFloat32(float val) {
|
||||
Float<M, E> ret;
|
||||
ret.value = val;
|
||||
return ret;
|
||||
}
|
||||
/**
|
||||
* Template class for converting arbitrary Pica float types to IEEE 754 32-bit single-precision
|
||||
* floating point.
|
||||
*
|
||||
* When decoding, format is as follows:
|
||||
* - The first `M` bits are the mantissa
|
||||
* - The next `E` bits are the exponent
|
||||
* - The last bit is the sign bit
|
||||
*
|
||||
* @todo Verify on HW if this conversion is sufficiently accurate.
|
||||
*/
|
||||
template <unsigned M, unsigned E>
|
||||
struct Float {
|
||||
public:
|
||||
static Float<M, E> fromFloat32(float val) {
|
||||
Float<M, E> ret;
|
||||
ret.value = val;
|
||||
return ret;
|
||||
}
|
||||
|
||||
static Float<M, E> fromRaw(u32 hex) {
|
||||
Float<M, E> res;
|
||||
static Float<M, E> fromRaw(u32 hex) {
|
||||
Float<M, E> res;
|
||||
|
||||
const int width = M + E + 1;
|
||||
const int bias = 128 - (1 << (E - 1));
|
||||
int exponent = (hex >> M) & ((1 << E) - 1);
|
||||
const unsigned mantissa = hex & ((1 << M) - 1);
|
||||
const unsigned sign = (hex >> (E + M)) << 31;
|
||||
const int width = M + E + 1;
|
||||
const int bias = 128 - (1 << (E - 1));
|
||||
int exponent = (hex >> M) & ((1 << E) - 1);
|
||||
const unsigned mantissa = hex & ((1 << M) - 1);
|
||||
const unsigned sign = (hex >> (E + M)) << 31;
|
||||
|
||||
if (hex & ((1 << (width - 1)) - 1)) {
|
||||
if (exponent == (1 << E) - 1)
|
||||
exponent = 255;
|
||||
else
|
||||
exponent += bias;
|
||||
hex = sign | (mantissa << (23 - M)) | (exponent << 23);
|
||||
}
|
||||
else {
|
||||
hex = sign;
|
||||
}
|
||||
if (hex & ((1 << (width - 1)) - 1)) {
|
||||
if (exponent == (1 << E) - 1)
|
||||
exponent = 255;
|
||||
else
|
||||
exponent += bias;
|
||||
hex = sign | (mantissa << (23 - M)) | (exponent << 23);
|
||||
} else {
|
||||
hex = sign;
|
||||
}
|
||||
|
||||
std::memcpy(&res.value, &hex, sizeof(float));
|
||||
std::memcpy(&res.value, &hex, sizeof(float));
|
||||
|
||||
return res;
|
||||
}
|
||||
return res;
|
||||
}
|
||||
|
||||
static Float<M, E> zero() {
|
||||
return fromFloat32(0.f);
|
||||
}
|
||||
static Float<M, E> zero() { return fromFloat32(0.f); }
|
||||
|
||||
// Not recommended for anything but logging
|
||||
float toFloat32() const {
|
||||
return value;
|
||||
}
|
||||
// Not recommended for anything but logging
|
||||
float toFloat32() const { return value; }
|
||||
|
||||
double toFloat64() const {
|
||||
return static_cast<double>(value);
|
||||
}
|
||||
double toFloat64() const { return static_cast<double>(value); }
|
||||
|
||||
operator float() {
|
||||
return toFloat32();
|
||||
}
|
||||
operator float() { return toFloat32(); }
|
||||
|
||||
operator double() {
|
||||
return toFloat64();
|
||||
}
|
||||
operator double() { return toFloat64(); }
|
||||
|
||||
Float<M, E> operator*(const Float<M, E>& flt) const {
|
||||
float result = value * flt.toFloat32();
|
||||
// PICA gives 0 instead of NaN when multiplying by inf
|
||||
if (std::isnan(result))
|
||||
if (!std::isnan(value) && !std::isnan(flt.toFloat32()))
|
||||
result = 0.f;
|
||||
return Float<M, E>::fromFloat32(result);
|
||||
}
|
||||
Float<M, E> operator*(const Float<M, E>& flt) const {
|
||||
float result = value * flt.toFloat32();
|
||||
// PICA gives 0 instead of NaN when multiplying by inf
|
||||
if (std::isnan(result))
|
||||
if (!std::isnan(value) && !std::isnan(flt.toFloat32())) result = 0.f;
|
||||
return Float<M, E>::fromFloat32(result);
|
||||
}
|
||||
|
||||
Float<M, E> operator/(const Float<M, E>& flt) const {
|
||||
return Float<M, E>::fromFloat32(toFloat32() / flt.toFloat32());
|
||||
}
|
||||
Float<M, E> operator/(const Float<M, E>& flt) const { return Float<M, E>::fromFloat32(toFloat32() / flt.toFloat32()); }
|
||||
|
||||
Float<M, E> operator+(const Float<M, E>& flt) const {
|
||||
return Float<M, E>::fromFloat32(toFloat32() + flt.toFloat32());
|
||||
}
|
||||
Float<M, E> operator+(const Float<M, E>& flt) const { return Float<M, E>::fromFloat32(toFloat32() + flt.toFloat32()); }
|
||||
|
||||
Float<M, E> operator-(const Float<M, E>& flt) const {
|
||||
return Float<M, E>::fromFloat32(toFloat32() - flt.toFloat32());
|
||||
}
|
||||
Float<M, E> operator-(const Float<M, E>& flt) const { return Float<M, E>::fromFloat32(toFloat32() - flt.toFloat32()); }
|
||||
|
||||
Float<M, E>& operator*=(const Float<M, E>& flt) {
|
||||
value = operator*(flt).value;
|
||||
return *this;
|
||||
}
|
||||
Float<M, E>& operator*=(const Float<M, E>& flt) {
|
||||
value = operator*(flt).value;
|
||||
return *this;
|
||||
}
|
||||
|
||||
Float<M, E>& operator/=(const Float<M, E>& flt) {
|
||||
value /= flt.toFloat32();
|
||||
return *this;
|
||||
}
|
||||
Float<M, E>& operator/=(const Float<M, E>& flt) {
|
||||
value /= flt.toFloat32();
|
||||
return *this;
|
||||
}
|
||||
|
||||
Float<M, E>& operator+=(const Float<M, E>& flt) {
|
||||
value += flt.toFloat32();
|
||||
return *this;
|
||||
}
|
||||
Float<M, E>& operator+=(const Float<M, E>& flt) {
|
||||
value += flt.toFloat32();
|
||||
return *this;
|
||||
}
|
||||
|
||||
Float<M, E>& operator-=(const Float<M, E>& flt) {
|
||||
value -= flt.toFloat32();
|
||||
return *this;
|
||||
}
|
||||
Float<M, E>& operator-=(const Float<M, E>& flt) {
|
||||
value -= flt.toFloat32();
|
||||
return *this;
|
||||
}
|
||||
|
||||
Float<M, E> operator-() const {
|
||||
return Float<M, E>::fromFloat32(-toFloat32());
|
||||
}
|
||||
Float<M, E> operator-() const { return Float<M, E>::fromFloat32(-toFloat32()); }
|
||||
|
||||
bool operator<(const Float<M, E>& flt) const {
|
||||
return toFloat32() < flt.toFloat32();
|
||||
}
|
||||
bool operator<(const Float<M, E>& flt) const { return toFloat32() < flt.toFloat32(); }
|
||||
|
||||
bool operator>(const Float<M, E>& flt) const {
|
||||
return toFloat32() > flt.toFloat32();
|
||||
}
|
||||
bool operator>(const Float<M, E>& flt) const { return toFloat32() > flt.toFloat32(); }
|
||||
|
||||
bool operator>=(const Float<M, E>& flt) const {
|
||||
return toFloat32() >= flt.toFloat32();
|
||||
}
|
||||
bool operator>=(const Float<M, E>& flt) const { return toFloat32() >= flt.toFloat32(); }
|
||||
|
||||
bool operator<=(const Float<M, E>& flt) const {
|
||||
return toFloat32() <= flt.toFloat32();
|
||||
}
|
||||
bool operator<=(const Float<M, E>& flt) const { return toFloat32() <= flt.toFloat32(); }
|
||||
|
||||
bool operator==(const Float<M, E>& flt) const {
|
||||
return toFloat32() == flt.toFloat32();
|
||||
}
|
||||
bool operator==(const Float<M, E>& flt) const { return toFloat32() == flt.toFloat32(); }
|
||||
|
||||
bool operator!=(const Float<M, E>& flt) const {
|
||||
return toFloat32() != flt.toFloat32();
|
||||
}
|
||||
bool operator!=(const Float<M, E>& flt) const { return toFloat32() != flt.toFloat32(); }
|
||||
|
||||
private:
|
||||
static constexpr unsigned MASK = (1 << (M + E + 1)) - 1;
|
||||
static constexpr unsigned MANTISSA_MASK = (1 << M) - 1;
|
||||
static constexpr unsigned EXPONENT_MASK = (1 << E) - 1;
|
||||
private:
|
||||
static constexpr unsigned MASK = (1 << (M + E + 1)) - 1;
|
||||
static constexpr unsigned MANTISSA_MASK = (1 << M) - 1;
|
||||
static constexpr unsigned EXPONENT_MASK = (1 << E) - 1;
|
||||
|
||||
// Stored as a regular float, merely for convenience
|
||||
// TODO: Perform proper arithmetic on this!
|
||||
float value;
|
||||
};
|
||||
// Stored as a regular float, merely for convenience
|
||||
// TODO: Perform proper arithmetic on this!
|
||||
float value;
|
||||
};
|
||||
|
||||
using f24 = Float<16, 7>;
|
||||
using f20 = Float<12, 7>;
|
||||
using f16 = Float<10, 5>;
|
||||
using f24 = Float<16, 7>;
|
||||
using f20 = Float<12, 7>;
|
||||
using f16 = Float<10, 5>;
|
||||
|
||||
} // namespace Floats
|
||||
} // namespace Floats
|
||||
|
|
|
@ -89,6 +89,7 @@ class GPU {
|
|||
PICA::Vertex getImmediateModeVertex();
|
||||
|
||||
void getAcceleratedDrawInfo(PICA::DrawAcceleration& accel, bool indexed);
|
||||
|
||||
public:
|
||||
// 256 entries per LUT with each LUT as its own row forming a 2D image 256 * LUT_COUNT
|
||||
// Encoded in PICA native format
|
||||
|
@ -134,6 +135,8 @@ class GPU {
|
|||
|
||||
// Used for setting the size of the window we'll be outputting graphics to
|
||||
void setOutputSize(u32 width, u32 height) { renderer->setOutputSize(width, height); }
|
||||
// Used for notifying the renderer the screen layout has changed
|
||||
void reloadScreenLayout() { renderer->reloadScreenLayout(); }
|
||||
|
||||
// TODO: Emulate the transfer engine & its registers
|
||||
// Then this can be emulated by just writing the appropriate values there
|
||||
|
@ -181,6 +184,7 @@ class GPU {
|
|||
}
|
||||
|
||||
Renderer* getRenderer() { return renderer.get(); }
|
||||
|
||||
private:
|
||||
// GPU external registers
|
||||
// We have them in the end of the struct for cache locality reasons. Tl;dr we want the more commonly used things to be packed in the start
|
||||
|
@ -189,8 +193,8 @@ class GPU {
|
|||
|
||||
ALWAYS_INLINE void setVsOutputMask(u32 val) {
|
||||
val &= 0xffff;
|
||||
|
||||
// Avoid recomputing this if not necessary
|
||||
|
||||
// Avoid recomputing this if not necessary
|
||||
if (oldVsOutputMask != val) [[unlikely]] {
|
||||
oldVsOutputMask = val;
|
||||
|
||||
|
|
|
@ -107,7 +107,7 @@ class PICAShader {
|
|||
alignas(16) std::array<vec4f, 16> inputs; // Attributes passed to the shader
|
||||
alignas(16) std::array<vec4f, 16> outputs;
|
||||
alignas(16) vec4f dummy = vec4f({f24::zero(), f24::zero(), f24::zero(), f24::zero()}); // Dummy register used by the JIT
|
||||
|
||||
|
||||
// We use a hashmap for matching 3DS shaders to their equivalent compiled code in our shader cache in the shader JIT
|
||||
// We choose our hash type to be a 64-bit integer by default, as the collision chance is very tiny and generating it is decently optimal
|
||||
// Ideally we want to be able to support multiple different types of hash depending on compilation settings, but let's get this working first
|
||||
|
@ -234,7 +234,7 @@ class PICAShader {
|
|||
|
||||
public:
|
||||
static constexpr size_t maxInstructionCount = 4096;
|
||||
std::array<u32, maxInstructionCount> loadedShader; // Currently loaded & active shader
|
||||
std::array<u32, maxInstructionCount> loadedShader; // Currently loaded & active shader
|
||||
|
||||
PICAShader(ShaderType type) : type(type) {}
|
||||
|
||||
|
|
|
@ -2,8 +2,8 @@
|
|||
|
||||
namespace PICA::ShaderGen {
|
||||
// Graphics API this shader is targetting
|
||||
enum class API { GL, GLES, Vulkan };
|
||||
enum class API { GL, GLES, Vulkan, Metal };
|
||||
|
||||
// Shading language to use (Only GLSL for the time being)
|
||||
enum class Language { GLSL };
|
||||
} // namespace PICA::ShaderGen
|
||||
// Shading language to use
|
||||
enum class Language { GLSL, MSL };
|
||||
} // namespace PICA::ShaderGen
|
||||
|
|
9
include/audio/audio_device.hpp
Normal file
9
include/audio/audio_device.hpp
Normal file
|
@ -0,0 +1,9 @@
|
|||
#pragma once
|
||||
|
||||
#if defined(__LIBRETRO__) && defined(USE_LIBRETRO_AUDIO_DEVICE)
|
||||
#include "audio/libretro_audio_device.hpp"
|
||||
using AudioDevice = LibretroAudioDevice;
|
||||
#else
|
||||
#include "audio/miniaudio_device.hpp"
|
||||
using AudioDevice = MiniAudioDevice;
|
||||
#endif
|
36
include/audio/audio_device_interface.hpp
Normal file
36
include/audio/audio_device_interface.hpp
Normal file
|
@ -0,0 +1,36 @@
|
|||
#pragma once
|
||||
#include <array>
|
||||
|
||||
#include "config.hpp"
|
||||
#include "helpers.hpp"
|
||||
#include "ring_buffer.hpp"
|
||||
|
||||
class AudioDeviceInterface {
|
||||
protected:
|
||||
static constexpr usize maxFrameCount = 0x2000;
|
||||
|
||||
using Samples = Common::RingBuffer<s16, maxFrameCount * 2>;
|
||||
using RenderBatchCallback = usize (*)(const s16*, usize);
|
||||
|
||||
Samples* samples = nullptr;
|
||||
|
||||
const AudioDeviceConfig& audioSettings;
|
||||
// Store the last stereo sample we output. We play this when underruning to avoid pops.
|
||||
std::array<s16, 2> lastStereoSample{};
|
||||
|
||||
public:
|
||||
AudioDeviceInterface(Samples* samples, const AudioDeviceConfig& audioSettings) : samples(samples), audioSettings(audioSettings) {}
|
||||
|
||||
bool running = false;
|
||||
Samples* getSamples() { return samples; }
|
||||
|
||||
// If safe is on, we create a null audio device
|
||||
virtual void init(Samples& samples, bool safe = false) = 0;
|
||||
virtual void close() = 0;
|
||||
|
||||
virtual void start() = 0;
|
||||
virtual void stop() = 0;
|
||||
|
||||
// Only used for audio devices that render multiple audio frames in one go, eg the libretro audio device.
|
||||
virtual void renderBatch(RenderBatchCallback callback) {}
|
||||
};
|
29
include/audio/dsp_binary.hpp
Normal file
29
include/audio/dsp_binary.hpp
Normal file
|
@ -0,0 +1,29 @@
|
|||
#pragma once
|
||||
#include "helpers.hpp"
|
||||
|
||||
struct Dsp1 {
|
||||
// All sizes are in bytes unless otherwise specified
|
||||
u8 signature[0x100];
|
||||
u8 magic[4];
|
||||
u32 size;
|
||||
u8 codeMemLayout;
|
||||
u8 dataMemLayout;
|
||||
u8 pad[3];
|
||||
u8 specialType;
|
||||
u8 segmentCount;
|
||||
u8 flags;
|
||||
u32 specialStart;
|
||||
u32 specialSize;
|
||||
u64 zeroBits;
|
||||
|
||||
struct Segment {
|
||||
u32 offs; // Offset of the segment data
|
||||
u32 dspAddr; // Start of the segment in 16-bit units
|
||||
u32 size;
|
||||
u8 pad[3];
|
||||
u8 type;
|
||||
u8 hash[0x20];
|
||||
};
|
||||
|
||||
Segment segments[10];
|
||||
};
|
|
@ -1,8 +1,5 @@
|
|||
#pragma once
|
||||
#include <array>
|
||||
#include <functional>
|
||||
#include <memory>
|
||||
#include <optional>
|
||||
#include <string>
|
||||
#include <vector>
|
||||
|
||||
|
@ -63,6 +60,24 @@ namespace Audio {
|
|||
|
||||
Samples& getSamples() { return sampleBuffer; }
|
||||
virtual void setAudioEnabled(bool enable) { audioEnabled = enable; }
|
||||
|
||||
virtual Type getType() = 0;
|
||||
virtual void* getRegisters() { return nullptr; }
|
||||
|
||||
// Read a word from program memory. By default, just perform a regular DSP RAM read for the HLE cores
|
||||
// The LLE cores translate the address, accounting for the way Teak memory is mapped
|
||||
virtual u16 readProgramWord(u32 address) {
|
||||
u8* dspRam = getDspMemory();
|
||||
|
||||
auto readByte = [&](u32 addr) {
|
||||
if (addr >= 256_KB) return u8(0);
|
||||
return dspRam[addr];
|
||||
};
|
||||
|
||||
u16 lsb = u16(readByte(address));
|
||||
u16 msb = u16(readByte(address + 1));
|
||||
return u16(lsb | (msb << 8));
|
||||
}
|
||||
};
|
||||
|
||||
std::unique_ptr<DSPCore> makeDSPCore(EmulatorConfig& config, Memory& mem, Scheduler& scheduler, DSPService& dspService);
|
||||
|
|
|
@ -69,7 +69,9 @@ namespace Audio {
|
|||
// In order to save up on CPU time.
|
||||
uint enabledMixStages = 0;
|
||||
|
||||
u32 samplePosition; // Sample number into the current audio buffer
|
||||
u32 samplePosition; // Sample number into the current audio buffer
|
||||
u32 currentBufferPaddr; // Physical address of current audio buffer
|
||||
|
||||
float rateMultiplier;
|
||||
u16 syncCount;
|
||||
u16 currentBufferID;
|
||||
|
@ -213,6 +215,7 @@ namespace Audio {
|
|||
void runAudioFrame(u64 eventTimestamp) override;
|
||||
|
||||
u8* getDspMemory() override { return dspRam.rawMemory.data(); }
|
||||
DSPCore::Type getType() override { return DSPCore::Type::HLE; }
|
||||
|
||||
u16 recvData(u32 regId) override;
|
||||
bool recvDataIsReady(u32 regId) override { return true; } // Treat data as always ready
|
||||
|
|
61
include/audio/libretro_audio_device.hpp
Normal file
61
include/audio/libretro_audio_device.hpp
Normal file
|
@ -0,0 +1,61 @@
|
|||
#pragma once
|
||||
#include <cstring>
|
||||
|
||||
#include "audio/audio_device_interface.hpp"
|
||||
|
||||
class LibretroAudioDevice final : public AudioDeviceInterface {
|
||||
bool initialized = false;
|
||||
|
||||
public:
|
||||
LibretroAudioDevice(const AudioDeviceConfig& audioSettings) : AudioDeviceInterface(nullptr, audioSettings), initialized(false) {
|
||||
running = false;
|
||||
}
|
||||
|
||||
void init(Samples& samples, bool safe = false) override {
|
||||
this->samples = &samples;
|
||||
|
||||
initialized = true;
|
||||
running = false;
|
||||
}
|
||||
|
||||
void close() override {
|
||||
initialized = false;
|
||||
running = false;
|
||||
};
|
||||
|
||||
void start() override { running = true; }
|
||||
void stop() override { running = false; };
|
||||
|
||||
void renderBatch(RenderBatchCallback callback) override {
|
||||
if (running) {
|
||||
static constexpr usize sampleRate = 32768; // 3DS samples per second
|
||||
static constexpr usize frameCount = sampleRate / 60; // 3DS samples per video frame
|
||||
static constexpr usize channelCount = 2;
|
||||
static s16 audioBuffer[frameCount * channelCount];
|
||||
|
||||
usize samplesWritten = 0;
|
||||
samplesWritten += samples->pop(audioBuffer, frameCount * channelCount);
|
||||
|
||||
// Get the last sample for underrun handling
|
||||
if (samplesWritten != 0) {
|
||||
std::memcpy(&lastStereoSample[0], &audioBuffer[(samplesWritten - 1) * 2], sizeof(lastStereoSample));
|
||||
}
|
||||
|
||||
// If underruning, copy the last output sample
|
||||
{
|
||||
s16* pointer = &audioBuffer[samplesWritten * 2];
|
||||
s16 l = lastStereoSample[0];
|
||||
s16 r = lastStereoSample[1];
|
||||
|
||||
for (usize i = samplesWritten; i < frameCount; i++) {
|
||||
*pointer++ = l;
|
||||
*pointer++ = r;
|
||||
}
|
||||
}
|
||||
|
||||
callback(audioBuffer, sizeof(audioBuffer) / (channelCount * sizeof(s16)));
|
||||
}
|
||||
}
|
||||
|
||||
bool isInitialized() const { return initialized; }
|
||||
};
|
|
@ -3,39 +3,31 @@
|
|||
#include <string>
|
||||
#include <vector>
|
||||
|
||||
#include "config.hpp"
|
||||
#include "helpers.hpp"
|
||||
#include "audio/audio_device_interface.hpp"
|
||||
#include "miniaudio.h"
|
||||
#include "ring_buffer.hpp"
|
||||
|
||||
class MiniAudioDevice {
|
||||
using Samples = Common::RingBuffer<ma_int16, 0x2000 * 2>;
|
||||
class MiniAudioDevice final : public AudioDeviceInterface {
|
||||
static constexpr ma_uint32 sampleRate = 32768; // 3DS sample rate
|
||||
static constexpr ma_uint32 channelCount = 2; // Audio output is stereo
|
||||
|
||||
bool initialized = false;
|
||||
|
||||
ma_device device;
|
||||
ma_context context;
|
||||
ma_device_config deviceConfig;
|
||||
Samples* samples = nullptr;
|
||||
|
||||
const AudioDeviceConfig& audioSettings;
|
||||
|
||||
bool initialized = false;
|
||||
bool running = false;
|
||||
|
||||
// Store the last stereo sample we output. We play this when underruning to avoid pops.
|
||||
std::array<s16, 2> lastStereoSample;
|
||||
std::vector<std::string> audioDevices;
|
||||
|
||||
public:
|
||||
MiniAudioDevice(const AudioDeviceConfig& audioSettings);
|
||||
|
||||
// If safe is on, we create a null audio device
|
||||
void init(Samples& samples, bool safe = false);
|
||||
void close();
|
||||
void init(Samples& samples, bool safe = false) override;
|
||||
void close() override;
|
||||
|
||||
void start();
|
||||
void stop();
|
||||
void start() override;
|
||||
void stop() override;
|
||||
|
||||
bool isInitialized() const { return initialized; }
|
||||
};
|
|
@ -30,6 +30,7 @@ namespace Audio {
|
|||
void runAudioFrame(u64 eventTimestamp) override;
|
||||
|
||||
u8* getDspMemory() override { return dspRam.data(); }
|
||||
DSPCore::Type getType() override { return DSPCore::Type::Null; }
|
||||
|
||||
u16 recvData(u32 regId) override;
|
||||
bool recvDataIsReady(u32 regId) override { return true; } // Treat data as always ready
|
||||
|
|
|
@ -15,7 +15,7 @@ namespace Audio {
|
|||
bool signalledData;
|
||||
bool signalledSemaphore;
|
||||
|
||||
uint audioFrameIndex = 0; // Index in our audio frame
|
||||
uint audioFrameIndex = 0; // Index in our audio frame
|
||||
std::array<s16, 160 * 2> audioFrame;
|
||||
|
||||
// Get a pointer to a data memory address
|
||||
|
@ -90,6 +90,9 @@ namespace Audio {
|
|||
|
||||
void setAudioEnabled(bool enable) override;
|
||||
u8* getDspMemory() override { return teakra.GetDspMemory().data(); }
|
||||
void* getRegisters() override;
|
||||
DSPCore::Type getType() override { return DSPCore::Type::Teakra; }
|
||||
u16 readProgramWord(u32 address) override { return teakra.ProgramRead(address); }
|
||||
|
||||
u16 recvData(u32 regId) override { return teakra.RecvData(regId); }
|
||||
bool recvDataIsReady(u32 regId) override { return teakra.RecvDataIsReady(regId); }
|
||||
|
|
|
@ -23,7 +23,7 @@ namespace Common {
|
|||
// pc: program counter of the instruction to disassemble
|
||||
// bytes: Byte representation of instruction
|
||||
// buffer: text buffer to output the disassembly too
|
||||
usize disassemble(std::string& buffer, u32 pc, std::span<u8> bytes, u64 offset = 0) {
|
||||
usize disassemble(std::string& buffer, u32 pc, std::span<const u8> bytes, u64 offset = 0) {
|
||||
if (!initialized) {
|
||||
buffer = "Capstone was not properly initialized";
|
||||
return 0;
|
||||
|
|
|
@ -2,6 +2,7 @@
|
|||
#include <filesystem>
|
||||
#include <string>
|
||||
|
||||
#include "screen_layout.hpp"
|
||||
#include "audio/dsp_core.hpp"
|
||||
#include "frontend_settings.hpp"
|
||||
#include "renderer.hpp"
|
||||
|
@ -55,9 +56,23 @@ struct EmulatorConfig {
|
|||
static constexpr bool audioEnabledDefault = false;
|
||||
#endif
|
||||
|
||||
// We default to OpenGL on all platforms other than iOS
|
||||
#if defined(PANDA3DS_IOS)
|
||||
static constexpr RendererType rendererDefault = RendererType::Metal;
|
||||
#else
|
||||
static constexpr RendererType rendererDefault = RendererType::OpenGL;
|
||||
#endif
|
||||
|
||||
static constexpr bool hashTexturesDefault = false;
|
||||
|
||||
bool shaderJitEnabled = shaderJitDefault;
|
||||
bool useUbershaders = ubershaderDefault;
|
||||
bool accelerateShaders = accelerateShadersDefault;
|
||||
bool hashTextures = hashTexturesDefault;
|
||||
|
||||
ScreenLayout::Layout screenLayout = ScreenLayout::Layout::Default;
|
||||
float topScreenSize = 0.5;
|
||||
|
||||
bool accurateShaderMul = false;
|
||||
bool discordRpcEnabled = false;
|
||||
|
||||
|
@ -65,11 +80,12 @@ struct EmulatorConfig {
|
|||
bool forceShadergenForLights = true;
|
||||
int lightShadergenThreshold = 1;
|
||||
|
||||
RendererType rendererType = RendererType::OpenGL;
|
||||
RendererType rendererType = rendererDefault;
|
||||
Audio::DSPCore::Type dspType = Audio::DSPCore::Type::HLE;
|
||||
|
||||
bool sdCardInserted = true;
|
||||
bool sdWriteProtected = false;
|
||||
bool circlePadProEnabled = true;
|
||||
bool usePortableBuild = false;
|
||||
|
||||
bool audioEnabled = audioEnabledDefault;
|
||||
|
@ -116,4 +132,4 @@ struct EmulatorConfig {
|
|||
|
||||
static LanguageCodes languageCodeFromString(std::string inString);
|
||||
static const char* languageCodeToString(LanguageCodes code);
|
||||
};
|
||||
};
|
||||
|
|
|
@ -181,5 +181,7 @@ class CPU {
|
|||
void addTicks(u64 ticks) { env.AddTicks(ticks); }
|
||||
|
||||
void clearCache() { jit->ClearCache(); }
|
||||
void clearCacheRange(u32 start, u32 size) { jit->InvalidateCacheRange(start, size); }
|
||||
|
||||
void runFrame();
|
||||
};
|
|
@ -7,8 +7,8 @@
|
|||
#include <span>
|
||||
|
||||
#include "PICA/gpu.hpp"
|
||||
#include "audio/audio_device.hpp"
|
||||
#include "audio/dsp_core.hpp"
|
||||
#include "audio/miniaudio_device.hpp"
|
||||
#include "cheats.hpp"
|
||||
#include "config.hpp"
|
||||
#include "cpu.hpp"
|
||||
|
@ -48,14 +48,14 @@ class Emulator {
|
|||
Scheduler scheduler;
|
||||
|
||||
Crypto::AESEngine aesEngine;
|
||||
MiniAudioDevice audioDevice;
|
||||
AudioDevice audioDevice;
|
||||
Cheats cheats;
|
||||
|
||||
public:
|
||||
static constexpr u32 width = 400;
|
||||
static constexpr u32 height = 240 * 2; // * 2 because 2 screens
|
||||
ROMType romType = ROMType::None;
|
||||
bool running = false; // Is the emulator running a game?
|
||||
bool running = false; // Is the emulator running a game?
|
||||
|
||||
private:
|
||||
#ifdef PANDA3DS_ENABLE_HTTP_SERVER
|
||||
|
@ -115,17 +115,24 @@ class Emulator {
|
|||
|
||||
RomFS::DumpingResult dumpRomFS(const std::filesystem::path& path);
|
||||
void setOutputSize(u32 width, u32 height) { gpu.setOutputSize(width, height); }
|
||||
void reloadScreenLayout() { gpu.reloadScreenLayout(); }
|
||||
|
||||
void deinitGraphicsContext() { gpu.deinitGraphicsContext(); }
|
||||
|
||||
// Reloads some settings that require special handling, such as audio enable
|
||||
void reloadSettings();
|
||||
|
||||
CPU& getCPU() { return cpu; }
|
||||
Memory& getMemory() { return memory; }
|
||||
Kernel& getKernel() { return kernel; }
|
||||
Scheduler& getScheduler() { return scheduler; }
|
||||
Audio::DSPCore* getDSP() { return dsp.get(); }
|
||||
|
||||
EmulatorConfig& getConfig() { return config; }
|
||||
Cheats& getCheats() { return cheats; }
|
||||
ServiceManager& getServiceManager() { return kernel.getServiceManager(); }
|
||||
LuaManager& getLua() { return lua; }
|
||||
Scheduler& getScheduler() { return scheduler; }
|
||||
Memory& getMemory() { return memory; }
|
||||
AudioDeviceInterface& getAudioDevice() { return audioDevice; }
|
||||
|
||||
RendererType getRendererType() const { return config.rendererType; }
|
||||
Renderer* getRenderer() { return gpu.getRenderer(); }
|
||||
|
|
|
@ -11,6 +11,7 @@ struct FrontendSettings {
|
|||
Dark = 2,
|
||||
GreetingsCat = 3,
|
||||
Cream = 4,
|
||||
Oled = 5,
|
||||
};
|
||||
|
||||
// Different panda-themed window icons
|
||||
|
@ -20,6 +21,7 @@ struct FrontendSettings {
|
|||
Rnap = 2,
|
||||
Rcow = 3,
|
||||
SkyEmu = 4,
|
||||
Runpog = 5,
|
||||
};
|
||||
|
||||
Theme theme = Theme::Dark;
|
||||
|
|
|
@ -271,7 +271,7 @@ class ArchiveBase {
|
|||
bool isSafeTextPath(const FSPath& path) {
|
||||
if (path.type == PathType::UTF16) {
|
||||
return isPathSafe<PathType::UTF16>(path);
|
||||
} else if (path.type == PathType::ASCII){
|
||||
} else if (path.type == PathType::ASCII) {
|
||||
return isPathSafe<PathType::ASCII>(path);
|
||||
}
|
||||
|
||||
|
|
|
@ -2,11 +2,13 @@
|
|||
#include "archive_base.hpp"
|
||||
|
||||
class ExtSaveDataArchive : public ArchiveBase {
|
||||
public:
|
||||
ExtSaveDataArchive(Memory& mem, const std::string& folder, bool isShared = false) : ArchiveBase(mem),
|
||||
isShared(isShared), backingFolder(folder) {}
|
||||
public:
|
||||
ExtSaveDataArchive(Memory& mem, const std::string& folder, bool isShared = false) : ArchiveBase(mem), isShared(isShared), backingFolder(folder) {}
|
||||
|
||||
u64 getFreeBytes() override { Helpers::panic("ExtSaveData::GetFreeBytes unimplemented"); return 0; }
|
||||
u64 getFreeBytes() override {
|
||||
Helpers::panic("ExtSaveData::GetFreeBytes unimplemented");
|
||||
return 0;
|
||||
}
|
||||
std::string name() override { return "ExtSaveData::" + backingFolder; }
|
||||
|
||||
HorizonResult createDirectory(const FSPath& path) override;
|
||||
|
@ -29,5 +31,5 @@ public:
|
|||
std::string getExtSaveDataPathFromBinary(const FSPath& path);
|
||||
|
||||
bool isShared = false;
|
||||
std::string backingFolder; // Backing folder for the archive. Can be NAND, Gamecard or SD depending on the archive path.
|
||||
std::string backingFolder; // Backing folder for the archive. Can be NAND, Gamecard or SD depending on the archive path.
|
||||
};
|
|
@ -2,10 +2,13 @@
|
|||
#include "archive_base.hpp"
|
||||
|
||||
class NCCHArchive : public ArchiveBase {
|
||||
public:
|
||||
public:
|
||||
NCCHArchive(Memory& mem) : ArchiveBase(mem) {}
|
||||
|
||||
u64 getFreeBytes() override { Helpers::panic("NCCH::GetFreeBytes unimplemented"); return 0; }
|
||||
u64 getFreeBytes() override {
|
||||
Helpers::panic("NCCH::GetFreeBytes unimplemented");
|
||||
return 0;
|
||||
}
|
||||
std::string name() override { return "NCCH"; }
|
||||
|
||||
HorizonResult createFile(const FSPath& path, u64 size) override;
|
||||
|
|
|
@ -2,7 +2,7 @@
|
|||
#include "archive_base.hpp"
|
||||
|
||||
class SaveDataArchive : public ArchiveBase {
|
||||
public:
|
||||
public:
|
||||
SaveDataArchive(Memory& mem) : ArchiveBase(mem) {}
|
||||
|
||||
u64 getFreeBytes() override { return 32_MB; }
|
||||
|
@ -27,6 +27,6 @@ public:
|
|||
// Returns whether the cart has save data or not
|
||||
bool cartHasSaveData() {
|
||||
auto cxi = mem.getCXI();
|
||||
return (cxi != nullptr && cxi->hasSaveData()); // We need to have a CXI file with more than 0 bytes of save data
|
||||
return (cxi != nullptr && cxi->hasSaveData()); // We need to have a CXI file with more than 0 bytes of save data
|
||||
}
|
||||
};
|
|
@ -2,7 +2,7 @@
|
|||
#include "archive_base.hpp"
|
||||
|
||||
class SelfNCCHArchive : public ArchiveBase {
|
||||
public:
|
||||
public:
|
||||
SelfNCCHArchive(Memory& mem) : ArchiveBase(mem) {}
|
||||
|
||||
u64 getFreeBytes() override { return 0; }
|
||||
|
|
|
@ -3,6 +3,7 @@
|
|||
|
||||
class UserSaveDataArchive : public ArchiveBase {
|
||||
u32 archiveID;
|
||||
|
||||
public:
|
||||
UserSaveDataArchive(Memory& mem, u32 archiveID) : ArchiveBase(mem), archiveID(archiveID) {}
|
||||
|
||||
|
|
|
@ -1,132 +1,69 @@
|
|||
// Generated with https://github.com/B3n30/citra_system_archives
|
||||
#pragma once
|
||||
|
||||
const unsigned char BAD_WORD_LIST_DATA[] = {
|
||||
0x28, 0x00, 0x00, 0x00, 0x28, 0x00, 0x00, 0x00, 0x0c, 0x00, 0x00, 0x00,
|
||||
0x34, 0x00, 0x00, 0x00, 0x18, 0x00, 0x00, 0x00, 0x4c, 0x00, 0x00, 0x00,
|
||||
0x4c, 0x00, 0x00, 0x00, 0x98, 0x00, 0x00, 0x00, 0x24, 0x03, 0x00, 0x00,
|
||||
0xc0, 0x03, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xff, 0xff, 0xff, 0xff,
|
||||
0xff, 0xff, 0xff, 0xff, 0x00, 0x00, 0x00, 0x00, 0xff, 0xff, 0xff, 0xff,
|
||||
0xff, 0xff, 0xff, 0xff, 0x00, 0x00, 0x00, 0x00, 0xff, 0xff, 0xff, 0xff,
|
||||
0x00, 0x00, 0x00, 0x00, 0xec, 0x02, 0x00, 0x00, 0x10, 0x02, 0x00, 0x00,
|
||||
0xc0, 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x68, 0x02, 0x00, 0x00,
|
||||
0xff, 0xff, 0xff, 0xff, 0x8c, 0x01, 0x00, 0x00, 0x84, 0x00, 0x00, 0x00,
|
||||
0xff, 0xff, 0xff, 0xff, 0xe4, 0x01, 0x00, 0x00, 0x94, 0x02, 0x00, 0x00,
|
||||
0xff, 0xff, 0xff, 0xff, 0x3c, 0x02, 0x00, 0x00, 0x34, 0x01, 0x00, 0x00,
|
||||
0x2c, 0x00, 0x00, 0x00, 0x58, 0x00, 0x00, 0x00, 0xff, 0xff, 0xff, 0xff,
|
||||
0xb8, 0x01, 0x00, 0x00, 0x60, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
||||
0x2c, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
||||
0x16, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xff, 0xff, 0xff, 0xff,
|
||||
0x0a, 0x00, 0x00, 0x00, 0x30, 0x00, 0x2e, 0x00, 0x74, 0x00, 0x78, 0x00,
|
||||
0x74, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x58, 0x00, 0x00, 0x00,
|
||||
0x20, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x16, 0x00, 0x00, 0x00,
|
||||
0x00, 0x00, 0x00, 0x00, 0xff, 0xff, 0xff, 0xff, 0x0a, 0x00, 0x00, 0x00,
|
||||
0x31, 0x00, 0x2e, 0x00, 0x74, 0x00, 0x78, 0x00, 0x74, 0x00, 0x00, 0x00,
|
||||
0x00, 0x00, 0x00, 0x00, 0x84, 0x00, 0x00, 0x00, 0x40, 0x00, 0x00, 0x00,
|
||||
0x00, 0x00, 0x00, 0x00, 0x16, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
||||
0xff, 0xff, 0xff, 0xff, 0x0c, 0x00, 0x00, 0x00, 0x31, 0x00, 0x30, 0x00,
|
||||
0x2e, 0x00, 0x74, 0x00, 0x78, 0x00, 0x74, 0x00, 0x00, 0x00, 0x00, 0x00,
|
||||
0xb0, 0x00, 0x00, 0x00, 0x60, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
||||
0x16, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xff, 0xff, 0xff, 0xff,
|
||||
0x0c, 0x00, 0x00, 0x00, 0x31, 0x00, 0x31, 0x00, 0x2e, 0x00, 0x74, 0x00,
|
||||
0x78, 0x00, 0x74, 0x00, 0x00, 0x00, 0x00, 0x00, 0xdc, 0x00, 0x00, 0x00,
|
||||
0x80, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x16, 0x00, 0x00, 0x00,
|
||||
0x00, 0x00, 0x00, 0x00, 0xff, 0xff, 0xff, 0xff, 0x0c, 0x00, 0x00, 0x00,
|
||||
0x31, 0x00, 0x32, 0x00, 0x2e, 0x00, 0x74, 0x00, 0x78, 0x00, 0x74, 0x00,
|
||||
0x00, 0x00, 0x00, 0x00, 0x08, 0x01, 0x00, 0x00, 0xa0, 0x00, 0x00, 0x00,
|
||||
0x00, 0x00, 0x00, 0x00, 0x16, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
||||
0xff, 0xff, 0xff, 0xff, 0x0c, 0x00, 0x00, 0x00, 0x31, 0x00, 0x33, 0x00,
|
||||
0x2e, 0x00, 0x74, 0x00, 0x78, 0x00, 0x74, 0x00, 0x00, 0x00, 0x00, 0x00,
|
||||
0x34, 0x01, 0x00, 0x00, 0xc0, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
||||
0x16, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xff, 0xff, 0xff, 0xff,
|
||||
0x0c, 0x00, 0x00, 0x00, 0x31, 0x00, 0x34, 0x00, 0x2e, 0x00, 0x74, 0x00,
|
||||
0x78, 0x00, 0x74, 0x00, 0x00, 0x00, 0x00, 0x00, 0x60, 0x01, 0x00, 0x00,
|
||||
0xe0, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x16, 0x00, 0x00, 0x00,
|
||||
0x00, 0x00, 0x00, 0x00, 0xff, 0xff, 0xff, 0xff, 0x0c, 0x00, 0x00, 0x00,
|
||||
0x31, 0x00, 0x35, 0x00, 0x2e, 0x00, 0x74, 0x00, 0x78, 0x00, 0x74, 0x00,
|
||||
0x00, 0x00, 0x00, 0x00, 0x8c, 0x01, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00,
|
||||
0x00, 0x00, 0x00, 0x00, 0x16, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
||||
0xff, 0xff, 0xff, 0xff, 0x0c, 0x00, 0x00, 0x00, 0x31, 0x00, 0x36, 0x00,
|
||||
0x2e, 0x00, 0x74, 0x00, 0x78, 0x00, 0x74, 0x00, 0x00, 0x00, 0x00, 0x00,
|
||||
0xb8, 0x01, 0x00, 0x00, 0x20, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
||||
0x16, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xff, 0xff, 0xff, 0xff,
|
||||
0x0a, 0x00, 0x00, 0x00, 0x32, 0x00, 0x2e, 0x00, 0x74, 0x00, 0x78, 0x00,
|
||||
0x74, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xe4, 0x01, 0x00, 0x00,
|
||||
0x40, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x16, 0x00, 0x00, 0x00,
|
||||
0x00, 0x00, 0x00, 0x00, 0xff, 0xff, 0xff, 0xff, 0x0a, 0x00, 0x00, 0x00,
|
||||
0x33, 0x00, 0x2e, 0x00, 0x74, 0x00, 0x78, 0x00, 0x74, 0x00, 0x00, 0x00,
|
||||
0x00, 0x00, 0x00, 0x00, 0x10, 0x02, 0x00, 0x00, 0x60, 0x01, 0x00, 0x00,
|
||||
0x00, 0x00, 0x00, 0x00, 0x16, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
||||
0xff, 0xff, 0xff, 0xff, 0x0a, 0x00, 0x00, 0x00, 0x34, 0x00, 0x2e, 0x00,
|
||||
0x74, 0x00, 0x78, 0x00, 0x74, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
||||
0x3c, 0x02, 0x00, 0x00, 0x80, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
||||
0x16, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xff, 0xff, 0xff, 0xff,
|
||||
0x0a, 0x00, 0x00, 0x00, 0x35, 0x00, 0x2e, 0x00, 0x74, 0x00, 0x78, 0x00,
|
||||
0x74, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x68, 0x02, 0x00, 0x00,
|
||||
0xa0, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x16, 0x00, 0x00, 0x00,
|
||||
0x00, 0x00, 0x00, 0x00, 0xb0, 0x00, 0x00, 0x00, 0x0a, 0x00, 0x00, 0x00,
|
||||
0x36, 0x00, 0x2e, 0x00, 0x74, 0x00, 0x78, 0x00, 0x74, 0x00, 0x00, 0x00,
|
||||
0x00, 0x00, 0x00, 0x00, 0x94, 0x02, 0x00, 0x00, 0xc0, 0x01, 0x00, 0x00,
|
||||
0x00, 0x00, 0x00, 0x00, 0x16, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
||||
0xdc, 0x00, 0x00, 0x00, 0x0a, 0x00, 0x00, 0x00, 0x37, 0x00, 0x2e, 0x00,
|
||||
0x74, 0x00, 0x78, 0x00, 0x74, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
||||
0xc0, 0x02, 0x00, 0x00, 0xe0, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
||||
0x16, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xff, 0xff, 0xff, 0xff,
|
||||
0x0a, 0x00, 0x00, 0x00, 0x38, 0x00, 0x2e, 0x00, 0x74, 0x00, 0x78, 0x00,
|
||||
0x74, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xec, 0x02, 0x00, 0x00,
|
||||
0x00, 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x16, 0x00, 0x00, 0x00,
|
||||
0x00, 0x00, 0x00, 0x00, 0x08, 0x01, 0x00, 0x00, 0x0a, 0x00, 0x00, 0x00,
|
||||
0x39, 0x00, 0x2e, 0x00, 0x74, 0x00, 0x78, 0x00, 0x74, 0x00, 0x00, 0x00,
|
||||
0x00, 0x00, 0x00, 0x00, 0xff, 0xff, 0xff, 0xff, 0x20, 0x02, 0x00, 0x00,
|
||||
0x00, 0x00, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
||||
0xff, 0xff, 0xff, 0xff, 0x16, 0x00, 0x00, 0x00, 0x76, 0x00, 0x65, 0x00,
|
||||
0x72, 0x00, 0x73, 0x00, 0x69, 0x00, 0x6f, 0x00, 0x6e, 0x00, 0x2e, 0x00,
|
||||
0x64, 0x00, 0x61, 0x00, 0x74, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
||||
0xff, 0xfe, 0x5e, 0x00, 0x62, 0x00, 0x61, 0x00, 0x64, 0x00, 0x77, 0x00,
|
||||
0x6f, 0x00, 0x72, 0x00, 0x64, 0x00, 0x24, 0x00, 0x0a, 0x00, 0x00, 0x00,
|
||||
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xff, 0xfe, 0x5e, 0x00,
|
||||
0x62, 0x00, 0x61, 0x00, 0x64, 0x00, 0x77, 0x00, 0x6f, 0x00, 0x72, 0x00,
|
||||
0x64, 0x00, 0x24, 0x00, 0x0a, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
||||
0x00, 0x00, 0x00, 0x00, 0xff, 0xfe, 0x5e, 0x00, 0x62, 0x00, 0x61, 0x00,
|
||||
0x64, 0x00, 0x77, 0x00, 0x6f, 0x00, 0x72, 0x00, 0x64, 0x00, 0x24, 0x00,
|
||||
0x0a, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
||||
0xff, 0xfe, 0x5e, 0x00, 0x62, 0x00, 0x61, 0x00, 0x64, 0x00, 0x77, 0x00,
|
||||
0x6f, 0x00, 0x72, 0x00, 0x64, 0x00, 0x24, 0x00, 0x0a, 0x00, 0x00, 0x00,
|
||||
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xff, 0xfe, 0x5e, 0x00,
|
||||
0x62, 0x00, 0x61, 0x00, 0x64, 0x00, 0x77, 0x00, 0x6f, 0x00, 0x72, 0x00,
|
||||
0x64, 0x00, 0x24, 0x00, 0x0a, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
||||
0x00, 0x00, 0x00, 0x00, 0xff, 0xfe, 0x5e, 0x00, 0x62, 0x00, 0x61, 0x00,
|
||||
0x64, 0x00, 0x77, 0x00, 0x6f, 0x00, 0x72, 0x00, 0x64, 0x00, 0x24, 0x00,
|
||||
0x0a, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
||||
0xff, 0xfe, 0x5e, 0x00, 0x62, 0x00, 0x61, 0x00, 0x64, 0x00, 0x77, 0x00,
|
||||
0x6f, 0x00, 0x72, 0x00, 0x64, 0x00, 0x24, 0x00, 0x0a, 0x00, 0x00, 0x00,
|
||||
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xff, 0xfe, 0x5e, 0x00,
|
||||
0x62, 0x00, 0x61, 0x00, 0x64, 0x00, 0x77, 0x00, 0x6f, 0x00, 0x72, 0x00,
|
||||
0x64, 0x00, 0x24, 0x00, 0x0a, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
||||
0x00, 0x00, 0x00, 0x00, 0xff, 0xfe, 0x5e, 0x00, 0x62, 0x00, 0x61, 0x00,
|
||||
0x64, 0x00, 0x77, 0x00, 0x6f, 0x00, 0x72, 0x00, 0x64, 0x00, 0x24, 0x00,
|
||||
0x0a, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
||||
0xff, 0xfe, 0x5e, 0x00, 0x62, 0x00, 0x61, 0x00, 0x64, 0x00, 0x77, 0x00,
|
||||
0x6f, 0x00, 0x72, 0x00, 0x64, 0x00, 0x24, 0x00, 0x0a, 0x00, 0x00, 0x00,
|
||||
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xff, 0xfe, 0x5e, 0x00,
|
||||
0x62, 0x00, 0x61, 0x00, 0x64, 0x00, 0x77, 0x00, 0x6f, 0x00, 0x72, 0x00,
|
||||
0x64, 0x00, 0x24, 0x00, 0x0a, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
||||
0x00, 0x00, 0x00, 0x00, 0xff, 0xfe, 0x5e, 0x00, 0x62, 0x00, 0x61, 0x00,
|
||||
0x64, 0x00, 0x77, 0x00, 0x6f, 0x00, 0x72, 0x00, 0x64, 0x00, 0x24, 0x00,
|
||||
0x0a, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
||||
0xff, 0xfe, 0x5e, 0x00, 0x62, 0x00, 0x61, 0x00, 0x64, 0x00, 0x77, 0x00,
|
||||
0x6f, 0x00, 0x72, 0x00, 0x64, 0x00, 0x24, 0x00, 0x0a, 0x00, 0x00, 0x00,
|
||||
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xff, 0xfe, 0x5e, 0x00,
|
||||
0x62, 0x00, 0x61, 0x00, 0x64, 0x00, 0x77, 0x00, 0x6f, 0x00, 0x72, 0x00,
|
||||
0x64, 0x00, 0x24, 0x00, 0x0a, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
||||
0x00, 0x00, 0x00, 0x00, 0xff, 0xfe, 0x5e, 0x00, 0x62, 0x00, 0x61, 0x00,
|
||||
0x64, 0x00, 0x77, 0x00, 0x6f, 0x00, 0x72, 0x00, 0x64, 0x00, 0x24, 0x00,
|
||||
0x0a, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
||||
0xff, 0xfe, 0x5e, 0x00, 0x62, 0x00, 0x61, 0x00, 0x64, 0x00, 0x77, 0x00,
|
||||
0x6f, 0x00, 0x72, 0x00, 0x64, 0x00, 0x24, 0x00, 0x0a, 0x00, 0x00, 0x00,
|
||||
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xff, 0xfe, 0x5e, 0x00,
|
||||
0x62, 0x00, 0x61, 0x00, 0x64, 0x00, 0x77, 0x00, 0x6f, 0x00, 0x72, 0x00,
|
||||
0x64, 0x00, 0x24, 0x00, 0x0a, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
||||
0x00, 0x00, 0x00, 0x00, 0x12, 0x00, 0x00, 0x00
|
||||
constexpr unsigned char BAD_WORD_LIST_DATA[] = {
|
||||
0x28, 0x00, 0x00, 0x00, 0x28, 0x00, 0x00, 0x00, 0x0c, 0x00, 0x00, 0x00, 0x34, 0x00, 0x00, 0x00, 0x18, 0x00, 0x00, 0x00, 0x4c, 0x00, 0x00, 0x00,
|
||||
0x4c, 0x00, 0x00, 0x00, 0x98, 0x00, 0x00, 0x00, 0x24, 0x03, 0x00, 0x00, 0xc0, 0x03, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xff, 0xff, 0xff, 0xff,
|
||||
0xff, 0xff, 0xff, 0xff, 0x00, 0x00, 0x00, 0x00, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0x00, 0x00, 0x00, 0x00, 0xff, 0xff, 0xff, 0xff,
|
||||
0x00, 0x00, 0x00, 0x00, 0xec, 0x02, 0x00, 0x00, 0x10, 0x02, 0x00, 0x00, 0xc0, 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x68, 0x02, 0x00, 0x00,
|
||||
0xff, 0xff, 0xff, 0xff, 0x8c, 0x01, 0x00, 0x00, 0x84, 0x00, 0x00, 0x00, 0xff, 0xff, 0xff, 0xff, 0xe4, 0x01, 0x00, 0x00, 0x94, 0x02, 0x00, 0x00,
|
||||
0xff, 0xff, 0xff, 0xff, 0x3c, 0x02, 0x00, 0x00, 0x34, 0x01, 0x00, 0x00, 0x2c, 0x00, 0x00, 0x00, 0x58, 0x00, 0x00, 0x00, 0xff, 0xff, 0xff, 0xff,
|
||||
0xb8, 0x01, 0x00, 0x00, 0x60, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x2c, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
||||
0x16, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xff, 0xff, 0xff, 0xff, 0x0a, 0x00, 0x00, 0x00, 0x30, 0x00, 0x2e, 0x00, 0x74, 0x00, 0x78, 0x00,
|
||||
0x74, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x58, 0x00, 0x00, 0x00, 0x20, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x16, 0x00, 0x00, 0x00,
|
||||
0x00, 0x00, 0x00, 0x00, 0xff, 0xff, 0xff, 0xff, 0x0a, 0x00, 0x00, 0x00, 0x31, 0x00, 0x2e, 0x00, 0x74, 0x00, 0x78, 0x00, 0x74, 0x00, 0x00, 0x00,
|
||||
0x00, 0x00, 0x00, 0x00, 0x84, 0x00, 0x00, 0x00, 0x40, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x16, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
||||
0xff, 0xff, 0xff, 0xff, 0x0c, 0x00, 0x00, 0x00, 0x31, 0x00, 0x30, 0x00, 0x2e, 0x00, 0x74, 0x00, 0x78, 0x00, 0x74, 0x00, 0x00, 0x00, 0x00, 0x00,
|
||||
0xb0, 0x00, 0x00, 0x00, 0x60, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x16, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xff, 0xff, 0xff, 0xff,
|
||||
0x0c, 0x00, 0x00, 0x00, 0x31, 0x00, 0x31, 0x00, 0x2e, 0x00, 0x74, 0x00, 0x78, 0x00, 0x74, 0x00, 0x00, 0x00, 0x00, 0x00, 0xdc, 0x00, 0x00, 0x00,
|
||||
0x80, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x16, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xff, 0xff, 0xff, 0xff, 0x0c, 0x00, 0x00, 0x00,
|
||||
0x31, 0x00, 0x32, 0x00, 0x2e, 0x00, 0x74, 0x00, 0x78, 0x00, 0x74, 0x00, 0x00, 0x00, 0x00, 0x00, 0x08, 0x01, 0x00, 0x00, 0xa0, 0x00, 0x00, 0x00,
|
||||
0x00, 0x00, 0x00, 0x00, 0x16, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xff, 0xff, 0xff, 0xff, 0x0c, 0x00, 0x00, 0x00, 0x31, 0x00, 0x33, 0x00,
|
||||
0x2e, 0x00, 0x74, 0x00, 0x78, 0x00, 0x74, 0x00, 0x00, 0x00, 0x00, 0x00, 0x34, 0x01, 0x00, 0x00, 0xc0, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
||||
0x16, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xff, 0xff, 0xff, 0xff, 0x0c, 0x00, 0x00, 0x00, 0x31, 0x00, 0x34, 0x00, 0x2e, 0x00, 0x74, 0x00,
|
||||
0x78, 0x00, 0x74, 0x00, 0x00, 0x00, 0x00, 0x00, 0x60, 0x01, 0x00, 0x00, 0xe0, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x16, 0x00, 0x00, 0x00,
|
||||
0x00, 0x00, 0x00, 0x00, 0xff, 0xff, 0xff, 0xff, 0x0c, 0x00, 0x00, 0x00, 0x31, 0x00, 0x35, 0x00, 0x2e, 0x00, 0x74, 0x00, 0x78, 0x00, 0x74, 0x00,
|
||||
0x00, 0x00, 0x00, 0x00, 0x8c, 0x01, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x16, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
||||
0xff, 0xff, 0xff, 0xff, 0x0c, 0x00, 0x00, 0x00, 0x31, 0x00, 0x36, 0x00, 0x2e, 0x00, 0x74, 0x00, 0x78, 0x00, 0x74, 0x00, 0x00, 0x00, 0x00, 0x00,
|
||||
0xb8, 0x01, 0x00, 0x00, 0x20, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x16, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xff, 0xff, 0xff, 0xff,
|
||||
0x0a, 0x00, 0x00, 0x00, 0x32, 0x00, 0x2e, 0x00, 0x74, 0x00, 0x78, 0x00, 0x74, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xe4, 0x01, 0x00, 0x00,
|
||||
0x40, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x16, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xff, 0xff, 0xff, 0xff, 0x0a, 0x00, 0x00, 0x00,
|
||||
0x33, 0x00, 0x2e, 0x00, 0x74, 0x00, 0x78, 0x00, 0x74, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x10, 0x02, 0x00, 0x00, 0x60, 0x01, 0x00, 0x00,
|
||||
0x00, 0x00, 0x00, 0x00, 0x16, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xff, 0xff, 0xff, 0xff, 0x0a, 0x00, 0x00, 0x00, 0x34, 0x00, 0x2e, 0x00,
|
||||
0x74, 0x00, 0x78, 0x00, 0x74, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x3c, 0x02, 0x00, 0x00, 0x80, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
||||
0x16, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xff, 0xff, 0xff, 0xff, 0x0a, 0x00, 0x00, 0x00, 0x35, 0x00, 0x2e, 0x00, 0x74, 0x00, 0x78, 0x00,
|
||||
0x74, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x68, 0x02, 0x00, 0x00, 0xa0, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x16, 0x00, 0x00, 0x00,
|
||||
0x00, 0x00, 0x00, 0x00, 0xb0, 0x00, 0x00, 0x00, 0x0a, 0x00, 0x00, 0x00, 0x36, 0x00, 0x2e, 0x00, 0x74, 0x00, 0x78, 0x00, 0x74, 0x00, 0x00, 0x00,
|
||||
0x00, 0x00, 0x00, 0x00, 0x94, 0x02, 0x00, 0x00, 0xc0, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x16, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
||||
0xdc, 0x00, 0x00, 0x00, 0x0a, 0x00, 0x00, 0x00, 0x37, 0x00, 0x2e, 0x00, 0x74, 0x00, 0x78, 0x00, 0x74, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
||||
0xc0, 0x02, 0x00, 0x00, 0xe0, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x16, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xff, 0xff, 0xff, 0xff,
|
||||
0x0a, 0x00, 0x00, 0x00, 0x38, 0x00, 0x2e, 0x00, 0x74, 0x00, 0x78, 0x00, 0x74, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xec, 0x02, 0x00, 0x00,
|
||||
0x00, 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x16, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x08, 0x01, 0x00, 0x00, 0x0a, 0x00, 0x00, 0x00,
|
||||
0x39, 0x00, 0x2e, 0x00, 0x74, 0x00, 0x78, 0x00, 0x74, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xff, 0xff, 0xff, 0xff, 0x20, 0x02, 0x00, 0x00,
|
||||
0x00, 0x00, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xff, 0xff, 0xff, 0xff, 0x16, 0x00, 0x00, 0x00, 0x76, 0x00, 0x65, 0x00,
|
||||
0x72, 0x00, 0x73, 0x00, 0x69, 0x00, 0x6f, 0x00, 0x6e, 0x00, 0x2e, 0x00, 0x64, 0x00, 0x61, 0x00, 0x74, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
||||
0xff, 0xfe, 0x5e, 0x00, 0x62, 0x00, 0x61, 0x00, 0x64, 0x00, 0x77, 0x00, 0x6f, 0x00, 0x72, 0x00, 0x64, 0x00, 0x24, 0x00, 0x0a, 0x00, 0x00, 0x00,
|
||||
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xff, 0xfe, 0x5e, 0x00, 0x62, 0x00, 0x61, 0x00, 0x64, 0x00, 0x77, 0x00, 0x6f, 0x00, 0x72, 0x00,
|
||||
0x64, 0x00, 0x24, 0x00, 0x0a, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xff, 0xfe, 0x5e, 0x00, 0x62, 0x00, 0x61, 0x00,
|
||||
0x64, 0x00, 0x77, 0x00, 0x6f, 0x00, 0x72, 0x00, 0x64, 0x00, 0x24, 0x00, 0x0a, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
||||
0xff, 0xfe, 0x5e, 0x00, 0x62, 0x00, 0x61, 0x00, 0x64, 0x00, 0x77, 0x00, 0x6f, 0x00, 0x72, 0x00, 0x64, 0x00, 0x24, 0x00, 0x0a, 0x00, 0x00, 0x00,
|
||||
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xff, 0xfe, 0x5e, 0x00, 0x62, 0x00, 0x61, 0x00, 0x64, 0x00, 0x77, 0x00, 0x6f, 0x00, 0x72, 0x00,
|
||||
0x64, 0x00, 0x24, 0x00, 0x0a, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xff, 0xfe, 0x5e, 0x00, 0x62, 0x00, 0x61, 0x00,
|
||||
0x64, 0x00, 0x77, 0x00, 0x6f, 0x00, 0x72, 0x00, 0x64, 0x00, 0x24, 0x00, 0x0a, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
||||
0xff, 0xfe, 0x5e, 0x00, 0x62, 0x00, 0x61, 0x00, 0x64, 0x00, 0x77, 0x00, 0x6f, 0x00, 0x72, 0x00, 0x64, 0x00, 0x24, 0x00, 0x0a, 0x00, 0x00, 0x00,
|
||||
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xff, 0xfe, 0x5e, 0x00, 0x62, 0x00, 0x61, 0x00, 0x64, 0x00, 0x77, 0x00, 0x6f, 0x00, 0x72, 0x00,
|
||||
0x64, 0x00, 0x24, 0x00, 0x0a, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xff, 0xfe, 0x5e, 0x00, 0x62, 0x00, 0x61, 0x00,
|
||||
0x64, 0x00, 0x77, 0x00, 0x6f, 0x00, 0x72, 0x00, 0x64, 0x00, 0x24, 0x00, 0x0a, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
||||
0xff, 0xfe, 0x5e, 0x00, 0x62, 0x00, 0x61, 0x00, 0x64, 0x00, 0x77, 0x00, 0x6f, 0x00, 0x72, 0x00, 0x64, 0x00, 0x24, 0x00, 0x0a, 0x00, 0x00, 0x00,
|
||||
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xff, 0xfe, 0x5e, 0x00, 0x62, 0x00, 0x61, 0x00, 0x64, 0x00, 0x77, 0x00, 0x6f, 0x00, 0x72, 0x00,
|
||||
0x64, 0x00, 0x24, 0x00, 0x0a, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xff, 0xfe, 0x5e, 0x00, 0x62, 0x00, 0x61, 0x00,
|
||||
0x64, 0x00, 0x77, 0x00, 0x6f, 0x00, 0x72, 0x00, 0x64, 0x00, 0x24, 0x00, 0x0a, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
||||
0xff, 0xfe, 0x5e, 0x00, 0x62, 0x00, 0x61, 0x00, 0x64, 0x00, 0x77, 0x00, 0x6f, 0x00, 0x72, 0x00, 0x64, 0x00, 0x24, 0x00, 0x0a, 0x00, 0x00, 0x00,
|
||||
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xff, 0xfe, 0x5e, 0x00, 0x62, 0x00, 0x61, 0x00, 0x64, 0x00, 0x77, 0x00, 0x6f, 0x00, 0x72, 0x00,
|
||||
0x64, 0x00, 0x24, 0x00, 0x0a, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xff, 0xfe, 0x5e, 0x00, 0x62, 0x00, 0x61, 0x00,
|
||||
0x64, 0x00, 0x77, 0x00, 0x6f, 0x00, 0x72, 0x00, 0x64, 0x00, 0x24, 0x00, 0x0a, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
||||
0xff, 0xfe, 0x5e, 0x00, 0x62, 0x00, 0x61, 0x00, 0x64, 0x00, 0x77, 0x00, 0x6f, 0x00, 0x72, 0x00, 0x64, 0x00, 0x24, 0x00, 0x0a, 0x00, 0x00, 0x00,
|
||||
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xff, 0xfe, 0x5e, 0x00, 0x62, 0x00, 0x61, 0x00, 0x64, 0x00, 0x77, 0x00, 0x6f, 0x00, 0x72, 0x00,
|
||||
0x64, 0x00, 0x24, 0x00, 0x0a, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x12, 0x00, 0x00, 0x00
|
||||
};
|
||||
const unsigned int BAD_WORD_LIST_DATA_len = 1508;
|
File diff suppressed because it is too large
Load diff
File diff suppressed because it is too large
Load diff
|
@ -19,7 +19,7 @@ class Emulator;
|
|||
namespace httplib {
|
||||
class Server;
|
||||
struct Response;
|
||||
}
|
||||
} // namespace httplib
|
||||
|
||||
// Wrapper for httplib::Response that allows the HTTP server to wait for the response to be ready
|
||||
struct DeferredResponseWrapper {
|
||||
|
@ -63,7 +63,7 @@ struct HttpServer {
|
|||
std::thread httpServerThread;
|
||||
std::queue<std::unique_ptr<HttpAction>> actionQueue;
|
||||
std::mutex actionQueueMutex;
|
||||
std::unique_ptr<HttpAction> currentStepAction {};
|
||||
std::unique_ptr<HttpAction> currentStepAction{};
|
||||
|
||||
std::map<std::string, u32> keyMap;
|
||||
bool paused = false;
|
||||
|
|
File diff suppressed because it is too large
Load diff
|
@ -1,5 +1,10 @@
|
|||
#pragma once
|
||||
|
||||
#include <filesystem>
|
||||
#include <fstream>
|
||||
#include <map>
|
||||
#include <optional>
|
||||
#include <toml.hpp>
|
||||
#include <unordered_map>
|
||||
|
||||
#include "helpers.hpp"
|
||||
|
@ -15,8 +20,108 @@ struct InputMappings {
|
|||
}
|
||||
|
||||
void setMapping(Scancode scancode, u32 key) { container[scancode] = key; }
|
||||
|
||||
template <typename ScancodeToString>
|
||||
void serialize(const std::filesystem::path& path, const std::string& frontend, ScancodeToString scancodeToString) const {
|
||||
toml::basic_value<toml::preserve_comments, std::map> data;
|
||||
|
||||
std::error_code error;
|
||||
if (std::filesystem::exists(path, error)) {
|
||||
try {
|
||||
data = toml::parse<toml::preserve_comments, std::map>(path);
|
||||
} catch (const std::exception& ex) {
|
||||
Helpers::warn("Exception trying to parse mappings file. Exception: %s\n", ex.what());
|
||||
return;
|
||||
}
|
||||
} else {
|
||||
if (error) {
|
||||
Helpers::warn("Filesystem error accessing %s (error: %s)\n", path.string().c_str(), error.message().c_str());
|
||||
}
|
||||
printf("Saving new mappings file %s\n", path.string().c_str());
|
||||
}
|
||||
|
||||
data["Metadata"]["Name"] = name.empty() ? "Unnamed Mappings" : name;
|
||||
data["Metadata"]["Device"] = device.empty() ? "Unknown Device" : device;
|
||||
data["Metadata"]["Frontend"] = frontend;
|
||||
|
||||
data["Mappings"] = toml::table{};
|
||||
|
||||
for (const auto& [scancode, key] : container) {
|
||||
const std::string& keyName = HID::Keys::keyToName(key);
|
||||
if (!data["Mappings"].contains(keyName)) {
|
||||
data["Mappings"][keyName] = toml::array{};
|
||||
}
|
||||
data["Mappings"][keyName].push_back(scancodeToString(scancode));
|
||||
}
|
||||
|
||||
std::ofstream file(path, std::ios::out);
|
||||
file << data;
|
||||
}
|
||||
|
||||
template <typename ScancodeFromString>
|
||||
static std::optional<InputMappings> deserialize(
|
||||
const std::filesystem::path& path, const std::string& wantFrontend, ScancodeFromString stringToScancode
|
||||
) {
|
||||
toml::basic_value<toml::preserve_comments, std::map> data;
|
||||
std::error_code error;
|
||||
|
||||
if (!std::filesystem::exists(path, error)) {
|
||||
if (error) {
|
||||
Helpers::warn("Filesystem error accessing %s (error: %s)\n", path.string().c_str(), error.message().c_str());
|
||||
}
|
||||
|
||||
return std::nullopt;
|
||||
}
|
||||
|
||||
InputMappings mappings;
|
||||
|
||||
try {
|
||||
data = toml::parse<toml::preserve_comments, std::map>(path);
|
||||
|
||||
const auto metadata = toml::find(data, "Metadata");
|
||||
mappings.name = toml::find_or<std::string>(metadata, "Name", "Unnamed Mappings");
|
||||
mappings.device = toml::find_or<std::string>(metadata, "Device", "Unknown Device");
|
||||
|
||||
std::string haveFrontend = toml::find_or<std::string>(metadata, "Frontend", "Unknown Frontend");
|
||||
|
||||
bool equal = std::equal(haveFrontend.begin(), haveFrontend.end(), wantFrontend.begin(), wantFrontend.end(), [](char a, char b) {
|
||||
return std::tolower(a) == std::tolower(b);
|
||||
});
|
||||
|
||||
if (!equal) {
|
||||
Helpers::warn(
|
||||
"Mappings file %s was created for frontend %s, but we are using frontend %s\n", path.string().c_str(), haveFrontend.c_str(),
|
||||
wantFrontend.c_str()
|
||||
);
|
||||
|
||||
return std::nullopt;
|
||||
}
|
||||
} catch (const std::exception& ex) {
|
||||
Helpers::warn("Exception trying to parse config file. Exception: %s\n", ex.what());
|
||||
return std::nullopt;
|
||||
}
|
||||
|
||||
const auto& mappingsTable = toml::find_or<toml::table>(data, "Mappings", toml::table{});
|
||||
for (const auto& [keyName, scancodes] : mappingsTable) {
|
||||
for (const auto& scancodeVal : scancodes.as_array()) {
|
||||
std::string scancodeStr = scancodeVal.as_string();
|
||||
mappings.setMapping(stringToScancode(scancodeStr), HID::Keys::nameToKey(keyName));
|
||||
}
|
||||
}
|
||||
|
||||
return mappings;
|
||||
}
|
||||
|
||||
static InputMappings defaultKeyboardMappings();
|
||||
|
||||
auto begin() { return container.begin(); }
|
||||
auto end() { return container.end(); }
|
||||
|
||||
auto begin() const { return container.begin(); }
|
||||
auto end() const { return container.end(); }
|
||||
|
||||
private:
|
||||
Container container;
|
||||
std::string name;
|
||||
std::string device;
|
||||
};
|
||||
|
|
9
include/ios_driver.h
Normal file
9
include/ios_driver.h
Normal file
|
@ -0,0 +1,9 @@
|
|||
#pragma once
|
||||
#include <Foundation/Foundation.h>
|
||||
#include <QuartzCore/QuartzCore.h>
|
||||
#include <stdint.h>
|
||||
|
||||
void iosCreateEmulator();
|
||||
void iosLoadROM(NSString* pathNS);
|
||||
void iosRunFrame(CAMetalLayer* layer);
|
||||
void iosSetOutputSize(uint32_t width, uint32_t height);
|
|
@ -15,6 +15,7 @@
|
|||
#include "services/service_manager.hpp"
|
||||
|
||||
class CPU;
|
||||
class LuaManager;
|
||||
struct Scheduler;
|
||||
|
||||
class Kernel {
|
||||
|
@ -47,12 +48,12 @@ class Kernel {
|
|||
Handle currentProcess;
|
||||
Handle mainThread;
|
||||
int currentThreadIndex;
|
||||
Handle srvHandle; // Handle for the special service manager port "srv:"
|
||||
Handle errorPortHandle; // Handle for the err:f port used for displaying errors
|
||||
Handle srvHandle; // Handle for the special service manager port "srv:"
|
||||
Handle errorPortHandle; // Handle for the err:f port used for displaying errors
|
||||
|
||||
u32 arbiterCount;
|
||||
u32 threadCount; // How many threads in our thread pool have been used as of now (Up to 32)
|
||||
u32 aliveThreadCount; // How many of these threads are actually alive?
|
||||
u32 threadCount; // How many threads in our thread pool have been used as of now (Up to 32)
|
||||
u32 aliveThreadCount; // How many of these threads are actually alive?
|
||||
ServiceManager serviceManager;
|
||||
|
||||
// Top 8 bits are the major version, bottom 8 are the minor version
|
||||
|
@ -65,10 +66,10 @@ class Kernel {
|
|||
Handle makeProcess(u32 id);
|
||||
Handle makePort(const char* name);
|
||||
Handle makeSession(Handle port);
|
||||
Handle makeThread(u32 entrypoint, u32 initialSP, u32 priority, ProcessorID id, u32 arg,ThreadStatus status = ThreadStatus::Dormant);
|
||||
Handle makeThread(u32 entrypoint, u32 initialSP, u32 priority, ProcessorID id, u32 arg, ThreadStatus status = ThreadStatus::Dormant);
|
||||
Handle makeMemoryBlock(u32 addr, u32 size, u32 myPermission, u32 otherPermission);
|
||||
|
||||
public:
|
||||
public:
|
||||
// Needs to be public to be accessible to the APT/HID services
|
||||
Handle makeEvent(ResetType resetType, Event::CallbackType callback = Event::CallbackType::None);
|
||||
// Needs to be public to be accessible to the APT/DSP services
|
||||
|
@ -175,6 +176,8 @@ public:
|
|||
void svcSignalEvent();
|
||||
void svcSetTimer();
|
||||
void svcSleepThread();
|
||||
void svcInvalidateInstructionCacheRange();
|
||||
void svcInvalidateEntireInstructionCache();
|
||||
void connectToPort();
|
||||
void outputDebugString();
|
||||
void waitSynchronization1();
|
||||
|
@ -196,8 +199,8 @@ public:
|
|||
void closeDirectory(u32 messagePointer, Handle directory);
|
||||
void readDirectory(u32 messagePointer, Handle directory);
|
||||
|
||||
public:
|
||||
Kernel(CPU& cpu, Memory& mem, GPU& gpu, const EmulatorConfig& config);
|
||||
public:
|
||||
Kernel(CPU& cpu, Memory& mem, GPU& gpu, const EmulatorConfig& config, LuaManager& lua);
|
||||
void initializeFS() { return serviceManager.initializeFS(); }
|
||||
void setVersion(u8 major, u8 minor);
|
||||
void serviceSVC(u32 svc);
|
||||
|
@ -222,9 +225,7 @@ public:
|
|||
return handleCounter++;
|
||||
}
|
||||
|
||||
std::vector<KernelObject>& getObjects() {
|
||||
return objects;
|
||||
}
|
||||
std::vector<KernelObject>& getObjects() { return objects; }
|
||||
|
||||
// Get pointer to the object with the specified handle
|
||||
KernelObject* getObject(Handle handle) {
|
||||
|
@ -250,4 +251,16 @@ public:
|
|||
|
||||
void sendGPUInterrupt(GPUInterrupt type) { serviceManager.sendGPUInterrupt(type); }
|
||||
void clearInstructionCache();
|
||||
void clearInstructionCacheRange(u32 start, u32 size);
|
||||
u32 getSharedFontVaddr();
|
||||
|
||||
// For debuggers: Returns information about the main process' (alive) threads in a vector for the frontend to display
|
||||
std::vector<Thread> getMainProcessThreads();
|
||||
|
||||
// For debuggers: Sets the entrypoint and initial SP for the main thread (thread 0) so that the debugger can display them
|
||||
void setMainThreadEntrypointAndSP(u32 entrypoint, u32 initialSP) {
|
||||
auto& t = threads[0];
|
||||
t.entrypoint = entrypoint;
|
||||
t.initialSP = initialSP;
|
||||
}
|
||||
};
|
|
@ -19,7 +19,7 @@ struct ResourceLimitValues {
|
|||
// APPLICATION resource limit
|
||||
static constexpr ResourceLimitValues appResourceLimits = {
|
||||
.maxPriority = 0x18,
|
||||
.maxCommit = 0x4000000,
|
||||
.maxCommit = 64_MB + 16_MB, // We're currently giving 80MB to all apps. TODO: Implement extended memory properly
|
||||
.maxThreads = 0x20,
|
||||
.maxEvents = 0x20,
|
||||
.maxMutexes = 0x20,
|
||||
|
@ -33,7 +33,7 @@ static constexpr ResourceLimitValues appResourceLimits = {
|
|||
// SYS_APPLET resource limit
|
||||
static constexpr ResourceLimitValues sysAppletResourceLimits = {
|
||||
.maxPriority = 0x4,
|
||||
.maxCommit = 0x5E00000,
|
||||
.maxCommit = 0x5E00000 - 16_MB,
|
||||
.maxThreads = 0x1D,
|
||||
.maxEvents = 0xB,
|
||||
.maxMutexes = 0x8,
|
||||
|
|
|
@ -47,6 +47,9 @@ class LuaManager {
|
|||
signalEventInternal(e);
|
||||
}
|
||||
}
|
||||
|
||||
bool signalInterceptedService(const std::string& service, u32 function, u32 messagePointer, int callbackRef);
|
||||
void removeInterceptedService(const std::string& service, u32 function, int callbackRef);
|
||||
};
|
||||
|
||||
#else // Lua not enabled, Lua manager does nothing
|
||||
|
@ -60,5 +63,7 @@ class LuaManager {
|
|||
void loadString(const std::string& code) {}
|
||||
void reset() {}
|
||||
void signalEvent(LuaEvent e) {}
|
||||
bool signalInterceptedService(const std::string& service, u32 function, u32 messagePointer, int callbackRef) { return false; }
|
||||
void removeInterceptedService(const std::string& service, u32 function, int callbackRef) {}
|
||||
};
|
||||
#endif
|
||||
|
|
|
@ -132,7 +132,7 @@ public:
|
|||
static constexpr u32 totalPageCount = 1 << (32 - pageShift);
|
||||
|
||||
static constexpr u32 FCRAM_SIZE = u32(128_MB);
|
||||
static constexpr u32 FCRAM_APPLICATION_SIZE = u32(64_MB);
|
||||
static constexpr u32 FCRAM_APPLICATION_SIZE = u32(80_MB);
|
||||
static constexpr u32 FCRAM_PAGE_COUNT = FCRAM_SIZE / pageSize;
|
||||
static constexpr u32 FCRAM_APPLICATION_PAGE_COUNT = FCRAM_APPLICATION_SIZE / pageSize;
|
||||
|
||||
|
|
|
@ -13,4 +13,4 @@ namespace Helpers {
|
|||
static constexpr void static_for(Func&& f) {
|
||||
static_for_impl<T, Begin>(std::forward<Func>(f), std::make_integer_sequence<T, End - Begin>{});
|
||||
}
|
||||
}
|
||||
} // namespace Helpers
|
|
@ -16,6 +16,7 @@
|
|||
|
||||
#include "emulator.hpp"
|
||||
#include "frontend_settings.hpp"
|
||||
#include "panda_qt/input_window.hpp"
|
||||
|
||||
class ConfigWindow : public QDialog {
|
||||
Q_OBJECT
|
||||
|
@ -30,8 +31,9 @@ class ConfigWindow : public QDialog {
|
|||
QTextEdit* helpText = nullptr;
|
||||
QListWidget* widgetList = nullptr;
|
||||
QStackedWidget* widgetContainer = nullptr;
|
||||
InputWindow* inputWindow = nullptr;
|
||||
|
||||
static constexpr size_t settingWidgetCount = 6;
|
||||
static constexpr size_t settingWidgetCount = 7;
|
||||
std::array<QString, settingWidgetCount> helpTexts;
|
||||
|
||||
// The config class holds a copy of the emulator config which it edits and sends
|
||||
|
@ -52,6 +54,7 @@ class ConfigWindow : public QDialog {
|
|||
~ConfigWindow();
|
||||
|
||||
EmulatorConfig& getConfig() { return config; }
|
||||
InputWindow* getInputWindow() { return inputWindow; }
|
||||
|
||||
private:
|
||||
Emulator* emu;
|
||||
|
|
45
include/panda_qt/cpu_debugger.hpp
Normal file
45
include/panda_qt/cpu_debugger.hpp
Normal file
|
@ -0,0 +1,45 @@
|
|||
#pragma once
|
||||
#include <QLineEdit>
|
||||
#include <QListWidget>
|
||||
#include <QPlainTextEdit>
|
||||
#include <QScrollBar>
|
||||
#include <QWidget>
|
||||
|
||||
#include "capstone.hpp"
|
||||
#include "emulator.hpp"
|
||||
#include "panda_qt/disabled_widget_overlay.hpp"
|
||||
|
||||
class CPUDebugger : public QWidget {
|
||||
Q_OBJECT
|
||||
Emulator* emu;
|
||||
|
||||
QListWidget* disasmListWidget;
|
||||
QScrollBar* verticalScrollBar;
|
||||
QPlainTextEdit* registerTextEdit;
|
||||
QTimer* updateTimer;
|
||||
QLineEdit* addressInput;
|
||||
|
||||
DisabledWidgetOverlay* disabledOverlay;
|
||||
|
||||
bool enabled = false;
|
||||
bool followPC = false;
|
||||
Common::CapstoneDisassembler disassembler;
|
||||
|
||||
public:
|
||||
CPUDebugger(Emulator* emulator, QWidget* parent = nullptr);
|
||||
void enable();
|
||||
void disable();
|
||||
|
||||
private:
|
||||
// Update the state of the disassembler. Qt events should always call update, not updateDisasm/updateRegister
|
||||
// As update properly handles thread safety
|
||||
void update();
|
||||
void updateDisasm();
|
||||
void updateRegisters();
|
||||
void scrollToPC();
|
||||
|
||||
bool eventFilter(QObject* obj, QEvent* event) override;
|
||||
void showEvent(QShowEvent* event) override;
|
||||
void resizeEvent(QResizeEvent* event) override;
|
||||
void keyPressEvent(QKeyEvent* event) override;
|
||||
};
|
28
include/panda_qt/disabled_widget_overlay.hpp
Normal file
28
include/panda_qt/disabled_widget_overlay.hpp
Normal file
|
@ -0,0 +1,28 @@
|
|||
#pragma once
|
||||
|
||||
#include <QtWidgets>
|
||||
|
||||
class DisabledWidgetOverlay : public QWidget {
|
||||
Q_OBJECT
|
||||
|
||||
public:
|
||||
DisabledWidgetOverlay(QWidget *parent = nullptr, QString overlayText = tr("This widget is disabled")) : text(overlayText), QWidget(parent) {
|
||||
setVisible(false);
|
||||
}
|
||||
|
||||
private:
|
||||
QString text;
|
||||
|
||||
void paintEvent(QPaintEvent *) override {
|
||||
QPainter painter = QPainter(this);
|
||||
painter.fillRect(rect(), QColor(60, 60, 60, 128));
|
||||
painter.setPen(Qt::gray);
|
||||
|
||||
QFont font = painter.font();
|
||||
font.setBold(true);
|
||||
font.setPointSize(18);
|
||||
|
||||
painter.setFont(font);
|
||||
painter.drawText(rect(), Qt::AlignCenter, text);
|
||||
}
|
||||
};
|
47
include/panda_qt/dsp_debugger.hpp
Normal file
47
include/panda_qt/dsp_debugger.hpp
Normal file
|
@ -0,0 +1,47 @@
|
|||
#pragma once
|
||||
#include <QLineEdit>
|
||||
#include <QListWidget>
|
||||
#include <QPlainTextEdit>
|
||||
#include <QScrollBar>
|
||||
#include <QWidget>
|
||||
|
||||
#include "emulator.hpp"
|
||||
#include "panda_qt/disabled_widget_overlay.hpp"
|
||||
|
||||
class DSPDebugger : public QWidget {
|
||||
Q_OBJECT
|
||||
Emulator* emu;
|
||||
|
||||
QListWidget* disasmListWidget;
|
||||
QScrollBar* verticalScrollBar;
|
||||
QPlainTextEdit* registerTextEdit;
|
||||
QTimer* updateTimer;
|
||||
QLineEdit* addressInput;
|
||||
|
||||
DisabledWidgetOverlay* disabledOverlay;
|
||||
DisabledWidgetOverlay* disabledRegisterEditOverlay;
|
||||
|
||||
bool enabled = false;
|
||||
bool followPC = false;
|
||||
|
||||
public:
|
||||
DSPDebugger(Emulator* emulator, QWidget* parent = nullptr);
|
||||
void enable();
|
||||
void disable();
|
||||
|
||||
private:
|
||||
// Get the full PC value of the DSP, including the current progrma page value
|
||||
u32 getPC();
|
||||
|
||||
// Update the state of the disassembler. Qt events should always call update, not updateDisasm/updateRegister
|
||||
// As update properly handles thread safety
|
||||
void update();
|
||||
void updateDisasm();
|
||||
void updateRegisters();
|
||||
void scrollToPC();
|
||||
|
||||
bool eventFilter(QObject* obj, QEvent* event) override;
|
||||
void showEvent(QShowEvent* event) override;
|
||||
void resizeEvent(QResizeEvent* event) override;
|
||||
void keyPressEvent(QKeyEvent* event) override;
|
||||
};
|
32
include/panda_qt/input_window.hpp
Normal file
32
include/panda_qt/input_window.hpp
Normal file
|
@ -0,0 +1,32 @@
|
|||
#pragma once
|
||||
|
||||
#include <QDialog>
|
||||
#include <QKeySequence>
|
||||
#include <QMap>
|
||||
#include <QPushButton>
|
||||
|
||||
#include "input_mappings.hpp"
|
||||
|
||||
class InputWindow : public QDialog {
|
||||
Q_OBJECT
|
||||
|
||||
public:
|
||||
InputWindow(QWidget* parent = nullptr);
|
||||
|
||||
void loadFromMappings(const InputMappings& mappings);
|
||||
void applyToMappings(InputMappings& mappings) const;
|
||||
|
||||
signals:
|
||||
void mappingsChanged();
|
||||
|
||||
protected:
|
||||
bool eventFilter(QObject* obj, QEvent* event) override;
|
||||
|
||||
private:
|
||||
QMap<QString, QPushButton*> buttonMap;
|
||||
QMap<QString, QKeySequence> keyMappings;
|
||||
|
||||
QString waitingForAction;
|
||||
|
||||
void startKeyCapture(const QString& action);
|
||||
};
|
|
@ -17,10 +17,13 @@
|
|||
#include "panda_qt/about_window.hpp"
|
||||
#include "panda_qt/cheats_window.hpp"
|
||||
#include "panda_qt/config_window.hpp"
|
||||
#include "panda_qt/cpu_debugger.hpp"
|
||||
#include "panda_qt/dsp_debugger.hpp"
|
||||
#include "panda_qt/patch_window.hpp"
|
||||
#include "panda_qt/screen.hpp"
|
||||
#include "panda_qt/shader_editor.hpp"
|
||||
#include "panda_qt/text_editor.hpp"
|
||||
#include "panda_qt/thread_debugger.hpp"
|
||||
#include "services/hid.hpp"
|
||||
|
||||
struct CheatMessage {
|
||||
|
@ -40,7 +43,6 @@ class MainWindow : public QMainWindow {
|
|||
Pause,
|
||||
Resume,
|
||||
TogglePause,
|
||||
DumpRomFS,
|
||||
PressKey,
|
||||
ReleaseKey,
|
||||
SetCirclePadX,
|
||||
|
@ -109,6 +111,9 @@ class MainWindow : public QMainWindow {
|
|||
TextEditorWindow* luaEditor;
|
||||
PatchWindow* patchWindow;
|
||||
ShaderEditorWindow* shaderEditor;
|
||||
CPUDebugger* cpuDebugger;
|
||||
DSPDebugger* dspDebugger;
|
||||
ThreadDebugger* threadDebugger;
|
||||
|
||||
// We use SDL's game controller API since it's the sanest API that supports as many controllers as possible
|
||||
SDL_GameController* gameController = nullptr;
|
||||
|
@ -128,6 +133,9 @@ class MainWindow : public QMainWindow {
|
|||
void dispatchMessage(const EmulatorMessage& message);
|
||||
void loadTranslation();
|
||||
|
||||
void loadKeybindings();
|
||||
void saveKeybindings();
|
||||
|
||||
// Tracks whether we are using an OpenGL-backed renderer or a Vulkan-backed renderer
|
||||
bool usingGL = false;
|
||||
bool usingVk = false;
|
||||
|
@ -139,6 +147,9 @@ class MainWindow : public QMainWindow {
|
|||
bool keyboardAnalogX = false;
|
||||
bool keyboardAnalogY = false;
|
||||
|
||||
// Tracks if keybindings changed, in which case we should update the keybindings file when closing the emulator
|
||||
bool keybindingsChanged = false;
|
||||
|
||||
public:
|
||||
MainWindow(QApplication* app, QWidget* parent = nullptr);
|
||||
~MainWindow();
|
||||
|
@ -157,4 +168,7 @@ class MainWindow : public QMainWindow {
|
|||
|
||||
void handleScreenResize(u32 width, u32 height);
|
||||
void handleTouchscreenPress(QMouseEvent* event);
|
||||
|
||||
signals:
|
||||
void emulatorPaused();
|
||||
};
|
||||
|
|
|
@ -4,6 +4,7 @@
|
|||
#include <memory>
|
||||
|
||||
#include "gl/context.h"
|
||||
#include "screen_layout.hpp"
|
||||
#include "window_info.h"
|
||||
|
||||
// OpenGL widget for drawing the 3DS screen
|
||||
|
@ -29,6 +30,15 @@ class ScreenWidget : public QWidget {
|
|||
u32 previousWidth = 0;
|
||||
u32 previousHeight = 0;
|
||||
|
||||
// Coordinates (x/y/width/height) for the two screens in window space, used for properly handling touchscreen regardless
|
||||
// of layout or resizing
|
||||
ScreenLayout::WindowCoordinates screenCoordinates;
|
||||
// Screen layouts and sizes
|
||||
ScreenLayout::Layout screenLayout = ScreenLayout::Layout::Default;
|
||||
float topScreenSize = 0.5f;
|
||||
|
||||
void reloadScreenLayout(ScreenLayout::Layout newLayout, float newTopScreenSize);
|
||||
|
||||
private:
|
||||
std::unique_ptr<GL::Context> glContext = nullptr;
|
||||
ResizeCallback resizeCallback;
|
||||
|
@ -39,4 +49,6 @@ class ScreenWidget : public QWidget {
|
|||
int scaledWindowWidth() const;
|
||||
int scaledWindowHeight() const;
|
||||
std::optional<WindowInfo> getWindowInfo();
|
||||
|
||||
void reloadScreenCoordinates();
|
||||
};
|
||||
|
|
25
include/panda_qt/thread_debugger.hpp
Normal file
25
include/panda_qt/thread_debugger.hpp
Normal file
|
@ -0,0 +1,25 @@
|
|||
#pragma once
|
||||
|
||||
#include <QString>
|
||||
#include <QTableWidget>
|
||||
#include <QTimer>
|
||||
#include <QVBoxLayout>
|
||||
#include <QtWidgets>
|
||||
|
||||
#include "emulator.hpp"
|
||||
|
||||
class ThreadDebugger : public QWidget {
|
||||
Q_OBJECT
|
||||
Emulator* emu;
|
||||
|
||||
QVBoxLayout* mainLayout;
|
||||
QTableWidget* threadTable;
|
||||
|
||||
public:
|
||||
ThreadDebugger(Emulator* emu, QWidget* parent = nullptr);
|
||||
void update();
|
||||
|
||||
private:
|
||||
void setListItem(int row, int column, const QString& str);
|
||||
void setListItem(int row, int column, const std::string& str);
|
||||
};
|
|
@ -6,6 +6,7 @@
|
|||
|
||||
#include "emulator.hpp"
|
||||
#include "input_mappings.hpp"
|
||||
#include "screen_layout.hpp"
|
||||
|
||||
class FrontendSDL {
|
||||
Emulator emu;
|
||||
|
@ -27,7 +28,11 @@ class FrontendSDL {
|
|||
u32 windowHeight = 480;
|
||||
int gameControllerID;
|
||||
bool programRunning = true;
|
||||
|
||||
|
||||
// Coordinates (x/y/width/height) for the two screens in window space, used for properly handling touchscreen regardless
|
||||
// of layout or resizing
|
||||
ScreenLayout::WindowCoordinates screenCoordinates;
|
||||
|
||||
// For tracking whether to update gyroscope
|
||||
// We bind gyro to right click + mouse movement
|
||||
bool holdingRightClick = false;
|
||||
|
@ -38,5 +43,7 @@ class FrontendSDL {
|
|||
bool keyboardAnalogX = false;
|
||||
bool keyboardAnalogY = false;
|
||||
|
||||
private:
|
||||
void setupControllerSensors(SDL_GameController* controller);
|
||||
void handleLeftClick(int mouseX, int mouseY);
|
||||
};
|
|
@ -51,8 +51,14 @@ class Renderer {
|
|||
u32 outputWindowWidth = 400;
|
||||
u32 outputWindowHeight = 240 * 2;
|
||||
|
||||
// Should hw renderers hash textures? Stored separately from emulatorConfig because we'll be accessing it constantly, might be merged eventually
|
||||
bool hashTextures = false;
|
||||
bool outputSizeChanged = true;
|
||||
|
||||
EmulatorConfig* emulatorConfig = nullptr;
|
||||
|
||||
void doSoftwareTextureCopy(u32 inputAddr, u32 outputAddr, u32 copySize, u32 inputWidth, u32 inputGap, u32 outputWidth, u32 outputGap);
|
||||
|
||||
public:
|
||||
Renderer(GPU& gpu, const std::array<u32, regNum>& internalRegs, const std::array<u32, extRegNum>& externalRegs);
|
||||
virtual ~Renderer();
|
||||
|
@ -85,6 +91,10 @@ class Renderer {
|
|||
// Called to notify the core to use OpenGL ES and not desktop GL
|
||||
virtual void setupGLES() {}
|
||||
|
||||
// Only relevant for Metal renderer on iOS
|
||||
// Passes a SwiftUI MTKView's layer (CAMetalLayer) to the renderer
|
||||
virtual void setMTKLayer(void* layer) {};
|
||||
|
||||
// This function is called on every draw call before parsing vertex data.
|
||||
// It is responsible for things like looking up which vertex/fragment shaders to use, recompiling them if they don't exist, choosing between
|
||||
// ubershaders and shadergen, and so on.
|
||||
|
@ -113,9 +123,12 @@ class Renderer {
|
|||
void setDepthBufferLoc(u32 loc) { depthBufferLoc = loc; }
|
||||
|
||||
void setOutputSize(u32 width, u32 height) {
|
||||
outputSizeChanged = true;
|
||||
outputWindowWidth = width;
|
||||
outputWindowHeight = height;
|
||||
}
|
||||
|
||||
void setConfig(EmulatorConfig* config) { emulatorConfig = config; }
|
||||
void setHashTextures(bool setting) { hashTextures = setting; }
|
||||
void reloadScreenLayout() { outputSizeChanged = true; }
|
||||
};
|
||||
|
|
|
@ -35,7 +35,7 @@ struct GLStateManager {
|
|||
bool depthMask;
|
||||
|
||||
float clearRed, clearBlue, clearGreen, clearAlpha;
|
||||
|
||||
|
||||
GLuint stencilMask;
|
||||
GLuint boundVAO;
|
||||
GLuint currentProgram;
|
||||
|
@ -241,13 +241,12 @@ struct GLStateManager {
|
|||
void setBlendFunc(GLenum sourceRGB, GLenum destRGB, GLenum sourceAlpha, GLenum destAlpha) {
|
||||
if (blendFuncSourceRGB != sourceRGB || blendFuncDestRGB != destRGB || blendFuncSourceAlpha != sourceAlpha ||
|
||||
blendFuncDestAlpha != destAlpha) {
|
||||
|
||||
blendFuncSourceRGB = sourceRGB;
|
||||
blendFuncDestRGB = destRGB;
|
||||
blendFuncSourceAlpha = sourceAlpha;
|
||||
blendFuncDestAlpha = destAlpha;
|
||||
|
||||
glBlendFuncSeparate(sourceRGB, destRGB,sourceAlpha, destAlpha);
|
||||
glBlendFuncSeparate(sourceRGB, destRGB, sourceAlpha, destAlpha);
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -40,7 +40,7 @@ class RendererGL final : public Renderer {
|
|||
OpenGL::VertexArray hwShaderVAO;
|
||||
OpenGL::VertexBuffer vbo;
|
||||
|
||||
// Data
|
||||
// Data that will be uploaded to the ubershader
|
||||
struct {
|
||||
// TEV configuration uniform locations
|
||||
GLint textureEnvSourceLoc = -1;
|
||||
|
@ -146,6 +146,27 @@ class RendererGL final : public Renderer {
|
|||
PICA::ShaderGen::FragmentGenerator fragShaderGen;
|
||||
OpenGL::Driver driverInfo;
|
||||
|
||||
// Information about the final 3DS screen -> Window blit, accounting for things like scaling and shifting the output based on
|
||||
// the window's dimensions. Updated whenever the screen size or layout changes.
|
||||
struct {
|
||||
int topScreenX = 0;
|
||||
int topScreenY = 0;
|
||||
int topScreenWidth = 400;
|
||||
int topScreenHeight = 240;
|
||||
|
||||
int bottomScreenX = 40;
|
||||
int bottomScreenY = 240;
|
||||
int bottomScreenWidth = 320;
|
||||
int bottomScreenHeight = 240;
|
||||
|
||||
// For optimizing the final screen blit into a single blit instead of 2 when possible:
|
||||
int destX = 0;
|
||||
int destY = 0;
|
||||
int destWidth = 400;
|
||||
int destHeight = 480;
|
||||
bool canDoSingleBlit = true;
|
||||
} blitInfo;
|
||||
|
||||
MAKE_LOG_FUNCTION(log, rendererLogger)
|
||||
void setupBlending();
|
||||
void setupStencilTest(bool stencilEnable);
|
||||
|
|
|
@ -1,6 +1,9 @@
|
|||
#pragma once
|
||||
#include <array>
|
||||
#include <functional>
|
||||
#include <map>
|
||||
#include <optional>
|
||||
|
||||
#include "surfaces.hpp"
|
||||
#include "textures.hpp"
|
||||
|
||||
|
@ -17,41 +20,69 @@
|
|||
// - A "location" member which tells us which location in 3DS memory this surface occupies
|
||||
template <typename SurfaceType, size_t capacity, bool evictOnOverflow = false>
|
||||
class SurfaceCache {
|
||||
// Vanilla std::optional can't hold actual references
|
||||
using OptionalRef = std::optional<std::reference_wrapper<SurfaceType>>;
|
||||
// Vanilla std::optional can't hold actual references
|
||||
using OptionalRef = std::optional<std::reference_wrapper<SurfaceType>>;
|
||||
|
||||
size_t size;
|
||||
size_t evictionIndex;
|
||||
std::array<SurfaceType, capacity> buffer;
|
||||
size_t size = 0;
|
||||
size_t evictionIndex = 0;
|
||||
std::array<SurfaceType, capacity> buffer;
|
||||
|
||||
public:
|
||||
void reset() {
|
||||
size = 0;
|
||||
evictionIndex = 0;
|
||||
for (auto& e : buffer) { // Free the VRAM of all surfaces
|
||||
e.free();
|
||||
}
|
||||
}
|
||||
// Map from address to a surface in the above buffer.
|
||||
// Several cached surfaces may have the same starting address, so we use a multimap.
|
||||
std::multimap<u32, SurfaceType*> surfaceMap;
|
||||
|
||||
OptionalRef find(SurfaceType& other) {
|
||||
for (auto& e : buffer) {
|
||||
if (e.matches(other) && e.valid)
|
||||
return e;
|
||||
}
|
||||
// Adds a surface to our map
|
||||
void indexSurface(SurfaceType& surface) { surfaceMap.emplace(surface.location, &surface); }
|
||||
|
||||
return std::nullopt;
|
||||
}
|
||||
// Removes a surface from our map
|
||||
void unindexSurface(SurfaceType& surface) {
|
||||
auto range = surfaceMap.equal_range(surface.location);
|
||||
for (auto it = range.first; it != range.second; ++it) {
|
||||
if (it->second == &surface) {
|
||||
surfaceMap.erase(it);
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
OptionalRef findFromAddress(u32 address) {
|
||||
for (auto& e : buffer) {
|
||||
if (e.location <= address && e.location + e.sizeInBytes() > address && e.valid)
|
||||
return e;
|
||||
}
|
||||
public:
|
||||
void reset() {
|
||||
size = 0;
|
||||
evictionIndex = 0;
|
||||
surfaceMap.clear();
|
||||
|
||||
return std::nullopt;
|
||||
}
|
||||
// Free the memory of all surfaces
|
||||
for (auto& e : buffer) {
|
||||
e.free();
|
||||
e.valid = false;
|
||||
}
|
||||
}
|
||||
|
||||
// Adds a surface object to the cache and returns it
|
||||
// Use our map to only scan the surfaces with the same starting location
|
||||
OptionalRef find(SurfaceType& other) {
|
||||
auto range = surfaceMap.equal_range(other.location);
|
||||
for (auto it = range.first; it != range.second; ++it) {
|
||||
SurfaceType* candidate = it->second;
|
||||
if (candidate->valid && candidate->matches(other)) {
|
||||
return *candidate;
|
||||
}
|
||||
}
|
||||
|
||||
return std::nullopt;
|
||||
}
|
||||
|
||||
OptionalRef findFromAddress(u32 address) {
|
||||
for (auto it = surfaceMap.begin(); it != surfaceMap.end(); ++it) {
|
||||
SurfaceType* surface = it->second;
|
||||
if (surface->valid && surface->location <= address && surface->location + surface->sizeInBytes() > address) {
|
||||
return *surface;
|
||||
}
|
||||
}
|
||||
|
||||
return std::nullopt;
|
||||
}
|
||||
|
||||
// Adds a surface object to the cache and returns it
|
||||
SurfaceType& add(const SurfaceType& surface) {
|
||||
if (size >= capacity) {
|
||||
if constexpr (evictOnOverflow) { // Do a ring buffer if evictOnOverflow is true
|
||||
|
@ -60,12 +91,14 @@ public:
|
|||
}
|
||||
|
||||
auto& e = buffer[evictionIndex];
|
||||
unindexSurface(e);
|
||||
evictionIndex = (evictionIndex + 1) % capacity;
|
||||
|
||||
e.valid = false;
|
||||
e.free();
|
||||
e = surface;
|
||||
e.allocate();
|
||||
indexSurface(e);
|
||||
return e;
|
||||
} else {
|
||||
Helpers::panic("Surface cache full! Add emptying!");
|
||||
|
@ -74,12 +107,14 @@ public:
|
|||
|
||||
size++;
|
||||
|
||||
// Find an existing surface we completely invalidate and overwrite it with the new surface
|
||||
// See if any existing surface fully overlaps
|
||||
for (auto& e : buffer) {
|
||||
if (e.valid && e.range.lower() >= surface.range.lower() && e.range.upper() <= surface.range.upper()) {
|
||||
unindexSurface(e);
|
||||
e.free();
|
||||
e = surface;
|
||||
e.allocate();
|
||||
indexSurface(e);
|
||||
return e;
|
||||
}
|
||||
}
|
||||
|
@ -89,6 +124,7 @@ public:
|
|||
if (!e.valid) {
|
||||
e = surface;
|
||||
e.allocate();
|
||||
indexSurface(e);
|
||||
return e;
|
||||
}
|
||||
}
|
||||
|
@ -97,11 +133,6 @@ public:
|
|||
Helpers::panic("Couldn't add surface to cache\n");
|
||||
}
|
||||
|
||||
SurfaceType& operator[](size_t i) {
|
||||
return buffer[i];
|
||||
}
|
||||
|
||||
const SurfaceType& operator[](size_t i) const {
|
||||
return buffer[i];
|
||||
}
|
||||
};
|
||||
SurfaceType& operator[](size_t i) { return buffer[i]; }
|
||||
const SurfaceType& operator[](size_t i) const { return buffer[i]; }
|
||||
};
|
|
@ -133,7 +133,7 @@ struct DepthBuffer {
|
|||
GL_DEPTH_COMPONENT,
|
||||
GL_DEPTH_STENCIL,
|
||||
};
|
||||
|
||||
|
||||
static constexpr std::array<GLenum, 4> types = {
|
||||
GL_UNSIGNED_SHORT,
|
||||
GL_UNSIGNED_INT,
|
||||
|
@ -172,4 +172,4 @@ struct DepthBuffer {
|
|||
size_t sizeInBytes() {
|
||||
return (size_t)size.x() * (size_t)size.y() * PICA::sizePerPixel(format);
|
||||
}
|
||||
};
|
||||
};
|
|
@ -1,6 +1,8 @@
|
|||
#pragma once
|
||||
#include <array>
|
||||
#include <string>
|
||||
|
||||
#include "PICA/pica_hash.hpp"
|
||||
#include "PICA/regs.hpp"
|
||||
#include "boost/icl/interval.hpp"
|
||||
#include "helpers.hpp"
|
||||
|
@ -11,55 +13,55 @@ template <typename T>
|
|||
using Interval = boost::icl::right_open_interval<T>;
|
||||
|
||||
struct Texture {
|
||||
u32 location;
|
||||
u32 config; // Magnification/minification filter, wrapping configs, etc
|
||||
PICA::TextureFmt format;
|
||||
OpenGL::uvec2 size;
|
||||
bool valid;
|
||||
using Hash = PICAHash::HashType;
|
||||
|
||||
// Range of VRAM taken up by buffer
|
||||
Interval<u32> range;
|
||||
// OpenGL resources allocated to buffer
|
||||
OpenGL::Texture texture;
|
||||
u32 location;
|
||||
u32 config; // Magnification/minification filter, wrapping configs, etc
|
||||
Hash hash = Hash(0);
|
||||
|
||||
Texture() : valid(false) {}
|
||||
PICA::TextureFmt format;
|
||||
OpenGL::uvec2 size;
|
||||
bool valid;
|
||||
|
||||
Texture(u32 loc, PICA::TextureFmt format, u32 x, u32 y, u32 config, bool valid = true)
|
||||
: location(loc), format(format), size({x, y}), config(config), valid(valid) {
|
||||
// Range of VRAM taken up by buffer
|
||||
Interval<u32> range;
|
||||
// OpenGL resources allocated to buffer
|
||||
OpenGL::Texture texture;
|
||||
|
||||
u64 endLoc = (u64)loc + sizeInBytes();
|
||||
// Check if start and end are valid here
|
||||
range = Interval<u32>(loc, (u32)endLoc);
|
||||
}
|
||||
Texture() : valid(false) {}
|
||||
|
||||
// For 2 textures to "match" we only care about their locations, formats, and dimensions to match
|
||||
// For other things, such as filtering mode, etc, we can just switch the attributes of the cached texture
|
||||
bool matches(Texture& other) {
|
||||
return location == other.location && format == other.format &&
|
||||
size.x() == other.size.x() && size.y() == other.size.y();
|
||||
}
|
||||
Texture(u32 loc, PICA::TextureFmt format, u32 x, u32 y, u32 config, bool valid = true)
|
||||
: location(loc), format(format), size({x, y}), config(config), valid(valid) {
|
||||
u64 endLoc = (u64)loc + sizeInBytes();
|
||||
// Check if start and end are valid here
|
||||
range = Interval<u32>(loc, (u32)endLoc);
|
||||
}
|
||||
|
||||
void allocate();
|
||||
void setNewConfig(u32 newConfig);
|
||||
void decodeTexture(std::span<const u8> data);
|
||||
void free();
|
||||
u64 sizeInBytes();
|
||||
// For 2 textures to "match" we only care about their locations, formats, and dimensions to match
|
||||
// For other things, such as filtering mode, etc, we can just switch the attributes of the cached texture
|
||||
bool matches(Texture& other) {
|
||||
return location == other.location && hash == other.hash && format == other.format && size.x() == other.size.x() && size.y() == other.size.y();
|
||||
}
|
||||
|
||||
u32 decodeTexel(u32 u, u32 v, PICA::TextureFmt fmt, std::span<const u8> data);
|
||||
void allocate();
|
||||
void setNewConfig(u32 newConfig);
|
||||
void decodeTexture(std::span<const u8> data);
|
||||
void free();
|
||||
u64 sizeInBytes();
|
||||
|
||||
// Get the morton interleave offset of a texel based on its U and V values
|
||||
static u32 mortonInterleave(u32 u, u32 v);
|
||||
// Get the byte offset of texel (u, v) in the texture
|
||||
static u32 getSwizzledOffset(u32 u, u32 v, u32 width, u32 bytesPerPixel);
|
||||
static u32 getSwizzledOffset_4bpp(u32 u, u32 v, u32 width);
|
||||
u32 decodeTexel(u32 u, u32 v, PICA::TextureFmt fmt, std::span<const u8> data);
|
||||
|
||||
// Returns the format of this texture as a string
|
||||
std::string_view formatToString() {
|
||||
return PICA::textureFormatToString(format);
|
||||
}
|
||||
// Get the morton interleave offset of a texel based on its U and V values
|
||||
static u32 mortonInterleave(u32 u, u32 v);
|
||||
// Get the byte offset of texel (u, v) in the texture
|
||||
static u32 getSwizzledOffset(u32 u, u32 v, u32 width, u32 bytesPerPixel);
|
||||
static u32 getSwizzledOffset_4bpp(u32 u, u32 v, u32 width);
|
||||
|
||||
// Returns the texel at coordinates (u, v) of an ETC1(A4) texture
|
||||
// TODO: Make hasAlpha a template parameter
|
||||
u32 getTexelETC(bool hasAlpha, u32 u, u32 v, u32 width, std::span<const u8> data);
|
||||
u32 decodeETC(u32 alpha, u32 u, u32 v, u64 colourData);
|
||||
// Returns the format of this texture as a string
|
||||
std::string_view formatToString() { return PICA::textureFormatToString(format); }
|
||||
|
||||
// Returns the texel at coordinates (u, v) of an ETC1(A4) texture
|
||||
// TODO: Make hasAlpha a template parameter
|
||||
u32 getTexelETC(bool hasAlpha, u32 u, u32 v, u32 width, std::span<const u8> data);
|
||||
u32 decodeETC(u32 alpha, u32 u, u32 v, u64 colourData);
|
||||
};
|
||||
|
|
|
@ -57,7 +57,7 @@ namespace Metal {
|
|||
} else if (std::is_same<Format_t, PICA::DepthFmt>::value) {
|
||||
pixelFormat = PICA::toMTLPixelFormatDepth((PICA::DepthFmt)format);
|
||||
} else {
|
||||
panic("Invalid format type");
|
||||
Helpers::panic("Invalid format type");
|
||||
}
|
||||
|
||||
MTL::TextureDescriptor* descriptor = MTL::TextureDescriptor::alloc()->init();
|
||||
|
|
|
@ -4,22 +4,28 @@
|
|||
#include <array>
|
||||
#include <string>
|
||||
|
||||
#include "PICA/pica_hash.hpp"
|
||||
#include "PICA/regs.hpp"
|
||||
#include "boost/icl/interval.hpp"
|
||||
#include "helpers.hpp"
|
||||
#include "math_util.hpp"
|
||||
#include "opengl.hpp"
|
||||
#include "renderer_mtl/pica_to_mtl.hpp"
|
||||
// TODO: remove dependency on OpenGL
|
||||
#include "opengl.hpp"
|
||||
|
||||
template <typename T>
|
||||
using Interval = boost::icl::right_open_interval<T>;
|
||||
|
||||
namespace Metal {
|
||||
struct Texture {
|
||||
using Hash = PICAHash::HashType;
|
||||
|
||||
MTL::Device* device;
|
||||
|
||||
u32 location;
|
||||
u32 config; // Magnification/minification filter, wrapping configs, etc
|
||||
Hash hash = Hash(0);
|
||||
|
||||
PICA::TextureFmt format;
|
||||
OpenGL::uvec2 size;
|
||||
bool valid;
|
||||
|
@ -27,7 +33,8 @@ namespace Metal {
|
|||
// Range of VRAM taken up by buffer
|
||||
Interval<u32> range;
|
||||
|
||||
PICA::PixelFormatInfo formatInfo;
|
||||
PICA::MTLPixelFormatInfo formatInfo;
|
||||
MTL::Texture* base = nullptr;
|
||||
MTL::Texture* texture = nullptr;
|
||||
MTL::SamplerState* sampler = nullptr;
|
||||
|
||||
|
@ -43,7 +50,8 @@ namespace Metal {
|
|||
// For 2 textures to "match" we only care about their locations, formats, and dimensions to match
|
||||
// For other things, such as filtering mode, etc, we can just switch the attributes of the cached texture
|
||||
bool matches(Texture& other) {
|
||||
return location == other.location && format == other.format && size.x() == other.size.x() && size.y() == other.size.y();
|
||||
return location == other.location && hash == other.hash && format == other.format && size.x() == other.size.x() &&
|
||||
size.y() == other.size.y();
|
||||
}
|
||||
|
||||
void allocate();
|
||||
|
@ -52,22 +60,7 @@ namespace Metal {
|
|||
void free();
|
||||
u64 sizeInBytes();
|
||||
|
||||
u8 decodeTexelU8(u32 u, u32 v, PICA::TextureFmt fmt, std::span<const u8> data);
|
||||
u16 decodeTexelU16(u32 u, u32 v, PICA::TextureFmt fmt, std::span<const u8> data);
|
||||
u32 decodeTexelU32(u32 u, u32 v, PICA::TextureFmt fmt, std::span<const u8> data);
|
||||
|
||||
// Get the morton interleave offset of a texel based on its U and V values
|
||||
static u32 mortonInterleave(u32 u, u32 v);
|
||||
// Get the byte offset of texel (u, v) in the texture
|
||||
static u32 getSwizzledOffset(u32 u, u32 v, u32 width, u32 bytesPerPixel);
|
||||
static u32 getSwizzledOffset_4bpp(u32 u, u32 v, u32 width);
|
||||
|
||||
// Returns the format of this texture as a string
|
||||
std::string_view formatToString() { return PICA::textureFormatToString(format); }
|
||||
|
||||
// Returns the texel at coordinates (u, v) of an ETC1(A4) texture
|
||||
// TODO: Make hasAlpha a template parameter
|
||||
u32 getTexelETC(bool hasAlpha, u32 u, u32 v, u32 width, std::span<const u8> data);
|
||||
u32 decodeETC(u32 alpha, u32 u, u32 v, u64 colourData);
|
||||
};
|
||||
} // namespace Metal
|
||||
|
|
|
@ -3,31 +3,28 @@
|
|||
#include <Metal/Metal.hpp>
|
||||
|
||||
#include "PICA/regs.hpp"
|
||||
// TODO: remove dependency on OpenGL
|
||||
#include "opengl.hpp"
|
||||
|
||||
namespace PICA {
|
||||
struct PixelFormatInfo {
|
||||
struct MTLPixelFormatInfo {
|
||||
MTL::PixelFormat pixelFormat;
|
||||
size_t bytesPerTexel;
|
||||
void (*decoder)(OpenGL::uvec2, u32, u32, std::span<const u8>, u8*);
|
||||
|
||||
bool needsSwizzle = false;
|
||||
MTL::TextureSwizzleChannels swizzle{
|
||||
.red = MTL::TextureSwizzleRed,
|
||||
.green = MTL::TextureSwizzleGreen,
|
||||
.blue = MTL::TextureSwizzleBlue,
|
||||
.alpha = MTL::TextureSwizzleAlpha,
|
||||
};
|
||||
};
|
||||
|
||||
constexpr PixelFormatInfo pixelFormatInfos[14] = {
|
||||
{MTL::PixelFormatRGBA8Unorm, 4}, // RGBA8
|
||||
{MTL::PixelFormatRGBA8Unorm, 4}, // RGB8
|
||||
{MTL::PixelFormatBGR5A1Unorm, 2}, // RGBA5551
|
||||
{MTL::PixelFormatB5G6R5Unorm, 2}, // RGB565
|
||||
{MTL::PixelFormatABGR4Unorm, 2}, // RGBA4
|
||||
{MTL::PixelFormatRGBA8Unorm, 4}, // IA8
|
||||
{MTL::PixelFormatRG8Unorm, 2}, // RG8
|
||||
{MTL::PixelFormatRGBA8Unorm, 4}, // I8
|
||||
{MTL::PixelFormatA8Unorm, 1}, // A8
|
||||
{MTL::PixelFormatABGR4Unorm, 2}, // IA4
|
||||
{MTL::PixelFormatABGR4Unorm, 2}, // I4
|
||||
{MTL::PixelFormatA8Unorm, 1}, // A4
|
||||
{MTL::PixelFormatRGBA8Unorm, 4}, // ETC1
|
||||
{MTL::PixelFormatRGBA8Unorm, 4}, // ETC1A4
|
||||
};
|
||||
extern MTLPixelFormatInfo mtlPixelFormatInfos[14];
|
||||
|
||||
inline PixelFormatInfo getPixelFormatInfo(TextureFmt format) { return pixelFormatInfos[static_cast<int>(format)]; }
|
||||
void checkForMTLPixelFormatSupport(MTL::Device* device);
|
||||
inline MTLPixelFormatInfo getMTLPixelFormatInfo(TextureFmt format) { return mtlPixelFormatInfos[static_cast<int>(format)]; }
|
||||
|
||||
inline MTL::PixelFormat toMTLPixelFormatColor(ColorFmt format) {
|
||||
switch (format) {
|
||||
|
@ -35,7 +32,11 @@ namespace PICA {
|
|||
case ColorFmt::RGB8: return MTL::PixelFormatRGBA8Unorm;
|
||||
case ColorFmt::RGBA5551: return MTL::PixelFormatRGBA8Unorm; // TODO: use MTL::PixelFormatBGR5A1Unorm?
|
||||
case ColorFmt::RGB565: return MTL::PixelFormatRGBA8Unorm; // TODO: use MTL::PixelFormatB5G6R5Unorm?
|
||||
#ifdef PANDA3DS_IOS
|
||||
case ColorFmt::RGBA4: return MTL::PixelFormatRGBA8Unorm; // IOS + Metal doesn't support AGBR4 properly, at least on simulator
|
||||
#else
|
||||
case ColorFmt::RGBA4: return MTL::PixelFormatABGR4Unorm;
|
||||
#endif
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -42,11 +42,13 @@ class RendererMTL final : public Renderer {
|
|||
virtual void initGraphicsContext([[maybe_unused]] GL::Context* context) override {}
|
||||
#endif
|
||||
|
||||
private:
|
||||
CA::MetalLayer* metalLayer;
|
||||
virtual void setMTKLayer(void* layer) override;
|
||||
|
||||
MTL::Device* device;
|
||||
MTL::CommandQueue* commandQueue;
|
||||
private:
|
||||
CA::MetalLayer* metalLayer = nullptr;
|
||||
|
||||
MTL::Device* device = nullptr;
|
||||
MTL::CommandQueue* commandQueue = nullptr;
|
||||
|
||||
Metal::CommandEncoder commandEncoder;
|
||||
|
||||
|
@ -86,6 +88,20 @@ class RendererMTL final : public Renderer {
|
|||
MTL::Texture* lastColorTexture = nullptr;
|
||||
MTL::Texture* lastDepthTexture = nullptr;
|
||||
|
||||
// Information about the final 3DS screen -> Window blit, accounting for things like scaling and shifting the output based on
|
||||
// the window's dimensions. Updated whenever the screen size or layout changes.
|
||||
struct {
|
||||
float topScreenX = 0;
|
||||
float topScreenY = 0;
|
||||
float topScreenWidth = 400;
|
||||
float topScreenHeight = 240;
|
||||
|
||||
float bottomScreenX = 40;
|
||||
float bottomScreenY = 240;
|
||||
float bottomScreenWidth = 320;
|
||||
float bottomScreenHeight = 240;
|
||||
} blitInfo;
|
||||
|
||||
// Debug
|
||||
std::string nextRenderPassName;
|
||||
|
||||
|
@ -98,6 +114,7 @@ class RendererMTL final : public Renderer {
|
|||
void endRenderPass() {
|
||||
if (renderCommandEncoder) {
|
||||
renderCommandEncoder->endEncoding();
|
||||
renderCommandEncoder->release();
|
||||
renderCommandEncoder = nullptr;
|
||||
}
|
||||
}
|
||||
|
|
24
include/renderer_mtl/texture_decoder.hpp
Normal file
24
include/renderer_mtl/texture_decoder.hpp
Normal file
|
@ -0,0 +1,24 @@
|
|||
#pragma once
|
||||
|
||||
#include "helpers.hpp"
|
||||
// TODO: remove dependency on OpenGL
|
||||
#include "opengl.hpp"
|
||||
|
||||
void decodeTexelABGR8ToRGBA8(OpenGL::uvec2 size, u32 u, u32 v, std::span<const u8> inData, u8* outData);
|
||||
void decodeTexelBGR8ToRGBA8(OpenGL::uvec2 size, u32 u, u32 v, std::span<const u8> inData, u8* outData);
|
||||
void decodeTexelA1BGR5ToBGR5A1(OpenGL::uvec2 size, u32 u, u32 v, std::span<const u8> inData, u8* outData);
|
||||
void decodeTexelA1BGR5ToRGBA8(OpenGL::uvec2 size, u32 u, u32 v, std::span<const u8> inData, u8* outData);
|
||||
void decodeTexelB5G6R5ToB5G6R5(OpenGL::uvec2 size, u32 u, u32 v, std::span<const u8> inData, u8* outData);
|
||||
void decodeTexelB5G6R5ToRGBA8(OpenGL::uvec2 size, u32 u, u32 v, std::span<const u8> inData, u8* outData);
|
||||
void decodeTexelABGR4ToABGR4(OpenGL::uvec2 size, u32 u, u32 v, std::span<const u8> inData, u8* outData);
|
||||
void decodeTexelABGR4ToRGBA8(OpenGL::uvec2 size, u32 u, u32 v, std::span<const u8> inData, u8* outData);
|
||||
void decodeTexelAI8ToRG8(OpenGL::uvec2 size, u32 u, u32 v, std::span<const u8> inData, u8* outData);
|
||||
void decodeTexelGR8ToRG8(OpenGL::uvec2 size, u32 u, u32 v, std::span<const u8> inData, u8* outData);
|
||||
void decodeTexelI8ToR8(OpenGL::uvec2 size, u32 u, u32 v, std::span<const u8> inData, u8* outData);
|
||||
void decodeTexelA8ToA8(OpenGL::uvec2 size, u32 u, u32 v, std::span<const u8> inData, u8* outData);
|
||||
void decodeTexelAI4ToABGR4(OpenGL::uvec2 size, u32 u, u32 v, std::span<const u8> inData, u8* outData);
|
||||
void decodeTexelAI4ToRG8(OpenGL::uvec2 size, u32 u, u32 v, std::span<const u8> inData, u8* outData);
|
||||
void decodeTexelI4ToR8(OpenGL::uvec2 size, u32 u, u32 v, std::span<const u8> inData, u8* outData);
|
||||
void decodeTexelA4ToA8(OpenGL::uvec2 size, u32 u, u32 v, std::span<const u8> inData, u8* outData);
|
||||
void decodeTexelETC1ToRGBA8(OpenGL::uvec2 size, u32 u, u32 v, std::span<const u8> inData, u8* outData);
|
||||
void decodeTexelETC1A4ToRGBA8(OpenGL::uvec2 size, u32 u, u32 v, std::span<const u8> inData, u8* outData);
|
|
@ -9,8 +9,8 @@
|
|||
namespace Vulkan {
|
||||
|
||||
VKAPI_ATTR VkBool32 VKAPI_CALL debugMessageCallback(
|
||||
VkDebugUtilsMessageSeverityFlagBitsEXT messageSeverity, VkDebugUtilsMessageTypeFlagsEXT messageType,
|
||||
const VkDebugUtilsMessengerCallbackDataEXT* callbackData, void* userData
|
||||
vk::DebugUtilsMessageSeverityFlagBitsEXT messageSeverity, vk::DebugUtilsMessageTypeFlagsEXT messageType,
|
||||
const vk::DebugUtilsMessengerCallbackDataEXT* callbackData, void* userData
|
||||
);
|
||||
|
||||
void setObjectName(vk::Device device, vk::ObjectType objectType, const void* objectHandle, const char* format, ...);
|
||||
|
|
|
@ -2,8 +2,9 @@
|
|||
|
||||
#include "result_cfg.hpp"
|
||||
#include "result_common.hpp"
|
||||
#include "result_kernel.hpp"
|
||||
#include "result_os.hpp"
|
||||
#include "result_fnd.hpp"
|
||||
#include "result_fs.hpp"
|
||||
#include "result_gsp.hpp"
|
||||
#include "result_gsp.hpp"
|
||||
#include "result_ir.hpp"
|
||||
#include "result_kernel.hpp"
|
||||
#include "result_os.hpp"
|
|
@ -42,7 +42,7 @@ namespace Result {
|
|||
OS = 6,
|
||||
DBG = 7,
|
||||
DMNT = 8,
|
||||
PDN = 9 ,
|
||||
PDN = 9,
|
||||
GSP = 10,
|
||||
I2C = 11,
|
||||
GPIO = 12,
|
||||
|
@ -132,7 +132,7 @@ namespace Result {
|
|||
};
|
||||
|
||||
class HorizonResult {
|
||||
private:
|
||||
private:
|
||||
static const uint32_t DescriptionBits = 10;
|
||||
static const uint32_t ModuleBits = 8;
|
||||
static const uint32_t ReservedBits = 3;
|
||||
|
@ -156,7 +156,7 @@ namespace Result {
|
|||
return (description << DescriptionOffset) | (module << ModuleOffset) | (summary << SummaryOffset) | (level << LevelOffset);
|
||||
}
|
||||
|
||||
public:
|
||||
public:
|
||||
constexpr HorizonResult() : m_value(0) {}
|
||||
constexpr HorizonResult(uint32_t value) : m_value(value) {}
|
||||
constexpr HorizonResult(uint32_t description, HorizonResultModule module, HorizonResultSummary summary, HorizonResultLevel level) : m_value(makeValue(description, static_cast<uint32_t>(module), static_cast<uint32_t>(summary), static_cast<uint32_t>(level))) {}
|
||||
|
@ -193,14 +193,14 @@ namespace Result {
|
|||
|
||||
static_assert(std::is_trivially_destructible<HorizonResult>::value, "std::is_trivially_destructible<HorizonResult>::value");
|
||||
|
||||
#define DEFINE_HORIZON_RESULT_MODULE(ns, value) \
|
||||
namespace ns::Detail {\
|
||||
static constexpr HorizonResultModule ModuleId = HorizonResultModule::value; \
|
||||
}
|
||||
#define DEFINE_HORIZON_RESULT_MODULE(ns, value) \
|
||||
namespace ns::Detail { \
|
||||
static constexpr HorizonResultModule ModuleId = HorizonResultModule::value; \
|
||||
}
|
||||
|
||||
#define DEFINE_HORIZON_RESULT(name, description, summary, level) \
|
||||
static constexpr HorizonResult name(description, Detail::ModuleId, HorizonResultSummary::summary, HorizonResultLevel::level);
|
||||
#define DEFINE_HORIZON_RESULT(name, description, summary, level) \
|
||||
static constexpr HorizonResult name(description, Detail::ModuleId, HorizonResultSummary::summary, HorizonResultLevel::level);
|
||||
|
||||
static constexpr HorizonResult Success(0);
|
||||
static constexpr HorizonResult FailurePlaceholder(UINT32_MAX);
|
||||
};
|
||||
}; // namespace Result
|
||||
|
|
8
include/result/result_ir.hpp
Normal file
8
include/result/result_ir.hpp
Normal file
|
@ -0,0 +1,8 @@
|
|||
#pragma once
|
||||
#include "result_common.hpp"
|
||||
|
||||
DEFINE_HORIZON_RESULT_MODULE(Result::IR, IR);
|
||||
|
||||
namespace Result::IR {
|
||||
DEFINE_HORIZON_RESULT(NoDeviceConnected, 13, InvalidState, Status);
|
||||
};
|
|
@ -12,7 +12,8 @@ struct Scheduler {
|
|||
UpdateTimers = 1, // Update kernel timer objects
|
||||
RunDSP = 2, // Make the emulated DSP run for one audio frame
|
||||
SignalY2R = 3, // Signal that a Y2R conversion has finished
|
||||
Panic = 4, // Dummy event that is always pending and should never be triggered (Timestamp = UINT64_MAX)
|
||||
UpdateIR = 4, // Update an IR device (For now, just the CirclePad Pro/N3DS controls)
|
||||
Panic = 5, // Dummy event that is always pending and should never be triggered (Timestamp = UINT64_MAX)
|
||||
TotalNumberOfEvents // How many event types do we have in total?
|
||||
};
|
||||
static constexpr usize totalNumberOfEvents = static_cast<usize>(EventType::TotalNumberOfEvents);
|
||||
|
|
58
include/screen_layout.hpp
Normal file
58
include/screen_layout.hpp
Normal file
|
@ -0,0 +1,58 @@
|
|||
#pragma once
|
||||
#include <string>
|
||||
|
||||
#include "helpers.hpp"
|
||||
|
||||
namespace ScreenLayout {
|
||||
static constexpr u32 TOP_SCREEN_WIDTH = 400;
|
||||
static constexpr u32 BOTTOM_SCREEN_WIDTH = 320;
|
||||
static constexpr u32 TOP_SCREEN_HEIGHT = 240;
|
||||
static constexpr u32 BOTTOM_SCREEN_HEIGHT = 240;
|
||||
|
||||
// The bottom screen is less wide by 80 pixels, so we center it by offsetting it 40 pixels right
|
||||
static constexpr u32 BOTTOM_SCREEN_X_OFFSET = 40;
|
||||
static constexpr u32 CONSOLE_HEIGHT = TOP_SCREEN_HEIGHT + BOTTOM_SCREEN_HEIGHT;
|
||||
|
||||
enum class Layout {
|
||||
Default = 0, // Top screen up, bottom screen down
|
||||
DefaultFlipped, // Top screen down, bottom screen up
|
||||
SideBySide, // Top screen left, bottom screen right,
|
||||
SideBySideFlipped, // Top screen right, bottom screen left,
|
||||
};
|
||||
|
||||
// For properly handling touchscreen, we have to remember what window coordinates our screens map to
|
||||
// We also remember some more information that is useful to our renderers, particularly for the final screen blit.
|
||||
struct WindowCoordinates {
|
||||
u32 topScreenX = 0;
|
||||
u32 topScreenY = 0;
|
||||
u32 topScreenWidth = TOP_SCREEN_WIDTH;
|
||||
u32 topScreenHeight = TOP_SCREEN_HEIGHT;
|
||||
|
||||
u32 bottomScreenX = BOTTOM_SCREEN_X_OFFSET;
|
||||
u32 bottomScreenY = TOP_SCREEN_HEIGHT;
|
||||
u32 bottomScreenWidth = BOTTOM_SCREEN_WIDTH;
|
||||
u32 bottomScreenHeight = BOTTOM_SCREEN_HEIGHT;
|
||||
|
||||
u32 windowWidth = topScreenWidth;
|
||||
u32 windowHeight = topScreenHeight + bottomScreenHeight;
|
||||
|
||||
// Information used when optimizing the final screen blit into a single blit
|
||||
struct {
|
||||
// Can we actually render both of the screens in a single blit?
|
||||
bool canDoSingleBlit = false;
|
||||
// Blit information used if we can
|
||||
int destX = 0, destY = 0;
|
||||
int destWidth = TOP_SCREEN_WIDTH;
|
||||
int destHeight = CONSOLE_HEIGHT;
|
||||
} singleBlitInfo;
|
||||
};
|
||||
|
||||
// Calculate screen coordinates on the screen for a given layout & a given size for the output window
|
||||
// Used in both the renderers and in the frontends (To eg calculate touch screen boundaries)
|
||||
void calculateCoordinates(
|
||||
WindowCoordinates& coordinates, u32 outputWindowWidth, u32 outputWindowHeight, float topScreenPercentage, Layout layout
|
||||
);
|
||||
|
||||
Layout layoutFromString(std::string inString);
|
||||
const char* layoutToString(Layout layout);
|
||||
} // namespace ScreenLayout
|
|
@ -1,6 +1,6 @@
|
|||
#pragma once
|
||||
|
||||
#include <cmath>
|
||||
#include <algorithm>
|
||||
#include <glm/glm.hpp>
|
||||
|
||||
#include "helpers.hpp"
|
||||
|
@ -10,29 +10,29 @@
|
|||
// We use the same code for Android as well, since the values we get from Android are in the same format as SDL (m/s^2 for acceleration, rad/s for
|
||||
// rotation)
|
||||
namespace Sensors::SDL {
|
||||
// Convert the rotation data we get from SDL sensor events to rotation data we can feed right to HID
|
||||
// Returns [pitch, roll, yaw]
|
||||
static glm::vec3 convertRotation(glm::vec3 rotation) {
|
||||
// Annoyingly, Android doesn't support the <numbers> header yet so we define pi ourselves
|
||||
static constexpr double pi = 3.141592653589793;
|
||||
// Convert the rotation from rad/s to deg/s and scale by the gyroscope coefficient in HID
|
||||
constexpr float scale = 180.f / pi * HIDService::gyroscopeCoeff;
|
||||
// The axes are also inverted, so invert scale before the multiplication.
|
||||
return rotation * -scale;
|
||||
}
|
||||
// Convert the rotation data we get from SDL sensor events to rotation data we can feed right to HID
|
||||
// Returns [pitch, roll, yaw]
|
||||
static glm::vec3 convertRotation(glm::vec3 rotation) {
|
||||
// Annoyingly, Android doesn't support the <numbers> header yet so we define pi ourselves
|
||||
static constexpr double pi = 3.141592653589793;
|
||||
// Convert the rotation from rad/s to deg/s and scale by the gyroscope coefficient in HID
|
||||
constexpr float scale = 180.f / pi * HIDService::gyroscopeCoeff;
|
||||
// The axes are also inverted, so invert scale before the multiplication.
|
||||
return rotation * -scale;
|
||||
}
|
||||
|
||||
static glm::vec3 convertAcceleration(float* data) {
|
||||
// Set our cap to ~9 m/s^2. The 3DS sensors cap at -930 and +930, so values above this value will get clamped to 930
|
||||
// At rest (3DS laid flat on table), hardware reads around ~0 for x and z axis, and around ~480 for y axis due to gravity.
|
||||
// This code tries to mimic this approximately, with offsets based on measurements from my DualShock 4.
|
||||
static constexpr float accelMax = 9.f;
|
||||
// We define standard gravity(g) ourself instead of using the SDL one in order for the code to work on Android too.
|
||||
static constexpr float standardGravity = 9.80665f;
|
||||
static glm::vec3 convertAcceleration(float* data) {
|
||||
// Set our cap to ~9 m/s^2. The 3DS sensors cap at -930 and +930, so values above this value will get clamped to 930
|
||||
// At rest (3DS laid flat on table), hardware reads around ~0 for x and z axis, and around ~480 for y axis due to gravity.
|
||||
// This code tries to mimic this approximately, with offsets based on measurements from my DualShock 4.
|
||||
static constexpr float accelMax = 9.f;
|
||||
// We define standard gravity(g) ourself instead of using the SDL one in order for the code to work on Android too.
|
||||
static constexpr float standardGravity = 9.80665f;
|
||||
|
||||
s16 x = std::clamp<s16>(s16(data[0] / accelMax * 930.f), -930, +930);
|
||||
s16 y = std::clamp<s16>(s16(data[1] / (standardGravity * accelMax) * 930.f - 350.f), -930, +930);
|
||||
s16 z = std::clamp<s16>(s16((data[2] - 2.1f) / accelMax * 930.f), -930, +930);
|
||||
s16 x = std::clamp<s16>(s16(data[0] / accelMax * 930.f), -930, +930);
|
||||
s16 y = std::clamp<s16>(s16(data[1] / (standardGravity * accelMax) * 930.f - 350.f), -930, +930);
|
||||
s16 z = std::clamp<s16>(s16((data[2] - 2.1f) / accelMax * 930.f), -930, +930);
|
||||
|
||||
return glm::vec3(x, y, z);
|
||||
}
|
||||
return glm::vec3(x, y, z);
|
||||
}
|
||||
} // namespace Sensors::SDL
|
||||
|
|
|
@ -1,5 +1,6 @@
|
|||
#pragma once
|
||||
#include <array>
|
||||
|
||||
#include "helpers.hpp"
|
||||
#include "io_file.hpp"
|
||||
#include "nfc_types.hpp"
|
||||
|
|
|
@ -1,13 +1,13 @@
|
|||
#pragma once
|
||||
#include <optional>
|
||||
|
||||
#include "applets/applet_manager.hpp"
|
||||
#include "helpers.hpp"
|
||||
#include "kernel_types.hpp"
|
||||
#include "logger.hpp"
|
||||
#include "memory.hpp"
|
||||
#include "result/result.hpp"
|
||||
|
||||
#include "applets/applet_manager.hpp"
|
||||
|
||||
// Yay, more circular dependencies
|
||||
class Kernel;
|
||||
|
||||
|
|
|
@ -18,10 +18,13 @@ class CECDService {
|
|||
MAKE_LOG_FUNCTION(log, cecdLogger)
|
||||
|
||||
std::optional<Handle> infoEvent;
|
||||
std::optional<Handle> changeStateEvent;
|
||||
|
||||
// Service commands
|
||||
void getChangeStateEventHandle(u32 messagePointer);
|
||||
void getInfoEventHandle(u32 messagePointer);
|
||||
void openAndRead(u32 messagePointer);
|
||||
void stop(u32 messagePointer);
|
||||
|
||||
public:
|
||||
CECDService(Memory& mem, Kernel& kernel) : mem(mem), kernel(kernel) {}
|
||||
|
|
|
@ -23,6 +23,7 @@ class CFGService {
|
|||
void getConfigInfoBlk2(u32 messagePointer);
|
||||
void getConfigInfoBlk8(u32 messagePointer, u32 commandWord);
|
||||
void getCountryCodeID(u32 messagePointer);
|
||||
void getCountryCodeString(u32 messagePointer);
|
||||
void getLocalFriendCodeSeed(u32 messagePointer);
|
||||
void getRegionCanadaUSA(u32 messagePointer);
|
||||
void getSystemModel(u32 messagePointer);
|
||||
|
|
|
@ -1,6 +1,7 @@
|
|||
#pragma once
|
||||
#include <array>
|
||||
#include <optional>
|
||||
#include <string>
|
||||
|
||||
#include "helpers.hpp"
|
||||
#include "kernel_types.hpp"
|
||||
|
@ -27,12 +28,21 @@ namespace HID::Keys {
|
|||
GPIO0Inv = 1 << 12, // Inverted value of GPIO bit 0
|
||||
GPIO14Inv = 1 << 13, // Inverted value of GPIO bit 14
|
||||
|
||||
// CirclePad Pro buttons. We store them in the HID service for ease, even though they're only used by the IR service
|
||||
// Whenever the HID service writes to shared memory, we remember to mask them out
|
||||
ZL = 1 << 14,
|
||||
ZR = 1 << 15,
|
||||
CirclePadProButtons = ZL | ZR,
|
||||
|
||||
CirclePadRight = 1 << 28, // X >= 41
|
||||
CirclePadLeft = 1 << 29, // X <= -41
|
||||
CirclePadUp = 1 << 30, // Y >= 41
|
||||
CirclePadDown = 1u << 31 // Y <= -41
|
||||
};
|
||||
}
|
||||
|
||||
const char* keyToName(u32 key);
|
||||
u32 nameToKey(std::string name);
|
||||
} // namespace HID::Keys
|
||||
|
||||
// Circular dependency because we need HID to spawn events
|
||||
class Kernel;
|
||||
|
@ -58,6 +68,9 @@ class HIDService {
|
|||
s16 roll, pitch, yaw; // Gyroscope state
|
||||
s16 accelX, accelY, accelZ; // Accelerometer state
|
||||
|
||||
// New 3DS/CirclePad Pro C-stick state
|
||||
s16 cStickX, cStickY;
|
||||
|
||||
bool accelerometerEnabled;
|
||||
bool eventsInitialized;
|
||||
bool gyroEnabled;
|
||||
|
@ -113,11 +126,11 @@ class HIDService {
|
|||
|
||||
// Turn bits 28 and 29 off in the new button state, which indicate whether the circlepad is steering left or right
|
||||
// Then, set them according to the new value of x
|
||||
newButtons &= ~0x3000'0000;
|
||||
newButtons &= ~(HID::Keys::CirclePadLeft | HID::Keys::CirclePadRight);
|
||||
if (x >= 41) // Pressing right
|
||||
newButtons |= 1 << 28;
|
||||
newButtons |= HID::Keys::CirclePadRight;
|
||||
else if (x <= -41) // Pressing left
|
||||
newButtons |= 1 << 29;
|
||||
newButtons |= HID::Keys::CirclePadLeft;
|
||||
}
|
||||
|
||||
void setCirclepadY(s16 y) {
|
||||
|
@ -125,13 +138,19 @@ class HIDService {
|
|||
|
||||
// Turn bits 30 and 31 off in the new button state, which indicate whether the circlepad is steering up or down
|
||||
// Then, set them according to the new value of y
|
||||
newButtons &= ~0xC000'0000;
|
||||
newButtons &= ~(HID::Keys::CirclePadUp | HID::Keys::CirclePadDown);
|
||||
if (y >= 41) // Pressing up
|
||||
newButtons |= 1 << 30;
|
||||
newButtons |= HID::Keys::CirclePadUp;
|
||||
else if (y <= -41) // Pressing down
|
||||
newButtons |= 1 << 31;
|
||||
newButtons |= HID::Keys::CirclePadDown;
|
||||
}
|
||||
|
||||
void setCStickX(s16 x) { cStickX = x; }
|
||||
void setCStickY(s16 y) { cStickY = y; }
|
||||
|
||||
s16 getCStickX() { return cStickX; }
|
||||
s16 getCStickY() { return cStickY; }
|
||||
|
||||
void setRoll(s16 value) { roll = value; }
|
||||
void setPitch(s16 value) { pitch = value; }
|
||||
void setYaw(s16 value) { yaw = value; }
|
||||
|
@ -157,9 +176,6 @@ class HIDService {
|
|||
touchScreenPressed = true;
|
||||
}
|
||||
|
||||
void releaseTouchScreen() {
|
||||
touchScreenPressed = false;
|
||||
}
|
||||
|
||||
void releaseTouchScreen() { touchScreenPressed = false; }
|
||||
bool isTouchScreenPressed() { return touchScreenPressed; }
|
||||
};
|
||||
|
|
53
include/services/ir/circlepad_pro.hpp
Normal file
53
include/services/ir/circlepad_pro.hpp
Normal file
|
@ -0,0 +1,53 @@
|
|||
#pragma once
|
||||
#include "bitfield.hpp"
|
||||
#include "services/ir/ir_device.hpp"
|
||||
#include "services/ir/ir_types.hpp"
|
||||
|
||||
namespace IR {
|
||||
class CirclePadPro : public IR::Device {
|
||||
public:
|
||||
struct ButtonState {
|
||||
static constexpr int C_STICK_CENTER = 0x800;
|
||||
|
||||
union {
|
||||
BitField<0, 8, u32> header;
|
||||
BitField<8, 12, u32> x;
|
||||
BitField<20, 12, u32> y;
|
||||
} cStick;
|
||||
|
||||
union {
|
||||
BitField<0, 5, u8> batteryLevel;
|
||||
BitField<5, 1, u8> zlNotPressed;
|
||||
BitField<6, 1, u8> zrNotPressed;
|
||||
BitField<7, 1, u8> rNotPressed;
|
||||
} buttons;
|
||||
|
||||
u8 unknown;
|
||||
|
||||
ButtonState() {
|
||||
// Response header for button state reads
|
||||
cStick.header = u8(CPPResponseID::PollButtons);
|
||||
cStick.x = u32(CirclePadPro::ButtonState::C_STICK_CENTER);
|
||||
cStick.y = u32(CirclePadPro::ButtonState::C_STICK_CENTER);
|
||||
|
||||
// Fully charged
|
||||
buttons.batteryLevel = 0x1F;
|
||||
buttons.zrNotPressed = 1;
|
||||
buttons.zlNotPressed = 1;
|
||||
buttons.rNotPressed = 1;
|
||||
unknown = 0;
|
||||
}
|
||||
};
|
||||
static_assert(sizeof(ButtonState) == 6, "Circlepad Pro button state has wrong size");
|
||||
|
||||
virtual void connect() override;
|
||||
virtual void disconnect() override;
|
||||
virtual void receivePayload(Payload payload) override;
|
||||
|
||||
CirclePadPro(SendCallback sendCallback, Scheduler& scheduler) : IR::Device(sendCallback, scheduler) {}
|
||||
|
||||
ButtonState state;
|
||||
// The current polling period in cycles, configured via the ConfigurePolling command
|
||||
s64 period = Scheduler::nsToCycles(16000);
|
||||
};
|
||||
} // namespace IR
|
25
include/services/ir/ir_device.hpp
Normal file
25
include/services/ir/ir_device.hpp
Normal file
|
@ -0,0 +1,25 @@
|
|||
#pragma once
|
||||
#include <functional>
|
||||
#include <span>
|
||||
|
||||
#include "helpers.hpp"
|
||||
#include "scheduler.hpp"
|
||||
|
||||
namespace IR {
|
||||
class Device {
|
||||
public:
|
||||
using Payload = std::span<const u8>;
|
||||
|
||||
protected:
|
||||
using SendCallback = std::function<void(Payload)>; // Callback for sending data from IR device->3DS
|
||||
Scheduler& scheduler;
|
||||
SendCallback sendCallback;
|
||||
|
||||
public:
|
||||
virtual void connect() = 0;
|
||||
virtual void disconnect() = 0;
|
||||
virtual void receivePayload(Payload payload) = 0;
|
||||
|
||||
Device(SendCallback sendCallback, Scheduler& scheduler) : sendCallback(sendCallback), scheduler(scheduler) {}
|
||||
};
|
||||
} // namespace IR
|
132
include/services/ir/ir_types.hpp
Normal file
132
include/services/ir/ir_types.hpp
Normal file
|
@ -0,0 +1,132 @@
|
|||
#pragma once
|
||||
#include <array>
|
||||
|
||||
#include "bitfield.hpp"
|
||||
#include "helpers.hpp"
|
||||
#include "memory.hpp"
|
||||
#include "swap.hpp"
|
||||
|
||||
// Type definitions for the IR service
|
||||
// Based off https://github.com/azahar-emu/azahar/blob/de7b457ee466e432047c4ee8d37fca9eae7e031e/src/core/hle/service/ir/ir_user.cpp#L88
|
||||
|
||||
namespace IR {
|
||||
class Buffer {
|
||||
public:
|
||||
Buffer(Memory& memory, u32 sharedMemBaseVaddr, u32 infoOffset, u32 bufferOffset, u32 maxPackets, u32 bufferSize)
|
||||
: memory(memory), sharedMemBaseVaddr(sharedMemBaseVaddr), infoOffset(infoOffset), bufferOffset(bufferOffset), maxPackets(maxPackets),
|
||||
maxDataSize(bufferSize - sizeof(PacketInfo) * maxPackets) {
|
||||
updateBufferInfo();
|
||||
}
|
||||
|
||||
u8* getPointer(u32 offset) { return (u8*)memory.getReadPointer(sharedMemBaseVaddr + offset); }
|
||||
|
||||
bool put(std::span<const u8> packet) {
|
||||
if (info.packetCount == maxPackets) {
|
||||
return false;
|
||||
}
|
||||
|
||||
u32 offset;
|
||||
|
||||
// finds free space offset in data buffer
|
||||
if (info.packetCount == 0) {
|
||||
offset = 0;
|
||||
if (packet.size() > maxDataSize) {
|
||||
return false;
|
||||
}
|
||||
} else {
|
||||
const u32 lastIndex = (info.endIndex + maxPackets - 1) % maxPackets;
|
||||
const PacketInfo first = getPacketInfo(info.beginIndex);
|
||||
const PacketInfo last = getPacketInfo(lastIndex);
|
||||
offset = (last.offset + last.size) % maxDataSize;
|
||||
const u32 freeSpace = (first.offset + maxDataSize - offset) % maxDataSize;
|
||||
|
||||
if (packet.size() > freeSpace) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
// Writes packet info
|
||||
PacketInfo packet_info{offset, static_cast<u32>(packet.size())};
|
||||
setPacketInfo(info.endIndex, packet_info);
|
||||
|
||||
// Writes packet data
|
||||
for (std::size_t i = 0; i < packet.size(); ++i) {
|
||||
*getDataBufferPointer((offset + i) % maxDataSize) = packet[i];
|
||||
}
|
||||
|
||||
// Updates buffer info
|
||||
info.endIndex++;
|
||||
info.endIndex %= maxPackets;
|
||||
info.packetCount++;
|
||||
updateBufferInfo();
|
||||
return true;
|
||||
}
|
||||
|
||||
bool release(u32 count) {
|
||||
if (info.packetCount < count) {
|
||||
return false;
|
||||
}
|
||||
|
||||
info.packetCount -= count;
|
||||
info.beginIndex += count;
|
||||
info.beginIndex %= maxPackets;
|
||||
updateBufferInfo();
|
||||
return true;
|
||||
}
|
||||
|
||||
u32 getPacketCount() { return info.packetCount; }
|
||||
|
||||
private:
|
||||
struct BufferInfo {
|
||||
u32_le beginIndex;
|
||||
u32_le endIndex;
|
||||
u32_le packetCount;
|
||||
u32_le unknown;
|
||||
};
|
||||
static_assert(sizeof(BufferInfo) == 16, "IR::BufferInfo has wrong size!");
|
||||
|
||||
struct PacketInfo {
|
||||
u32_le offset;
|
||||
u32_le size;
|
||||
};
|
||||
static_assert(sizeof(PacketInfo) == 8, "IR::PacketInfo has wrong size!");
|
||||
|
||||
u8* getPacketInfoPointer(u32 index) { return getPointer(bufferOffset + sizeof(PacketInfo) * index); }
|
||||
void setPacketInfo(u32 index, const PacketInfo& packet_info) { std::memcpy(getPacketInfoPointer(index), &packet_info, sizeof(PacketInfo)); }
|
||||
|
||||
PacketInfo getPacketInfo(u32 index) {
|
||||
PacketInfo packet_info;
|
||||
std::memcpy(&packet_info, getPacketInfoPointer(index), sizeof(PacketInfo));
|
||||
return packet_info;
|
||||
}
|
||||
|
||||
u8* getDataBufferPointer(u32 offset) { return getPointer(bufferOffset + sizeof(PacketInfo) * maxPackets + offset); }
|
||||
void updateBufferInfo() {
|
||||
if (infoOffset) {
|
||||
std::memcpy(getPointer(infoOffset), &info, sizeof(info));
|
||||
}
|
||||
}
|
||||
|
||||
BufferInfo info{0, 0, 0, 0};
|
||||
Memory& memory;
|
||||
u32 sharedMemBaseVaddr;
|
||||
u32 infoOffset;
|
||||
u32 bufferOffset;
|
||||
u32 maxPackets;
|
||||
u32 maxDataSize;
|
||||
};
|
||||
|
||||
namespace CPPResponseID {
|
||||
enum : u8 {
|
||||
PollButtons = 0x10, // This response contains the current Circlepad Pro button/stick state
|
||||
ReadCalibrationData = 0x11, // Responding to a ReadCalibrationData request
|
||||
};
|
||||
}
|
||||
|
||||
namespace CPPRequestID {
|
||||
enum : u8 {
|
||||
ConfigurePolling = 1, // Configures if & how often to poll the Circlepad Pro button/stick state
|
||||
ReadCalibrationData = 2, // Reads the Circlepad Pro calibration data
|
||||
};
|
||||
}
|
||||
} // namespace IR
|
|
@ -1,16 +1,22 @@
|
|||
#pragma once
|
||||
#include <memory>
|
||||
#include <optional>
|
||||
#include <span>
|
||||
|
||||
#include "config.hpp"
|
||||
#include "helpers.hpp"
|
||||
#include "kernel_types.hpp"
|
||||
#include "logger.hpp"
|
||||
#include "memory.hpp"
|
||||
#include "result/result.hpp"
|
||||
#include "services/hid.hpp"
|
||||
#include "services/ir/circlepad_pro.hpp"
|
||||
|
||||
// Circular dependencies in this project? Never
|
||||
class Kernel;
|
||||
|
||||
class IRUserService {
|
||||
using Payload = IR::Device::Payload;
|
||||
using Handle = HorizonHandle;
|
||||
|
||||
enum class DeviceID : u8 {
|
||||
|
@ -20,23 +26,34 @@ class IRUserService {
|
|||
Handle handle = KernelHandles::IR_USER;
|
||||
Memory& mem;
|
||||
Kernel& kernel;
|
||||
// The IR service has a reference to the HID service as that's where the frontends store CirclePad Pro button state in
|
||||
HIDService& hid;
|
||||
const EmulatorConfig& config;
|
||||
|
||||
MAKE_LOG_FUNCTION(log, irUserLogger)
|
||||
|
||||
// Service commands
|
||||
void clearReceiveBuffer(u32 messagePointer);
|
||||
void clearSendBuffer(u32 messagePointer);
|
||||
void disconnect(u32 messagePointer);
|
||||
void finalizeIrnop(u32 messagePointer);
|
||||
void getConnectionStatusEvent(u32 messagePointer);
|
||||
void getReceiveEvent(u32 messagePointer);
|
||||
void getSendEvent(u32 messagePointer);
|
||||
void initializeIrnopShared(u32 messagePointer);
|
||||
void requireConnection(u32 messagePointer);
|
||||
void sendIrnop(u32 messagePointer);
|
||||
void releaseReceivedData(u32 messagePointer);
|
||||
|
||||
using IREvent = std::optional<Handle>;
|
||||
|
||||
IREvent connectionStatusEvent = std::nullopt;
|
||||
IREvent receiveEvent = std::nullopt;
|
||||
IREvent sendEvent = std::nullopt;
|
||||
IR::CirclePadPro cpp;
|
||||
|
||||
std::optional<MemoryBlock> sharedMemory = std::nullopt;
|
||||
std::unique_ptr<IR::Buffer> receiveBuffer;
|
||||
bool connectedDevice = false;
|
||||
|
||||
// Header of the IR shared memory containing various bits of info
|
||||
|
@ -56,8 +73,19 @@ class IRUserService {
|
|||
};
|
||||
static_assert(sizeof(SharedMemoryStatus) == 16);
|
||||
|
||||
// The IR service uses CRC8 with generator polynomial = 0x07 for verifying packets received from IR devices
|
||||
static u8 crc8(std::span<const u8> data);
|
||||
|
||||
// IR devices call this to send a device->console payload
|
||||
void sendPayload(Payload payload);
|
||||
|
||||
public:
|
||||
IRUserService(Memory& mem, Kernel& kernel) : mem(mem), kernel(kernel) {}
|
||||
IRUserService(Memory& mem, HIDService& hid, const EmulatorConfig& config, Kernel& kernel);
|
||||
|
||||
void setCStickX(s16 value) { cpp.state.cStick.x = value; }
|
||||
void setCStickY(s16 value) { cpp.state.cStick.y = value; }
|
||||
|
||||
void reset();
|
||||
void handleSyncRequest(u32 messagePointer);
|
||||
void updateCirclePadPro();
|
||||
};
|
|
@ -244,18 +244,18 @@ namespace Service::NFC {
|
|||
|
||||
#pragma pack(1)
|
||||
struct EncryptedAmiiboFile {
|
||||
u8 constant_value; // Must be A5
|
||||
u16_be write_counter; // Number of times the amiibo has been written?
|
||||
u8 amiibo_version; // Amiibo file version
|
||||
AmiiboSettings settings; // Encrypted amiibo settings
|
||||
HashData hmac_tag; // Hash
|
||||
AmiiboModelInfo model_info; // Encrypted amiibo model info
|
||||
HashData keygen_salt; // Salt
|
||||
HashData hmac_data; // Hash
|
||||
ChecksummedMiiData owner_mii; // Encrypted Mii data
|
||||
u64_be application_id; // Encrypted Game id
|
||||
u16_be application_write_counter; // Encrypted Counter
|
||||
u32_be application_area_id; // Encrypted Game id
|
||||
u8 constant_value; // Must be A5
|
||||
u16_be write_counter; // Number of times the amiibo has been written?
|
||||
u8 amiibo_version; // Amiibo file version
|
||||
AmiiboSettings settings; // Encrypted amiibo settings
|
||||
HashData hmac_tag; // Hash
|
||||
AmiiboModelInfo model_info; // Encrypted amiibo model info
|
||||
HashData keygen_salt; // Salt
|
||||
HashData hmac_data; // Hash
|
||||
ChecksummedMiiData owner_mii; // Encrypted Mii data
|
||||
u64_be application_id; // Encrypted Game id
|
||||
u16_be application_write_counter; // Encrypted Counter
|
||||
u32_be application_area_id; // Encrypted Game id
|
||||
u8 application_id_byte;
|
||||
u8 unknown;
|
||||
u64 mii_extension;
|
||||
|
@ -274,9 +274,9 @@ namespace Service::NFC {
|
|||
u16_be write_counter; // Number of times the amiibo has been written?
|
||||
u8 amiibo_version; // Amiibo file version
|
||||
AmiiboSettings settings;
|
||||
ChecksummedMiiData owner_mii; // Mii data
|
||||
u64_be application_id; // Game id
|
||||
u16_be application_write_counter; // Counter
|
||||
ChecksummedMiiData owner_mii; // Mii data
|
||||
u64_be application_id; // Game id
|
||||
u16_be application_write_counter; // Counter
|
||||
u32_be application_area_id;
|
||||
u8 application_id_byte;
|
||||
u8 unknown;
|
||||
|
|
28
include/services/service_intercept.hpp
Normal file
28
include/services/service_intercept.hpp
Normal file
|
@ -0,0 +1,28 @@
|
|||
#pragma once
|
||||
#include <functional>
|
||||
#include <string>
|
||||
|
||||
#include "helpers.hpp"
|
||||
|
||||
// We allow Lua scripts to intercept service calls and allow their own code to be ran on SyncRequests
|
||||
// For example, if we want to intercept dsp::DSP ReadPipe (Header: 0x000E00C0), the "serviceName" field would be "dsp::DSP"
|
||||
// and the "function" field would be 0x000E00C0
|
||||
struct InterceptedService {
|
||||
std::string serviceName; // Name of the service whose function
|
||||
u32 function; // Header of the function to intercept
|
||||
|
||||
InterceptedService(const std::string& name, u32 header) : serviceName(name), function(header) {}
|
||||
bool operator==(const InterceptedService& other) const { return serviceName == other.serviceName && function == other.function; }
|
||||
};
|
||||
|
||||
// Define hashing function for InterceptedService to use it with unordered_map
|
||||
namespace std {
|
||||
template <>
|
||||
struct hash<InterceptedService> {
|
||||
usize operator()(const InterceptedService& s) const noexcept {
|
||||
const usize hash1 = std::hash<std::string>{}(s.serviceName);
|
||||
const usize hash2 = std::hash<u32>{}(s.function);
|
||||
return hash1 ^ (hash2 << 1);
|
||||
}
|
||||
};
|
||||
} // namespace std
|
|
@ -2,9 +2,12 @@
|
|||
#include <array>
|
||||
#include <optional>
|
||||
#include <span>
|
||||
#include <string>
|
||||
#include <unordered_map>
|
||||
|
||||
#include "kernel_types.hpp"
|
||||
#include "logger.hpp"
|
||||
#include "lua_manager.hpp"
|
||||
#include "memory.hpp"
|
||||
#include "services/ac.hpp"
|
||||
#include "services/act.hpp"
|
||||
|
@ -23,7 +26,7 @@
|
|||
#include "services/gsp_lcd.hpp"
|
||||
#include "services/hid.hpp"
|
||||
#include "services/http.hpp"
|
||||
#include "services/ir_user.hpp"
|
||||
#include "services/ir/ir_user.hpp"
|
||||
#include "services/ldr_ro.hpp"
|
||||
#include "services/mcu/mcu_hwc.hpp"
|
||||
#include "services/mic.hpp"
|
||||
|
@ -34,6 +37,7 @@
|
|||
#include "services/ns.hpp"
|
||||
#include "services/nwm_uds.hpp"
|
||||
#include "services/ptm.hpp"
|
||||
#include "services/service_intercept.hpp"
|
||||
#include "services/soc.hpp"
|
||||
#include "services/ssl.hpp"
|
||||
#include "services/y2r.hpp"
|
||||
|
@ -86,16 +90,32 @@ class ServiceManager {
|
|||
|
||||
MCU::HWCService mcu_hwc;
|
||||
|
||||
// We allow Lua scripts to intercept service calls and allow their own code to be ran on SyncRequests
|
||||
// For example, if we want to intercept dsp::DSP ReadPipe (Header: 0x000E00C0), the "serviceName" field would be "dsp::DSP"
|
||||
// and the "function" field would be 0x000E00C0
|
||||
LuaManager& lua;
|
||||
|
||||
// Map from service intercept entries to their corresponding Lua callbacks
|
||||
std::unordered_map<InterceptedService, int> interceptedServices = {};
|
||||
// Calling std::unordered_map<T>::size() compiles to a non-trivial function call on Clang, so we store this
|
||||
// separately and check it on service calls, for performance reasons
|
||||
bool haveServiceIntercepts = false;
|
||||
|
||||
// Checks for whether a service call is intercepted by Lua and handles it. Returns true if Lua told us not to handle the function,
|
||||
// or false if we should handle it as normal
|
||||
bool checkForIntercept(u32 messagePointer, Handle handle);
|
||||
|
||||
// "srv:" commands
|
||||
void enableNotification(u32 messagePointer);
|
||||
void getServiceHandle(u32 messagePointer);
|
||||
void publishToSubscriber(u32 messagePointer);
|
||||
void receiveNotification(u32 messagePointer);
|
||||
void registerClient(u32 messagePointer);
|
||||
void subscribe(u32 messagePointer);
|
||||
void unsubscribe(u32 messagePointer);
|
||||
|
||||
public:
|
||||
ServiceManager(std::span<u32, 16> regs, Memory& mem, GPU& gpu, u32& currentPID, Kernel& kernel, const EmulatorConfig& config);
|
||||
ServiceManager(std::span<u32, 16> regs, Memory& mem, GPU& gpu, u32& currentPID, Kernel& kernel, const EmulatorConfig& config, LuaManager& lua);
|
||||
void reset();
|
||||
void initializeFS() { fs.initializeFilesystem(); }
|
||||
void handleSyncRequest(u32 messagePointer);
|
||||
|
@ -114,4 +134,26 @@ class ServiceManager {
|
|||
NFCService& getNFC() { return nfc; }
|
||||
DSPService& getDSP() { return dsp; }
|
||||
Y2RService& getY2R() { return y2r; }
|
||||
IRUserService& getIRUser() { return ir_user; }
|
||||
|
||||
void addServiceIntercept(const std::string& service, u32 function, int callbackRef) {
|
||||
auto success = interceptedServices.try_emplace(InterceptedService(service, function), callbackRef);
|
||||
if (!success.second) {
|
||||
// An intercept for this service function already exists
|
||||
// Remove the old callback and set the new one
|
||||
lua.removeInterceptedService(service, function, success.first->second);
|
||||
success.first->second = callbackRef;
|
||||
}
|
||||
|
||||
haveServiceIntercepts = true;
|
||||
}
|
||||
|
||||
void clearServiceIntercepts() {
|
||||
for (const auto& [interceptedService, callbackRef] : interceptedServices) {
|
||||
lua.removeInterceptedService(interceptedService.serviceName, interceptedService.function, callbackRef);
|
||||
}
|
||||
|
||||
interceptedServices.clear();
|
||||
haveServiceIntercepts = false;
|
||||
}
|
||||
};
|
||||
|
|
29
include/services/service_map.hpp
Normal file
29
include/services/service_map.hpp
Normal file
|
@ -0,0 +1,29 @@
|
|||
#pragma once
|
||||
#include <string>
|
||||
#include <type_traits>
|
||||
#include <utility>
|
||||
|
||||
#include "handles.hpp"
|
||||
|
||||
// Helpers for constructing std::maps to look up OS services.
|
||||
// We want to be able to map both service names -> services (Used for OS emulation)
|
||||
// And service handles -> services (For Lua service call intercepts)
|
||||
using ServiceMapEntry = std::pair<std::string, HorizonHandle>;
|
||||
|
||||
// Comparator for constructing a name->handle service map
|
||||
struct ServiceMapByNameComparator {
|
||||
// The comparators must be transparent, as our search key is different from our set key
|
||||
// Our set key is a ServiceMapEntry, while the comparator each time is either the name or the service handle
|
||||
using is_transparent = std::true_type;
|
||||
bool operator()(const ServiceMapEntry& lhs, std::string_view rhs) const { return lhs.first < rhs; }
|
||||
bool operator()(std::string_view lhs, const ServiceMapEntry& rhs) const { return lhs < rhs.first; }
|
||||
bool operator()(const ServiceMapEntry& lhs, const ServiceMapEntry& rhs) const { return lhs.first < rhs.first; }
|
||||
};
|
||||
|
||||
// Comparator for constructing a handle->name service map
|
||||
struct ServiceMapByHandleComparator {
|
||||
using is_transparent = std::true_type;
|
||||
bool operator()(const ServiceMapEntry& lhs, HorizonHandle rhs) const { return lhs.second < rhs; }
|
||||
bool operator()(HorizonHandle lhs, const ServiceMapEntry& rhs) const { return lhs < rhs.second; }
|
||||
bool operator()(const ServiceMapEntry& lhs, const ServiceMapEntry& rhs) const { return lhs.second < rhs.second; }
|
||||
};
|
719
include/swap.hpp
719
include/swap.hpp
|
@ -23,6 +23,7 @@
|
|||
#include <cstdlib>
|
||||
#endif
|
||||
#include <cstring>
|
||||
|
||||
#include "helpers.hpp"
|
||||
|
||||
// GCC
|
||||
|
@ -57,15 +58,9 @@
|
|||
namespace Common {
|
||||
|
||||
#ifdef _MSC_VER
|
||||
[[nodiscard]] inline u16 swap16(u16 data) noexcept {
|
||||
return _byteswap_ushort(data);
|
||||
}
|
||||
[[nodiscard]] inline u32 swap32(u32 data) noexcept {
|
||||
return _byteswap_ulong(data);
|
||||
}
|
||||
[[nodiscard]] inline u64 swap64(u64 data) noexcept {
|
||||
return _byteswap_uint64(data);
|
||||
}
|
||||
[[nodiscard]] inline u16 swap16(u16 data) noexcept { return _byteswap_ushort(data); }
|
||||
[[nodiscard]] inline u32 swap32(u32 data) noexcept { return _byteswap_ulong(data); }
|
||||
[[nodiscard]] inline u64 swap64(u64 data) noexcept { return _byteswap_uint64(data); }
|
||||
#elif defined(__clang__) || defined(__GNUC__)
|
||||
#if defined(__Bitrig__) || defined(__OpenBSD__)
|
||||
// redefine swap16, swap32, swap64 as inline functions
|
||||
|
@ -73,523 +68,441 @@ namespace Common {
|
|||
#undef swap32
|
||||
#undef swap64
|
||||
#endif
|
||||
[[nodiscard]] inline u16 swap16(u16 data) noexcept {
|
||||
return __builtin_bswap16(data);
|
||||
}
|
||||
[[nodiscard]] inline u32 swap32(u32 data) noexcept {
|
||||
return __builtin_bswap32(data);
|
||||
}
|
||||
[[nodiscard]] inline u64 swap64(u64 data) noexcept {
|
||||
return __builtin_bswap64(data);
|
||||
}
|
||||
[[nodiscard]] inline u16 swap16(u16 data) noexcept { return __builtin_bswap16(data); }
|
||||
[[nodiscard]] inline u32 swap32(u32 data) noexcept { return __builtin_bswap32(data); }
|
||||
[[nodiscard]] inline u64 swap64(u64 data) noexcept { return __builtin_bswap64(data); }
|
||||
#else
|
||||
// Generic implementation.
|
||||
[[nodiscard]] inline u16 swap16(u16 data) noexcept {
|
||||
return (data >> 8) | (data << 8);
|
||||
}
|
||||
[[nodiscard]] inline u32 swap32(u32 data) noexcept {
|
||||
return ((data & 0xFF000000U) >> 24) | ((data & 0x00FF0000U) >> 8) |
|
||||
((data & 0x0000FF00U) << 8) | ((data & 0x000000FFU) << 24);
|
||||
}
|
||||
[[nodiscard]] inline u64 swap64(u64 data) noexcept {
|
||||
return ((data & 0xFF00000000000000ULL) >> 56) | ((data & 0x00FF000000000000ULL) >> 40) |
|
||||
((data & 0x0000FF0000000000ULL) >> 24) | ((data & 0x000000FF00000000ULL) >> 8) |
|
||||
((data & 0x00000000FF000000ULL) << 8) | ((data & 0x0000000000FF0000ULL) << 24) |
|
||||
((data & 0x000000000000FF00ULL) << 40) | ((data & 0x00000000000000FFULL) << 56);
|
||||
}
|
||||
// Generic implementation.
|
||||
[[nodiscard]] inline u16 swap16(u16 data) noexcept { return (data >> 8) | (data << 8); }
|
||||
[[nodiscard]] inline u32 swap32(u32 data) noexcept {
|
||||
return ((data & 0xFF000000U) >> 24) | ((data & 0x00FF0000U) >> 8) | ((data & 0x0000FF00U) << 8) | ((data & 0x000000FFU) << 24);
|
||||
}
|
||||
[[nodiscard]] inline u64 swap64(u64 data) noexcept {
|
||||
return ((data & 0xFF00000000000000ULL) >> 56) | ((data & 0x00FF000000000000ULL) >> 40) | ((data & 0x0000FF0000000000ULL) >> 24) |
|
||||
((data & 0x000000FF00000000ULL) >> 8) | ((data & 0x00000000FF000000ULL) << 8) | ((data & 0x0000000000FF0000ULL) << 24) |
|
||||
((data & 0x000000000000FF00ULL) << 40) | ((data & 0x00000000000000FFULL) << 56);
|
||||
}
|
||||
#endif
|
||||
|
||||
[[nodiscard]] inline float swapf(float f) noexcept {
|
||||
static_assert(sizeof(u32) == sizeof(float), "float must be the same size as uint32_t.");
|
||||
[[nodiscard]] inline float swapf(float f) noexcept {
|
||||
static_assert(sizeof(u32) == sizeof(float), "float must be the same size as uint32_t.");
|
||||
|
||||
u32 value;
|
||||
std::memcpy(&value, &f, sizeof(u32));
|
||||
u32 value;
|
||||
std::memcpy(&value, &f, sizeof(u32));
|
||||
|
||||
value = swap32(value);
|
||||
std::memcpy(&f, &value, sizeof(u32));
|
||||
value = swap32(value);
|
||||
std::memcpy(&f, &value, sizeof(u32));
|
||||
|
||||
return f;
|
||||
}
|
||||
return f;
|
||||
}
|
||||
|
||||
[[nodiscard]] inline double swapd(double f) noexcept {
|
||||
static_assert(sizeof(u64) == sizeof(double), "double must be the same size as uint64_t.");
|
||||
[[nodiscard]] inline double swapd(double f) noexcept {
|
||||
static_assert(sizeof(u64) == sizeof(double), "double must be the same size as uint64_t.");
|
||||
|
||||
u64 value;
|
||||
std::memcpy(&value, &f, sizeof(u64));
|
||||
u64 value;
|
||||
std::memcpy(&value, &f, sizeof(u64));
|
||||
|
||||
value = swap64(value);
|
||||
std::memcpy(&f, &value, sizeof(u64));
|
||||
value = swap64(value);
|
||||
std::memcpy(&f, &value, sizeof(u64));
|
||||
|
||||
return f;
|
||||
}
|
||||
return f;
|
||||
}
|
||||
|
||||
} // Namespace Common
|
||||
} // Namespace Common
|
||||
|
||||
template <typename T, typename F>
|
||||
struct swap_struct_t {
|
||||
using swapped_t = swap_struct_t;
|
||||
using swapped_t = swap_struct_t;
|
||||
|
||||
protected:
|
||||
T value;
|
||||
protected:
|
||||
T value;
|
||||
|
||||
static T swap(T v) {
|
||||
return F::swap(v);
|
||||
}
|
||||
static T swap(T v) { return F::swap(v); }
|
||||
|
||||
public:
|
||||
T swap() const {
|
||||
return swap(value);
|
||||
}
|
||||
swap_struct_t() = default;
|
||||
swap_struct_t(const T& v) : value(swap(v)) {}
|
||||
public:
|
||||
T swap() const { return swap(value); }
|
||||
swap_struct_t() = default;
|
||||
swap_struct_t(const T& v) : value(swap(v)) {}
|
||||
|
||||
template <typename S>
|
||||
swapped_t& operator=(const S& source) {
|
||||
value = swap(static_cast<T>(source));
|
||||
return *this;
|
||||
}
|
||||
template <typename S>
|
||||
swapped_t& operator=(const S& source) {
|
||||
value = swap(static_cast<T>(source));
|
||||
return *this;
|
||||
}
|
||||
|
||||
operator s8() const {
|
||||
return static_cast<s8>(swap());
|
||||
}
|
||||
operator u8() const {
|
||||
return static_cast<u8>(swap());
|
||||
}
|
||||
operator s16() const {
|
||||
return static_cast<s16>(swap());
|
||||
}
|
||||
operator u16() const {
|
||||
return static_cast<u16>(swap());
|
||||
}
|
||||
operator s32() const {
|
||||
return static_cast<s32>(swap());
|
||||
}
|
||||
operator u32() const {
|
||||
return static_cast<u32>(swap());
|
||||
}
|
||||
operator s64() const {
|
||||
return static_cast<s64>(swap());
|
||||
}
|
||||
operator u64() const {
|
||||
return static_cast<u64>(swap());
|
||||
}
|
||||
operator float() const {
|
||||
return static_cast<float>(swap());
|
||||
}
|
||||
operator double() const {
|
||||
return static_cast<double>(swap());
|
||||
}
|
||||
operator s8() const { return static_cast<s8>(swap()); }
|
||||
operator u8() const { return static_cast<u8>(swap()); }
|
||||
operator s16() const { return static_cast<s16>(swap()); }
|
||||
operator u16() const { return static_cast<u16>(swap()); }
|
||||
operator s32() const { return static_cast<s32>(swap()); }
|
||||
operator u32() const { return static_cast<u32>(swap()); }
|
||||
operator s64() const { return static_cast<s64>(swap()); }
|
||||
operator u64() const { return static_cast<u64>(swap()); }
|
||||
operator float() const { return static_cast<float>(swap()); }
|
||||
operator double() const { return static_cast<double>(swap()); }
|
||||
|
||||
// +v
|
||||
swapped_t operator+() const {
|
||||
return +swap();
|
||||
}
|
||||
// -v
|
||||
swapped_t operator-() const {
|
||||
return -swap();
|
||||
}
|
||||
// +v
|
||||
swapped_t operator+() const { return +swap(); }
|
||||
// -v
|
||||
swapped_t operator-() const { return -swap(); }
|
||||
|
||||
// v / 5
|
||||
swapped_t operator/(const swapped_t& i) const {
|
||||
return swap() / i.swap();
|
||||
}
|
||||
template <typename S>
|
||||
swapped_t operator/(const S& i) const {
|
||||
return swap() / i;
|
||||
}
|
||||
// v / 5
|
||||
swapped_t operator/(const swapped_t& i) const { return swap() / i.swap(); }
|
||||
template <typename S>
|
||||
swapped_t operator/(const S& i) const {
|
||||
return swap() / i;
|
||||
}
|
||||
|
||||
// v * 5
|
||||
swapped_t operator*(const swapped_t& i) const {
|
||||
return swap() * i.swap();
|
||||
}
|
||||
template <typename S>
|
||||
swapped_t operator*(const S& i) const {
|
||||
return swap() * i;
|
||||
}
|
||||
// v * 5
|
||||
swapped_t operator*(const swapped_t& i) const { return swap() * i.swap(); }
|
||||
template <typename S>
|
||||
swapped_t operator*(const S& i) const {
|
||||
return swap() * i;
|
||||
}
|
||||
|
||||
// v + 5
|
||||
swapped_t operator+(const swapped_t& i) const {
|
||||
return swap() + i.swap();
|
||||
}
|
||||
template <typename S>
|
||||
swapped_t operator+(const S& i) const {
|
||||
return swap() + static_cast<T>(i);
|
||||
}
|
||||
// v - 5
|
||||
swapped_t operator-(const swapped_t& i) const {
|
||||
return swap() - i.swap();
|
||||
}
|
||||
template <typename S>
|
||||
swapped_t operator-(const S& i) const {
|
||||
return swap() - static_cast<T>(i);
|
||||
}
|
||||
// v + 5
|
||||
swapped_t operator+(const swapped_t& i) const { return swap() + i.swap(); }
|
||||
template <typename S>
|
||||
swapped_t operator+(const S& i) const {
|
||||
return swap() + static_cast<T>(i);
|
||||
}
|
||||
// v - 5
|
||||
swapped_t operator-(const swapped_t& i) const { return swap() - i.swap(); }
|
||||
template <typename S>
|
||||
swapped_t operator-(const S& i) const {
|
||||
return swap() - static_cast<T>(i);
|
||||
}
|
||||
|
||||
// v += 5
|
||||
swapped_t& operator+=(const swapped_t& i) {
|
||||
value = swap(swap() + i.swap());
|
||||
return *this;
|
||||
}
|
||||
template <typename S>
|
||||
swapped_t& operator+=(const S& i) {
|
||||
value = swap(swap() + static_cast<T>(i));
|
||||
return *this;
|
||||
}
|
||||
// v -= 5
|
||||
swapped_t& operator-=(const swapped_t& i) {
|
||||
value = swap(swap() - i.swap());
|
||||
return *this;
|
||||
}
|
||||
template <typename S>
|
||||
swapped_t& operator-=(const S& i) {
|
||||
value = swap(swap() - static_cast<T>(i));
|
||||
return *this;
|
||||
}
|
||||
// v += 5
|
||||
swapped_t& operator+=(const swapped_t& i) {
|
||||
value = swap(swap() + i.swap());
|
||||
return *this;
|
||||
}
|
||||
template <typename S>
|
||||
swapped_t& operator+=(const S& i) {
|
||||
value = swap(swap() + static_cast<T>(i));
|
||||
return *this;
|
||||
}
|
||||
// v -= 5
|
||||
swapped_t& operator-=(const swapped_t& i) {
|
||||
value = swap(swap() - i.swap());
|
||||
return *this;
|
||||
}
|
||||
template <typename S>
|
||||
swapped_t& operator-=(const S& i) {
|
||||
value = swap(swap() - static_cast<T>(i));
|
||||
return *this;
|
||||
}
|
||||
|
||||
// ++v
|
||||
swapped_t& operator++() {
|
||||
value = swap(swap() + 1);
|
||||
return *this;
|
||||
}
|
||||
// --v
|
||||
swapped_t& operator--() {
|
||||
value = swap(swap() - 1);
|
||||
return *this;
|
||||
}
|
||||
// ++v
|
||||
swapped_t& operator++() {
|
||||
value = swap(swap() + 1);
|
||||
return *this;
|
||||
}
|
||||
// --v
|
||||
swapped_t& operator--() {
|
||||
value = swap(swap() - 1);
|
||||
return *this;
|
||||
}
|
||||
|
||||
// v++
|
||||
swapped_t operator++(int) {
|
||||
swapped_t old = *this;
|
||||
value = swap(swap() + 1);
|
||||
return old;
|
||||
}
|
||||
// v--
|
||||
swapped_t operator--(int) {
|
||||
swapped_t old = *this;
|
||||
value = swap(swap() - 1);
|
||||
return old;
|
||||
}
|
||||
// Comparaison
|
||||
// v == i
|
||||
bool operator==(const swapped_t& i) const {
|
||||
return swap() == i.swap();
|
||||
}
|
||||
template <typename S>
|
||||
bool operator==(const S& i) const {
|
||||
return swap() == i;
|
||||
}
|
||||
// v++
|
||||
swapped_t operator++(int) {
|
||||
swapped_t old = *this;
|
||||
value = swap(swap() + 1);
|
||||
return old;
|
||||
}
|
||||
// v--
|
||||
swapped_t operator--(int) {
|
||||
swapped_t old = *this;
|
||||
value = swap(swap() - 1);
|
||||
return old;
|
||||
}
|
||||
// Comparaison
|
||||
// v == i
|
||||
bool operator==(const swapped_t& i) const { return swap() == i.swap(); }
|
||||
template <typename S>
|
||||
bool operator==(const S& i) const {
|
||||
return swap() == i;
|
||||
}
|
||||
|
||||
// v != i
|
||||
bool operator!=(const swapped_t& i) const {
|
||||
return swap() != i.swap();
|
||||
}
|
||||
template <typename S>
|
||||
bool operator!=(const S& i) const {
|
||||
return swap() != i;
|
||||
}
|
||||
// v != i
|
||||
bool operator!=(const swapped_t& i) const { return swap() != i.swap(); }
|
||||
template <typename S>
|
||||
bool operator!=(const S& i) const {
|
||||
return swap() != i;
|
||||
}
|
||||
|
||||
// v > i
|
||||
bool operator>(const swapped_t& i) const {
|
||||
return swap() > i.swap();
|
||||
}
|
||||
template <typename S>
|
||||
bool operator>(const S& i) const {
|
||||
return swap() > i;
|
||||
}
|
||||
// v > i
|
||||
bool operator>(const swapped_t& i) const { return swap() > i.swap(); }
|
||||
template <typename S>
|
||||
bool operator>(const S& i) const {
|
||||
return swap() > i;
|
||||
}
|
||||
|
||||
// v < i
|
||||
bool operator<(const swapped_t& i) const {
|
||||
return swap() < i.swap();
|
||||
}
|
||||
template <typename S>
|
||||
bool operator<(const S& i) const {
|
||||
return swap() < i;
|
||||
}
|
||||
// v < i
|
||||
bool operator<(const swapped_t& i) const { return swap() < i.swap(); }
|
||||
template <typename S>
|
||||
bool operator<(const S& i) const {
|
||||
return swap() < i;
|
||||
}
|
||||
|
||||
// v >= i
|
||||
bool operator>=(const swapped_t& i) const {
|
||||
return swap() >= i.swap();
|
||||
}
|
||||
template <typename S>
|
||||
bool operator>=(const S& i) const {
|
||||
return swap() >= i;
|
||||
}
|
||||
// v >= i
|
||||
bool operator>=(const swapped_t& i) const { return swap() >= i.swap(); }
|
||||
template <typename S>
|
||||
bool operator>=(const S& i) const {
|
||||
return swap() >= i;
|
||||
}
|
||||
|
||||
// v <= i
|
||||
bool operator<=(const swapped_t& i) const {
|
||||
return swap() <= i.swap();
|
||||
}
|
||||
template <typename S>
|
||||
bool operator<=(const S& i) const {
|
||||
return swap() <= i;
|
||||
}
|
||||
// v <= i
|
||||
bool operator<=(const swapped_t& i) const { return swap() <= i.swap(); }
|
||||
template <typename S>
|
||||
bool operator<=(const S& i) const {
|
||||
return swap() <= i;
|
||||
}
|
||||
|
||||
// logical
|
||||
swapped_t operator!() const {
|
||||
return !swap();
|
||||
}
|
||||
// logical
|
||||
swapped_t operator!() const { return !swap(); }
|
||||
|
||||
// bitmath
|
||||
swapped_t operator~() const {
|
||||
return ~swap();
|
||||
}
|
||||
// bitmath
|
||||
swapped_t operator~() const { return ~swap(); }
|
||||
|
||||
swapped_t operator&(const swapped_t& b) const {
|
||||
return swap() & b.swap();
|
||||
}
|
||||
template <typename S>
|
||||
swapped_t operator&(const S& b) const {
|
||||
return swap() & b;
|
||||
}
|
||||
swapped_t& operator&=(const swapped_t& b) {
|
||||
value = swap(swap() & b.swap());
|
||||
return *this;
|
||||
}
|
||||
template <typename S>
|
||||
swapped_t& operator&=(const S b) {
|
||||
value = swap(swap() & b);
|
||||
return *this;
|
||||
}
|
||||
swapped_t operator&(const swapped_t& b) const { return swap() & b.swap(); }
|
||||
template <typename S>
|
||||
swapped_t operator&(const S& b) const {
|
||||
return swap() & b;
|
||||
}
|
||||
swapped_t& operator&=(const swapped_t& b) {
|
||||
value = swap(swap() & b.swap());
|
||||
return *this;
|
||||
}
|
||||
template <typename S>
|
||||
swapped_t& operator&=(const S b) {
|
||||
value = swap(swap() & b);
|
||||
return *this;
|
||||
}
|
||||
|
||||
swapped_t operator|(const swapped_t& b) const {
|
||||
return swap() | b.swap();
|
||||
}
|
||||
template <typename S>
|
||||
swapped_t operator|(const S& b) const {
|
||||
return swap() | b;
|
||||
}
|
||||
swapped_t& operator|=(const swapped_t& b) {
|
||||
value = swap(swap() | b.swap());
|
||||
return *this;
|
||||
}
|
||||
template <typename S>
|
||||
swapped_t& operator|=(const S& b) {
|
||||
value = swap(swap() | b);
|
||||
return *this;
|
||||
}
|
||||
swapped_t operator|(const swapped_t& b) const { return swap() | b.swap(); }
|
||||
template <typename S>
|
||||
swapped_t operator|(const S& b) const {
|
||||
return swap() | b;
|
||||
}
|
||||
swapped_t& operator|=(const swapped_t& b) {
|
||||
value = swap(swap() | b.swap());
|
||||
return *this;
|
||||
}
|
||||
template <typename S>
|
||||
swapped_t& operator|=(const S& b) {
|
||||
value = swap(swap() | b);
|
||||
return *this;
|
||||
}
|
||||
|
||||
swapped_t operator^(const swapped_t& b) const {
|
||||
return swap() ^ b.swap();
|
||||
}
|
||||
template <typename S>
|
||||
swapped_t operator^(const S& b) const {
|
||||
return swap() ^ b;
|
||||
}
|
||||
swapped_t& operator^=(const swapped_t& b) {
|
||||
value = swap(swap() ^ b.swap());
|
||||
return *this;
|
||||
}
|
||||
template <typename S>
|
||||
swapped_t& operator^=(const S& b) {
|
||||
value = swap(swap() ^ b);
|
||||
return *this;
|
||||
}
|
||||
swapped_t operator^(const swapped_t& b) const { return swap() ^ b.swap(); }
|
||||
template <typename S>
|
||||
swapped_t operator^(const S& b) const {
|
||||
return swap() ^ b;
|
||||
}
|
||||
swapped_t& operator^=(const swapped_t& b) {
|
||||
value = swap(swap() ^ b.swap());
|
||||
return *this;
|
||||
}
|
||||
template <typename S>
|
||||
swapped_t& operator^=(const S& b) {
|
||||
value = swap(swap() ^ b);
|
||||
return *this;
|
||||
}
|
||||
|
||||
template <typename S>
|
||||
swapped_t operator<<(const S& b) const {
|
||||
return swap() << b;
|
||||
}
|
||||
template <typename S>
|
||||
swapped_t& operator<<=(const S& b) const {
|
||||
value = swap(swap() << b);
|
||||
return *this;
|
||||
}
|
||||
template <typename S>
|
||||
swapped_t operator<<(const S& b) const {
|
||||
return swap() << b;
|
||||
}
|
||||
template <typename S>
|
||||
swapped_t& operator<<=(const S& b) const {
|
||||
value = swap(swap() << b);
|
||||
return *this;
|
||||
}
|
||||
|
||||
template <typename S>
|
||||
swapped_t operator>>(const S& b) const {
|
||||
return swap() >> b;
|
||||
}
|
||||
template <typename S>
|
||||
swapped_t& operator>>=(const S& b) const {
|
||||
value = swap(swap() >> b);
|
||||
return *this;
|
||||
}
|
||||
template <typename S>
|
||||
swapped_t operator>>(const S& b) const {
|
||||
return swap() >> b;
|
||||
}
|
||||
template <typename S>
|
||||
swapped_t& operator>>=(const S& b) const {
|
||||
value = swap(swap() >> b);
|
||||
return *this;
|
||||
}
|
||||
|
||||
// Member
|
||||
/** todo **/
|
||||
// Member
|
||||
/** todo **/
|
||||
|
||||
// Arithmetics
|
||||
template <typename S, typename T2, typename F2>
|
||||
friend S operator+(const S& p, const swapped_t v);
|
||||
// Arithmetics
|
||||
template <typename S, typename T2, typename F2>
|
||||
friend S operator+(const S& p, const swapped_t v);
|
||||
|
||||
template <typename S, typename T2, typename F2>
|
||||
friend S operator-(const S& p, const swapped_t v);
|
||||
template <typename S, typename T2, typename F2>
|
||||
friend S operator-(const S& p, const swapped_t v);
|
||||
|
||||
template <typename S, typename T2, typename F2>
|
||||
friend S operator/(const S& p, const swapped_t v);
|
||||
template <typename S, typename T2, typename F2>
|
||||
friend S operator/(const S& p, const swapped_t v);
|
||||
|
||||
template <typename S, typename T2, typename F2>
|
||||
friend S operator*(const S& p, const swapped_t v);
|
||||
template <typename S, typename T2, typename F2>
|
||||
friend S operator*(const S& p, const swapped_t v);
|
||||
|
||||
template <typename S, typename T2, typename F2>
|
||||
friend S operator%(const S& p, const swapped_t v);
|
||||
template <typename S, typename T2, typename F2>
|
||||
friend S operator%(const S& p, const swapped_t v);
|
||||
|
||||
// Arithmetics + assignments
|
||||
template <typename S, typename T2, typename F2>
|
||||
friend S operator+=(const S& p, const swapped_t v);
|
||||
// Arithmetics + assignments
|
||||
template <typename S, typename T2, typename F2>
|
||||
friend S operator+=(const S& p, const swapped_t v);
|
||||
|
||||
template <typename S, typename T2, typename F2>
|
||||
friend S operator-=(const S& p, const swapped_t v);
|
||||
template <typename S, typename T2, typename F2>
|
||||
friend S operator-=(const S& p, const swapped_t v);
|
||||
|
||||
// Bitmath
|
||||
template <typename S, typename T2, typename F2>
|
||||
friend S operator&(const S& p, const swapped_t v);
|
||||
// Bitmath
|
||||
template <typename S, typename T2, typename F2>
|
||||
friend S operator&(const S& p, const swapped_t v);
|
||||
|
||||
// Comparison
|
||||
template <typename S, typename T2, typename F2>
|
||||
friend bool operator<(const S& p, const swapped_t v);
|
||||
// Comparison
|
||||
template <typename S, typename T2, typename F2>
|
||||
friend bool operator<(const S& p, const swapped_t v);
|
||||
|
||||
template <typename S, typename T2, typename F2>
|
||||
friend bool operator>(const S& p, const swapped_t v);
|
||||
template <typename S, typename T2, typename F2>
|
||||
friend bool operator>(const S& p, const swapped_t v);
|
||||
|
||||
template <typename S, typename T2, typename F2>
|
||||
friend bool operator<=(const S& p, const swapped_t v);
|
||||
template <typename S, typename T2, typename F2>
|
||||
friend bool operator<=(const S& p, const swapped_t v);
|
||||
|
||||
template <typename S, typename T2, typename F2>
|
||||
friend bool operator>=(const S& p, const swapped_t v);
|
||||
template <typename S, typename T2, typename F2>
|
||||
friend bool operator>=(const S& p, const swapped_t v);
|
||||
|
||||
template <typename S, typename T2, typename F2>
|
||||
friend bool operator!=(const S& p, const swapped_t v);
|
||||
template <typename S, typename T2, typename F2>
|
||||
friend bool operator!=(const S& p, const swapped_t v);
|
||||
|
||||
template <typename S, typename T2, typename F2>
|
||||
friend bool operator==(const S& p, const swapped_t v);
|
||||
template <typename S, typename T2, typename F2>
|
||||
friend bool operator==(const S& p, const swapped_t v);
|
||||
};
|
||||
|
||||
// Arithmetics
|
||||
template <typename S, typename T, typename F>
|
||||
S operator+(const S& i, const swap_struct_t<T, F> v) {
|
||||
return i + v.swap();
|
||||
return i + v.swap();
|
||||
}
|
||||
|
||||
template <typename S, typename T, typename F>
|
||||
S operator-(const S& i, const swap_struct_t<T, F> v) {
|
||||
return i - v.swap();
|
||||
return i - v.swap();
|
||||
}
|
||||
|
||||
template <typename S, typename T, typename F>
|
||||
S operator/(const S& i, const swap_struct_t<T, F> v) {
|
||||
return i / v.swap();
|
||||
return i / v.swap();
|
||||
}
|
||||
|
||||
template <typename S, typename T, typename F>
|
||||
S operator*(const S& i, const swap_struct_t<T, F> v) {
|
||||
return i * v.swap();
|
||||
return i * v.swap();
|
||||
}
|
||||
|
||||
template <typename S, typename T, typename F>
|
||||
S operator%(const S& i, const swap_struct_t<T, F> v) {
|
||||
return i % v.swap();
|
||||
return i % v.swap();
|
||||
}
|
||||
|
||||
// Arithmetics + assignments
|
||||
template <typename S, typename T, typename F>
|
||||
S& operator+=(S& i, const swap_struct_t<T, F> v) {
|
||||
i += v.swap();
|
||||
return i;
|
||||
i += v.swap();
|
||||
return i;
|
||||
}
|
||||
|
||||
template <typename S, typename T, typename F>
|
||||
S& operator-=(S& i, const swap_struct_t<T, F> v) {
|
||||
i -= v.swap();
|
||||
return i;
|
||||
i -= v.swap();
|
||||
return i;
|
||||
}
|
||||
|
||||
// Logical
|
||||
template <typename S, typename T, typename F>
|
||||
S operator&(const S& i, const swap_struct_t<T, F> v) {
|
||||
return i & v.swap();
|
||||
return i & v.swap();
|
||||
}
|
||||
|
||||
template <typename S, typename T, typename F>
|
||||
S operator&(const swap_struct_t<T, F> v, const S& i) {
|
||||
return static_cast<S>(v.swap() & i);
|
||||
return static_cast<S>(v.swap() & i);
|
||||
}
|
||||
|
||||
// Comparaison
|
||||
template <typename S, typename T, typename F>
|
||||
bool operator<(const S& p, const swap_struct_t<T, F> v) {
|
||||
return p < v.swap();
|
||||
return p < v.swap();
|
||||
}
|
||||
template <typename S, typename T, typename F>
|
||||
bool operator>(const S& p, const swap_struct_t<T, F> v) {
|
||||
return p > v.swap();
|
||||
return p > v.swap();
|
||||
}
|
||||
template <typename S, typename T, typename F>
|
||||
bool operator<=(const S& p, const swap_struct_t<T, F> v) {
|
||||
return p <= v.swap();
|
||||
return p <= v.swap();
|
||||
}
|
||||
template <typename S, typename T, typename F>
|
||||
bool operator>=(const S& p, const swap_struct_t<T, F> v) {
|
||||
return p >= v.swap();
|
||||
return p >= v.swap();
|
||||
}
|
||||
template <typename S, typename T, typename F>
|
||||
bool operator!=(const S& p, const swap_struct_t<T, F> v) {
|
||||
return p != v.swap();
|
||||
return p != v.swap();
|
||||
}
|
||||
template <typename S, typename T, typename F>
|
||||
bool operator==(const S& p, const swap_struct_t<T, F> v) {
|
||||
return p == v.swap();
|
||||
return p == v.swap();
|
||||
}
|
||||
|
||||
template <typename T>
|
||||
struct swap_64_t {
|
||||
static T swap(T x) {
|
||||
return static_cast<T>(Common::swap64(x));
|
||||
}
|
||||
static T swap(T x) { return static_cast<T>(Common::swap64(x)); }
|
||||
};
|
||||
|
||||
template <typename T>
|
||||
struct swap_32_t {
|
||||
static T swap(T x) {
|
||||
return static_cast<T>(Common::swap32(x));
|
||||
}
|
||||
static T swap(T x) { return static_cast<T>(Common::swap32(x)); }
|
||||
};
|
||||
|
||||
template <typename T>
|
||||
struct swap_16_t {
|
||||
static T swap(T x) {
|
||||
return static_cast<T>(Common::swap16(x));
|
||||
}
|
||||
static T swap(T x) { return static_cast<T>(Common::swap16(x)); }
|
||||
};
|
||||
|
||||
template <typename T>
|
||||
struct swap_float_t {
|
||||
static T swap(T x) {
|
||||
return static_cast<T>(Common::swapf(x));
|
||||
}
|
||||
static T swap(T x) { return static_cast<T>(Common::swapf(x)); }
|
||||
};
|
||||
|
||||
template <typename T>
|
||||
struct swap_double_t {
|
||||
static T swap(T x) {
|
||||
return static_cast<T>(Common::swapd(x));
|
||||
}
|
||||
static T swap(T x) { return static_cast<T>(Common::swapd(x)); }
|
||||
};
|
||||
|
||||
template <typename T>
|
||||
struct swap_enum_t {
|
||||
static_assert(std::is_enum_v<T>);
|
||||
using base = std::underlying_type_t<T>;
|
||||
static_assert(std::is_enum_v<T>);
|
||||
using base = std::underlying_type_t<T>;
|
||||
|
||||
public:
|
||||
swap_enum_t() = default;
|
||||
swap_enum_t(const T& v) : value(swap(v)) {}
|
||||
public:
|
||||
swap_enum_t() = default;
|
||||
swap_enum_t(const T& v) : value(swap(v)) {}
|
||||
|
||||
swap_enum_t& operator=(const T& v) {
|
||||
value = swap(v);
|
||||
return *this;
|
||||
}
|
||||
swap_enum_t& operator=(const T& v) {
|
||||
value = swap(v);
|
||||
return *this;
|
||||
}
|
||||
|
||||
operator T() const {
|
||||
return swap(value);
|
||||
}
|
||||
operator T() const { return swap(value); }
|
||||
|
||||
explicit operator base() const {
|
||||
return static_cast<base>(swap(value));
|
||||
}
|
||||
explicit operator base() const { return static_cast<base>(swap(value)); }
|
||||
|
||||
protected:
|
||||
T value{};
|
||||
// clang-format off
|
||||
protected:
|
||||
T value{};
|
||||
// clang-format off
|
||||
using swap_t = std::conditional_t<
|
||||
std::is_same_v<base, u16>, swap_16_t<u16>, std::conditional_t<
|
||||
std::is_same_v<base, s16>, swap_16_t<s16>, std::conditional_t<
|
||||
|
@ -597,14 +510,12 @@ protected:
|
|||
std::is_same_v<base, s32>, swap_32_t<s32>, std::conditional_t<
|
||||
std::is_same_v<base, u64>, swap_64_t<u64>, std::conditional_t<
|
||||
std::is_same_v<base, s64>, swap_64_t<s64>, void>>>>>>;
|
||||
// clang-format on
|
||||
static T swap(T x) {
|
||||
return static_cast<T>(swap_t::swap(static_cast<base>(x)));
|
||||
}
|
||||
// clang-format on
|
||||
static T swap(T x) { return static_cast<T>(swap_t::swap(static_cast<base>(x))); }
|
||||
};
|
||||
|
||||
struct SwapTag {}; // Use the different endianness from the system
|
||||
struct KeepTag {}; // Use the same endianness as the system
|
||||
struct SwapTag {}; // Use the different endianness from the system
|
||||
struct KeepTag {}; // Use the same endianness as the system
|
||||
|
||||
template <typename T, typename Tag>
|
||||
struct AddEndian;
|
||||
|
@ -613,65 +524,65 @@ struct AddEndian;
|
|||
|
||||
template <typename T>
|
||||
struct AddEndian<T, KeepTag> {
|
||||
using type = T;
|
||||
using type = T;
|
||||
};
|
||||
|
||||
// SwapTag specializations
|
||||
|
||||
template <>
|
||||
struct AddEndian<u8, SwapTag> {
|
||||
using type = u8;
|
||||
using type = u8;
|
||||
};
|
||||
|
||||
template <>
|
||||
struct AddEndian<u16, SwapTag> {
|
||||
using type = swap_struct_t<u16, swap_16_t<u16>>;
|
||||
using type = swap_struct_t<u16, swap_16_t<u16>>;
|
||||
};
|
||||
|
||||
template <>
|
||||
struct AddEndian<u32, SwapTag> {
|
||||
using type = swap_struct_t<u32, swap_32_t<u32>>;
|
||||
using type = swap_struct_t<u32, swap_32_t<u32>>;
|
||||
};
|
||||
|
||||
template <>
|
||||
struct AddEndian<u64, SwapTag> {
|
||||
using type = swap_struct_t<u64, swap_64_t<u64>>;
|
||||
using type = swap_struct_t<u64, swap_64_t<u64>>;
|
||||
};
|
||||
|
||||
template <>
|
||||
struct AddEndian<s8, SwapTag> {
|
||||
using type = s8;
|
||||
using type = s8;
|
||||
};
|
||||
|
||||
template <>
|
||||
struct AddEndian<s16, SwapTag> {
|
||||
using type = swap_struct_t<s16, swap_16_t<s16>>;
|
||||
using type = swap_struct_t<s16, swap_16_t<s16>>;
|
||||
};
|
||||
|
||||
template <>
|
||||
struct AddEndian<s32, SwapTag> {
|
||||
using type = swap_struct_t<s32, swap_32_t<s32>>;
|
||||
using type = swap_struct_t<s32, swap_32_t<s32>>;
|
||||
};
|
||||
|
||||
template <>
|
||||
struct AddEndian<s64, SwapTag> {
|
||||
using type = swap_struct_t<s64, swap_64_t<s64>>;
|
||||
using type = swap_struct_t<s64, swap_64_t<s64>>;
|
||||
};
|
||||
|
||||
template <>
|
||||
struct AddEndian<float, SwapTag> {
|
||||
using type = swap_struct_t<float, swap_float_t<float>>;
|
||||
using type = swap_struct_t<float, swap_float_t<float>>;
|
||||
};
|
||||
|
||||
template <>
|
||||
struct AddEndian<double, SwapTag> {
|
||||
using type = swap_struct_t<double, swap_double_t<double>>;
|
||||
using type = swap_struct_t<double, swap_double_t<double>>;
|
||||
};
|
||||
|
||||
template <typename T>
|
||||
struct AddEndian<T, SwapTag> {
|
||||
static_assert(std::is_enum_v<T>);
|
||||
using type = swap_enum_t<T>;
|
||||
static_assert(std::is_enum_v<T>);
|
||||
using type = swap_enum_t<T>;
|
||||
};
|
||||
|
||||
// Alias LETag/BETag as KeepTag/SwapTag depending on the system
|
||||
|
|
|
@ -18,6 +18,6 @@ namespace SystemModel {
|
|||
KTR = NewNintendo3DS,
|
||||
FTR = Nintendo2DS,
|
||||
RED = NewNintendo3DS_XL,
|
||||
JAN = NewNintendo2DS_XL
|
||||
JAN = NewNintendo2DS_XL,
|
||||
};
|
||||
}
|
|
@ -35,7 +35,7 @@ Panda3DS is still in the early stages of development. Many games boot, many don'
|
|||
|
||||
For documenting game compatibility, make sure to visit the [games list repository](https://github.com/Panda3DS-emu/Panda3DS-Games-List). For miscellaneous issues or more technical issues, feel free to use this repo's issues tab.
|
||||
# Why?
|
||||
The 3DS emulation scene is already pretty mature, with offerings such as [Citra](https://github.com/citra-emu/citra) which can offer a great playing experience for most games in the library, [Corgi3DS](https://github.com/PSI-Rockin/Corgi3DS), an innovative LLE emulator, or [Mikage](https://mikage.app/). However, there's always room for more emulators! While Panda3DS was initially a mere curiosity, there's many different concepts I would like to explore with it in the future, such as:
|
||||
The 3DS emulation scene is already pretty mature, with offerings such as Citra which can offer a great playing experience for most games in the library, [Corgi3DS](https://github.com/PSI-Rockin/Corgi3DS), an innovative LLE emulator, or [Mikage](https://mikage.app/). However, there's always room for more emulators! While Panda3DS was initially a mere curiosity, there's many different concepts I would like to explore with it in the future, such as:
|
||||
|
||||
- Virtualization. What motivated the creation of this emulator was actually a discussion on whether it is possible to get fast 3DS emulation on low-end hardware such as the Raspberry Pi 4, using the KVM API. At the moment, Panda3DS is powered by Dynarmic rather than using virtualization, but this is definitely a concept I want to explore in the future.
|
||||
|
||||
|
@ -113,11 +113,12 @@ Panda3DS also supports controller input using the SDL2 GameController API.
|
|||
- [SkyEmu](https://github.com/skylersaleh/SkyEmu): A seagull-themed low-level GameBoy, GameBoy Color, GameBoy Advance and Nintendo DS emulator that is designed to be easy to use, cross platform and accurate.
|
||||
- [NanoBoyAdvance](https://github.com/nba-emu/NanoBoyAdvance): A Game Boy Advance emulator focusing on hardware research and cycle-accurate emulation
|
||||
- [Dust](https://github.com/kelpsyberry/dust): Nintendo DS emulator for desktop devices and the web
|
||||
- [felix86](https://github.com/OFFTKP/felix86): A new x86-64 → RISC-V Linux userspace emulator
|
||||
- [ChonkyStation](https://github.com/liuk7071/ChonkyStation): Work-in-progress PlayStation emulator
|
||||
- [ChonkyStation 3](https://github.com/liuk7071/ChonkyStation3): Experimental HLE PS3 emulator for Windows, MacOS and Linux
|
||||
- [MelonDS](https://github.com/melonDS-emu/melonDS): "DS emulator, sorta" - Arisotura
|
||||
- [Kaizen](https://github.com/SimoneN64/Kaizen): Experimental work-in-progress low-level N64 emulator
|
||||
- [ChonkyStation](https://github.com/liuk7071/ChonkyStation): Work-in-progress PlayStation emulator
|
||||
- [shadPS4](https://github.com/shadps4-emu/shadPS4): Work-in-progress PS4 emulator by the founder of PCSX, PCSX2 and more
|
||||
- [Hydra](https://github.com/hydra-emu/hydra): Cross-platform GameBoy, NES, N64 and Chip-8 emulator
|
||||
- [Tanuki3DS](https://github.com/burhanr13/Tanuki3DS/): A new 3DS emulator for MacOS and Linux
|
||||
# Support
|
||||
If you find this project exciting and want to support the founder, check out [his Patreon](https://www.patreon.com/wheremyfoodat) or [Ko-fi](https://ko-fi.com/wheremyfoodat)
|
||||
|
|
|
@ -47,6 +47,7 @@ void EmulatorConfig::load() {
|
|||
defaultRomPath = toml::find_or<std::string>(general, "DefaultRomPath", "");
|
||||
|
||||
printAppVersion = toml::find_or<toml::boolean>(general, "PrintAppVersion", true);
|
||||
circlePadProEnabled = toml::find_or<toml::boolean>(general, "EnableCirclePadPro", true);
|
||||
systemLanguage = languageCodeFromString(toml::find_or<std::string>(general, "SystemLanguage", "en"));
|
||||
}
|
||||
}
|
||||
|
@ -72,14 +73,14 @@ void EmulatorConfig::load() {
|
|||
auto gpu = gpuResult.unwrap();
|
||||
|
||||
// Get renderer
|
||||
auto rendererName = toml::find_or<std::string>(gpu, "Renderer", "OpenGL");
|
||||
auto rendererName = toml::find_or<std::string>(gpu, "Renderer", Renderer::typeToString(rendererDefault));
|
||||
auto configRendererType = Renderer::typeFromString(rendererName);
|
||||
|
||||
if (configRendererType.has_value()) {
|
||||
rendererType = configRendererType.value();
|
||||
} else {
|
||||
Helpers::warn("Invalid renderer specified: %s\n", rendererName.c_str());
|
||||
rendererType = RendererType::OpenGL;
|
||||
rendererType = rendererDefault;
|
||||
}
|
||||
|
||||
shaderJitEnabled = toml::find_or<toml::boolean>(gpu, "EnableShaderJIT", shaderJitDefault);
|
||||
|
@ -90,7 +91,12 @@ void EmulatorConfig::load() {
|
|||
|
||||
forceShadergenForLights = toml::find_or<toml::boolean>(gpu, "ForceShadergenForLighting", true);
|
||||
lightShadergenThreshold = toml::find_or<toml::integer>(gpu, "ShadergenLightThreshold", 1);
|
||||
hashTextures = toml::find_or<toml::boolean>(gpu, "HashTextures", hashTexturesDefault);
|
||||
enableRenderdoc = toml::find_or<toml::boolean>(gpu, "EnableRenderdoc", false);
|
||||
|
||||
auto screenLayoutName = toml::find_or<std::string>(gpu, "ScreenLayout", "Default");
|
||||
screenLayout = ScreenLayout::layoutFromString(screenLayoutName);
|
||||
topScreenSize = float(std::clamp(toml::find_or<toml::floating>(gpu, "TopScreenSize", 0.5), 0.0, 1.0));
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -101,7 +107,7 @@ void EmulatorConfig::load() {
|
|||
|
||||
auto dspCoreName = toml::find_or<std::string>(audio, "DSPEmulation", "HLE");
|
||||
dspType = Audio::DSPCore::typeFromString(dspCoreName);
|
||||
|
||||
|
||||
audioEnabled = toml::find_or<toml::boolean>(audio, "EnableAudio", audioEnabledDefault);
|
||||
aacEnabled = toml::find_or<toml::boolean>(audio, "EnableAACAudio", true);
|
||||
printDSPFirmware = toml::find_or<toml::boolean>(audio, "PrintDSPFirmware", false);
|
||||
|
@ -173,6 +179,7 @@ void EmulatorConfig::save() {
|
|||
data["General"]["DefaultRomPath"] = defaultRomPath.string();
|
||||
data["General"]["PrintAppVersion"] = printAppVersion;
|
||||
data["General"]["SystemLanguage"] = languageCodeToString(systemLanguage);
|
||||
data["General"]["EnableCirclePadPro"] = circlePadProEnabled;
|
||||
|
||||
data["Window"]["AppVersionOnWindow"] = windowSettings.showAppVersion;
|
||||
data["Window"]["RememberWindowPosition"] = windowSettings.rememberPosition;
|
||||
|
@ -180,7 +187,7 @@ void EmulatorConfig::save() {
|
|||
data["Window"]["WindowPosY"] = windowSettings.y;
|
||||
data["Window"]["WindowWidth"] = windowSettings.width;
|
||||
data["Window"]["WindowHeight"] = windowSettings.height;
|
||||
|
||||
|
||||
data["GPU"]["EnableShaderJIT"] = shaderJitEnabled;
|
||||
data["GPU"]["Renderer"] = std::string(Renderer::typeToString(rendererType));
|
||||
data["GPU"]["EnableVSync"] = vsyncEnabled;
|
||||
|
@ -190,6 +197,9 @@ void EmulatorConfig::save() {
|
|||
data["GPU"]["ShadergenLightThreshold"] = lightShadergenThreshold;
|
||||
data["GPU"]["AccelerateShaders"] = accelerateShaders;
|
||||
data["GPU"]["EnableRenderdoc"] = enableRenderdoc;
|
||||
data["GPU"]["HashTextures"] = hashTextures;
|
||||
data["GPU"]["ScreenLayout"] = std::string(ScreenLayout::layoutToString(screenLayout));
|
||||
data["GPU"]["TopScreenSize"] = double(topScreenSize);
|
||||
|
||||
data["Audio"]["DSPEmulation"] = std::string(Audio::DSPCore::typeToString(dspType));
|
||||
data["Audio"]["EnableAudio"] = audioEnabled;
|
||||
|
|
|
@ -6,118 +6,114 @@
|
|||
|
||||
#include <bit>
|
||||
#include <functional>
|
||||
|
||||
#include "cpu_dynarmic.hpp"
|
||||
#include "helpers.hpp"
|
||||
|
||||
namespace {
|
||||
template <size_t N>
|
||||
struct StringLiteral {
|
||||
constexpr StringLiteral(const char(&str)[N]) {
|
||||
std::copy_n(str, N, value);
|
||||
}
|
||||
template <size_t N>
|
||||
struct StringLiteral {
|
||||
constexpr StringLiteral(const char (&str)[N]) { std::copy_n(str, N, value); }
|
||||
|
||||
static constexpr std::size_t strlen = N - 1;
|
||||
static constexpr std::size_t size = N;
|
||||
static constexpr std::size_t strlen = N - 1;
|
||||
static constexpr std::size_t size = N;
|
||||
|
||||
char value[N];
|
||||
};
|
||||
char value[N];
|
||||
};
|
||||
|
||||
template <StringLiteral haystack, StringLiteral needle>
|
||||
constexpr u32 GetMatchingBitsFromStringLiteral() {
|
||||
u32 result = 0;
|
||||
for (size_t i = 0; i < haystack.strlen; i++) {
|
||||
for (size_t a = 0; a < needle.strlen; a++) {
|
||||
if (haystack.value[i] == needle.value[a]) {
|
||||
result |= 1 << (haystack.strlen - 1 - i);
|
||||
}
|
||||
}
|
||||
}
|
||||
return result;
|
||||
}
|
||||
template <StringLiteral haystack, StringLiteral needle>
|
||||
constexpr u32 GetMatchingBitsFromStringLiteral() {
|
||||
u32 result = 0;
|
||||
for (size_t i = 0; i < haystack.strlen; i++) {
|
||||
for (size_t a = 0; a < needle.strlen; a++) {
|
||||
if (haystack.value[i] == needle.value[a]) {
|
||||
result |= 1 << (haystack.strlen - 1 - i);
|
||||
}
|
||||
}
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
template <u32 mask_>
|
||||
constexpr u32 DepositBits(u32 val) {
|
||||
u32 mask = mask_;
|
||||
u32 res = 0;
|
||||
for (u32 bb = 1; mask; bb += bb) {
|
||||
u32 neg_mask = 0 - mask;
|
||||
if (val & bb)
|
||||
res |= mask & neg_mask;
|
||||
mask &= mask - 1;
|
||||
}
|
||||
return res;
|
||||
}
|
||||
template <u32 mask_>
|
||||
constexpr u32 DepositBits(u32 val) {
|
||||
u32 mask = mask_;
|
||||
u32 res = 0;
|
||||
for (u32 bb = 1; mask; bb += bb) {
|
||||
u32 neg_mask = 0 - mask;
|
||||
if (val & bb) res |= mask & neg_mask;
|
||||
mask &= mask - 1;
|
||||
}
|
||||
return res;
|
||||
}
|
||||
|
||||
template <StringLiteral haystack>
|
||||
struct MatcherArg {
|
||||
template <StringLiteral needle>
|
||||
u32 Get() {
|
||||
return DepositBits<GetMatchingBitsFromStringLiteral<haystack, needle>()>(instruction);
|
||||
}
|
||||
template <StringLiteral haystack>
|
||||
struct MatcherArg {
|
||||
template <StringLiteral needle>
|
||||
u32 Get() {
|
||||
return DepositBits<GetMatchingBitsFromStringLiteral<haystack, needle>()>(instruction);
|
||||
}
|
||||
|
||||
u32 instruction;
|
||||
};
|
||||
u32 instruction;
|
||||
};
|
||||
|
||||
struct Matcher {
|
||||
u32 mask;
|
||||
u32 expect;
|
||||
std::function<u64(u32)> fn;
|
||||
};
|
||||
struct Matcher {
|
||||
u32 mask;
|
||||
u32 expect;
|
||||
std::function<u64(u32)> fn;
|
||||
};
|
||||
|
||||
u64 DataProcessing_imm(auto i) {
|
||||
if (i.template Get<"d">() == 15) {
|
||||
return 7;
|
||||
}
|
||||
return 1;
|
||||
}
|
||||
u64 DataProcessing_reg(auto i) {
|
||||
if (i.template Get<"d">() == 15) {
|
||||
return 7;
|
||||
}
|
||||
return 1;
|
||||
}
|
||||
u64 DataProcessing_rsr(auto i) {
|
||||
if (i.template Get<"d">() == 15) {
|
||||
return 8;
|
||||
}
|
||||
return 2;
|
||||
}
|
||||
u64 LoadStoreSingle_imm(auto) {
|
||||
return 2;
|
||||
}
|
||||
u64 LoadStoreSingle_reg(auto i) {
|
||||
// TODO: Load PC
|
||||
if (i.template Get<"u">() == 1 && i.template Get<"r">() == 0 &&
|
||||
(i.template Get<"v">() == 0 || i.template Get<"v">() == 2)) {
|
||||
return 2;
|
||||
}
|
||||
return 4;
|
||||
}
|
||||
u64 LoadStoreMultiple(auto i) {
|
||||
// TODO: Load PC
|
||||
return 1 + std::popcount(i.template Get<"x">()) / 2;
|
||||
}
|
||||
u64 DataProcessing_imm(auto i) {
|
||||
if (i.template Get<"d">() == 15) {
|
||||
return 7;
|
||||
}
|
||||
return 1;
|
||||
}
|
||||
u64 DataProcessing_reg(auto i) {
|
||||
if (i.template Get<"d">() == 15) {
|
||||
return 7;
|
||||
}
|
||||
return 1;
|
||||
}
|
||||
u64 DataProcessing_rsr(auto i) {
|
||||
if (i.template Get<"d">() == 15) {
|
||||
return 8;
|
||||
}
|
||||
return 2;
|
||||
}
|
||||
u64 LoadStoreSingle_imm(auto) { return 2; }
|
||||
u64 LoadStoreSingle_reg(auto i) {
|
||||
// TODO: Load PC
|
||||
if (i.template Get<"u">() == 1 && i.template Get<"r">() == 0 && (i.template Get<"v">() == 0 || i.template Get<"v">() == 2)) {
|
||||
return 2;
|
||||
}
|
||||
return 4;
|
||||
}
|
||||
u64 LoadStoreMultiple(auto i) {
|
||||
// TODO: Load PC
|
||||
return 1 + std::popcount(i.template Get<"x">()) / 2;
|
||||
}
|
||||
|
||||
u64 SupervisorCall(auto i) {
|
||||
// Consume extra cycles for the GetSystemTick SVC since some games wait with it in a loop rather than
|
||||
// Properly sleeping until a VBlank interrupt
|
||||
if (i.template Get<"v">() == 0x28) {
|
||||
return 152;
|
||||
}
|
||||
u64 SupervisorCall(auto i) {
|
||||
// Consume extra cycles for the GetSystemTick SVC since some games wait with it in a loop rather than
|
||||
// Properly sleeping until a VBlank interrupt
|
||||
if (i.template Get<"v">() == 0x28) {
|
||||
return 152;
|
||||
}
|
||||
|
||||
return 8;
|
||||
}
|
||||
return 8;
|
||||
}
|
||||
|
||||
#define INST(NAME, BS, CYCLES) \
|
||||
Matcher{GetMatchingBitsFromStringLiteral<BS, "01">(), \
|
||||
GetMatchingBitsFromStringLiteral<BS, "1">(), \
|
||||
std::function<u64(u32)>{[](u32 instruction) -> u64 { \
|
||||
[[maybe_unused]] MatcherArg<BS> i{instruction}; \
|
||||
return (CYCLES); \
|
||||
}}},
|
||||
Matcher{ \
|
||||
GetMatchingBitsFromStringLiteral<BS, "01">(), GetMatchingBitsFromStringLiteral<BS, "1">(), \
|
||||
std::function<u64(u32)>{[](u32 instruction) -> u64 { \
|
||||
[[maybe_unused]] MatcherArg<BS> i{instruction}; \
|
||||
return (CYCLES); \
|
||||
}} \
|
||||
},
|
||||
|
||||
const std::array arm_matchers{
|
||||
// clang-format off
|
||||
const std::array arm_matchers{
|
||||
// clang-format off
|
||||
|
||||
// Branch instructions
|
||||
INST("BLX (imm)", "1111101hvvvvvvvvvvvvvvvvvvvvvvvv", 5) // v5
|
||||
|
@ -389,11 +385,11 @@ namespace {
|
|||
INST("RFE", "1111100--0-1----0000101000000000", 9) // v6
|
||||
INST("SRS", "1111100--1-0110100000101000-----", 1) // v6
|
||||
|
||||
// clang-format on
|
||||
};
|
||||
// clang-format on
|
||||
};
|
||||
|
||||
const std::array thumb_matchers{
|
||||
// clang-format off
|
||||
const std::array thumb_matchers{
|
||||
// clang-format off
|
||||
|
||||
// Shift (immediate) add, subtract, move and compare instructions
|
||||
INST("LSL (imm)", "00000vvvvvmmmddd", 1)
|
||||
|
@ -487,23 +483,21 @@ namespace {
|
|||
INST("BL (imm)", "11110Svvvvvvvvvv11j1jvvvvvvvvvvv", 4) // v4T
|
||||
INST("BLX (imm)", "11110Svvvvvvvvvv11j0jvvvvvvvvvvv", 5) // v5T
|
||||
|
||||
// clang-format on
|
||||
};
|
||||
// clang-format on
|
||||
};
|
||||
|
||||
} // namespace
|
||||
} // namespace
|
||||
|
||||
u64 MyEnvironment::getCyclesForInstruction(bool is_thumb, u32 instruction) {
|
||||
if (is_thumb) {
|
||||
return 1;
|
||||
}
|
||||
if (is_thumb) {
|
||||
return 1;
|
||||
}
|
||||
|
||||
const auto matches_instruction = [instruction](const auto& matcher) {
|
||||
return (instruction & matcher.mask) == matcher.expect;
|
||||
};
|
||||
const auto matches_instruction = [instruction](const auto& matcher) { return (instruction & matcher.mask) == matcher.expect; };
|
||||
|
||||
auto iter = std::find_if(arm_matchers.begin(), arm_matchers.end(), matches_instruction);
|
||||
if (iter != arm_matchers.end()) {
|
||||
return iter->fn(instruction);
|
||||
}
|
||||
return 1;
|
||||
auto iter = std::find_if(arm_matchers.begin(), arm_matchers.end(), matches_instruction);
|
||||
if (iter != arm_matchers.end()) {
|
||||
return iter->fn(instruction);
|
||||
}
|
||||
return 1;
|
||||
}
|
||||
|
|
|
@ -27,6 +27,7 @@ static constexpr Xmm scratch2 = xmm1;
|
|||
static constexpr Xmm src1_xmm = xmm2;
|
||||
static constexpr Xmm src2_xmm = xmm3;
|
||||
static constexpr Xmm src3_xmm = xmm4;
|
||||
static constexpr Xmm scratch3 = xmm5;
|
||||
|
||||
#if defined(PANDA3DS_MS_ABI)
|
||||
// Register that points to PICA state. Must be volatile for the aforementioned reasons
|
||||
|
@ -382,20 +383,12 @@ void ShaderEmitter::storeRegister(Xmm source, const PICAShader& shader, u32 dest
|
|||
(((writeMask & 0b0010) ? 0 : 1) << 4) |
|
||||
(((writeMask & 0b0001) ? 2 : 3) << 6);
|
||||
|
||||
// Reorder instructions based on whether the source == scratch1. This is to avoid overwriting scratch1 if it's the source,
|
||||
// While also having the memory load come first to mitigate execution hazards and give the load more time to complete before reading if possible
|
||||
if (source != scratch1) {
|
||||
movaps(scratch1, xword[statePointer + offset]);
|
||||
movaps(scratch2, source);
|
||||
} else {
|
||||
movaps(scratch2, source);
|
||||
movaps(scratch1, xword[statePointer + offset]);
|
||||
}
|
||||
|
||||
unpckhps(scratch2, scratch1); // Unpack X/Y components of source and destination
|
||||
unpcklps(scratch1, source); // Unpack Z/W components of source and destination
|
||||
shufps(scratch1, scratch2, selector); // "merge-shuffle" dest and source using selecto
|
||||
movaps(xword[statePointer + offset], scratch1); // Write back
|
||||
movaps(scratch3, xword[statePointer + offset]);
|
||||
movaps(scratch2, source);
|
||||
unpckhps(scratch2, scratch3); // Unpack X/Y components of source and destination
|
||||
unpcklps(scratch3, source); // Unpack Z/W components of source and destination
|
||||
shufps(scratch3, scratch2, selector); // "merge-shuffle" dest and source using selecto
|
||||
movaps(xword[statePointer + offset], scratch3); // Write back
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -441,7 +441,7 @@ void GPU::fireDMA(u32 dest, u32 source, u32 size) {
|
|||
u8* fcram = mem.getFCRAM();
|
||||
std::memcpy(&vram[dest - vramStart], &fcram[source - fcramStart], size);
|
||||
} else {
|
||||
printf("Non-trivially optimizable GPU DMA. Falling back to byte-by-byte transfer\n");
|
||||
log("Non-trivially optimizable GPU DMA. Falling back to byte-by-byte transfer\n");
|
||||
|
||||
for (u32 i = 0; i < size; i++) {
|
||||
mem.write8(dest + i, mem.read8(source + i));
|
||||
|
|
Some files were not shown because too many files have changed in this diff Show more
Loading…
Add table
Add a link
Reference in a new issue