Compare commits

..

No commits in common. "master" and "v0.8.5" have entirely different histories.

130 changed files with 960 additions and 5805 deletions

22
.github/gles.patch vendored
View file

@ -1,3 +1,25 @@
diff --git a/src/host_shaders/opengl_display.frag b/src/host_shaders/opengl_display.frag
index 612671c8..1937f711 100644
--- a/src/host_shaders/opengl_display.frag
+++ b/src/host_shaders/opengl_display.frag
@@ -1,4 +1,5 @@
-#version 410 core
+#version 300 es
+precision mediump float;
in vec2 UV;
out vec4 FragColor;
diff --git a/src/host_shaders/opengl_display.vert b/src/host_shaders/opengl_display.vert
index 990e2f80..2e7842ac 100644
--- a/src/host_shaders/opengl_display.vert
+++ b/src/host_shaders/opengl_display.vert
@@ -1,4 +1,5 @@
-#version 410 core
+#version 300 es
+precision mediump float;
out vec2 UV;
void main() {
diff --git a/src/host_shaders/opengl_fragment_shader.frag b/src/host_shaders/opengl_fragment_shader.frag
index 9f07df0b..96a35afa 100644
--- a/src/host_shaders/opengl_fragment_shader.frag

View file

@ -23,9 +23,6 @@ jobs:
- name: Fetch submodules
run: git submodule update --init --recursive
- name: Setup CCache
uses: hendrikmuhs/ccache-action@v1.2
- name: Set up gradle caches
uses: actions/cache@v4
with:
@ -37,9 +34,9 @@ jobs:
${{ runner.os }}-pandroid-x86_64-
- name: Setup Vulkan SDK
uses: humbletim/setup-vulkan-sdk@main
uses: humbletim/setup-vulkan-sdk@v1.2.0
with:
vulkan-query-version: 1.3.296.0
vulkan-query-version: latest
vulkan-use-cache: true
vulkan-components: Vulkan-Headers, Vulkan-Loader, SPIRV-Tools, Glslang
@ -50,7 +47,7 @@ jobs:
java-version: '17'
- name: Configure CMake
run: cmake -B ${{github.workspace}}/build -DBUILD_HYDRA_CORE=1 -DCMAKE_BUILD_TYPE=${{env.BUILD_TYPE}} -DCMAKE_TOOLCHAIN_FILE=${ANDROID_NDK_ROOT}/build/cmake/android.toolchain.cmake -DANDROID_ABI=x86_64 -DENABLE_VULKAN=0 -DENABLE_USER_BUILD=ON -DCMAKE_C_COMPILER_LAUNCHER=ccache -DCMAKE_CXX_COMPILER_LAUNCHER=ccache
run: cmake -B ${{github.workspace}}/build -DBUILD_HYDRA_CORE=1 -DCMAKE_BUILD_TYPE=${{env.BUILD_TYPE}} -DCMAKE_TOOLCHAIN_FILE=${ANDROID_NDK_ROOT}/build/cmake/android.toolchain.cmake -DANDROID_ABI=x86_64 -DENABLE_VULKAN=0 -DENABLE_USER_BUILD=ON
- name: Build
run: |
@ -91,9 +88,6 @@ jobs:
- name: Fetch submodules
run: git submodule update --init --recursive
- name: Setup CCache
uses: hendrikmuhs/ccache-action@v1.2
- name: Set up gradle caches
uses: actions/cache@v4
with:
@ -105,9 +99,9 @@ jobs:
${{ runner.os }}-pandroid-arm64-
- name: Setup Vulkan SDK
uses: humbletim/setup-vulkan-sdk@main
uses: humbletim/setup-vulkan-sdk@v1.2.0
with:
vulkan-query-version: 1.3.296.0
vulkan-query-version: latest
vulkan-use-cache: true
vulkan-components: Vulkan-Headers, Vulkan-Loader, SPIRV-Tools, Glslang
@ -118,7 +112,7 @@ jobs:
java-version: '17'
- name: Configure CMake
run: cmake -B ${{github.workspace}}/build -DBUILD_HYDRA_CORE=1 -DCMAKE_BUILD_TYPE=${{env.BUILD_TYPE}} -DCMAKE_TOOLCHAIN_FILE=${ANDROID_NDK_ROOT}/build/cmake/android.toolchain.cmake -DANDROID_ABI=arm64-v8a -DENABLE_VULKAN=0 -DENABLE_USER_BUILD=ON -DCMAKE_CXX_FLAGS="-march=armv8-a+crypto" -DCMAKE_C_COMPILER_LAUNCHER=ccache -DCMAKE_CXX_COMPILER_LAUNCHER=ccache
run: cmake -B ${{github.workspace}}/build -DBUILD_HYDRA_CORE=1 -DCMAKE_BUILD_TYPE=${{env.BUILD_TYPE}} -DCMAKE_TOOLCHAIN_FILE=${ANDROID_NDK_ROOT}/build/cmake/android.toolchain.cmake -DANDROID_ABI=arm64-v8a -DENABLE_VULKAN=0 -DENABLE_USER_BUILD=ON -DCMAKE_CXX_FLAGS="-march=armv8-a+crypto"
- name: Build
run: |

View file

@ -30,9 +30,9 @@ jobs:
sudo ./llvm.sh 17
- name: Setup Vulkan SDK
uses: humbletim/setup-vulkan-sdk@main
uses: humbletim/setup-vulkan-sdk@v1.2.0
with:
vulkan-query-version: 1.3.296.0
vulkan-query-version: latest
vulkan-use-cache: true
vulkan-components: Vulkan-Headers, Vulkan-Loader, SPIRV-Tools, Glslang

View file

@ -20,9 +20,9 @@ jobs:
run: git submodule update --init --recursive
- name: Setup Vulkan SDK
uses: humbletim/setup-vulkan-sdk@main
uses: humbletim/setup-vulkan-sdk@v1.2.0
with:
vulkan-query-version: 1.3.296.0
vulkan-query-version: latest
vulkan-use-cache: true
vulkan-components: Vulkan-Headers, Vulkan-Loader, SPIRV-Tools, Glslang
@ -55,7 +55,7 @@ jobs:
${{github.workspace}}/docs/libretro/panda3ds_libretro.info
MacOS:
runs-on: macos-latest
runs-on: macos-13
steps:
- uses: actions/checkout@v4
@ -63,9 +63,9 @@ jobs:
run: git submodule update --init --recursive
- name: Setup Vulkan SDK
uses: humbletim/setup-vulkan-sdk@main
uses: humbletim/setup-vulkan-sdk@v1.2.0
with:
vulkan-query-version: 1.3.296.0
vulkan-query-version: latest
vulkan-use-cache: true
vulkan-components: Vulkan-Headers, Vulkan-Loader, SPIRV-Tools, Glslang
@ -116,9 +116,9 @@ jobs:
sudo ./llvm.sh 17
- name: Setup Vulkan SDK
uses: humbletim/setup-vulkan-sdk@main
uses: humbletim/setup-vulkan-sdk@v1.2.0
with:
vulkan-query-version: 1.3.296.0
vulkan-query-version: latest
vulkan-use-cache: true
vulkan-components: Vulkan-Headers, Vulkan-Loader, SPIRV-Tools, Glslang
@ -163,9 +163,9 @@ 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@main
uses: humbletim/setup-vulkan-sdk@v1.2.0
with:
vulkan-query-version: 1.3.296.0
vulkan-query-version: latest
vulkan-use-cache: true
vulkan-components: Vulkan-Headers, Vulkan-Loader, SPIRV-Tools, Glslang
@ -180,36 +180,3 @@ 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

View file

@ -33,9 +33,9 @@ jobs:
sudo ./llvm.sh 17
- name: Setup Vulkan SDK
uses: humbletim/setup-vulkan-sdk@main
uses: humbletim/setup-vulkan-sdk@v1.2.0
with:
vulkan-query-version: 1.3.296.0
vulkan-query-version: latest
vulkan-use-cache: true
vulkan-components: Vulkan-Headers, Vulkan-Loader, SPIRV-Tools, Glslang

View file

@ -33,9 +33,9 @@ jobs:
sudo ./llvm.sh 17
- name: Setup Vulkan SDK
uses: humbletim/setup-vulkan-sdk@main
uses: humbletim/setup-vulkan-sdk@v1.2.0
with:
vulkan-query-version: 1.3.296.0
vulkan-query-version: latest
vulkan-use-cache: true
vulkan-components: Vulkan-Headers, Vulkan-Loader, SPIRV-Tools, Glslang

View file

@ -17,7 +17,7 @@ jobs:
arch: [x86_64, arm64]
name: MacOS-${{ matrix.arch }}
runs-on: macos-latest
runs-on: macos-13
steps:
- uses: actions/checkout@v4
@ -25,9 +25,9 @@ jobs:
run: git submodule update --init --recursive
- name: Setup Vulkan SDK
uses: humbletim/setup-vulkan-sdk@main
uses: humbletim/setup-vulkan-sdk@v1.2.0
with:
vulkan-query-version: 1.3.296.0
vulkan-query-version: latest
vulkan-use-cache: true
vulkan-components: Vulkan-Headers, Vulkan-Loader, SPIRV-Tools, Glslang
@ -61,7 +61,7 @@ jobs:
MacOS-Universal:
name: MacOS-Universal
needs: [build]
runs-on: macos-latest
runs-on: macos-13
steps:
- name: Download x86_64

View file

@ -26,9 +26,9 @@ jobs:
version: 6.2.0
- name: Setup Vulkan SDK
uses: humbletim/setup-vulkan-sdk@main
uses: humbletim/setup-vulkan-sdk@v1.2.0
with:
vulkan-query-version: 1.3.296.0
vulkan-query-version: latest
vulkan-use-cache: true
vulkan-components: Vulkan-Headers, Vulkan-Loader, SPIRV-Tools, Glslang
@ -56,7 +56,7 @@ jobs:
arch: [x86_64, arm64]
name: MacOS-${{ matrix.arch }}
runs-on: macos-latest
runs-on: macos-13
steps:
- uses: actions/checkout@v4
@ -64,9 +64,9 @@ jobs:
run: git submodule update --init --recursive
- name: Setup Vulkan SDK
uses: humbletim/setup-vulkan-sdk@main
uses: humbletim/setup-vulkan-sdk@v1.2.0
with:
vulkan-query-version: 1.3.296.0
vulkan-query-version: latest
vulkan-use-cache: true
vulkan-components: Vulkan-Headers, Vulkan-Loader, SPIRV-Tools, Glslang
@ -109,7 +109,7 @@ jobs:
MacOS-Universal:
name: MacOS-Universal
needs: [MacOS]
runs-on: macos-latest
runs-on: macos-13
steps:
- name: Download x86_64
@ -162,9 +162,9 @@ jobs:
sudo ./llvm.sh 17
- name: Setup Vulkan SDK
uses: humbletim/setup-vulkan-sdk@main
uses: humbletim/setup-vulkan-sdk@v1.2.0
with:
vulkan-query-version: 1.3.296.0
vulkan-query-version: latest
vulkan-use-cache: true
vulkan-components: Vulkan-Headers, Vulkan-Loader, SPIRV-Tools, Glslang

View file

@ -24,9 +24,9 @@ jobs:
run: git submodule update --init --recursive
- name: Setup Vulkan SDK
uses: humbletim/setup-vulkan-sdk@main
uses: humbletim/setup-vulkan-sdk@v1.2.0
with:
vulkan-query-version: 1.3.296.0
vulkan-query-version: latest
vulkan-use-cache: true
vulkan-components: Vulkan-Headers, Vulkan-Loader, SPIRV-Tools, Glslang

View file

@ -1,39 +0,0 @@
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: 1.3.296.0
vulkan-use-cache: true
vulkan-components: Vulkan-Headers, Vulkan-Loader, SPIRV-Tools, Glslang
- name: Build core and frontend
run: cd src/pandios && ./build.sh

7
.gitmodules vendored
View file

@ -1,6 +1,9 @@
[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,7 +85,3 @@
[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

View file

@ -50,7 +50,6 @@ option(GPU_DEBUG_INFO "Enable additional GPU debugging info" OFF)
option(ENABLE_OPENGL "Enable OpenGL rendering backend" ON)
option(ENABLE_VULKAN "Enable Vulkan rendering backend" ON)
option(ENABLE_METAL "Enable Metal rendering backend (if available)" ON)
option(ENABLE_WAYLAND "Enable Wayland support on Linux platforms" ON)
option(ENABLE_LTO "Enable link-time optimization" OFF)
option(ENABLE_TESTS "Compile unit-tests" OFF)
option(ENABLE_USER_BUILD "Make a user-facing build. These builds have various assertions disabled, LTO, and more" OFF)
@ -64,14 +63,6 @@ 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)
@ -88,10 +79,6 @@ 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)
@ -101,7 +88,7 @@ endif()
# Generate versioning files
find_package(Git)
set(PANDA3DS_VERSION "0.9")
set(PANDA3DS_VERSION "0.8")
if(NOT EXISTS ${CMAKE_BINARY_DIR}/include/version.hpp.in)
file(WRITE ${CMAKE_BINARY_DIR}/include/version.hpp.in "#define PANDA3DS_VERSION \"\${PANDA3DS_VERSION}\"")
@ -110,29 +97,22 @@ endif()
if(GIT_FOUND AND ENABLE_GIT_VERSIONING)
execute_process(
WORKING_DIRECTORY ${PROJECT_SOURCE_DIR} COMMAND ${GIT_EXECUTABLE} describe --tags --abbrev=0
OUTPUT_VARIABLE git_version_tag OUTPUT_STRIP_TRAILING_WHITESPACE
OUTPUT_VARIABLE PANDA3DS_VERSION OUTPUT_STRIP_TRAILING_WHITESPACE
)
execute_process(
WORKING_DIRECTORY ${PROJECT_SOURCE_DIR} COMMAND ${GIT_EXECUTABLE} rev-parse --short=7 HEAD
OUTPUT_VARIABLE git_version_rev OUTPUT_STRIP_TRAILING_WHITESPACE
WORKING_DIRECTORY ${PROJECT_SOURCE_DIR} COMMAND ${GIT_EXECUTABLE} describe --tags
OUTPUT_VARIABLE git_version_tag OUTPUT_STRIP_TRAILING_WHITESPACE
)
if(NOT git_version_tag STREQUAL "")
set(PANDA3DS_VERSION "${git_version_tag}")
execute_process(
WORKING_DIRECTORY ${PROJECT_SOURCE_DIR} COMMAND ${GIT_EXECUTABLE} describe --tags
OUTPUT_VARIABLE git_version_desc OUTPUT_STRIP_TRAILING_WHITESPACE
)
if(git_version_tag STREQUAL git_version_desc)
set(git_version_rev "")
endif()
unset(git_version_desc)
endif()
if(NOT git_version_rev STREQUAL "")
set(PANDA3DS_VERSION "${PANDA3DS_VERSION}.${git_version_rev}")
if(NOT PANDA3DS_VERSION STREQUAL git_version_tag)
execute_process(
WORKING_DIRECTORY ${PROJECT_SOURCE_DIR} COMMAND ${GIT_EXECUTABLE} describe --always --abbrev=7
OUTPUT_VARIABLE git_version_rev OUTPUT_STRIP_TRAILING_WHITESPACE
)
set(PANDA3DS_VERSION "${PANDA3DS_VERSION}.${git_version_rev}")
unset(git_version_rev)
endif()
string(REGEX REPLACE "^v" "" PANDA3DS_VERSION "${PANDA3DS_VERSION}")
unset(git_version_tag)
unset(git_version_rev)
endif()
configure_file(${CMAKE_BINARY_DIR}/include/version.hpp.in ${CMAKE_BINARY_DIR}/include/version.hpp)
include_directories(${CMAKE_BINARY_DIR}/include/)
@ -160,15 +140,12 @@ add_compile_definitions(NOMINMAX) # Make windows.h not define min/ma
add_compile_definitions(WIN32_LEAN_AND_MEAN) # Make windows.h not include literally everything
add_compile_definitions(SDL_MAIN_HANDLED)
if(ENABLE_WAYLAND)
add_compile_definitions(WAYLAND_ENABLED)
endif()
if(ENABLE_DISCORD_RPC AND NOT ANDROID)
add_subdirectory(third_party/discord-rpc)
include_directories(third_party/discord-rpc/include)
endif()
if (NOT ANDROID)
if (USE_SYSTEM_SDL2)
find_package(SDL2 CONFIG REQUIRED)
@ -327,8 +304,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/renderdoc.cpp
src/frontend_settings.cpp src/miniaudio/miniaudio.cpp
src/discord_rpc.cpp src/lua.cpp src/memory_mapped_file.cpp src/miniaudio.cpp src/renderdoc.cpp
src/frontend_settings.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
@ -349,7 +326,6 @@ set(SERVICE_SOURCE_FILES src/core/services/service_manager.cpp src/core/services
src/core/services/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
)
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
@ -362,7 +338,6 @@ set(LOADER_SOURCE_FILES src/core/loader/elf.cpp src/core/loader/ncsd.cpp src/cor
set(FS_SOURCE_FILES src/core/fs/archive_self_ncch.cpp src/core/fs/archive_save_data.cpp src/core/fs/archive_sdmc.cpp
src/core/fs/archive_ext_save_data.cpp src/core/fs/archive_ncch.cpp src/core/fs/romfs.cpp
src/core/fs/ivfc.cpp src/core/fs/archive_user_save_data.cpp src/core/fs/archive_system_save_data.cpp
src/core/fs/archive_twl_photo.cpp src/core/fs/archive_twl_sound.cpp src/core/fs/archive_card_spi.cpp
)
set(APPLET_SOURCE_FILES src/core/applets/applet.cpp src/core/applets/mii_selector.cpp src/core/applets/software_keyboard.cpp src/core/applets/applet_manager.cpp
@ -412,20 +387,9 @@ set(HEADER_FILES include/emulator.hpp include/helpers.hpp include/termcolor.hpp
include/PICA/pica_vert_config.hpp include/sdl_sensors.hpp include/PICA/draw_acceleration.hpp include/renderdoc.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/audio/audio_device.hpp
include/audio/audio_device_interface.hpp include/audio/libretro_audio_device.hpp
include/services/dsp_firmware_db.hpp include/frontend_settings.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
@ -455,22 +419,16 @@ if(ENABLE_LUAJIT AND NOT ANDROID)
target_link_libraries(AlberCore PRIVATE uv_a)
endif()
set(GL_CONTEXT_SOURCE_FILES "")
if(ENABLE_QT_GUI)
set(GL_CONTEXT_SOURCE_FILES ${GL_CONTEXT_SOURCE_FILES} third_party/duckstation/window_info.cpp third_party/duckstation/gl/context.cpp)
set(THIRD_PARTY_SOURCE_FILES ${THIRD_PARTY_SOURCE_FILES} third_party/duckstation/window_info.cpp third_party/duckstation/gl/context.cpp)
if(APPLE)
set(GL_CONTEXT_SOURCE_FILES ${GL_CONTEXT_SOURCE_FILES} third_party/duckstation/gl/context_agl.mm)
set(THIRD_PARTY_SOURCE_FILES ${THIRD_PARTY_SOURCE_FILES} third_party/duckstation/gl/context_agl.mm)
elseif(WIN32)
set(GL_CONTEXT_SOURCE_FILES ${GL_CONTEXT_SOURCE_FILES} third_party/duckstation/gl/context_wgl.cpp)
set(THIRD_PARTY_SOURCE_FILES ${THIRD_PARTY_SOURCE_FILES} third_party/duckstation/gl/context_wgl.cpp)
else()
set(GL_CONTEXT_SOURCE_FILES ${GL_CONTEXT_SOURCE_FILES} third_party/duckstation/gl/context_egl.cpp third_party/duckstation/gl/context_egl_x11.cpp
third_party/duckstation/gl/context_glx.cpp third_party/duckstation/gl/x11_window.cpp)
if(ENABLE_WAYLAND)
set(GL_CONTEXT_SOURCE_FILES ${GL_CONTEXT_SOURCE_FILES} third_party/duckstation/gl/context_egl_wayland.cpp)
endif()
set(THIRD_PARTY_SOURCE_FILES ${THIRD_PARTY_SOURCE_FILES} third_party/duckstation/gl/context_egl.cpp third_party/duckstation/gl/context_egl_wayland.cpp
third_party/duckstation/gl/context_egl_x11.cpp third_party/duckstation/gl/context_glx.cpp third_party/duckstation/gl/x11_window.cpp)
endif()
endif()
@ -484,7 +442,7 @@ source_group("Source Files\\Core\\Applets" FILES ${APPLET_SOURCE_FILES})
source_group("Source Files\\Core\\PICA" FILES ${PICA_SOURCE_FILES})
source_group("Source Files\\Core\\Audio" FILES ${AUDIO_SOURCE_FILES})
source_group("Source Files\\Core\\Software Renderer" FILES ${RENDERER_SW_SOURCE_FILES})
source_group("Source Files\\Third Party" FILES ${THIRD_PARTY_SOURCE_FILES} ${GL_CONTEXT_SOURCE_FILES})
source_group("Source Files\\Third Party" FILES ${THIRD_PARTY_SOURCE_FILES})
set(RENDERER_GL_SOURCE_FILES "") # Empty by default unless we are compiling with the GL renderer
set(RENDERER_VK_SOURCE_FILES "") # Empty by default unless we are compiling with the VK renderer
@ -499,9 +457,8 @@ if(ENABLE_OPENGL)
set(RENDERER_GL_SOURCE_FILES src/core/renderer_gl/renderer_gl.cpp
src/core/renderer_gl/textures.cpp src/core/renderer_gl/etc1.cpp
src/core/renderer_gl/gl_state.cpp src/host_shaders/opengl_display.vert
src/host_shaders/opengl_display.frag src/host_shaders/opengl_es_display.vert
src/host_shaders/opengl_es_display.frag src/host_shaders/opengl_vertex_shader.vert
src/core/renderer_gl/gl_state.cpp src/host_shaders/opengl_display.frag
src/host_shaders/opengl_display.vert src/host_shaders/opengl_vertex_shader.vert
src/host_shaders/opengl_fragment_shader.frag
)
@ -514,10 +471,8 @@ if(ENABLE_OPENGL)
resources_renderer_gl
NAMESPACE RendererGL
WHENCE "src/host_shaders/"
"src/host_shaders/opengl_display.vert"
"src/host_shaders/opengl_display.frag"
"src/host_shaders/opengl_es_display.vert"
"src/host_shaders/opengl_es_display.frag"
"src/host_shaders/opengl_display.vert"
"src/host_shaders/opengl_vertex_shader.vert"
"src/host_shaders/opengl_fragment_shader.frag"
)
@ -604,16 +559,14 @@ 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
@ -627,26 +580,15 @@ 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 ${MetalSDK} metal -gline-tables-only -frecord-sources -o ${SHADER_IR} -c ${SHADER_SOURCE}
COMMAND xcrun -sdk macosx 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 ${MetalSDK} metallib -o ${SHADER_METALLIB} ${SHADER_IR}
COMMAND xcrun -sdk macosx metallib -o ${SHADER_METALLIB} ${SHADER_IR}
DEPENDS ${SHADER_IR}
VERBATIM)
set(RENDERER_MTL_HOST_SHADERS_SOURCES ${RENDERER_MTL_HOST_SHADERS_SOURCES} ${SHADER_METALLIB})
@ -675,7 +617,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 PUBLIC "-framework Metal" "-framework Foundation" "-framework QuartzCore" resources_renderer_mtl)
target_link_libraries(AlberCore PRIVATE "-framework Metal" "-framework Foundation" "-framework QuartzCore" resources_renderer_mtl)
endif()
source_group("Header Files\\Core" FILES ${HEADER_FILES})
@ -781,14 +723,11 @@ 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/runpog_icon.png
docs/img/rnap_icon.png docs/img/rcow_icon.png docs/img/skyemu_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
docs/translations/sv.ts
)
set(TRANSLATIONS_TS docs/translations/en.ts docs/translations/el.ts docs/translations/es.ts docs/translations/pt_br.ts)
set_source_files_properties(${TRANSLATIONS_TS} PROPERTIES OUTPUT_LOCATION "${CMAKE_CURRENT_BINARY_DIR}/translations")
qt_add_translation(TRANSLATIONS_QM ${TRANSLATIONS_TS})
@ -809,17 +748,11 @@ if(NOT BUILD_HYDRA_CORE AND NOT BUILD_LIBRETRO_CORE)
endif()
target_link_libraries(Alber PRIVATE AlberCore)
target_sources(Alber PRIVATE ${FRONTEND_SOURCE_FILES} ${FRONTEND_HEADER_FILES} ${GL_CONTEXT_SOURCE_FILES} ${APP_RESOURCES})
target_sources(Alber PRIVATE ${FRONTEND_SOURCE_FILES} ${FRONTEND_HEADER_FILES} ${APP_RESOURCES})
elseif(BUILD_HYDRA_CORE)
target_compile_definitions(AlberCore PRIVATE PANDA3DS_HYDRA_CORE=1)
include_directories(third_party/hydra_core/include)
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})
add_library(Alber SHARED src/hydra_core.cpp)
target_link_libraries(Alber PUBLIC AlberCore)
elseif(BUILD_LIBRETRO_CORE)
include_directories(third_party/libretro/include)

Binary file not shown.

Before

Width:  |  Height:  |  Size: 16 KiB

View file

@ -1,763 +0,0 @@
<?xml version="1.0" ?><!DOCTYPE TS><TS version="2.1" language="nl">
<context>
<name>AboutWindow</name>
<message>
<location filename="../../src/panda_qt/about_window.cpp" line="16"/>
<source>About Panda3DS</source>
<translation>Over 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 is een gratis, open source Nintendo 3DS-emulator voor Windows, MacOS en 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>Bezoek panda3ds.com voor ondersteuning van Panda3DS en links naar onze officiële ondersteuningskanalen.</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&apos;ve agreed to be listed here, in no particular order.&lt;br&gt;If you think you should be listed here too, please inform us&lt;br&gt;&lt;br&gt;- Peach (wheremyfoodat)&lt;br&gt;- noumidev&lt;br&gt;- liuk707&lt;br&gt;- Wunk&lt;br&gt;- marysaka&lt;br&gt;- Sky&lt;br&gt;- merryhime&lt;br&gt;- TGP17&lt;br&gt;- Shadow&lt;br&gt;</source>
<translation>Panda3DS wordt ontwikkeld door vrijwilligers in hun vrije tijd. Hieronder een lijst van sommige van deze vrijwilligers die akkoord zijn met een vermelding, in willekeurige volgorde.&lt;br&gt;Als jij vindt dat je op deze lijst zou moeten staan, laat het ons dan weten&lt;br&gt;&lt;br&gt;- Peach (wheremyfoodat)&lt;br&gt;- noumidev&lt;br&gt;- liuk707&lt;br&gt;- Wunk&lt;br&gt;- marysaka&lt;br&gt;- Sky&lt;br&gt;- merryhime&lt;br&gt;- TGP17&lt;br&gt;- Shadow&lt;br&gt;</translation>
</message>
</context>
<context>
<name>CheatEditDialog</name>
<message>
<location filename="../../src/panda_qt/cheats_window.cpp" line="72"/>
<source>Edit Cheat</source>
<translation>Cheat bewerken</translation>
</message>
<message>
<location filename="../../src/panda_qt/cheats_window.cpp" line="82"/>
<source>Cheat name</source>
<translation>Cheatnaam</translation>
</message>
</context>
<context>
<name>CheatEntryWidget</name>
<message>
<location filename="../../src/panda_qt/cheats_window.cpp" line="34"/>
<source>Edit</source>
<translation>Bewerken</translation>
</message>
</context>
<context>
<name>CheatsWindow</name>
<message>
<location filename="../../src/panda_qt/cheats_window.cpp" line="164"/>
<source>Cheats</source>
<translation>Cheats</translation>
</message>
<message>
<location filename="../../src/panda_qt/cheats_window.cpp" line="177"/>
<source>Add</source>
<translation>Toevoegen</translation>
</message>
<message>
<location filename="../../src/panda_qt/cheats_window.cpp" line="178"/>
<source>Remove</source>
<translation>Verwijderen</translation>
</message>
</context>
<context>
<name>ConfigWindow</name>
<message>
<location filename="../../src/panda_qt/config_window.cpp" line="7"/>
<source>Configuration</source>
<translation>Instellingen</translation>
</message>
<message>
<location filename="../../src/panda_qt/config_window.cpp" line="63"/>
<source>Interface Settings</source>
<translation>Interfaceinstellingen</translation>
</message>
<message>
<location filename="../../src/panda_qt/config_window.cpp" line="69"/>
<source>System</source>
<translation>Systeem</translation>
</message>
<message>
<location filename="../../src/panda_qt/config_window.cpp" line="70"/>
<source>Light</source>
<translation>Licht</translation>
</message>
<message>
<location filename="../../src/panda_qt/config_window.cpp" line="71"/>
<source>Dark</source>
<translation>Donker</translation>
</message>
<message>
<location filename="../../src/panda_qt/config_window.cpp" line="72"/>
<source>Greetings Cat</source>
<translation>Begroetingskat</translation>
</message>
<message>
<location filename="../../src/panda_qt/config_window.cpp" line="73"/>
<source>Cream</source>
<translation>Crème</translation>
</message>
<message>
<location filename="../../src/panda_qt/config_window.cpp" line="81"/>
<source>Color theme</source>
<translation>Kleurenthema</translation>
</message>
<message>
<location filename="../../src/panda_qt/config_window.cpp" line="84"/>
<source>Happy panda</source>
<translation>Blije panda</translation>
</message>
<message>
<location filename="../../src/panda_qt/config_window.cpp" line="85"/>
<source>Happy panda (colourful)</source>
<translation>Blije panda (kleurrijk)</translation>
</message>
<message>
<location filename="../../src/panda_qt/config_window.cpp" line="86"/>
<source>Sleepy panda</source>
<translation>Slaperige panda</translation>
</message>
<message>
<location filename="../../src/panda_qt/config_window.cpp" line="87"/>
<source>Cow panda</source>
<translation>Koeienpanda</translation>
</message>
<message>
<location filename="../../src/panda_qt/config_window.cpp" line="88"/>
<source>The penguin from SkyEmu</source>
<translation>De pinguïn van SkyEmu</translation>
</message>
<message>
<location filename="../../src/panda_qt/config_window.cpp" line="97"/>
<source>Window icon</source>
<translation>Venstericoon</translation>
</message>
<message>
<location filename="../../src/panda_qt/config_window.cpp" line="100"/>
<source>Language</source>
<translation>Taal</translation>
</message>
<message>
<location filename="../../src/panda_qt/config_window.cpp" line="102"/>
<source>Show version on window title</source>
<translation>Toon versie in venstertitel</translation>
</message>
<message>
<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>Vensterpositie onthouden</translation>
</message>
<message>
<location filename="../../src/panda_qt/config_window.cpp" line="119"/>
<source>General Settings</source>
<translation>Algemene instellingen</translation>
</message>
<message>
<location filename="../../src/panda_qt/config_window.cpp" line="130"/>
<source>Browse...</source>
<translation>Bladeren...</translation>
</message>
<message>
<location filename="../../src/panda_qt/config_window.cpp" line="134"/>
<source>Select Directory</source>
<translation>Kies map</translation>
</message>
<message>
<location filename="../../src/panda_qt/config_window.cpp" line="146"/>
<source>Default ROMs path</source>
<translation>Standaard pad voor ROMs</translation>
</message>
<message>
<location filename="../../src/panda_qt/config_window.cpp" line="148"/>
<source>Enable Discord RPC</source>
<translation>Discord RPC inschakelen</translation>
</message>
<message>
<location filename="../../src/panda_qt/config_window.cpp" line="152"/>
<source>Use portable build</source>
<translation>Portable build gebruiken</translation>
</message>
<message>
<location filename="../../src/panda_qt/config_window.cpp" line="156"/>
<source>Print version in console output</source>
<translation>Versie afdrukken in consoleuitvoer</translation>
</message>
<message>
<location filename="../../src/panda_qt/config_window.cpp" line="161"/>
<source>Graphics Settings</source>
<translation>Grafische instellingen</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>Renderen op videokaart</translation>
</message>
<message>
<location filename="../../src/panda_qt/config_window.cpp" line="177"/>
<source>Enable Renderdoc</source>
<translation>Renderdoc inschakelen</translation>
</message>
<message>
<location filename="../../src/panda_qt/config_window.cpp" line="181"/>
<source>Enable shader JIT</source>
<translation>Shader JIT inschakelen</translation>
</message>
<message>
<location filename="../../src/panda_qt/config_window.cpp" line="185"/>
<source>Enable VSync</source>
<translation>VSync inschakelen</translation>
</message>
<message>
<location filename="../../src/panda_qt/config_window.cpp" line="189"/>
<source>Use ubershaders (No stutter, maybe slower)</source>
<translation>Ubershaders gebruiken (geen haperingen, mogelijk langzamer)</translation>
</message>
<message>
<location filename="../../src/panda_qt/config_window.cpp" line="193"/>
<source>Accurate shader multiplication</source>
<translation>Nauwkeurige vermenigvuldigen in shaders</translation>
</message>
<message>
<location filename="../../src/panda_qt/config_window.cpp" line="197"/>
<source>Accelerate shaders</source>
<translation>Shaders versnellen</translation>
</message>
<message>
<location filename="../../src/panda_qt/config_window.cpp" line="201"/>
<source>Force shadergen when rendering lights</source>
<translation>Shadergen afdwingen bij tekenen licht</translation>
</message>
<message>
<location filename="../../src/panda_qt/config_window.cpp" line="212"/>
<source>Light threshold for forcing shadergen</source>
<translation>Lichtgrens voor afdwingen shadergen</translation>
</message>
<message>
<location filename="../../src/panda_qt/config_window.cpp" line="215"/>
<source>Audio Settings</source>
<translation>Audioinstellingen</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-emulatie</translation>
</message>
<message>
<location filename="../../src/panda_qt/config_window.cpp" line="231"/>
<source>Enable audio</source>
<translation>Audio inschakelen</translation>
</message>
<message>
<location filename="../../src/panda_qt/config_window.cpp" line="235"/>
<source>Enable AAC audio</source>
<translation>AAC-audio inschakelen</translation>
</message>
<message>
<location filename="../../src/panda_qt/config_window.cpp" line="239"/>
<source>Print DSP firmware</source>
<translation>DSP-firmware afdrukken</translation>
</message>
<message>
<location filename="../../src/panda_qt/config_window.cpp" line="243"/>
<source>Mute audio device</source>
<translation>Audioapparaat dempen</translation>
</message>
<message>
<location filename="../../src/panda_qt/config_window.cpp" line="248"/>
<source>Cubic</source>
<translation>Kubiek</translation>
</message>
<message>
<location filename="../../src/panda_qt/config_window.cpp" line="249"/>
<source>Linear</source>
<translation>Lineair</translation>
</message>
<message>
<location filename="../../src/panda_qt/config_window.cpp" line="255"/>
<source>Volume curve</source>
<translation>Volumecurve</translation>
</message>
<message>
<location filename="../../src/panda_qt/config_window.cpp" line="273"/>
<source>Audio device volume</source>
<translation>Volume audioapparaat</translation>
</message>
<message>
<location filename="../../src/panda_qt/config_window.cpp" line="276"/>
<source>Battery Settings</source>
<translation>Batterij-instellingen</translation>
</message>
<message>
<location filename="../../src/panda_qt/config_window.cpp" line="288"/>
<source>Battery percentage</source>
<translation>Batterijpercentage</translation>
</message>
<message>
<location filename="../../src/panda_qt/config_window.cpp" line="290"/>
<source>Charger plugged</source>
<translation>Oplader aangesloten</translation>
</message>
<message>
<location filename="../../src/panda_qt/config_window.cpp" line="295"/>
<source>SD Card Settings</source>
<translation>Instellingen SD-kaart</translation>
</message>
<message>
<location filename="../../src/panda_qt/config_window.cpp" line="300"/>
<source>Enable virtual SD card</source>
<translation>Virtuele SD-kaart inschakelen</translation>
</message>
<message>
<location filename="../../src/panda_qt/config_window.cpp" line="304"/>
<source>Write protect virtual SD card</source>
<translation>Virtuele SD-kaart schrijfbeveiligen</translation>
</message>
<message>
<location filename="../../src/panda_qt/config_window.cpp" line="309"/>
<source>Interface</source>
<translation>Interface</translation>
</message>
<message>
<location filename="../../src/panda_qt/config_window.cpp" line="309"/>
<source>User Interface settings</source>
<translation>Instellingen gebruikersinterface</translation>
</message>
<message>
<location filename="../../src/panda_qt/config_window.cpp" line="310"/>
<source>General</source>
<translation>Algemeen</translation>
</message>
<message>
<location filename="../../src/panda_qt/config_window.cpp" line="310"/>
<source>General emulator settings</source>
<translation>Algemene emulatorinstellingen</translation>
</message>
<message>
<location filename="../../src/panda_qt/config_window.cpp" line="311"/>
<source>Graphics</source>
<translation>Weergave</translation>
</message>
<message>
<location filename="../../src/panda_qt/config_window.cpp" line="311"/>
<source>Graphics emulation and output settings</source>
<translation>Instellingen grafische emulatie en weergave </translation>
</message>
<message>
<location filename="../../src/panda_qt/config_window.cpp" line="312"/>
<source>Audio</source>
<translation>Audio</translation>
</message>
<message>
<location filename="../../src/panda_qt/config_window.cpp" line="312"/>
<source>Audio emulation and output settings</source>
<translation>Instellingen audioemulatie en weergave</translation>
</message>
<message>
<location filename="../../src/panda_qt/config_window.cpp" line="313"/>
<source>Battery</source>
<translation>Batterij</translation>
</message>
<message>
<location filename="../../src/panda_qt/config_window.cpp" line="313"/>
<source>Battery emulation settings</source>
<translation>Instellingen batterijemulatie</translation>
</message>
<message>
<location filename="../../src/panda_qt/config_window.cpp" line="314"/>
<source>SD Card</source>
<translation>SD-kaart</translation>
</message>
<message>
<location filename="../../src/panda_qt/config_window.cpp" line="314"/>
<source>SD Card emulation settings</source>
<translation>Instellingen SD-kaart-emulatie</translation>
</message>
<message>
<location filename="../../src/panda_qt/translations.cpp" line="75"/>
<source>Language change successful</source>
<translation>Taal succesvol ingesteld</translation>
</message>
<message>
<location filename="../../src/panda_qt/translations.cpp" line="76"/>
<source>Restart Panda3DS for the new language to be used.</source>
<translation>Herstart Panda3DS om de nieuw gekozen taal te gebruiken.</translation>
</message>
<message>
<location filename="../../src/panda_qt/translations.cpp" line="82"/>
<source>Language change failed</source>
<translation>Wijzigen van taal mislukt</translation>
</message>
<message>
<location filename="../../src/panda_qt/translations.cpp" line="83"/>
<source>The language you selected is not included in Panda3DS. If you&apos;re seeing this, someone messed up the language UI code...</source>
<translation>De gekozen taal is niet beschikbaar in Panda3DS. Als je dit leest heeft iemand de taalcode verprutst...</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>Bestand</translation>
</message>
<message>
<location filename="../../src/panda_qt/main_window.cpp" line="39"/>
<source>Emulation</source>
<translation>Emulatie</translation>
</message>
<message>
<location filename="../../src/panda_qt/main_window.cpp" line="40"/>
<source>Tools</source>
<translation>Hulpmiddelen</translation>
</message>
<message>
<location filename="../../src/panda_qt/main_window.cpp" line="41"/>
<source>About</source>
<translation>Over</translation>
</message>
<message>
<location filename="../../src/panda_qt/main_window.cpp" line="44"/>
<source>Load game</source>
<translation>Spel laden</translation>
</message>
<message>
<location filename="../../src/panda_qt/main_window.cpp" line="45"/>
<source>Load Lua script</source>
<translation>LUA-script laden</translation>
</message>
<message>
<location filename="../../src/panda_qt/main_window.cpp" line="46"/>
<source>Open Panda3DS folder</source>
<translation>Open Panda3DS-map</translation>
</message>
<message>
<location filename="../../src/panda_qt/main_window.cpp" line="55"/>
<source>Pause</source>
<translation>Pauzeren</translation>
</message>
<message>
<location filename="../../src/panda_qt/main_window.cpp" line="56"/>
<source>Resume</source>
<translation>Hervatten</translation>
</message>
<message>
<location filename="../../src/panda_qt/main_window.cpp" line="57"/>
<source>Reset</source>
<translation>Reset</translation>
</message>
<message>
<location filename="../../src/panda_qt/main_window.cpp" line="58"/>
<source>Configure</source>
<translation>Instellingen</translation>
</message>
<message>
<location filename="../../src/panda_qt/main_window.cpp" line="64"/>
<source>Dump RomFS</source>
<translation>RomFS dumpen</translation>
</message>
<message>
<location filename="../../src/panda_qt/main_window.cpp" line="65"/>
<source>Open Lua Editor</source>
<translation>Open LUA-editor</translation>
</message>
<message>
<location filename="../../src/panda_qt/main_window.cpp" line="66"/>
<source>Open Cheats Editor</source>
<translation>Open cheats-editor</translation>
</message>
<message>
<location filename="../../src/panda_qt/main_window.cpp" line="67"/>
<source>Open Patch Window</source>
<translation>Open patchvenster</translation>
</message>
<message>
<location filename="../../src/panda_qt/main_window.cpp" line="68"/>
<source>Open Shader Editor</source>
<translation>Open shader-editor</translation>
</message>
<message>
<location filename="../../src/panda_qt/main_window.cpp" line="69"/>
<source>Dump loaded DSP firmware</source>
<translation>Geladen DSP-firmware dumpen</translation>
</message>
<message>
<location filename="../../src/panda_qt/main_window.cpp" line="78"/>
<source>About Panda3DS</source>
<translation>Over Panda3DS</translation>
</message>
<message>
<location filename="../../src/panda_qt/main_window.cpp" line="195"/>
<source>Select 3DS ROM to load</source>
<translation>Kies 3DS ROM om te laden</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 ROMs (*.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>Kies LUA-script om te laden</translation>
</message>
<message>
<location filename="../../src/panda_qt/main_window.cpp" line="209"/>
<source>Lua scripts (*.lua *.txt)</source>
<translation>LUA-scripts (*.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>Kies map om RomFS-bestanden heen te dumpen</translation>
</message>
<message>
<location filename="../../src/panda_qt/main_window.cpp" line="288"/>
<source>Invalid format for RomFS dumping</source>
<translation>Ongeldig formaat voor RomFS dump</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>Het formaat van de momenteel geladen applicatie ondersteunt geen 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>Geen RomFS gevonden</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>Geen RomFS-partitie gevonden in de geladen applicatie</translation>
</message>
<message>
<location filename="../../src/panda_qt/main_window.cpp" line="305"/>
<source>Select file</source>
<translation>Selecteer bestand</translation>
</message>
<message>
<location filename="../../src/panda_qt/main_window.cpp" line="305"/>
<source>DSP firmware file (*.cdc)</source>
<translation>DSP-firmware-bestand (*.cdc)</translation>
</message>
<message>
<location filename="../../src/panda_qt/main_window.cpp" line="320"/>
<source>No DSP firmware loaded</source>
<translation>Geen DSP-firmware geladen</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>De momenteel geladen applicatie heeft geen firmware geüpload naar de DSP</translation>
</message>
<message>
<location filename="../../src/panda_qt/main_window.cpp" line="331"/>
<source>Failed to open output file</source>
<translation>Uitvoerbestand openen mislukt</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>De momenteel geladen DSP-firmware kan niet worden geschreven naar het gekozen bestand. Controleer de permissies van het gekozen bestand</translation>
</message>
</context>
<context>
<name>PatchWindow</name>
<message>
<location filename="../../src/panda_qt/patch_window.cpp" line="15"/>
<source>ROM patcher</source>
<translation>ROM-patcher</translation>
</message>
<message>
<location filename="../../src/panda_qt/patch_window.cpp" line="23"/>
<source>Select input file</source>
<translation>Kies invoerbestand</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>Kies</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>Kies patchbestand</translation>
</message>
<message>
<location filename="../../src/panda_qt/patch_window.cpp" line="47"/>
<source>Apply patch</source>
<translation>Patch toepassen</translation>
</message>
<message>
<location filename="../../src/panda_qt/patch_window.cpp" line="56"/>
<source>Select file to patch</source>
<translation>Kies bestand om te patchen</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>Alle bestanden (*.*)</translation>
</message>
<message>
<location filename="../../src/panda_qt/patch_window.cpp" line="63"/>
<source>Patch files (*.ips *.ups *.bps)</source>
<translation>Patch-bestanden (*.ips *.ups *.bps)</translation>
</message>
<message>
<location filename="../../src/panda_qt/patch_window.cpp" line="71"/>
<source>Paths not provided correctly</source>
<translation>Paden incorrect meegegeven</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>Geef paden van invoerbestand en patchbestand op</translation>
</message>
<message>
<location filename="../../src/panda_qt/patch_window.cpp" line="80"/>
<source>Select file</source>
<translation>Kies bestand</translation>
</message>
<message>
<location filename="../../src/panda_qt/patch_window.cpp" line="84"/>
<source>No output path</source>
<translation>Geen uitvoerpad</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>Geen pad opgegeven voor uitvoerbestand, patch niet toegepast</translation>
</message>
<message>
<location filename="../../src/panda_qt/patch_window.cpp" line="99"/>
<source>Unknown patch format</source>
<translation>Onbekend patchformaat</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>Ongeldig formaat van patchbestand. Momenteel wordt IPS, UPS en BPS ondersteund</translation>
</message>
<message>
<location filename="../../src/panda_qt/patch_window.cpp" line="108"/>
<source>Failed to open input files</source>
<translation>Openen van invoerbestanden mislukt</translation>
</message>
<message>
<location filename="../../src/panda_qt/patch_window.cpp" line="108"/>
<source>Make sure they&apos;re in a directory Panda3DS has access to</source>
<translation>Zorg ervoor dat ze in een map staan waar Panda3DS toegang toe heeft</translation>
</message>
<message>
<location filename="../../src/panda_qt/patch_window.cpp" line="135"/>
<source>Patching Success</source>
<translation>Patch succesvol</translation>
</message>
<message>
<location filename="../../src/panda_qt/patch_window.cpp" line="135"/>
<source>Your file was patched successfully.</source>
<translation>Het bestand is succesvol gepatcht.</translation>
</message>
<message>
<location filename="../../src/panda_qt/patch_window.cpp" line="141"/>
<source>Checksum mismatch</source>
<translation>Checksum komt niet overeen</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>Patch is succesvol toegepast maar de checksum komt niet overeen. Invoer- of uitvoerbestand is mogelijk ongeldig</translation>
</message>
<message>
<location filename="../../src/panda_qt/patch_window.cpp" line="146"/>
<source>Patching error</source>
<translation>Fout tijdens patchen</translation>
</message>
<message>
<location filename="../../src/panda_qt/patch_window.cpp" line="146"/>
<source>An error occured while patching</source>
<translation>Er is bij het patchen een fout opgetreden</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>Shader herladen</translation>
</message>
</context>
<context>
<name>TextEditorWindow</name>
<message>
<location filename="../../src/panda_qt/text_editor.cpp" line="12"/>
<source>Lua Editor</source>
<translation>LUA-editor</translation>
</message>
<message>
<location filename="../../src/panda_qt/text_editor.cpp" line="27"/>
<source>Load script</source>
<translation>Script laden</translation>
</message>
</context>
</TS>

View file

@ -1,774 +0,0 @@
<?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 &lt;github@danielnylander.se&gt;</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 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&apos;ve agreed to be listed here, in no particular order.&lt;br&gt;If you think you should be listed here too, please inform us&lt;br&gt;&lt;br&gt;- Peach (wheremyfoodat)&lt;br&gt;- noumidev&lt;br&gt;- liuk707&lt;br&gt;- Wunk&lt;br&gt;- marysaka&lt;br&gt;- Sky&lt;br&gt;- merryhime&lt;br&gt;- TGP17&lt;br&gt;- Shadow&lt;br&gt;</source>
<translation>Panda3DS utvecklas av volontärer deras fritid. Nedan finns en lista över några av dessa volontärer som har gått med att listas här, utan någon särskild ordning.&lt;br&gt;Om du tycker att du också borde listas här, informera oss&lt;br&gt;&lt;br&gt;- Peach (wheremyfoodat)&lt;br&gt;- noumidev&lt;br&gt;- liuk707&lt;br&gt;- Wunk&lt;br&gt;- marysaka&lt;br&gt;- Sky&lt;br&gt;- merryhime&lt;br&gt;- TGP17&lt;br&gt;- Shadow&lt;br&gt;</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 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 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&apos;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&apos;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>

View file

@ -2,8 +2,8 @@
namespace PICA::ShaderGen {
// Graphics API this shader is targetting
enum class API { GL, GLES, Vulkan, Metal };
enum class API { GL, GLES, Vulkan };
// Shading language to use
enum class Language { GLSL, MSL };
} // namespace PICA::ShaderGen
// Shading language to use (Only GLSL for the time being)
enum class Language { GLSL };
} // namespace PICA::ShaderGen

View file

@ -1,9 +0,0 @@
#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

View file

@ -1,36 +0,0 @@
#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) {}
};

View file

@ -1,61 +0,0 @@
#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; }
};

View file

@ -3,31 +3,39 @@
#include <string>
#include <vector>
#include "audio/audio_device_interface.hpp"
#include "config.hpp"
#include "helpers.hpp"
#include "miniaudio.h"
#include "ring_buffer.hpp"
class MiniAudioDevice final : public AudioDeviceInterface {
class MiniAudioDevice {
using Samples = Common::RingBuffer<ma_int16, 0x2000 * 2>;
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) override;
void close() override;
void init(Samples& samples, bool safe = false);
void close();
void start() override;
void stop() override;
void start();
void stop();
bool isInitialized() const { return initialized; }
};

View file

@ -5,7 +5,6 @@
#include "audio/dsp_core.hpp"
#include "frontend_settings.hpp"
#include "renderer.hpp"
#include "services/region_codes.hpp"
struct AudioDeviceConfig {
// Audio curve to use for volumes between 0-100
@ -49,19 +48,6 @@ struct EmulatorConfig {
#endif
static constexpr bool accelerateShadersDefault = true;
#if defined(__LIBRETRO__)
static constexpr bool audioEnabledDefault = true;
#else
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
bool shaderJitEnabled = shaderJitDefault;
bool useUbershaders = ubershaderDefault;
bool accelerateShaders = accelerateShadersDefault;
@ -72,14 +58,14 @@ struct EmulatorConfig {
bool forceShadergenForLights = true;
int lightShadergenThreshold = 1;
RendererType rendererType = rendererDefault;
RendererType rendererType = RendererType::OpenGL;
Audio::DSPCore::Type dspType = Audio::DSPCore::Type::HLE;
bool sdCardInserted = true;
bool sdWriteProtected = false;
bool usePortableBuild = false;
bool audioEnabled = audioEnabledDefault;
bool audioEnabled = false;
bool vsyncEnabled = true;
bool aacEnabled = true; // Enable AAC audio?
@ -91,8 +77,6 @@ struct EmulatorConfig {
// Default to 3% battery to make users suffer
int batteryPercentage = 3;
LanguageCodes systemLanguage = LanguageCodes::English;
// Default ROM path to open in Qt and misc frontends
std::filesystem::path defaultRomPath = "";
std::filesystem::path filePath;
@ -120,7 +104,4 @@ struct EmulatorConfig {
EmulatorConfig(const std::filesystem::path& path);
void load();
void save();
static LanguageCodes languageCodeFromString(std::string inString);
static const char* languageCodeToString(LanguageCodes code);
};

View file

@ -181,7 +181,5 @@ 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();
};

View file

@ -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;
AudioDevice audioDevice;
MiniAudioDevice 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
@ -89,6 +89,7 @@ class Emulator {
~Emulator();
void step();
void render();
void reset(ReloadOption reload);
void runFrame();
// Poll the scheduler for events
@ -126,7 +127,6 @@ class Emulator {
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(); }

View file

@ -11,7 +11,6 @@ struct FrontendSettings {
Dark = 2,
GreetingsCat = 3,
Cream = 4,
Oled = 5,
};
// Different panda-themed window icons
@ -21,7 +20,6 @@ struct FrontendSettings {
Rnap = 2,
Rcow = 3,
SkyEmu = 4,
Runpog = 5,
};
Theme theme = Theme::Dark;

View file

@ -7,7 +7,6 @@
#include <string>
#include <type_traits>
#include <vector>
#include "helpers.hpp"
#include "memory.hpp"
#include "result.hpp"
@ -16,13 +15,13 @@
using Result::HorizonResult;
namespace PathType {
enum : u32 {
Invalid = 0,
Empty = 1,
Binary = 2,
ASCII = 3,
UTF16 = 4,
};
enum : u32 {
Invalid = 0,
Empty = 1,
Binary = 2,
ASCII = 3,
UTF16 = 4,
};
}
namespace ArchiveID {
@ -35,103 +34,91 @@ namespace ArchiveID {
SDMC = 9,
SDMCWriteOnly = 0xA,
CardSPI = 0x12345679,
SavedataAndNcch = 0x2345678A,
// 3DBrew: This is the same as the regular SaveData archive, except with this the savedata ID and mediatype is loaded from the input archive
// lowpath.
UserSaveData1 = 0x567890B2,
// 3DBrew: Similar to 0x567890B2 but can only access Accessible Save specified in exheader?
UserSaveData2 = 0x567890B4,
TwlPhoto = 0x567890AC,
TwlSound = 0x567890AD,
};
static std::string toString(u32 id) {
switch (id) {
case SelfNCCH: return "SelfNCCH";
case SaveData: return "SaveData";
case ExtSaveData: return "ExtSaveData";
case SharedExtSaveData: return "SharedExtSaveData";
case SystemSaveData: return "SystemSaveData";
case SDMC: return "SDMC";
case SDMCWriteOnly: return "SDMC (Write-only)";
case SavedataAndNcch: return "Savedata & NCCH (archive 0x2345678A)";
case TwlPhoto: return "TWL_PHOTO";
case TwlSound: return "TWL_SOUND";
default: return "Unknown archive";
}
}
} // namespace ArchiveID
static std::string toString(u32 id) {
switch (id) {
case SelfNCCH: return "SelfNCCH";
case SaveData: return "SaveData";
case ExtSaveData: return "ExtSaveData";
case SharedExtSaveData: return "SharedExtSaveData";
case SystemSaveData: return "SystemSaveData";
case SDMC: return "SDMC";
case SDMCWriteOnly: return "SDMC (Write-only)";
case SavedataAndNcch: return "Savedata & NCCH (archive 0x2345678A)";
default: return "Unknown archive";
}
}
}
struct FSPath {
u32 type = PathType::Invalid;
u32 type = PathType::Invalid;
std::vector<u8> binary; // Path data for binary paths
std::string string; // Path data for ASCII paths
std::u16string utf16_string;
std::vector<u8> binary; // Path data for binary paths
std::string string; // Path data for ASCII paths
std::u16string utf16_string;
FSPath() {}
FSPath() {}
FSPath(u32 type, const std::vector<u8>& vec) : type(type) {
switch (type) {
case PathType::Binary: binary = std::move(vec); break;
FSPath(u32 type, const std::vector<u8>& vec) : type(type) {
switch (type) {
case PathType::Binary:
binary = std::move(vec);
break;
case PathType::ASCII:
string.resize(vec.size() - 1); // -1 because of the null terminator
std::memcpy(string.data(), vec.data(), vec.size() - 1); // Copy string data
break;
case PathType::ASCII:
string.resize(vec.size() - 1); // -1 because of the null terminator
std::memcpy(string.data(), vec.data(), vec.size() - 1); // Copy string data
break;
case PathType::UTF16: {
const size_t size = vec.size() / sizeof(u16) - 1; // Character count. -1 because null terminator here too
utf16_string.resize(size);
std::memcpy(utf16_string.data(), vec.data(), size * sizeof(u16));
break;
};
}
}
bool isUTF16() const { return type == PathType::UTF16; }
bool isASCII() const { return type == PathType::ASCII; }
bool isBinary() const { return type == PathType::Binary; }
// This is not called "isEmpty()" to make obvious that we're talking about an empty-type path, NOT an empty text path
bool isEmptyType() const { return type == PathType::Empty; }
bool isTextPath() const { return isUTF16() || isASCII(); }
case PathType::UTF16: {
const size_t size = vec.size() / sizeof(u16) - 1; // Character count. -1 because null terminator here too
utf16_string.resize(size);
std::memcpy(utf16_string.data(), vec.data(), size * sizeof(u16));
break;
}
; }
}
};
struct FilePerms {
u32 raw;
u32 raw;
FilePerms(u32 val) : raw(val) {}
bool read() const { return (raw & 1) != 0; }
bool write() const { return (raw & 2) != 0; }
bool create() const { return (raw & 4) != 0; }
FilePerms(u32 val) : raw(val) {}
bool read() const { return (raw & 1) != 0; }
bool write() const { return (raw & 2) != 0; }
bool create() const { return (raw & 4) != 0; }
};
class ArchiveBase;
struct FileSession {
ArchiveBase* archive = nullptr;
FILE* fd = nullptr; // File descriptor for file sessions that require them.
FSPath path;
FSPath archivePath;
u32 priority = 0; // TODO: What does this even do
bool isOpen;
ArchiveBase* archive = nullptr;
FILE* fd = nullptr; // File descriptor for file sessions that require them.
FSPath path;
FSPath archivePath;
u32 priority = 0; // TODO: What does this even do
bool isOpen;
FileSession(ArchiveBase* archive, const FSPath& filePath, const FSPath& archivePath, FILE* fd, bool isOpen = true)
: archive(archive), path(filePath), archivePath(archivePath), fd(fd), isOpen(isOpen), priority(0) {}
FileSession(ArchiveBase* archive, const FSPath& filePath, const FSPath& archivePath, FILE* fd, bool isOpen = true) :
archive(archive), path(filePath), archivePath(archivePath), fd(fd), isOpen(isOpen), priority(0) {}
// For cloning a file session
FileSession(const FileSession& other)
: archive(other.archive), path(other.path), archivePath(other.archivePath), fd(other.fd), isOpen(other.isOpen), priority(other.priority) {}
// For cloning a file session
FileSession(const FileSession& other) : archive(other.archive), path(other.path),
archivePath(other.archivePath), fd(other.fd), isOpen(other.isOpen), priority(other.priority) {}
};
struct ArchiveSession {
ArchiveBase* archive = nullptr;
FSPath path;
bool isOpen;
ArchiveBase* archive = nullptr;
FSPath path;
bool isOpen;
ArchiveSession(ArchiveBase* archive, const FSPath& filePath, bool isOpen = true) : archive(archive), path(filePath), isOpen(isOpen) {}
ArchiveSession(ArchiveBase* archive, const FSPath& filePath, bool isOpen = true) : archive(archive), path(filePath), isOpen(isOpen) {}
};
struct DirectoryEntry {
@ -169,125 +156,106 @@ struct DirectorySession {
using FileDescriptor = std::optional<FILE*>;
class ArchiveBase {
public:
struct FormatInfo {
u32 size; // Archive size
u32 numOfDirectories; // Number of directories
u32 numOfFiles; // Number of files
bool duplicateData; // Whether to duplicate data or not
};
public:
struct FormatInfo {
u32 size; // Archive size
u32 numOfDirectories; // Number of directories
u32 numOfFiles; // Number of files
bool duplicateData; // Whether to duplicate data or not
};
protected:
using Handle = u32;
protected:
using Handle = u32;
static constexpr FileDescriptor NoFile = nullptr;
static constexpr FileDescriptor FileError = std::nullopt;
Memory& mem;
static constexpr FileDescriptor NoFile = nullptr;
static constexpr FileDescriptor FileError = std::nullopt;
Memory& mem;
// Returns if a specified 3DS path in UTF16 or ASCII format is safe or not
// A 3DS path is considered safe if its first character is '/' which means we're not trying to access anything outside the root of the fs
// And if it doesn't contain enough instances of ".." (Indicating "climb up a folder" in filesystems) to let the software climb up the directory
// tree And access files outside of the emulator's app data folder
template <u32 format>
bool isPathSafe(const FSPath& path) {
static_assert(format == PathType::ASCII || format == PathType::UTF16);
using String = typename std::conditional<format == PathType::UTF16, std::u16string, std::string>::type; // String type for the path
using Char = typename String::value_type; // Char type for the path
// Returns if a specified 3DS path in UTF16 or ASCII format is safe or not
// A 3DS path is considered safe if its first character is '/' which means we're not trying to access anything outside the root of the fs
// And if it doesn't contain enough instances of ".." (Indicating "climb up a folder" in filesystems) to let the software climb up the directory tree
// And access files outside of the emulator's app data folder
template <u32 format>
bool isPathSafe(const FSPath& path) {
static_assert(format == PathType::ASCII || format == PathType::UTF16);
using String = typename std::conditional<format == PathType::UTF16, std::u16string, std::string>::type; // String type for the path
using Char = typename String::value_type; // Char type for the path
String pathString, dots;
if constexpr (std::is_same<String, std::u16string>()) {
pathString = path.utf16_string;
dots = u"..";
} else {
pathString = path.string;
dots = "..";
}
String pathString, dots;
if constexpr (std::is_same<String, std::u16string>()) {
pathString = path.utf16_string;
dots = u"..";
} else {
pathString = path.string;
dots = "..";
}
// If the path string doesn't begin with / then that means it's accessing outside the FS root, which is invalid & unsafe
if (pathString[0] != Char('/')) return false;
// If the path string doesn't begin with / then that means it's accessing outside the FS root, which is invalid & unsafe
if (pathString[0] != Char('/')) return false;
// Counts how many folders sans the root our file is nested under.
// If it's < 0 at any point of parsing, then the path is unsafe and tries to crawl outside our file sandbox.
// If it's 0 then this is the FS root.
// If it's > 0 then we're in a subdirectory of the root.
int level = 0;
// Counts how many folders sans the root our file is nested under.
// If it's < 0 at any point of parsing, then the path is unsafe and tries to crawl outside our file sandbox.
// If it's 0 then this is the FS root.
// If it's > 0 then we're in a subdirectory of the root.
int level = 0;
// Split the string on / characters and see how many of the substrings are ".."
size_t pos = 0;
while ((pos = pathString.find(Char('/'))) != String::npos) {
String token = pathString.substr(0, pos);
pathString.erase(0, pos + 1);
// Split the string on / characters and see how many of the substrings are ".."
size_t pos = 0;
while ((pos = pathString.find(Char('/'))) != String::npos) {
String token = pathString.substr(0, pos);
pathString.erase(0, pos + 1);
if (token == dots) {
level--;
if (level < 0) return false;
} else {
level++;
}
}
if (token == dots) {
level--;
if (level < 0) return false;
} else {
level++;
}
}
return true;
}
return true;
}
public:
virtual std::string name() = 0;
virtual u64 getFreeBytes() = 0;
virtual HorizonResult createFile(const FSPath& path, u64 size) = 0;
virtual HorizonResult deleteFile(const FSPath& path) = 0;
public:
virtual std::string name() = 0;
virtual u64 getFreeBytes() = 0;
virtual HorizonResult createFile(const FSPath& path, u64 size) = 0;
virtual HorizonResult deleteFile(const FSPath& path) = 0;
virtual Rust::Result<FormatInfo, HorizonResult> getFormatInfo(const FSPath& path) {
Helpers::panic("Unimplemented GetFormatInfo for %s archive", name().c_str());
// Return a dummy struct just to avoid the UB of not returning anything, even if we panic
return Ok(FormatInfo{.size = 0, .numOfDirectories = 0, .numOfFiles = 0, .duplicateData = false});
}
virtual Rust::Result<FormatInfo, HorizonResult> getFormatInfo(const FSPath& path) {
Helpers::panic("Unimplemented GetFormatInfo for %s archive", name().c_str());
// Return a dummy struct just to avoid the UB of not returning anything, even if we panic
return Ok(FormatInfo{ .size = 0, .numOfDirectories = 0, .numOfFiles = 0, .duplicateData = false });
}
virtual HorizonResult createDirectory(const FSPath& path) {
Helpers::panic("Unimplemented CreateDirectory for %s archive", name().c_str());
return Result::FS::AlreadyExists;
}
virtual HorizonResult createDirectory(const FSPath& path) {
Helpers::panic("Unimplemented CreateDirectory for %s archive", name().c_str());
return Result::FS::AlreadyExists;
}
// Returns nullopt if opening the file failed, otherwise returns a file descriptor to it (nullptr if none is needed)
virtual FileDescriptor openFile(const FSPath& path, const FilePerms& perms) = 0;
virtual Rust::Result<ArchiveBase*, HorizonResult> openArchive(const FSPath& path) = 0;
// Returns nullopt if opening the file failed, otherwise returns a file descriptor to it (nullptr if none is needed)
virtual FileDescriptor openFile(const FSPath& path, const FilePerms& perms) = 0;
virtual Rust::Result<ArchiveBase*, HorizonResult> openArchive(const FSPath& path) = 0;
virtual Rust::Result<DirectorySession, HorizonResult> openDirectory(const FSPath& path) {
Helpers::panic("Unimplemented OpenDirectory for %s archive", name().c_str());
return Err(Result::FS::FileNotFoundAlt);
}
virtual Rust::Result<DirectorySession, HorizonResult> openDirectory(const FSPath& path) {
Helpers::panic("Unimplemented OpenDirectory for %s archive", name().c_str());
return Err(Result::FS::FileNotFoundAlt);
}
virtual void format(const FSPath& path, const FormatInfo& info) { Helpers::panic("Unimplemented Format for %s archive", name().c_str()); }
virtual void format(const FSPath& path, const FormatInfo& info) {
Helpers::panic("Unimplemented Format for %s archive", name().c_str());
}
virtual HorizonResult renameFile(const FSPath& oldPath, const FSPath& newPath) {
virtual HorizonResult renameFile(const FSPath& oldPath, const FSPath& newPath) {
Helpers::panic("Unimplemented RenameFile for %s archive", name().c_str());
return Result::Success;
}
}
// Read size bytes from a file starting at offset "offset" into a certain buffer in memory
// Returns the number of bytes read, or nullopt if the read failed
virtual std::optional<u32> readFile(FileSession* file, u64 offset, u32 size, u32 dataPointer) = 0;
// Read size bytes from a file starting at offset "offset" into a certain buffer in memory
// Returns the number of bytes read, or nullopt if the read failed
virtual std::optional<u32> readFile(FileSession* file, u64 offset, u32 size, u32 dataPointer) = 0;
ArchiveBase(Memory& mem) : mem(mem) {}
bool isSafeTextPath(const FSPath& path) {
if (path.type == PathType::UTF16) {
return isPathSafe<PathType::UTF16>(path);
} else if (path.type == PathType::ASCII){
return isPathSafe<PathType::ASCII>(path);
}
Helpers::panic("ArchiveBase::IsSafeTextPath: Invalid path");
}
// Appends a 3DS path to an std::filesystem::path
void appendPath(std::filesystem::path& diskPath, const FSPath& guestPath) {
if (guestPath.type == PathType::UTF16) {
diskPath += std::filesystem::path(guestPath.utf16_string).make_preferred();
} else if (guestPath.type == PathType::ASCII) {
diskPath += std::filesystem::path(guestPath.string).make_preferred();
} else [[unlikely]] {
Helpers::panic("ArchiveBase::AppendPath: Invalid 3DS path");
}
}
ArchiveBase(Memory& mem) : mem(mem) {}
};
struct ArchiveResource {

View file

@ -1,30 +0,0 @@
#pragma once
#include "archive_base.hpp"
#include "result/result.hpp"
using Result::HorizonResult;
class CardSPIArchive : public ArchiveBase {
public:
CardSPIArchive(Memory& mem) : ArchiveBase(mem) {}
std::string name() override { return "Card SPI"; }
u64 getFreeBytes() override {
Helpers::warn("Unimplemented GetFreeBytes for Card SPI archive");
return 0_MB;
}
HorizonResult createDirectory(const FSPath& path) override;
HorizonResult createFile(const FSPath& path, u64 size) override;
HorizonResult deleteFile(const FSPath& path) override;
Rust::Result<ArchiveBase*, HorizonResult> openArchive(const FSPath& path) override;
Rust::Result<DirectorySession, HorizonResult> openDirectory(const FSPath& path) override;
FileDescriptor openFile(const FSPath& path, const FilePerms& perms) override;
std::optional<u32> readFile(FileSession* file, u64 offset, u32 size, u32 dataPointer) override {
Helpers::panic("Unimplemented ReadFile for Card SPI archive");
return {};
};
};

View file

@ -1,30 +0,0 @@
#pragma once
#include "archive_base.hpp"
#include "result/result.hpp"
using Result::HorizonResult;
class TWLPhotoArchive : public ArchiveBase {
public:
TWLPhotoArchive(Memory& mem) : ArchiveBase(mem) {}
std::string name() override { return "TWL_PHOTO"; }
u64 getFreeBytes() override {
Helpers::warn("Unimplemented GetFreeBytes for TWLPhoto archive");
return 32_MB;
}
HorizonResult createDirectory(const FSPath& path) override;
HorizonResult createFile(const FSPath& path, u64 size) override;
HorizonResult deleteFile(const FSPath& path) override;
Rust::Result<ArchiveBase*, HorizonResult> openArchive(const FSPath& path) override;
Rust::Result<DirectorySession, HorizonResult> openDirectory(const FSPath& path) override;
FileDescriptor openFile(const FSPath& path, const FilePerms& perms) override;
std::optional<u32> readFile(FileSession* file, u64 offset, u32 size, u32 dataPointer) override {
Helpers::panic("Unimplemented ReadFile for TWL_PHOTO archive");
return {};
};
};

View file

@ -1,30 +0,0 @@
#pragma once
#include "archive_base.hpp"
#include "result/result.hpp"
using Result::HorizonResult;
class TWLSoundArchive : public ArchiveBase {
public:
TWLSoundArchive(Memory& mem) : ArchiveBase(mem) {}
std::string name() override { return "TWL_SOUND"; }
u64 getFreeBytes() override {
Helpers::warn("Unimplemented GetFreeBytes for TWLSound archive");
return 32_MB;
}
HorizonResult createDirectory(const FSPath& path) override;
HorizonResult createFile(const FSPath& path, u64 size) override;
HorizonResult deleteFile(const FSPath& path) override;
Rust::Result<ArchiveBase*, HorizonResult> openArchive(const FSPath& path) override;
Rust::Result<DirectorySession, HorizonResult> openDirectory(const FSPath& path) override;
FileDescriptor openFile(const FSPath& path, const FilePerms& perms) override;
std::optional<u32> readFile(FileSession* file, u64 offset, u32 size, u32 dataPointer) override {
Helpers::panic("Unimplemented ReadFile for TWL_SOUND archive");
return {};
};
};

View file

@ -1,7 +0,0 @@
#pragma once
#include <Foundation/Foundation.h>
#include <QuartzCore/QuartzCore.h>
void iosCreateEmulator();
void iosLoadROM(NSString* pathNS);
void iosRunFrame(CAMetalLayer* layer);

View file

@ -2,19 +2,8 @@
#include <cstdint>
namespace IPC {
namespace BufferType {
enum : std::uint32_t {
Send = 1,
Receive = 2,
};
}
constexpr std::uint32_t responseHeader(std::uint32_t commandID, std::uint32_t normalResponses, std::uint32_t translateResponses) {
// TODO: Maybe validate the response count stuff fits in 6 bits
return (commandID << 16) | (normalResponses << 6) | translateResponses;
}
constexpr std::uint32_t pointerHeader(std::uint32_t index, std::uint32_t size, std::uint32_t type) {
return (size << 14) | (index << 10) | (type << 1);
}
} // namespace IPC
}

View file

@ -8,7 +8,6 @@ namespace ConfigMem {
KernelVersionMajor = 0x1FF80003,
SyscoreVer = 0x1FF80010,
EnvInfo = 0x1FF80014,
PrevFirm = 0x1FF80016,
AppMemAlloc = 0x1FF80040,
FirmUnknown = 0x1FF80060,
FirmRevision = 0x1FF80061,
@ -31,11 +30,6 @@ namespace ConfigMem {
// Shows what type of hardware we're running on
namespace HardwareCodes {
enum : u8 {
Product = 1,
Devboard = 2,
Debugger = 3,
Capture = 4,
};
enum : u8 { Product = 1, Devboard = 2, Debugger = 3, Capture = 4 };
}
} // namespace ConfigMem

View file

@ -20,7 +20,6 @@ namespace KernelHandles {
CFG_U, // CFG service (Console & region info)
CFG_I,
CFG_S, // Used by most system apps in lieu of cfg:u
CFG_NOR, // Used by system settings app
CSND, // Plays audio directly from PCM samples
DLP_SRVR, // Download Play: Server. Used for network play.
DSP, // DSP service (Used for audio decoding and output)
@ -39,14 +38,11 @@ namespace KernelHandles {
NIM, // Updates, DLC, etc
NDM, // ?????
NS_S, // Nintendo Shell service
NWM_EXT, // ?????
NWM_UDS, // Local multiplayer
NEWS_S, // news:u on steroids
NEWS_U, // This service literally has 1 command (AddNotification)
NEWS_U, // This service literally has 1 command (AddNotification) and I don't even understand what it does
PTM_U, // PTM service (Used for accessing various console info, such as battery, shell and pedometer state)
PTM_SYSM, // PTM system service
PTM_PLAY, // PTM Play service, used for retrieving play history
PTM_GETS, // PTM RTC service (GetSystemTime)
SOC, // Socket service
SSL, // SSL service (Totally didn't expect that)
Y2R, // Also does camera stuff
@ -86,8 +82,6 @@ namespace KernelHandles {
case CECD: return "CECD";
case CFG_U: return "CFG:U";
case CFG_I: return "CFG:I";
case CFG_S: return "CFG:S";
case CFG_NOR: return "CFG:NOR";
case CSND: return "CSND";
case DSP: return "DSP";
case DLP_SRVR: return "DLP::SRVR";
@ -103,16 +97,13 @@ namespace KernelHandles {
case MCU_HWC: return "MCU::HWC";
case MIC: return "MIC";
case NDM: return "NDM";
case NEWS_S: return "NEWS_S";
case NEWS_U: return "NEWS_U";
case NWM_EXT: return "nwm::EXT";
case NWM_UDS: return "nwm::UDS";
case NFC: return "NFC";
case NIM: return "NIM";
case PTM_U: return "PTM:U";
case PTM_SYSM: return "PTM:SYSM";
case PTM_PLAY: return "PTM:PLAY";
case PTM_GETS: return "PTM:GETS";
case SOC: return "SOC";
case SSL: return "SSL";
case Y2R: return "Y2R";

View file

@ -175,8 +175,6 @@ public:
void svcSignalEvent();
void svcSetTimer();
void svcSleepThread();
void svcInvalidateInstructionCacheRange();
void svcInvalidateEntireInstructionCache();
void connectToPort();
void outputDebugString();
void waitSynchronization1();
@ -252,6 +250,4 @@ public:
void sendGPUInterrupt(GPUInterrupt type) { serviceManager.sendGPUInterrupt(type); }
void clearInstructionCache();
void clearInstructionCacheRange(u32 start, u32 size);
u32 getSharedFontVaddr();
};

View file

@ -19,7 +19,7 @@ struct ResourceLimitValues {
// APPLICATION resource limit
static constexpr ResourceLimitValues appResourceLimits = {
.maxPriority = 0x18,
.maxCommit = 64_MB + 16_MB, // We're currently giving 80MB to all apps. TODO: Implement extended memory properly
.maxCommit = 0x4000000,
.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 - 16_MB,
.maxCommit = 0x5E00000,
.maxThreads = 0x1D,
.maxEvents = 0xB,
.maxMutexes = 0x8,

View file

@ -65,7 +65,6 @@ namespace Log {
static Logger<false> nwmUdsLogger;
static Logger<false> nimLogger;
static Logger<false> ndmLogger;
static Logger<false> nsLogger;
static Logger<false> ptmLogger;
static Logger<false> socLogger;
static Logger<false> sslLogger;

View file

@ -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(80_MB);
static constexpr u32 FCRAM_APPLICATION_SIZE = u32(64_MB);
static constexpr u32 FCRAM_PAGE_COUNT = FCRAM_SIZE / pageSize;
static constexpr u32 FCRAM_APPLICATION_PAGE_COUNT = FCRAM_APPLICATION_SIZE / pageSize;

View file

@ -146,15 +146,12 @@ class MainWindow : public QMainWindow {
void closeEvent(QCloseEvent* event) override;
void keyPressEvent(QKeyEvent* event) override;
void keyReleaseEvent(QKeyEvent* event) override;
void mousePressEvent(QMouseEvent* event) override;
void mouseReleaseEvent(QMouseEvent* event) override;
void mouseMoveEvent(QMouseEvent* event) override;
void loadLuaScript(const std::string& code);
void reloadShader(const std::string& shader);
void editCheat(u32 handle, const std::vector<uint8_t>& cheat, const std::function<void(u32)>& callback);
void handleScreenResize(u32 width, u32 height);
void handleTouchscreenPress(QMouseEvent* event);
};

View file

@ -53,7 +53,6 @@ class Renderer {
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();
@ -82,14 +81,6 @@ class Renderer {
virtual std::string getUbershader() { return ""; }
virtual void setUbershader(const std::string& shader) {}
// Only relevant for OpenGL renderer and other OpenGL-based backends (eg software)
// 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.

View file

@ -4,7 +4,6 @@
// Stuff like whether specific extensions are supported, and potentially things like OpenGL context information
namespace OpenGL {
struct Driver {
bool usingGLES = false;
bool supportsExtFbFetch = false;
bool supportsArmFbFetch = false;

View file

@ -40,7 +40,7 @@ class RendererGL final : public Renderer {
OpenGL::VertexArray hwShaderVAO;
OpenGL::VertexBuffer vbo;
// Data
// Data
struct {
// TEV configuration uniform locations
GLint textureEnvSourceLoc = -1;
@ -157,7 +157,6 @@ class RendererGL final : public Renderer {
void initGraphicsContextInternal();
void accelerateVertexUpload(ShaderUnit& shaderUnit, PICA::DrawAcceleration* accel);
void compileDisplayShader();
public:
RendererGL(GPU& gpu, const std::array<u32, regNum>& internalRegs, const std::array<u32, extRegNum>& externalRegs)
@ -170,15 +169,14 @@ class RendererGL final : public Renderer {
void clearBuffer(u32 startAddress, u32 endAddress, u32 value, u32 control) override; // Clear a GPU buffer in VRAM
void displayTransfer(u32 inputAddr, u32 outputAddr, u32 inputSize, u32 outputSize, u32 flags) override; // Perform display transfer
void textureCopy(u32 inputAddr, u32 outputAddr, u32 totalBytes, u32 inputSize, u32 outputSize, u32 flags) override;
void drawVertices(PICA::PrimType primType, std::span<const PICA::Vertex> vertices) override; // Draw the given vertices
void drawVertices(PICA::PrimType primType, std::span<const PICA::Vertex> vertices) override; // Draw the given vertices
void deinitGraphicsContext() override;
virtual bool supportsShaderReload() override { return true; }
virtual std::string getUbershader() override;
virtual void setUbershader(const std::string& shader) override;
virtual bool prepareForDraw(ShaderUnit& shaderUnit, PICA::DrawAcceleration* accel) override;
virtual void setupGLES() override;
std::optional<ColourBuffer> getColourBuffer(u32 addr, PICA::ColorFmt format, u32 width, u32 height, bool createIfnotFound = true);
// Note: The caller is responsible for deleting the currently bound FBO before calling this

View file

@ -57,7 +57,7 @@ namespace Metal {
} else if (std::is_same<Format_t, PICA::DepthFmt>::value) {
pixelFormat = PICA::toMTLPixelFormatDepth((PICA::DepthFmt)format);
} else {
Helpers::panic("Invalid format type");
panic("Invalid format type");
}
MTL::TextureDescriptor* descriptor = MTL::TextureDescriptor::alloc()->init();

View file

@ -8,9 +8,8 @@
#include "boost/icl/interval.hpp"
#include "helpers.hpp"
#include "math_util.hpp"
#include "renderer_mtl/pica_to_mtl.hpp"
// TODO: remove dependency on OpenGL
#include "opengl.hpp"
#include "renderer_mtl/pica_to_mtl.hpp"
template <typename T>
using Interval = boost::icl::right_open_interval<T>;
@ -28,8 +27,7 @@ namespace Metal {
// Range of VRAM taken up by buffer
Interval<u32> range;
PICA::MTLPixelFormatInfo formatInfo;
MTL::Texture* base = nullptr;
PICA::PixelFormatInfo formatInfo;
MTL::Texture* texture = nullptr;
MTL::SamplerState* sampler = nullptr;
@ -54,7 +52,22 @@ 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

View file

@ -3,28 +3,31 @@
#include <Metal/Metal.hpp>
#include "PICA/regs.hpp"
// TODO: remove dependency on OpenGL
#include "opengl.hpp"
namespace PICA {
struct MTLPixelFormatInfo {
struct PixelFormatInfo {
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,
};
};
extern MTLPixelFormatInfo mtlPixelFormatInfos[14];
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
};
void checkForMTLPixelFormatSupport(MTL::Device* device);
inline MTLPixelFormatInfo getMTLPixelFormatInfo(TextureFmt format) { return mtlPixelFormatInfos[static_cast<int>(format)]; }
inline PixelFormatInfo getPixelFormatInfo(TextureFmt format) { return pixelFormatInfos[static_cast<int>(format)]; }
inline MTL::PixelFormat toMTLPixelFormatColor(ColorFmt format) {
switch (format) {
@ -32,11 +35,7 @@ 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
}
}

View file

@ -42,13 +42,11 @@ class RendererMTL final : public Renderer {
virtual void initGraphicsContext([[maybe_unused]] GL::Context* context) override {}
#endif
virtual void setMTKLayer(void* layer) override;
private:
CA::MetalLayer* metalLayer = nullptr;
CA::MetalLayer* metalLayer;
MTL::Device* device = nullptr;
MTL::CommandQueue* commandQueue = nullptr;
MTL::Device* device;
MTL::CommandQueue* commandQueue;
Metal::CommandEncoder commandEncoder;
@ -100,7 +98,6 @@ class RendererMTL final : public Renderer {
void endRenderPass() {
if (renderCommandEncoder) {
renderCommandEncoder->endEncoding();
renderCommandEncoder->release();
renderCommandEncoder = nullptr;
}
}

View file

@ -1,24 +0,0 @@
#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);

View file

@ -19,7 +19,6 @@ class ACService {
void closeAsync(u32 messagePointer);
void createDefaultConfig(u32 messagePointer);
void getConnectingInfraPriority(u32 messagePointer);
void getNZoneBeaconNotFoundEvent(u32 messagePointer);
void getStatus(u32 messagePointer);
void getLastErrorCode(u32 messagePointer);
void getWifiStatus(u32 messagePointer);

View file

@ -14,14 +14,10 @@ class BOSSService {
// Service commands
void cancelTask(u32 messagePointer);
void deleteNsData(u32 messagePointer);
void initializeSession(u32 messagePointer);
void getAppNewFlag(u32 messagePointer);
void getErrorCode(u32 messagePointer);
void getNsDataHeaderInfo(u32 messagePointer);
void getNewArrivalFlag(u32 messagePointer);
void getNsDataIdList(u32 messagePointer, u32 commandWord);
void getNsDataLastUpdated(u32 messagePointer);
void getOptoutFlag(u32 messagePointer);
void getStorageEntryInfo(u32 messagePointer); // Unknown what this is, name taken from Citra
void getTaskIdList(u32 messagePointer);
@ -30,15 +26,12 @@ class BOSSService {
void getTaskState(u32 messagePointer);
void getTaskStatus(u32 messagePointer);
void getTaskStorageInfo(u32 messagePointer);
void readNsData(u32 messagePointer);
void receiveProperty(u32 messagePointer);
void registerNewArrivalEvent(u32 messagePointer);
void registerStorageEntry(u32 messagePointer);
void registerTask(u32 messagePointer);
void sendProperty(u32 messagePointer);
void setAppNewFlag(u32 messagePointer);
void setOptoutFlag(u32 messagePointer);
void startBgImmediate(u32 messagePointer);
void startTask(u32 messagePointer);
void unregisterStorage(u32 messagePointer);
void unregisterTask(u32 messagePointer);

View file

@ -1,7 +1,6 @@
#pragma once
#include <cstring>
#include "config.hpp"
#include "helpers.hpp"
#include "logger.hpp"
#include "memory.hpp"
@ -12,8 +11,6 @@ class CFGService {
using Handle = HorizonHandle;
Memory& mem;
const EmulatorConfig& settings;
CountryCodes country = CountryCodes::US; // Default to USA
MAKE_LOG_FUNCTION(log, cfgLogger)
@ -21,23 +18,15 @@ class CFGService {
// Service functions
void getConfigInfoBlk2(u32 messagePointer);
void getConfigInfoBlk8(u32 messagePointer, u32 commandWord);
void getConfigInfoBlk8(u32 messagePointer);
void getCountryCodeID(u32 messagePointer);
void getCountryCodeString(u32 messagePointer);
void getLocalFriendCodeSeed(u32 messagePointer);
void getRegionCanadaUSA(u32 messagePointer);
void getSystemModel(u32 messagePointer);
void genUniqueConsoleHash(u32 messagePointer);
void secureInfoGetByte101(u32 messagePointer);
void secureInfoGetRegion(u32 messagePointer);
void setConfigInfoBlk4(u32 messagePointer);
void updateConfigNANDSavegame(u32 messagePointer);
void translateCountryInfo(u32 messagePointer);
void isFangateSupported(u32 messagePointer);
// cfg:nor functions
void norInitialize(u32 messagePointer);
void norReadData(u32 messagePointer);
void getConfigInfo(u32 output, u32 blockID, u32 size, u32 permissionMask);
@ -49,7 +38,7 @@ class CFGService {
NOR, // cfg:nor
};
CFGService(Memory& mem, const EmulatorConfig& settings) : mem(mem), settings(settings) {}
CFGService(Memory& mem) : mem(mem) {}
void reset();
void handleSyncRequest(u32 messagePointer, Type type);
};

View file

@ -44,12 +44,9 @@ class DSPService {
size_t totalEventCount;
std::vector<u8> loadedComponent;
bool headphonesInserted = true;
// Service functions
void convertProcessAddressFromDspDram(u32 messagePointer); // Nice function name
void flushDataCache(u32 messagePointer);
void forceHeadphoneOut(u32 messagePointer);
void getHeadphoneStatus(u32 messagePointer);
void getSemaphoreEventHandle(u32 messagePointer);
void invalidateDCache(u32 messagePointer);

View file

@ -1,14 +1,11 @@
#pragma once
#include "config.hpp"
#include "fs/archive_card_spi.hpp"
#include "fs/archive_ext_save_data.hpp"
#include "fs/archive_ncch.hpp"
#include "fs/archive_save_data.hpp"
#include "fs/archive_sdmc.hpp"
#include "fs/archive_self_ncch.hpp"
#include "fs/archive_system_save_data.hpp"
#include "fs/archive_twl_photo.hpp"
#include "fs/archive_twl_sound.hpp"
#include "fs/archive_user_save_data.hpp"
#include "helpers.hpp"
#include "kernel_types.hpp"
@ -42,10 +39,6 @@ class FSService {
ExtSaveDataArchive sharedExtSaveData_nand;
SystemSaveDataArchive systemSaveData;
TWLPhotoArchive twlPhoto;
TWLSoundArchive twlSound;
CardSPIArchive cardSpi;
ArchiveBase* getArchiveFromID(u32 id, const FSPath& archivePath);
Rust::Result<Handle, HorizonResult> openArchiveHandle(u32 archiveID, const FSPath& path);
Rust::Result<Handle, HorizonResult> openDirectoryHandle(ArchiveBase* archive, const FSPath& path);
@ -94,8 +87,7 @@ class FSService {
FSService(Memory& mem, Kernel& kernel, const EmulatorConfig& config)
: mem(mem), saveData(mem), sharedExtSaveData_nand(mem, "../SharedFiles/NAND", true), extSaveData_sdmc(mem, "SDMC"), sdmc(mem),
sdmcWriteOnly(mem, true), selfNcch(mem), ncch(mem), userSaveData1(mem, ArchiveID::UserSaveData1),
userSaveData2(mem, ArchiveID::UserSaveData2), systemSaveData(mem), twlPhoto(mem), twlSound(mem), cardSpi(mem), kernel(kernel),
config(config) {}
userSaveData2(mem, ArchiveID::UserSaveData2), kernel(kernel), config(config), systemSaveData(mem) {}
void reset();
void handleSyncRequest(u32 messagePointer);

View file

@ -1,7 +1,6 @@
#pragma once
#include <cstring>
#include <optional>
#include "PICA/gpu.hpp"
#include "helpers.hpp"
#include "kernel_types.hpp"
@ -10,12 +9,12 @@
#include "result/result.hpp"
enum class GPUInterrupt : u8 {
PSC0 = 0, // Memory fill completed
PSC1 = 1, // ?
VBlank0 = 2, // ?
VBlank1 = 3, // ?
PPF = 4, // Display transfer finished
P3D = 5, // Command list processing finished
PSC0 = 0, // Memory fill completed
PSC1 = 1, // ?
VBlank0 = 2, // ?
VBlank1 = 3, // ?
PPF = 4, // Display transfer finished
P3D = 5, // Command list processing finished
DMA = 6
};
@ -29,8 +28,8 @@ class GPUService {
Memory& mem;
GPU& gpu;
Kernel& kernel;
u32& currentPID; // Process ID of the current process
u8* sharedMem; // Pointer to GSP shared memory
u32& currentPID; // Process ID of the current process
u8* sharedMem; // Pointer to GSP shared memory
// At any point in time only 1 process has privileges to use rendering functions
// This is the PID of that process
@ -65,8 +64,8 @@ class GPUService {
// Used for saving and restoring GPU state via ImportDisplayCaptureInfo
struct CaptureInfo {
u32 leftFramebuffer; // Left framebuffer VA
u32 rightFramebuffer; // Right framebuffer VA (Top screen only)
u32 leftFramebuffer; // Left framebuffer VA
u32 rightFramebuffer; // Right framebuffer VA (Top screen only)
u32 format;
u32 stride;
};
@ -75,7 +74,6 @@ class GPUService {
// Service commands
void acquireRight(u32 messagePointer);
void flushDataCache(u32 messagePointer);
void invalidateDataCache(u32 messagePointer);
void importDisplayCaptureInfo(u32 messagePointer);
void readHwRegs(u32 messagePointer);
void registerInterruptRelayQueue(u32 messagePointer);
@ -110,15 +108,16 @@ class GPUService {
FramebufferUpdate* getTopFramebufferInfo() { return getFramebufferInfo(0); }
FramebufferUpdate* getBottomFramebufferInfo() { return getFramebufferInfo(1); }
public:
GPUService(Memory& mem, GPU& gpu, Kernel& kernel, u32& currentPID) : mem(mem), gpu(gpu), kernel(kernel), currentPID(currentPID) {}
public:
GPUService(Memory& mem, GPU& gpu, Kernel& kernel, u32& currentPID) : mem(mem), gpu(gpu),
kernel(kernel), currentPID(currentPID) {}
void reset();
void handleSyncRequest(u32 messagePointer);
void requestInterrupt(GPUInterrupt type);
void setSharedMem(u8* ptr) {
sharedMem = ptr;
if (ptr != nullptr) { // Zero-fill shared memory in case the process tries to read stale service data or vice versa
if (ptr != nullptr) { // Zero-fill shared memory in case the process tries to read stale service data or vice versa
std::memset(ptr, 0, 0x1000);
}
}
};
};

View file

@ -6,13 +6,15 @@
#include "result/result.hpp"
class LCDService {
using Handle = HorizonHandle;
Handle handle = KernelHandles::LCD;
Memory& mem;
MAKE_LOG_FUNCTION(log, gspLCDLogger)
// Service commands
void setLedForceOff(u32 messagePointer);
public:
public:
LCDService(Memory& mem) : mem(mem) {}
void reset();
void handleSyncRequest(u32 messagePointer);

View file

@ -17,7 +17,6 @@ namespace MCU {
// Service commands
void getBatteryLevel(u32 messagePointer);
void setInfoLEDPattern(u32 messagePointer);
public:
HWCService(Memory& mem, const EmulatorConfig& config) : mem(mem), config(config) {}

View file

@ -1,25 +0,0 @@
#pragma once
#include "helpers.hpp"
#include "kernel_types.hpp"
#include "logger.hpp"
#include "memory.hpp"
#include "result/result.hpp"
class NSService {
Memory& mem;
MAKE_LOG_FUNCTION(log, nsLogger)
// Service commands
void launchTitle(u32 messagePointer);
public:
enum class Type {
S, // ns:s
P, // ns:p
C, // ns:c
};
NSService(Memory& mem) : mem(mem) {}
void reset();
void handleSyncRequest(u32 messagePointer, Type type);
};

View file

@ -13,21 +13,17 @@ class PTMService {
const EmulatorConfig& config;
// Service commands
void clearSoftwareClosedFlag(u32 messagePointer);
void configureNew3DSCPU(u32 messagePointer);
void getAdapterState(u32 messagePointer);
void getBatteryChargeState(u32 messagePointer);
void getBatteryLevel(u32 messagePointer);
void getSoftwareClosedFlag(u32 messagePointer);
void getPedometerState(u32 messagePointer);
void getStepHistory(u32 messagePointer);
void getStepHistoryAll(u32 messagePointer);
void getSystemTime(u32 messagePointer);
void getTotalStepCount(u32 messagePointer);
public:
enum class Type {
GETS, // ptm:gets
U, // ptm:u
SYSM, // ptm:sysm
PLAY, // ptm:play

View file

@ -28,11 +28,10 @@
#include "services/mcu/mcu_hwc.hpp"
#include "services/mic.hpp"
#include "services/ndm.hpp"
#include "services/nwm_uds.hpp"
#include "services/news_u.hpp"
#include "services/nfc.hpp"
#include "services/nim.hpp"
#include "services/ns.hpp"
#include "services/nwm_uds.hpp"
#include "services/ptm.hpp"
#include "services/soc.hpp"
#include "services/ssl.hpp"
@ -53,11 +52,11 @@ class ServiceManager {
MAKE_LOG_FUNCTION(log, srvLogger)
ACService ac;
ACService ac;
ACTService act;
AMService am;
AMService am;
APTService apt;
BOSSService boss;
BOSSService boss;
CAMService cam;
CECDService cecd;
CFGService cfg;
@ -77,8 +76,7 @@ class ServiceManager {
NewsUService news_u;
NFCService nfc;
NwmUdsService nwm_uds;
NIMService nim;
NSService ns;
NIMService nim;
PTMService ptm;
SOCService soc;
SSLService ssl;

View file

@ -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 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](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:
- 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,12 +113,11 @@ 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)

View file

@ -6,7 +6,6 @@
#include <fstream>
#include <map>
#include <string>
#include <unordered_map>
#include "helpers.hpp"
#include "toml.hpp"
@ -27,7 +26,6 @@ void EmulatorConfig::load() {
return;
}
printf("Loading existing configuration file %s\n", path.string().c_str());
toml::value data;
try {
@ -47,7 +45,6 @@ void EmulatorConfig::load() {
defaultRomPath = toml::find_or<std::string>(general, "DefaultRomPath", "");
printAppVersion = toml::find_or<toml::boolean>(general, "PrintAppVersion", true);
systemLanguage = languageCodeFromString(toml::find_or<std::string>(general, "SystemLanguage", "en"));
}
}
@ -72,14 +69,14 @@ void EmulatorConfig::load() {
auto gpu = gpuResult.unwrap();
// Get renderer
auto rendererName = toml::find_or<std::string>(gpu, "Renderer", Renderer::typeToString(rendererDefault));
auto rendererName = toml::find_or<std::string>(gpu, "Renderer", "OpenGL");
auto configRendererType = Renderer::typeFromString(rendererName);
if (configRendererType.has_value()) {
rendererType = configRendererType.value();
} else {
Helpers::warn("Invalid renderer specified: %s\n", rendererName.c_str());
rendererType = rendererDefault;
rendererType = RendererType::OpenGL;
}
shaderJitEnabled = toml::find_or<toml::boolean>(gpu, "EnableShaderJIT", shaderJitDefault);
@ -102,7 +99,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);
audioEnabled = toml::find_or<toml::boolean>(audio, "EnableAudio", false);
aacEnabled = toml::find_or<toml::boolean>(audio, "EnableAACAudio", true);
printDSPFirmware = toml::find_or<toml::boolean>(audio, "PrintDSPFirmware", false);
@ -172,7 +169,6 @@ void EmulatorConfig::save() {
data["General"]["UsePortableBuild"] = usePortableBuild;
data["General"]["DefaultRomPath"] = defaultRomPath.string();
data["General"]["PrintAppVersion"] = printAppVersion;
data["General"]["SystemLanguage"] = languageCodeToString(systemLanguage);
data["Window"]["AppVersionOnWindow"] = windowSettings.showAppVersion;
data["Window"]["RememberWindowPosition"] = windowSettings.rememberPosition;
@ -235,34 +231,4 @@ const char* AudioDeviceConfig::volumeCurveToString(AudioDeviceConfig::VolumeCurv
case VolumeCurve::Cubic:
default: return "cubic";
}
}
LanguageCodes EmulatorConfig::languageCodeFromString(std::string inString) { // Transform to lower-case to make the setting case-insensitive
std::transform(inString.begin(), inString.end(), inString.begin(), [](unsigned char c) { return std::tolower(c); });
static const std::unordered_map<std::string, LanguageCodes> map = {
{"ja", LanguageCodes::Japanese}, {"en", LanguageCodes::English}, {"fr", LanguageCodes::French}, {"de", LanguageCodes::German},
{"it", LanguageCodes::Italian}, {"es", LanguageCodes::Spanish}, {"zh", LanguageCodes::Chinese}, {"ko", LanguageCodes::Korean},
{"nl", LanguageCodes::Dutch}, {"pt", LanguageCodes::Portuguese}, {"ru", LanguageCodes::Russian}, {"tw", LanguageCodes::Taiwanese},
};
if (auto search = map.find(inString); search != map.end()) {
return search->second;
}
// Default to English if no language code in our map matches
return LanguageCodes::English;
}
const char* EmulatorConfig::languageCodeToString(LanguageCodes code) {
static constexpr std::array<const char*, 12> codes = {
"ja", "en", "fr", "de", "it", "es", "zh", "ko", "nl", "pt", "ru", "tw",
};
// Invalid country code, return english
if (static_cast<u32>(code) > static_cast<u32>(LanguageCodes::Taiwanese)) {
return "en";
} else {
return codes[static_cast<u32>(code)];
}
}

View file

@ -284,7 +284,7 @@ void GPU::writeInternalReg(u32 index, u32 value, u32 mask) {
break;
case VertexShaderOpDescriptorIndex: {
shaderUnit.vs.setOpDescriptorIndex(newValue);
shaderUnit.vs.setOpDescriptorIndex(value);
break;
}
@ -301,7 +301,7 @@ void GPU::writeInternalReg(u32 index, u32 value, u32 mask) {
}
case VertexBoolUniform: {
shaderUnit.vs.uploadBoolUniform(newValue & 0xffff);
shaderUnit.vs.uploadBoolUniform(value & 0xffff);
break;
}
@ -309,7 +309,7 @@ void GPU::writeInternalReg(u32 index, u32 value, u32 mask) {
case VertexIntUniform1:
case VertexIntUniform2:
case VertexIntUniform3: {
shaderUnit.vs.uploadIntUniform(index - VertexIntUniform0, newValue);
shaderUnit.vs.uploadIntUniform(index - VertexIntUniform0, value);
break;
}
@ -326,7 +326,7 @@ void GPU::writeInternalReg(u32 index, u32 value, u32 mask) {
}
case VertexShaderEntrypoint: {
shaderUnit.vs.entrypoint = newValue & 0xffff;
shaderUnit.vs.entrypoint = value & 0xffff;
break;
}
@ -336,13 +336,13 @@ void GPU::writeInternalReg(u32 index, u32 value, u32 mask) {
break;
*/
case VertexShaderTransferIndex: shaderUnit.vs.setBufferIndex(newValue); break;
case VertexShaderTransferIndex: shaderUnit.vs.setBufferIndex(value); break;
// Command lists can write to the command processor registers and change the command list stream
// Several games are known to do this, including New Super Mario Bros 2 and Super Mario 3D Land
case CmdBufTrigger0:
case CmdBufTrigger1: {
if (newValue != 0) { // A non-zero value triggers command list processing
if (value != 0) { // A non-zero value triggers command list processing
int bufferIndex = index - CmdBufTrigger0; // Index of the command buffer to execute (0 or 1)
u32 addr = (regs[CmdBufAddr0 + bufferIndex] & 0xfffffff) << 3;
u32 size = (regs[CmdBufSize0 + bufferIndex] & 0xfffff) << 3;

View file

@ -7,9 +7,8 @@
#include "helpers.hpp"
MiniAudioDevice::MiniAudioDevice(const AudioDeviceConfig& audioSettings) : AudioDeviceInterface(nullptr, audioSettings), initialized(false) {
running = false;
}
MiniAudioDevice::MiniAudioDevice(const AudioDeviceConfig& audioSettings)
: initialized(false), running(false), samples(nullptr), audioSettings(audioSettings) {}
void MiniAudioDevice::init(Samples& samples, bool safe) {
this->samples = &samples;
@ -213,4 +212,4 @@ void MiniAudioDevice::close() {
ma_device_uninit(&device);
ma_context_uninit(&context);
}
}
}

View file

@ -1,40 +0,0 @@
#include <algorithm>
#include <memory>
#include "fs/archive_card_spi.hpp"
namespace fs = std::filesystem;
HorizonResult CardSPIArchive::createFile(const FSPath& path, u64 size) {
Helpers::panic("[Card SPI] CreateFile not yet supported");
return Result::Success;
}
HorizonResult CardSPIArchive::deleteFile(const FSPath& path) {
Helpers::panic("[Card SPI] Unimplemented DeleteFile");
return Result::Success;
}
HorizonResult CardSPIArchive::createDirectory(const FSPath& path) {
Helpers::panic("[Card SPI] CreateDirectory not yet supported");
return Result::Success;
}
FileDescriptor CardSPIArchive::openFile(const FSPath& path, const FilePerms& perms) {
Helpers::panic("[Card SPI] OpenFile not yet supported");
return FileError;
}
Rust::Result<ArchiveBase*, HorizonResult> CardSPIArchive::openArchive(const FSPath& path) {
if (!path.isEmptyType()) {
Helpers::panic("Unimplemented path type for CardSPIArchive::OpenArchive");
}
Helpers::warn("Unimplemented: Card SPI archive");
return Err(Result::FailurePlaceholder);
}
Rust::Result<DirectorySession, HorizonResult> CardSPIArchive::openDirectory(const FSPath& path) {
Helpers::panic("[Card SPI] OpenDirectory not yet supported");
return Err(Result::FailurePlaceholder);
}

View file

@ -7,13 +7,12 @@ HorizonResult ExtSaveDataArchive::createFile(const FSPath& path, u64 size) {
if (size == 0)
Helpers::panic("ExtSaveData file does not support size == 0");
if (path.isTextPath()) {
if (!isSafeTextPath(path)) {
Helpers::panic("Unsafe path in ExtSaveData::OpenFile");
}
if (path.type == PathType::UTF16) {
if (!isPathSafe<PathType::UTF16>(path))
Helpers::panic("Unsafe path in ExtSaveData::CreateFile");
fs::path p = IOFile::getAppData() / backingFolder;
appendPath(p, path);
p += fs::path(path.utf16_string).make_preferred();
if (fs::exists(p))
return Result::FS::AlreadyExists;
@ -29,17 +28,17 @@ HorizonResult ExtSaveDataArchive::createFile(const FSPath& path, u64 size) {
return Result::FS::FileTooLarge;
}
Helpers::panic("ExtSaveDataArchive::CreateFile: Failed");
Helpers::panic("ExtSaveDataArchive::OpenFile: Failed");
return Result::Success;
}
HorizonResult ExtSaveDataArchive::deleteFile(const FSPath& path) {
if (path.isTextPath()) {
if (!isSafeTextPath(path))
if (path.type == PathType::UTF16) {
if (!isPathSafe<PathType::UTF16>(path))
Helpers::panic("Unsafe path in ExtSaveData::DeleteFile");
fs::path p = IOFile::getAppData() / backingFolder;
appendPath(p, path);
p += fs::path(path.utf16_string).make_preferred();
if (fs::is_directory(p)) {
Helpers::panic("ExtSaveData::DeleteFile: Tried to delete directory");
@ -66,16 +65,15 @@ HorizonResult ExtSaveDataArchive::deleteFile(const FSPath& path) {
}
FileDescriptor ExtSaveDataArchive::openFile(const FSPath& path, const FilePerms& perms) {
if (path.isTextPath()) {
if (!isSafeTextPath(path)) {
if (path.type == PathType::UTF16) {
if (!isPathSafe<PathType::UTF16>(path))
Helpers::panic("Unsafe path in ExtSaveData::OpenFile");
}
if (perms.create())
Helpers::panic("[ExtSaveData] Can't open file with create flag");
fs::path p = IOFile::getAppData() / backingFolder;
appendPath(p, path);
p += fs::path(path.utf16_string).make_preferred();
if (fs::exists(p)) { // Return file descriptor if the file exists
IOFile file(p.string().c_str(), "r+b"); // According to Citra, this ignores the OpenFlags field and always opens as r+b? TODO: Check
@ -90,7 +88,7 @@ FileDescriptor ExtSaveDataArchive::openFile(const FSPath& path, const FilePerms&
}
HorizonResult ExtSaveDataArchive::renameFile(const FSPath& oldPath, const FSPath& newPath) {
if (!oldPath.isUTF16() || !newPath.isUTF16()) {
if (oldPath.type != PathType::UTF16 || newPath.type != PathType::UTF16) {
Helpers::panic("Invalid path type for ExtSaveData::RenameFile");
}
@ -127,18 +125,15 @@ HorizonResult ExtSaveDataArchive::renameFile(const FSPath& oldPath, const FSPath
}
HorizonResult ExtSaveDataArchive::createDirectory(const FSPath& path) {
if (path.isTextPath()) {
if (!isSafeTextPath(path)) {
if (path.type == PathType::UTF16) {
if (!isPathSafe<PathType::UTF16>(path)) {
Helpers::panic("Unsafe path in ExtSaveData::OpenFile");
}
fs::path p = IOFile::getAppData() / backingFolder;
appendPath(p, path);
if (fs::is_directory(p)) {
return Result::FS::AlreadyExists;
}
p += fs::path(path.utf16_string).make_preferred();
if (fs::is_directory(p)) return Result::FS::AlreadyExists;
if (fs::is_regular_file(p)) {
Helpers::panic("File path passed to ExtSaveData::CreateDirectory");
}
@ -161,7 +156,7 @@ std::string ExtSaveDataArchive::getExtSaveDataPathFromBinary(const FSPath& path)
}
Rust::Result<ArchiveBase*, HorizonResult> ExtSaveDataArchive::openArchive(const FSPath& path) {
if (!path.isBinary() || path.binary.size() != 12) {
if (path.type != PathType::Binary || path.binary.size() != 12) {
Helpers::panic("ExtSaveData accessed with an invalid path in OpenArchive");
}
@ -177,12 +172,12 @@ Rust::Result<ArchiveBase*, HorizonResult> ExtSaveDataArchive::openArchive(const
}
Rust::Result<DirectorySession, HorizonResult> ExtSaveDataArchive::openDirectory(const FSPath& path) {
if (path.isTextPath()) {
if (!isSafeTextPath(path))
if (path.type == PathType::UTF16) {
if (!isPathSafe<PathType::UTF16>(path))
Helpers::panic("Unsafe path in ExtSaveData::OpenDirectory");
fs::path p = IOFile::getAppData() / backingFolder;
appendPath(p, path);
p += fs::path(path.utf16_string).make_preferred();
if (fs::is_regular_file(p)) {
printf("ExtSaveData: OpenArchive used with a file path");

View file

@ -32,7 +32,7 @@ HorizonResult NCCHArchive::deleteFile(const FSPath& path) {
}
FileDescriptor NCCHArchive::openFile(const FSPath& path, const FilePerms& perms) {
if (!path.isBinary() || path.binary.size() != 20) {
if (path.type != PathType::Binary || path.binary.size() != 20) {
Helpers::panic("NCCHArchive::OpenFile: Invalid path");
}
@ -49,7 +49,7 @@ FileDescriptor NCCHArchive::openFile(const FSPath& path, const FilePerms& perms)
}
Rust::Result<ArchiveBase*, HorizonResult> NCCHArchive::openArchive(const FSPath& path) {
if (!path.isBinary() || path.binary.size() != 16) {
if (path.type != PathType::Binary || path.binary.size() != 16) {
Helpers::panic("NCCHArchive::OpenArchive: Invalid path");
}

View file

@ -5,7 +5,7 @@
namespace fs = std::filesystem;
HorizonResult SaveDataArchive::createFile(const FSPath& path, u64 size) {
if (path.isUTF16()) {
if (path.type == PathType::UTF16) {
if (!isPathSafe<PathType::UTF16>(path))
Helpers::panic("Unsafe path in SaveData::CreateFile");
@ -39,7 +39,7 @@ HorizonResult SaveDataArchive::createFile(const FSPath& path, u64 size) {
}
HorizonResult SaveDataArchive::createDirectory(const FSPath& path) {
if (path.isUTF16()) {
if (path.type == PathType::UTF16) {
if (!isPathSafe<PathType::UTF16>(path)) {
Helpers::panic("Unsafe path in SaveData::OpenFile");
}
@ -63,7 +63,7 @@ HorizonResult SaveDataArchive::createDirectory(const FSPath& path) {
}
HorizonResult SaveDataArchive::deleteFile(const FSPath& path) {
if (path.isUTF16()) {
if (path.type == PathType::UTF16) {
if (!isPathSafe<PathType::UTF16>(path)) {
Helpers::panic("Unsafe path in SaveData::DeleteFile");
}
@ -96,7 +96,7 @@ HorizonResult SaveDataArchive::deleteFile(const FSPath& path) {
}
FileDescriptor SaveDataArchive::openFile(const FSPath& path, const FilePerms& perms) {
if (path.isUTF16()) {
if (path.type == PathType::UTF16) {
if (!isPathSafe<PathType::UTF16>(path)) {
Helpers::panic("Unsafe path in SaveData::OpenFile");
}
@ -132,7 +132,7 @@ FileDescriptor SaveDataArchive::openFile(const FSPath& path, const FilePerms& pe
}
Rust::Result<DirectorySession, HorizonResult> SaveDataArchive::openDirectory(const FSPath& path) {
if (path.isUTF16()) {
if (path.type == PathType::UTF16) {
if (!isPathSafe<PathType::UTF16>(path)) {
Helpers::panic("Unsafe path in SaveData::OpenDirectory");
}
@ -193,7 +193,7 @@ void SaveDataArchive::format(const FSPath& path, const ArchiveBase::FormatInfo&
}
Rust::Result<ArchiveBase*, HorizonResult> SaveDataArchive::openArchive(const FSPath& path) {
if (!path.isEmptyType()) {
if (path.type != PathType::Empty) {
Helpers::panic("Unimplemented path type for SaveData archive: %d\n", path.type);
return Err(Result::FS::NotFoundInvalid);
}

View file

@ -4,13 +4,13 @@
namespace fs = std::filesystem;
HorizonResult SDMCArchive::createFile(const FSPath& path, u64 size) {
if (path.isTextPath()) {
if (!isSafeTextPath(path)) {
if (path.type == PathType::UTF16) {
if (!isPathSafe<PathType::UTF16>(path)) {
Helpers::panic("Unsafe path in SDMC::CreateFile");
}
fs::path p = IOFile::getAppData() / "SDMC";
appendPath(p, path);
p += fs::path(path.utf16_string).make_preferred();
if (fs::exists(p)) {
return Result::FS::AlreadyExists;
@ -39,13 +39,13 @@ HorizonResult SDMCArchive::createFile(const FSPath& path, u64 size) {
}
HorizonResult SDMCArchive::deleteFile(const FSPath& path) {
if (path.isTextPath()) {
if (!isSafeTextPath(path)) {
if (path.type == PathType::UTF16) {
if (!isPathSafe<PathType::UTF16>(path)) {
Helpers::panic("Unsafe path in SDMC::DeleteFile");
}
fs::path p = IOFile::getAppData() / "SDMC";
appendPath(p, path);
p += fs::path(path.utf16_string).make_preferred();
if (fs::is_directory(p)) {
Helpers::panic("SDMC::DeleteFile: Tried to delete directory");
@ -171,13 +171,13 @@ Rust::Result<DirectorySession, HorizonResult> SDMCArchive::openDirectory(const F
return Err(Result::FS::UnexpectedFileOrDir);
}
if (path.isTextPath()) {
if (!isSafeTextPath(path)) {
if (path.type == PathType::UTF16) {
if (!isPathSafe<PathType::UTF16>(path)) {
Helpers::panic("Unsafe path in SDMC::OpenDirectory");
}
fs::path p = IOFile::getAppData() / "SDMC";
appendPath(p, path);
p += fs::path(path.utf16_string).make_preferred();
if (fs::is_regular_file(p)) {
printf("SDMC: OpenDirectory used with a file path");
@ -197,7 +197,7 @@ Rust::Result<DirectorySession, HorizonResult> SDMCArchive::openDirectory(const F
Rust::Result<ArchiveBase*, HorizonResult> SDMCArchive::openArchive(const FSPath& path) {
// TODO: Fail here if the SD is disabled in the connfig.
if (!path.isEmptyType()) {
if (path.type != PathType::Empty) {
Helpers::panic("Unimplemented path type for SDMC::OpenArchive");
}

View file

@ -26,7 +26,7 @@ FileDescriptor SelfNCCHArchive::openFile(const FSPath& path, const FilePerms& pe
return FileError;
}
if (!path.isBinary() || path.binary.size() != 12) {
if (path.type != PathType::Binary || path.binary.size() != 12) {
printf("Invalid SelfNCCH path type\n");
return FileError;
}
@ -42,7 +42,7 @@ FileDescriptor SelfNCCHArchive::openFile(const FSPath& path, const FilePerms& pe
}
Rust::Result<ArchiveBase*, HorizonResult> SelfNCCHArchive::openArchive(const FSPath& path) {
if (!path.isEmptyType()) {
if (path.type != PathType::Empty) {
Helpers::panic("Invalid path type for SelfNCCH archive: %d\n", path.type);
return Err(Result::FS::NotFoundInvalid);
}

View file

@ -4,7 +4,7 @@
namespace fs = std::filesystem;
Rust::Result<ArchiveBase*, HorizonResult> SystemSaveDataArchive::openArchive(const FSPath& path) {
if (!path.isBinary()) {
if (path.type != PathType::Binary) {
Helpers::panic("Unimplemented path type for SystemSaveData::OpenArchive");
}
@ -14,7 +14,7 @@ Rust::Result<ArchiveBase*, HorizonResult> SystemSaveDataArchive::openArchive(con
FileDescriptor SystemSaveDataArchive::openFile(const FSPath& path, const FilePerms& perms) {
// TODO: Validate this. Temporarily copied from SaveData archive
if (path.isUTF16()) {
if (path.type == PathType::UTF16) {
if (!isPathSafe<PathType::UTF16>(path)) {
Helpers::panic("Unsafe path in SystemSaveData::OpenFile");
}
@ -50,7 +50,7 @@ FileDescriptor SystemSaveDataArchive::openFile(const FSPath& path, const FilePer
}
HorizonResult SystemSaveDataArchive::createFile(const FSPath& path, u64 size) {
if (path.isUTF16()) {
if (path.type == PathType::UTF16) {
if (!isPathSafe<PathType::UTF16>(path)) {
Helpers::panic("Unsafe path in SystemSaveData::CreateFile");
}
@ -85,9 +85,9 @@ HorizonResult SystemSaveDataArchive::createFile(const FSPath& path, u64 size) {
}
HorizonResult SystemSaveDataArchive::createDirectory(const FSPath& path) {
if (path.isUTF16()) {
if (path.type == PathType::UTF16) {
if (!isPathSafe<PathType::UTF16>(path)) {
Helpers::panic("Unsafe path in SystemSaveData::CreateDirectory");
Helpers::panic("Unsafe path in SystemSaveData::OpenFile");
}
fs::path p = IOFile::getAppData() / ".." / "SharedFiles" / "SystemSaveData";
@ -110,7 +110,7 @@ HorizonResult SystemSaveDataArchive::createDirectory(const FSPath& path) {
HorizonResult SystemSaveDataArchive::deleteFile(const FSPath& path) {
if (path.isUTF16()) {
if (path.type == PathType::UTF16) {
if (!isPathSafe<PathType::UTF16>(path)) {
Helpers::panic("Unsafe path in SystemSaveData::DeleteFile");
}
@ -143,7 +143,7 @@ HorizonResult SystemSaveDataArchive::deleteFile(const FSPath& path) {
}
Rust::Result<DirectorySession, HorizonResult> SystemSaveDataArchive::openDirectory(const FSPath& path) {
if (path.isUTF16()) {
if (path.type == PathType::UTF16) {
if (!isPathSafe<PathType::UTF16>(path)) {
Helpers::warn("Unsafe path in SystemSaveData::OpenDirectory");
return Err(Result::FS::FileNotFoundAlt);

View file

@ -1,40 +0,0 @@
#include <algorithm>
#include <memory>
#include "fs/archive_twl_photo.hpp"
namespace fs = std::filesystem;
HorizonResult TWLPhotoArchive::createFile(const FSPath& path, u64 size) {
Helpers::panic("[TWL_PHOTO] CreateFile not yet supported");
return Result::Success;
}
HorizonResult TWLPhotoArchive::deleteFile(const FSPath& path) {
Helpers::panic("[TWL_PHOTO] Unimplemented DeleteFile");
return Result::Success;
}
HorizonResult TWLPhotoArchive::createDirectory(const FSPath& path) {
Helpers::panic("[TWL_PHOTO] CreateDirectory not yet supported");
return Result::Success;
}
FileDescriptor TWLPhotoArchive::openFile(const FSPath& path, const FilePerms& perms) {
Helpers::panic("[TWL_PHOTO] OpenFile not yet supported");
return FileError;
}
Rust::Result<ArchiveBase*, HorizonResult> TWLPhotoArchive::openArchive(const FSPath& path) {
if (!path.isEmptyType()) {
Helpers::panic("Unimplemented path type for TWLPhotoArchive::OpenArchive");
}
Helpers::warn("Unimplemented: TWL_PHOTO archive");
return Err(Result::FailurePlaceholder);
}
Rust::Result<DirectorySession, HorizonResult> TWLPhotoArchive::openDirectory(const FSPath& path) {
Helpers::panic("[TWL_PHOTO] OpenDirectory not yet supported");
return Err(Result::FailurePlaceholder);
}

View file

@ -1,40 +0,0 @@
#include <algorithm>
#include <memory>
#include "fs/archive_twl_sound.hpp"
namespace fs = std::filesystem;
HorizonResult TWLSoundArchive::createFile(const FSPath& path, u64 size) {
Helpers::panic("[TWL_SOUND] CreateFile not yet supported");
return Result::Success;
}
HorizonResult TWLSoundArchive::deleteFile(const FSPath& path) {
Helpers::panic("[TWL_SOUND] Unimplemented DeleteFile");
return Result::Success;
}
HorizonResult TWLSoundArchive::createDirectory(const FSPath& path) {
Helpers::panic("[TWL_SOUND] CreateDirectory not yet supported");
return Result::Success;
}
FileDescriptor TWLSoundArchive::openFile(const FSPath& path, const FilePerms& perms) {
Helpers::panic("[TWL_SOUND] OpenFile not yet supported");
return FileError;
}
Rust::Result<ArchiveBase*, HorizonResult> TWLSoundArchive::openArchive(const FSPath& path) {
if (!path.isEmptyType()) {
Helpers::panic("Unimplemented path type for TWLSoundArchive::OpenArchive");
}
Helpers::warn("Unimplemented: TWL_SOUND archive");
return Err(Result::FailurePlaceholder);
}
Rust::Result<DirectorySession, HorizonResult> TWLSoundArchive::openDirectory(const FSPath& path) {
Helpers::panic("[TWL_SOUND] OpenDirectory not yet supported");
return Err(Result::FailurePlaceholder);
}

View file

@ -6,15 +6,13 @@
namespace fs = std::filesystem;
HorizonResult UserSaveDataArchive::createFile(const FSPath& path, u64 size) {
if (path.isUTF16()) {
if (path.type == PathType::UTF16) {
if (!isPathSafe<PathType::UTF16>(path)) Helpers::panic("Unsafe path in UserSaveData::CreateFile");
fs::path p = IOFile::getAppData() / "SaveData";
p += fs::path(path.utf16_string).make_preferred();
if (fs::exists(p)) {
return Result::FS::AlreadyExists;
}
if (fs::exists(p)) return Result::FS::AlreadyExists;
IOFile file(p.string().c_str(), "wb");
@ -39,10 +37,8 @@ HorizonResult UserSaveDataArchive::createFile(const FSPath& path, u64 size) {
}
HorizonResult UserSaveDataArchive::createDirectory(const FSPath& path) {
if (path.isUTF16()) {
if (!isPathSafe<PathType::UTF16>(path)) {
Helpers::panic("Unsafe path in UserSaveData::OpenFile");
}
if (path.type == PathType::UTF16) {
if (!isPathSafe<PathType::UTF16>(path)) Helpers::panic("Unsafe path in UserSaveData::OpenFile");
fs::path p = IOFile::getAppData() / "SaveData";
p += fs::path(path.utf16_string).make_preferred();
@ -60,7 +56,7 @@ HorizonResult UserSaveDataArchive::createDirectory(const FSPath& path) {
}
HorizonResult UserSaveDataArchive::deleteFile(const FSPath& path) {
if (path.isUTF16()) {
if (path.type == PathType::UTF16) {
if (!isPathSafe<PathType::UTF16>(path)) Helpers::panic("Unsafe path in UserSaveData::DeleteFile");
fs::path p = IOFile::getAppData() / "SaveData";
@ -91,7 +87,7 @@ HorizonResult UserSaveDataArchive::deleteFile(const FSPath& path) {
}
FileDescriptor UserSaveDataArchive::openFile(const FSPath& path, const FilePerms& perms) {
if (path.isUTF16()) {
if (path.type == PathType::UTF16) {
if (!isPathSafe<PathType::UTF16>(path)) Helpers::panic("Unsafe path in UserSaveData::OpenFile");
if (perms.raw == 0 || (perms.create() && !perms.write())) Helpers::panic("[UserSaveData] Unsupported flags for OpenFile");
@ -123,7 +119,7 @@ FileDescriptor UserSaveDataArchive::openFile(const FSPath& path, const FilePerms
}
Rust::Result<DirectorySession, HorizonResult> UserSaveDataArchive::openDirectory(const FSPath& path) {
if (path.isUTF16()) {
if (path.type == PathType::UTF16) {
if (!isPathSafe<PathType::UTF16>(path)) Helpers::panic("Unsafe path in UserSaveData::OpenDirectory");
fs::path p = IOFile::getAppData() / "SaveData";

View file

@ -69,10 +69,6 @@ void Kernel::serviceSVC(u32 svc) {
case 0x3A: getResourceLimitCurrentValues(); break;
case 0x3B: getThreadContext(); break;
case 0x3D: outputDebugString(); break;
// Luma SVCs
case 0x93: svcInvalidateInstructionCacheRange(); break;
case 0x94: svcInvalidateEntireInstructionCache(); break;
default: Helpers::panic("Unimplemented svc: %X @ %08X", svc, regs[15]); break;
}
@ -302,23 +298,6 @@ void Kernel::duplicateHandle() {
}
void Kernel::clearInstructionCache() { cpu.clearCache(); }
void Kernel::clearInstructionCacheRange(u32 start, u32 size) { cpu.clearCacheRange(start, size); }
void Kernel::svcInvalidateInstructionCacheRange() {
const u32 start = regs[0];
const u32 size = regs[1];
logSVC("svcInvalidateInstructionCacheRange(start = %08X, size = %08X)\n", start, size);
clearInstructionCacheRange(start, size);
regs[0] = Result::Success;
}
void Kernel::svcInvalidateEntireInstructionCache() {
logSVC("svcInvalidateEntireInstructionCache()\n");
clearInstructionCache();
regs[0] = Result::Success;
}
namespace SystemInfoType {
enum : u32 {

View file

@ -122,10 +122,7 @@ void Kernel::mapMemoryBlock() {
}
if (KernelHandles::isSharedMemHandle(block)) {
if (block == KernelHandles::FontSharedMemHandle && addr == 0) {
addr = getSharedFontVaddr();
}
if (block == KernelHandles::FontSharedMemHandle && addr == 0) addr = 0x18000000;
u8* ptr = mem.mapSharedMemory(block, addr, myPerms, otherPerms); // Map shared memory block
// Pass pointer to shared memory to the appropriate service
@ -219,8 +216,3 @@ void Kernel::unmapMemoryBlock() {
Helpers::warn("Stubbed svcUnmapMemoryBlock!");
regs[0] = Result::Success;
}
u32 Kernel::getSharedFontVaddr() {
// Place shared font at the very beginning of system FCRAM
return mem.getLinearHeapVaddr() + Memory::FCRAM_APPLICATION_SIZE;
}

View file

@ -51,12 +51,17 @@ void RendererGL::reset() {
gl.useProgram(oldProgram); // Switch to old GL program
}
#ifdef USING_GLES
fragShaderGen.setTarget(PICA::ShaderGen::API::GLES, PICA::ShaderGen::Language::GLSL);
#endif
}
void RendererGL::initGraphicsContextInternal() {
gl.reset();
auto gl_resources = cmrc::RendererGL::get_filesystem();
auto vertexShaderSource = gl_resources.open("opengl_vertex_shader.vert");
auto fragmentShaderSource = gl_resources.open("opengl_fragment_shader.frag");
@ -65,7 +70,16 @@ void RendererGL::initGraphicsContextInternal() {
triangleProgram.create({vert, frag});
initUbershader(triangleProgram);
compileDisplayShader();
auto displayVertexShaderSource = gl_resources.open("opengl_display.vert");
auto displayFragmentShaderSource = gl_resources.open("opengl_display.frag");
OpenGL::Shader vertDisplay({displayVertexShaderSource.begin(), displayVertexShaderSource.size()}, OpenGL::Vertex);
OpenGL::Shader fragDisplay({displayFragmentShaderSource.begin(), displayFragmentShaderSource.size()}, OpenGL::Fragment);
displayProgram.create({vertDisplay, fragDisplay});
gl.useProgram(displayProgram);
glUniform1i(OpenGL::uniformLocation(displayProgram, "u_texture"), 0); // Init sampler object
// Create stream buffers for vertex, index and uniform buffers
static constexpr usize hwIndexBufferSize = 2_MB;
static constexpr usize hwVertexBufferSize = 16_MB;
@ -177,7 +191,6 @@ void RendererGL::initGraphicsContextInternal() {
}
reset();
fragShaderGen.setTarget(driverInfo.usingGLES ? PICA::ShaderGen::API::GLES : PICA::ShaderGen::API::GL, PICA::ShaderGen::Language::GLSL);
// Populate our driver info structure
driverInfo.supportsExtFbFetch = (GLAD_GL_EXT_shader_framebuffer_fetch != 0);
@ -792,8 +805,6 @@ void RendererGL::textureCopy(u32 inputAddr, u32 outputAddr, u32 totalBytes, u32
shutUpCounter++;
printf("RendererGL::TextureCopy failed to locate src framebuffer!\n");
}
doSoftwareTextureCopy(inputAddr, outputAddr, copySize, inputWidth, inputGap, outputWidth, outputGap);
return;
}
@ -839,9 +850,9 @@ OpenGL::Program& RendererGL::getSpecializedShader() {
PICA::FragmentConfig fsConfig(regs);
// If we're not on GLES, ignore the logic op configuration and don't generate redundant shaders for it, since we use hw logic ops
if (!driverInfo.usingGLES) {
fsConfig.outConfig.logicOpMode = PICA::LogicOpMode(0);
}
#ifndef USING_GLES
fsConfig.outConfig.logicOpMode = PICA::LogicOpMode(0);
#endif
OpenGL::Shader& fragShader = shaderCache.fragmentShaderCache[fsConfig];
if (!fragShader.exists()) {
@ -999,7 +1010,7 @@ bool RendererGL::prepareForDraw(ShaderUnit& shaderUnit, PICA::DrawAcceleration*
std::string picaShaderSource = PICA::ShaderGen::decompileShader(
shaderUnit.vs, *emulatorConfig, shaderUnit.vs.entrypoint,
driverInfo.usingGLES ? PICA::ShaderGen::API::GLES : PICA::ShaderGen::API::GL, PICA::ShaderGen::Language::GLSL
Helpers::isAndroid() ? PICA::ShaderGen::API::GLES : PICA::ShaderGen::API::GL, PICA::ShaderGen::Language::GLSL
);
// Empty source means compilation error, if the source is not empty then we convert the recompiled PICA code into a valid shader and upload
@ -1145,19 +1156,6 @@ void RendererGL::initUbershader(OpenGL::Program& program) {
glUniform1i(OpenGL::uniformLocation(program, "u_tex_luts"), 3);
}
void RendererGL::compileDisplayShader() {
auto gl_resources = cmrc::RendererGL::get_filesystem();
auto displayVertexShaderSource = driverInfo.usingGLES ? gl_resources.open("opengl_es_display.vert") : gl_resources.open("opengl_display.vert");
auto displayFragmentShaderSource = driverInfo.usingGLES ? gl_resources.open("opengl_es_display.frag") : gl_resources.open("opengl_display.frag");
OpenGL::Shader vertDisplay({displayVertexShaderSource.begin(), displayVertexShaderSource.size()}, OpenGL::Vertex);
OpenGL::Shader fragDisplay({displayFragmentShaderSource.begin(), displayFragmentShaderSource.size()}, OpenGL::Fragment);
displayProgram.create({vertDisplay, fragDisplay});
gl.useProgram(displayProgram);
glUniform1i(OpenGL::uniformLocation(displayProgram, "u_texture"), 0); // Init sampler object
}
void RendererGL::accelerateVertexUpload(ShaderUnit& shaderUnit, PICA::DrawAcceleration* accel) {
u32 buffer = 0; // Vertex buffer index for non-fixed attributes
u32 attrCount = 0;
@ -1252,20 +1250,4 @@ void RendererGL::accelerateVertexUpload(ShaderUnit& shaderUnit, PICA::DrawAccele
);
}
}
}
void RendererGL::setupGLES() {
driverInfo.usingGLES = true;
// OpenGL ES hardware is typically way too slow to use the ubershader (eg RPi, mobile phones, handhelds) or has other issues with it.
// So, display a warning and turn them off on OpenGL ES.
if (emulatorConfig->useUbershaders) {
emulatorConfig->useUbershaders = false;
Helpers::warn("Ubershaders enabled on OpenGL ES. This usually results in a worse experience, turning it off...");
}
// Stub out logic operations so that calling them doesn't crash the emulator
if (!glLogicOp) {
glLogicOp = [](GLenum) {};
}
}
}

View file

@ -0,0 +1,116 @@
#include <algorithm>
#include "colour.hpp"
#include "renderer_mtl/mtl_texture.hpp"
#include "renderer_mtl/renderer_mtl.hpp"
using namespace Helpers;
namespace Metal {
static constexpr u32 signExtend3To32(u32 val) {
return (u32)(s32(val) << 29 >> 29);
}
u32 Texture::getTexelETC(bool hasAlpha, u32 u, u32 v, u32 width, std::span<const u8> data) {
// Pixel offset of the 8x8 tile based on u, v and the width of the texture
u32 offs = ((u & ~7) * 8) + ((v & ~7) * width);
if (!hasAlpha) {
offs >>= 1;
}
// In-tile offsets for u/v
u &= 7;
v &= 7;
// ETC1(A4) also subdivide the 8x8 tile to 4 4x4 tiles
// Each tile is 8 bytes for ETC1, but since ETC1A4 has 4 alpha bits per pixel, that becomes 16 bytes
const u32 subTileSize = hasAlpha ? 16 : 8;
const u32 subTileIndex = (u / 4) + 2 * (v / 4); // Which of the 4 subtiles is this texel in?
// In-subtile offsets for u/v
u &= 3;
v &= 3;
offs += subTileSize * subTileIndex;
u32 alpha;
const u64* ptr = reinterpret_cast<const u64*>(data.data() + offs); // Cast to u64*
if (hasAlpha) {
// First 64 bits of the 4x4 subtile are alpha data
const u64 alphaData = *ptr++;
alpha = Colour::convert4To8Bit((alphaData >> (4 * (u * 4 + v))) & 0xf);
} else {
alpha = 0xff; // ETC1 without alpha uses ff for every pixel
}
// Next 64 bits of the subtile are colour data
u64 colourData = *ptr;
return decodeETC(alpha, u, v, colourData);
}
u32 Texture::decodeETC(u32 alpha, u32 u, u32 v, u64 colourData) {
static constexpr u32 modifiers[8][2] = {
{2, 8}, {5, 17}, {9, 29}, {13, 42}, {18, 60}, {24, 80}, {33, 106}, {47, 183},
};
// Parse colour data for 4x4 block
const u32 subindices = getBits<0, 16, u32>(colourData);
const u32 negationFlags = getBits<16, 16, u32>(colourData);
const bool flip = getBit<32>(colourData);
const bool diffMode = getBit<33>(colourData);
// Note: index1 is indeed stored on the higher bits, with index2 in the lower bits
const u32 tableIndex1 = getBits<37, 3, u32>(colourData);
const u32 tableIndex2 = getBits<34, 3, u32>(colourData);
const u32 texelIndex = u * 4 + v; // Index of the texel in the block
if (flip) std::swap(u, v);
s32 r, g, b;
if (diffMode) {
r = getBits<59, 5, s32>(colourData);
g = getBits<51, 5, s32>(colourData);
b = getBits<43, 5, s32>(colourData);
if (u >= 2) {
r += signExtend3To32(getBits<56, 3, u32>(colourData));
g += signExtend3To32(getBits<48, 3, u32>(colourData));
b += signExtend3To32(getBits<40, 3, u32>(colourData));
}
// Expand from 5 to 8 bits per channel
r = Colour::convert5To8Bit(r);
g = Colour::convert5To8Bit(g);
b = Colour::convert5To8Bit(b);
} else {
if (u < 2) {
r = getBits<60, 4, s32>(colourData);
g = getBits<52, 4, s32>(colourData);
b = getBits<44, 4, s32>(colourData);
} else {
r = getBits<56, 4, s32>(colourData);
g = getBits<48, 4, s32>(colourData);
b = getBits<40, 4, s32>(colourData);
}
// Expand from 4 to 8 bits per channel
r = Colour::convert4To8Bit(r);
g = Colour::convert4To8Bit(g);
b = Colour::convert4To8Bit(b);
}
const u32 index = (u < 2) ? tableIndex1 : tableIndex2;
s32 modifier = modifiers[index][(subindices >> texelIndex) & 1];
if (((negationFlags >> texelIndex) & 1) != 0) {
modifier = -modifier;
}
r = std::clamp(r + modifier, 0, 255);
g = std::clamp(g + modifier, 0, 255);
b = std::clamp(b + modifier, 0, 255);
return (alpha << 24) | (u32(b) << 16) | (u32(g) << 8) | u32(r);
}
} // namespace Metal

View file

@ -1,18 +1,16 @@
#include "renderer_mtl/mtl_texture.hpp"
#include <fmt/format.h>
#include <array>
#include <memory>
#include "colour.hpp"
#include "renderer_mtl/objc_helper.hpp"
using namespace Helpers;
namespace Metal {
void Texture::allocate() {
formatInfo = PICA::getMTLPixelFormatInfo(format);
formatInfo = PICA::getPixelFormatInfo(format);
MTL::TextureDescriptor* descriptor = MTL::TextureDescriptor::alloc()->init();
descriptor->setTextureType(MTL::TextureType2D);
@ -22,14 +20,11 @@ namespace Metal {
descriptor->setUsage(MTL::TextureUsageShaderRead);
descriptor->setStorageMode(MTL::StorageModeShared); // TODO: use private + staging buffers?
texture = device->newTexture(descriptor);
texture->setLabel(toNSString(fmt::format("Base texture {} {}x{}", std::string(PICA::textureFormatToString(format)), size.u(), size.v())));
texture->setLabel(toNSString(
"Texture " + std::string(PICA::textureFormatToString(format)) + " " + std::to_string(size.u()) + "x" + std::to_string(size.v())
));
descriptor->release();
if (formatInfo.needsSwizzle) {
base = texture;
texture = base->newTextureView(formatInfo.pixelFormat, MTL::TextureType2D, NS::Range(0, 1), NS::Range(0, 1), formatInfo.swizzle);
}
setNewConfig(config);
}
@ -63,11 +58,6 @@ namespace Metal {
if (texture) {
texture->release();
}
if (base) {
base->release();
}
if (sampler) {
sampler->release();
}
@ -109,19 +99,210 @@ namespace Metal {
}
}
// u and v are the UVs of the relevant texel
// Texture data is stored interleaved in Morton order, ie in a Z - order curve as shown here
// https://en.wikipedia.org/wiki/Z-order_curve
// Textures are split into 8x8 tiles.This function returns the in - tile offset depending on the u & v of the texel
// The in - tile offset is the sum of 2 offsets, one depending on the value of u % 8 and the other on the value of y % 8
// As documented in this picture https ://en.wikipedia.org/wiki/File:Moser%E2%80%93de_Bruijn_addition.svg
u32 Texture::mortonInterleave(u32 u, u32 v) {
static constexpr u32 xOffsets[] = {0, 1, 4, 5, 16, 17, 20, 21};
static constexpr u32 yOffsets[] = {0, 2, 8, 10, 32, 34, 40, 42};
return xOffsets[u & 7] + yOffsets[v & 7];
}
// Get the byte offset of texel (u, v) in the texture
u32 Texture::getSwizzledOffset(u32 u, u32 v, u32 width, u32 bytesPerPixel) {
u32 offset = ((u & ~7) * 8) + ((v & ~7) * width); // Offset of the 8x8 tile the texel belongs to
offset += mortonInterleave(u, v); // Add the in-tile offset of the texel
return offset * bytesPerPixel;
}
// Same as the above code except we need to divide by 2 because 4 bits is smaller than a byte
u32 Texture::getSwizzledOffset_4bpp(u32 u, u32 v, u32 width) {
u32 offset = ((u & ~7) * 8) + ((v & ~7) * width); // Offset of the 8x8 tile the texel belongs to
offset += mortonInterleave(u, v); // Add the in-tile offset of the texel
return offset / 2;
}
u8 Texture::decodeTexelU8(u32 u, u32 v, PICA::TextureFmt fmt, std::span<const u8> data) {
switch (fmt) {
case PICA::TextureFmt::A4: {
const u32 offset = getSwizzledOffset_4bpp(u, v, size.u());
// For odd U coordinates, grab the top 4 bits, and the low 4 bits for even coordinates
u8 alpha = data[offset] >> ((u % 2) ? 4 : 0);
alpha = Colour::convert4To8Bit(getBits<0, 4>(alpha));
// A8
return alpha;
}
case PICA::TextureFmt::A8: {
u32 offset = getSwizzledOffset(u, v, size.u(), 1);
const u8 alpha = data[offset];
// A8
return alpha;
}
default: Helpers::panic("[Texture::DecodeTexel] Unimplemented format = %d", static_cast<int>(fmt));
}
}
u16 Texture::decodeTexelU16(u32 u, u32 v, PICA::TextureFmt fmt, std::span<const u8> data) {
switch (fmt) {
case PICA::TextureFmt::RG8: {
u32 offset = getSwizzledOffset(u, v, size.u(), 2);
constexpr u8 b = 0;
const u8 g = data[offset];
const u8 r = data[offset + 1];
// RG8
return (g << 8) | r;
}
case PICA::TextureFmt::RGBA4: {
u32 offset = getSwizzledOffset(u, v, size.u(), 2);
u16 texel = u16(data[offset]) | (u16(data[offset + 1]) << 8);
u8 alpha = getBits<0, 4, u8>(texel);
u8 b = getBits<4, 4, u8>(texel);
u8 g = getBits<8, 4, u8>(texel);
u8 r = getBits<12, 4, u8>(texel);
// ABGR4
return (r << 12) | (g << 8) | (b << 4) | alpha;
}
case PICA::TextureFmt::RGBA5551: {
const u32 offset = getSwizzledOffset(u, v, size.u(), 2);
const u16 texel = u16(data[offset]) | (u16(data[offset + 1]) << 8);
u8 alpha = getBit<0>(texel) ? 0xff : 0;
u8 b = getBits<1, 5, u8>(texel);
u8 g = getBits<6, 5, u8>(texel);
u8 r = getBits<11, 5, u8>(texel);
// BGR5A1
return (alpha << 15) | (r << 10) | (g << 5) | b;
}
case PICA::TextureFmt::RGB565: {
const u32 offset = getSwizzledOffset(u, v, size.u(), 2);
const u16 texel = u16(data[offset]) | (u16(data[offset + 1]) << 8);
const u8 b = getBits<0, 5, u8>(texel);
const u8 g = getBits<5, 6, u8>(texel);
const u8 r = getBits<11, 5, u8>(texel);
// B5G6R5
return (r << 11) | (g << 5) | b;
}
case PICA::TextureFmt::IA4: {
const u32 offset = getSwizzledOffset(u, v, size.u(), 1);
const u8 texel = data[offset];
const u8 alpha = texel & 0xf;
const u8 intensity = texel >> 4;
// ABGR4
return (intensity << 12) | (intensity << 8) | (intensity << 4) | alpha;
}
case PICA::TextureFmt::I4: {
u32 offset = getSwizzledOffset_4bpp(u, v, size.u());
// For odd U coordinates, grab the top 4 bits, and the low 4 bits for even coordinates
u8 intensity = data[offset] >> ((u % 2) ? 4 : 0);
intensity = getBits<0, 4>(intensity);
// ABGR4
return (intensity << 12) | (intensity << 8) | (intensity << 4) | 0xff;
}
default: Helpers::panic("[Texture::DecodeTexel] Unimplemented format = %d", static_cast<int>(fmt));
}
}
u32 Texture::decodeTexelU32(u32 u, u32 v, PICA::TextureFmt fmt, std::span<const u8> data) {
switch (fmt) {
case PICA::TextureFmt::RGB8: {
const u32 offset = getSwizzledOffset(u, v, size.u(), 3);
const u8 b = data[offset];
const u8 g = data[offset + 1];
const u8 r = data[offset + 2];
// RGBA8
return (0xff << 24) | (b << 16) | (g << 8) | r;
}
case PICA::TextureFmt::RGBA8: {
const u32 offset = getSwizzledOffset(u, v, size.u(), 4);
const u8 alpha = data[offset];
const u8 b = data[offset + 1];
const u8 g = data[offset + 2];
const u8 r = data[offset + 3];
// RGBA8
return (alpha << 24) | (b << 16) | (g << 8) | r;
}
case PICA::TextureFmt::I8: {
u32 offset = getSwizzledOffset(u, v, size.u(), 1);
const u8 intensity = data[offset];
// RGBA8
return (0xff << 24) | (intensity << 16) | (intensity << 8) | intensity;
}
case PICA::TextureFmt::IA8: {
u32 offset = getSwizzledOffset(u, v, size.u(), 2);
// Same as I8 except each pixel gets its own alpha value too
const u8 alpha = data[offset];
const u8 intensity = data[offset + 1];
// RGBA8
return (alpha << 24) | (intensity << 16) | (intensity << 8) | intensity;
}
case PICA::TextureFmt::ETC1: return getTexelETC(false, u, v, size.u(), data);
case PICA::TextureFmt::ETC1A4: return getTexelETC(true, u, v, size.u(), data);
default: Helpers::panic("[Texture::DecodeTexel] Unimplemented format = %d", static_cast<int>(fmt));
}
}
void Texture::decodeTexture(std::span<const u8> data) {
std::unique_ptr<u8[]> decodedData(new u8[u64(size.u()) * u64(size.v()) * formatInfo.bytesPerTexel]);
// This pointer will be incremented by our texture decoders
u8* decodePtr = decodedData.get();
std::vector<u8> decoded;
decoded.reserve(u64(size.u()) * u64(size.v()) * formatInfo.bytesPerTexel);
// Decode texels line by line
for (u32 v = 0; v < size.v(); v++) {
for (u32 u = 0; u < size.u(); u++) {
formatInfo.decoder(size, u, v, data, decodePtr);
decodePtr += formatInfo.bytesPerTexel;
if (formatInfo.bytesPerTexel == 1) {
u8 texel = decodeTexelU8(u, v, format, data);
decoded.push_back(texel);
} else if (formatInfo.bytesPerTexel == 2) {
u16 texel = decodeTexelU16(u, v, format, data);
decoded.push_back((texel & 0x00ff) >> 0);
decoded.push_back((texel & 0xff00) >> 8);
} else if (formatInfo.bytesPerTexel == 4) {
u32 texel = decodeTexelU32(u, v, format, data);
decoded.push_back((texel & 0x000000ff) >> 0);
decoded.push_back((texel & 0x0000ff00) >> 8);
decoded.push_back((texel & 0x00ff0000) >> 16);
decoded.push_back((texel & 0xff000000) >> 24);
} else {
Helpers::panic("[Texture::decodeTexture] Unimplemented bytesPerTexel (%u)", formatInfo.bytesPerTexel);
}
}
}
texture->replaceRegion(MTL::Region(0, 0, size.u(), size.v()), 0, 0, decodedData.get(), formatInfo.bytesPerTexel * size.u(), 0);
texture->replaceRegion(MTL::Region(0, 0, size.u(), size.v()), 0, 0, decoded.data(), formatInfo.bytesPerTexel * size.u(), 0);
}
} // namespace Metal

View file

@ -1,62 +0,0 @@
#include "renderer_mtl/pica_to_mtl.hpp"
#include "renderer_mtl/texture_decoder.hpp"
using namespace Helpers;
namespace PICA {
MTLPixelFormatInfo mtlPixelFormatInfos[14] = {
{MTL::PixelFormatRGBA8Unorm, 4, decodeTexelABGR8ToRGBA8}, // RGBA8
{MTL::PixelFormatRGBA8Unorm, 4, decodeTexelBGR8ToRGBA8}, // RGB8
{MTL::PixelFormatBGR5A1Unorm, 2, decodeTexelA1BGR5ToBGR5A1}, // RGBA5551
{MTL::PixelFormatB5G6R5Unorm, 2, decodeTexelB5G6R5ToB5G6R5}, // RGB565
{MTL::PixelFormatABGR4Unorm, 2, decodeTexelABGR4ToABGR4}, // RGBA4
{MTL::PixelFormatRG8Unorm,
2,
decodeTexelAI8ToRG8,
true,
{
.red = MTL::TextureSwizzleRed,
.green = MTL::TextureSwizzleRed,
.blue = MTL::TextureSwizzleRed,
.alpha = MTL::TextureSwizzleGreen,
}}, // IA8
{MTL::PixelFormatRG8Unorm, 2, decodeTexelGR8ToRG8}, // RG8
{MTL::PixelFormatR8Unorm,
1,
decodeTexelI8ToR8,
true,
{.red = MTL::TextureSwizzleRed, .green = MTL::TextureSwizzleRed, .blue = MTL::TextureSwizzleRed, .alpha = MTL::TextureSwizzleOne}}, // I8
{MTL::PixelFormatA8Unorm, 1, decodeTexelA8ToA8}, // A8
{MTL::PixelFormatABGR4Unorm, 2, decodeTexelAI4ToABGR4}, // IA4
{MTL::PixelFormatR8Unorm,
1,
decodeTexelI4ToR8,
true,
{.red = MTL::TextureSwizzleRed, .green = MTL::TextureSwizzleRed, .blue = MTL::TextureSwizzleRed, .alpha = MTL::TextureSwizzleOne}}, // I4
{MTL::PixelFormatA8Unorm, 1, decodeTexelA4ToA8}, // A4
{MTL::PixelFormatRGBA8Unorm, 4, decodeTexelETC1ToRGBA8}, // ETC1
{MTL::PixelFormatRGBA8Unorm, 4, decodeTexelETC1A4ToRGBA8}, // ETC1A4
};
void checkForMTLPixelFormatSupport(MTL::Device* device) {
if (!device->supportsFamily(MTL::GPUFamilyApple1)) {
mtlPixelFormatInfos[2] = {MTL::PixelFormatRGBA8Unorm, 4, decodeTexelA1BGR5ToRGBA8};
mtlPixelFormatInfos[3] = {MTL::PixelFormatRGBA8Unorm, 4, decodeTexelB5G6R5ToRGBA8};
mtlPixelFormatInfos[4] = {MTL::PixelFormatRGBA8Unorm, 4, decodeTexelABGR4ToRGBA8};
mtlPixelFormatInfos[9] = {
MTL::PixelFormatRG8Unorm,
2,
decodeTexelAI4ToRG8,
true,
{
.red = MTL::TextureSwizzleRed,
.green = MTL::TextureSwizzleRed,
.blue = MTL::TextureSwizzleRed,
.alpha = MTL::TextureSwizzleGreen,
}
};
}
}
} // namespace PICA

View file

@ -30,6 +30,7 @@ PICA::ColorFmt ToColorFormat(u32 format) {
}
MTL::Library* loadLibrary(MTL::Device* device, const cmrc::file& shaderSource) {
// MTL::CompileOptions* compileOptions = MTL::CompileOptions::alloc()->init();
NS::Error* error = nullptr;
MTL::Library* library = device->newLibrary(Metal::createDispatchData(shaderSource.begin(), shaderSource.size()), &error);
// MTL::Library* library = device->newLibrary(NS::String::string(source.c_str(), NS::ASCIIStringEncoding), compileOptions, &error);
@ -55,18 +56,12 @@ void RendererMTL::reset() {
colorRenderTargetCache.reset();
}
void RendererMTL::setMTKLayer(void* layer) {
metalLayer = (CA::MetalLayer*)layer;
}
void RendererMTL::display() {
CA::MetalDrawable* drawable = metalLayer->nextDrawable();
if (!drawable) {
return;
}
MTL::Texture* texture = drawable->texture();
using namespace PICA::ExternalRegs;
// Top screen
@ -92,13 +87,13 @@ void RendererMTL::display() {
MTL::RenderPassDescriptor* renderPassDescriptor = MTL::RenderPassDescriptor::alloc()->init();
MTL::RenderPassColorAttachmentDescriptor* colorAttachment = renderPassDescriptor->colorAttachments()->object(0);
colorAttachment->setTexture(texture);
colorAttachment->setTexture(drawable->texture());
colorAttachment->setLoadAction(MTL::LoadActionClear);
colorAttachment->setClearColor(MTL::ClearColor{0.0f, 0.0f, 0.0f, 1.0f});
colorAttachment->setStoreAction(MTL::StoreActionStore);
nextRenderPassName = "Display";
beginRenderPassIfNeeded(renderPassDescriptor, false, texture);
beginRenderPassIfNeeded(renderPassDescriptor, false, drawable->texture());
renderCommandEncoder->setRenderPipelineState(displayPipeline);
renderCommandEncoder->setFragmentSamplerState(nearestSampler, 0);
@ -124,22 +119,17 @@ void RendererMTL::display() {
// Inform the vertex buffer cache that the frame ended
vertexBufferCache.endFrame();
// Release
drawable->release();
}
void RendererMTL::initGraphicsContext(SDL_Window* window) {
// On iOS, the SwiftUI side handles the MetalLayer
#ifdef PANDA3DS_IOS
device = MTL::CreateSystemDefaultDevice();
#else
// TODO: what should be the type of the view?
void* view = SDL_Metal_CreateView(window);
metalLayer = (CA::MetalLayer*)SDL_Metal_GetLayer(view);
device = MTL::CreateSystemDefaultDevice();
metalLayer->setDevice(device);
#endif
checkForMTLPixelFormatSupport(device);
commandQueue = device->newCommandQueue();
// Textures
@ -436,7 +426,7 @@ void RendererMTL::textureCopy(u32 inputAddr, u32 outputAddr, u32 totalBytes, u32
// Find the source surface.
auto srcFramebuffer = getColorRenderTarget(inputAddr, PICA::ColorFmt::RGBA8, copyStride, copyHeight, false);
if (!srcFramebuffer) {
doSoftwareTextureCopy(inputAddr, outputAddr, copySize, inputWidth, inputGap, outputWidth, outputGap);
Helpers::warn("RendererMTL::TextureCopy failed to locate src framebuffer!\n");
return;
}
nextRenderPassName = "Clear before texture copy";

View file

@ -1,334 +0,0 @@
#include "renderer_mtl/texture_decoder.hpp"
#include <array>
#include <string>
#include "colour.hpp"
#include "math_util.hpp"
using namespace Helpers;
// u and v are the UVs of the relevant texel
// Texture data is stored interleaved in Morton order, ie in a Z - order curve as shown here
// https://en.wikipedia.org/wiki/Z-order_curve
// Textures are split into 8x8 tiles.This function returns the in - tile offset depending on the u & v of the texel
// The in - tile offset is the sum of 2 offsets, one depending on the value of u % 8 and the other on the value of y % 8
// As documented in this picture https ://en.wikipedia.org/wiki/File:Moser%E2%80%93de_Bruijn_addition.svg
u32 mortonInterleave(u32 u, u32 v) {
static constexpr u32 xOffsets[] = {0, 1, 4, 5, 16, 17, 20, 21};
static constexpr u32 yOffsets[] = {0, 2, 8, 10, 32, 34, 40, 42};
return xOffsets[u & 7] + yOffsets[v & 7];
}
// Get the byte offset of texel (u, v) in the texture
u32 getSwizzledOffset(u32 u, u32 v, u32 width, u32 bytesPerPixel) {
u32 offset = ((u & ~7) * 8) + ((v & ~7) * width); // Offset of the 8x8 tile the texel belongs to
offset += mortonInterleave(u, v); // Add the in-tile offset of the texel
return offset * bytesPerPixel;
}
// Same as the above code except we need to divide by 2 because 4 bits is smaller than a byte
u32 getSwizzledOffset_4bpp(u32 u, u32 v, u32 width) {
u32 offset = ((u & ~7) * 8) + ((v & ~7) * width); // Offset of the 8x8 tile the texel belongs to
offset += mortonInterleave(u, v); // Add the in-tile offset of the texel
return offset / 2;
}
void decodeTexelABGR8ToRGBA8(OpenGL::uvec2 size, u32 u, u32 v, std::span<const u8> inData, u8* outData) {
const u32 offset = getSwizzledOffset(u, v, size.u(), 4);
const u8 alpha = inData[offset];
const u8 b = inData[offset + 1];
const u8 g = inData[offset + 2];
const u8 r = inData[offset + 3];
*outData++ = r;
*outData++ = g;
*outData++ = b;
*outData++ = alpha;
}
void decodeTexelBGR8ToRGBA8(OpenGL::uvec2 size, u32 u, u32 v, std::span<const u8> inData, u8* outData) {
const u32 offset = getSwizzledOffset(u, v, size.u(), 3);
const u8 b = inData[offset];
const u8 g = inData[offset + 1];
const u8 r = inData[offset + 2];
*outData++ = r;
*outData++ = g;
*outData++ = b;
*outData++ = 0xff;
}
void decodeTexelA1BGR5ToBGR5A1(OpenGL::uvec2 size, u32 u, u32 v, std::span<const u8> inData, u8* outData) {
const u32 offset = getSwizzledOffset(u, v, size.u(), 2);
const u16 texel = u16(inData[offset]) | (u16(inData[offset + 1]) << 8);
u8 alpha = getBit<0>(texel);
u8 b = getBits<1, 5, u8>(texel);
u8 g = getBits<6, 5, u8>(texel);
u8 r = getBits<11, 5, u8>(texel);
u16 outTexel = (alpha << 15) | (r << 10) | (g << 5) | b;
*outData++ = outTexel & 0xff;
*outData++ = (outTexel >> 8) & 0xff;
}
void decodeTexelA1BGR5ToRGBA8(OpenGL::uvec2 size, u32 u, u32 v, std::span<const u8> inData, u8* outData) {
const u32 offset = getSwizzledOffset(u, v, size.u(), 2);
const u16 texel = u16(inData[offset]) | (u16(inData[offset + 1]) << 8);
u8 alpha = getBit<0>(texel) ? 0xff : 0;
u8 b = Colour::convert5To8Bit(getBits<1, 5, u8>(texel));
u8 g = Colour::convert5To8Bit(getBits<6, 5, u8>(texel));
u8 r = Colour::convert5To8Bit(getBits<11, 5, u8>(texel));
*outData++ = r;
*outData++ = g;
*outData++ = b;
*outData++ = alpha;
}
void decodeTexelB5G6R5ToB5G6R5(OpenGL::uvec2 size, u32 u, u32 v, std::span<const u8> inData, u8* outData) {
const u32 offset = getSwizzledOffset(u, v, size.u(), 2);
const u16 texel = u16(inData[offset]) | (u16(inData[offset + 1]) << 8);
*outData++ = texel & 0xff;
*outData++ = (texel >> 8) & 0xff;
}
void decodeTexelB5G6R5ToRGBA8(OpenGL::uvec2 size, u32 u, u32 v, std::span<const u8> inData, u8* outData) {
const u32 offset = getSwizzledOffset(u, v, size.u(), 2);
const u16 texel = u16(inData[offset]) | (u16(inData[offset + 1]) << 8);
const u8 b = Colour::convert5To8Bit(getBits<0, 5, u8>(texel));
const u8 g = Colour::convert6To8Bit(getBits<5, 6, u8>(texel));
const u8 r = Colour::convert5To8Bit(getBits<11, 5, u8>(texel));
*outData++ = r;
*outData++ = g;
*outData++ = b;
*outData++ = 0xff;
}
void decodeTexelABGR4ToABGR4(OpenGL::uvec2 size, u32 u, u32 v, std::span<const u8> inData, u8* outData) {
u32 offset = getSwizzledOffset(u, v, size.u(), 2);
u16 texel = u16(inData[offset]) | (u16(inData[offset + 1]) << 8);
u8 alpha = getBits<0, 4, u8>(texel);
u8 b = getBits<4, 4, u8>(texel);
u8 g = getBits<8, 4, u8>(texel);
u8 r = getBits<12, 4, u8>(texel);
*outData++ = (b << 4) | alpha;
*outData++ = (r << 4) | g;
}
void decodeTexelABGR4ToRGBA8(OpenGL::uvec2 size, u32 u, u32 v, std::span<const u8> inData, u8* outData) {
u32 offset = getSwizzledOffset(u, v, size.u(), 2);
u16 texel = u16(inData[offset]) | (u16(inData[offset + 1]) << 8);
u8 alpha = Colour::convert4To8Bit(getBits<0, 4, u8>(texel));
u8 b = Colour::convert4To8Bit(getBits<4, 4, u8>(texel));
u8 g = Colour::convert4To8Bit(getBits<8, 4, u8>(texel));
u8 r = Colour::convert4To8Bit(getBits<12, 4, u8>(texel));
*outData++ = r;
*outData++ = g;
*outData++ = b;
*outData++ = alpha;
}
void decodeTexelAI8ToRG8(OpenGL::uvec2 size, u32 u, u32 v, std::span<const u8> inData, u8* outData) {
u32 offset = getSwizzledOffset(u, v, size.u(), 2);
// Same as I8 except each pixel gets its own alpha value too
const u8 alpha = inData[offset];
const u8 intensity = inData[offset + 1];
*outData++ = intensity;
*outData++ = alpha;
}
void decodeTexelGR8ToRG8(OpenGL::uvec2 size, u32 u, u32 v, std::span<const u8> inData, u8* outData) {
u32 offset = getSwizzledOffset(u, v, size.u(), 2);
constexpr u8 b = 0;
const u8 g = inData[offset];
const u8 r = inData[offset + 1];
*outData++ = r;
*outData++ = g;
}
void decodeTexelI8ToR8(OpenGL::uvec2 size, u32 u, u32 v, std::span<const u8> inData, u8* outData) {
u32 offset = getSwizzledOffset(u, v, size.u(), 1);
const u8 intensity = inData[offset];
*outData++ = intensity;
}
void decodeTexelA8ToA8(OpenGL::uvec2 size, u32 u, u32 v, std::span<const u8> inData, u8* outData) {
u32 offset = getSwizzledOffset(u, v, size.u(), 1);
const u8 alpha = inData[offset];
*outData++ = alpha;
}
void decodeTexelAI4ToABGR4(OpenGL::uvec2 size, u32 u, u32 v, std::span<const u8> inData, u8* outData) {
const u32 offset = getSwizzledOffset(u, v, size.u(), 1);
const u8 texel = inData[offset];
const u8 alpha = texel & 0xf;
const u8 intensity = texel >> 4;
*outData++ = (intensity << 4) | intensity;
*outData++ = (alpha << 4) | intensity;
}
void decodeTexelAI4ToRG8(OpenGL::uvec2 size, u32 u, u32 v, std::span<const u8> inData, u8* outData) {
const u32 offset = getSwizzledOffset(u, v, size.u(), 1);
const u8 texel = inData[offset];
const u8 alpha = Colour::convert4To8Bit(texel & 0xf);
const u8 intensity = Colour::convert4To8Bit(texel >> 4);
*outData++ = intensity;
*outData++ = alpha;
}
void decodeTexelI4ToR8(OpenGL::uvec2 size, u32 u, u32 v, std::span<const u8> inData, u8* outData) {
u32 offset = getSwizzledOffset_4bpp(u, v, size.u());
// For odd U coordinates, grab the top 4 bits, and the low 4 bits for even coordinates
u8 intensity = inData[offset] >> ((u % 2) ? 4 : 0);
intensity = Colour::convert4To8Bit(getBits<0, 4>(intensity));
*outData++ = intensity;
}
void decodeTexelA4ToA8(OpenGL::uvec2 size, u32 u, u32 v, std::span<const u8> inData, u8* outData) {
const u32 offset = getSwizzledOffset_4bpp(u, v, size.u());
// For odd U coordinates, grab the top 4 bits, and the low 4 bits for even coordinates
u8 alpha = inData[offset] >> ((u % 2) ? 4 : 0);
alpha = Colour::convert4To8Bit(getBits<0, 4>(alpha));
*outData++ = alpha;
}
static constexpr u32 signExtend3To32(u32 val) { return (u32)(s32(val) << 29 >> 29); }
void decodeETC(u32 u, u32 v, u64 colourData, u32 alpha, u8* outData) {
static constexpr u32 modifiers[8][2] = {
{2, 8}, {5, 17}, {9, 29}, {13, 42}, {18, 60}, {24, 80}, {33, 106}, {47, 183},
};
// Parse colour data for 4x4 block
const u32 subindices = getBits<0, 16, u32>(colourData);
const u32 negationFlags = getBits<16, 16, u32>(colourData);
const bool flip = getBit<32>(colourData);
const bool diffMode = getBit<33>(colourData);
// Note: index1 is indeed stored on the higher bits, with index2 in the lower bits
const u32 tableIndex1 = getBits<37, 3, u32>(colourData);
const u32 tableIndex2 = getBits<34, 3, u32>(colourData);
const u32 texelIndex = u * 4 + v; // Index of the texel in the block
if (flip) std::swap(u, v);
s32 r, g, b;
if (diffMode) {
r = getBits<59, 5, s32>(colourData);
g = getBits<51, 5, s32>(colourData);
b = getBits<43, 5, s32>(colourData);
if (u >= 2) {
r += signExtend3To32(getBits<56, 3, u32>(colourData));
g += signExtend3To32(getBits<48, 3, u32>(colourData));
b += signExtend3To32(getBits<40, 3, u32>(colourData));
}
// Expand from 5 to 8 bits per channel
r = Colour::convert5To8Bit(r);
g = Colour::convert5To8Bit(g);
b = Colour::convert5To8Bit(b);
} else {
if (u < 2) {
r = getBits<60, 4, s32>(colourData);
g = getBits<52, 4, s32>(colourData);
b = getBits<44, 4, s32>(colourData);
} else {
r = getBits<56, 4, s32>(colourData);
g = getBits<48, 4, s32>(colourData);
b = getBits<40, 4, s32>(colourData);
}
// Expand from 4 to 8 bits per channel
r = Colour::convert4To8Bit(r);
g = Colour::convert4To8Bit(g);
b = Colour::convert4To8Bit(b);
}
const u32 index = (u < 2) ? tableIndex1 : tableIndex2;
s32 modifier = modifiers[index][(subindices >> texelIndex) & 1];
if (((negationFlags >> texelIndex) & 1) != 0) {
modifier = -modifier;
}
r = std::clamp(r + modifier, 0, 255);
g = std::clamp(g + modifier, 0, 255);
b = std::clamp(b + modifier, 0, 255);
*outData++ = r;
*outData++ = g;
*outData++ = b;
*outData++ = alpha;
}
template <bool hasAlpha>
void getTexelETC(OpenGL::uvec2 size, u32 u, u32 v, std::span<const u8> inData, u8* outData) {
// Pixel offset of the 8x8 tile based on u, v and the width of the texture
u32 offs = ((u & ~7) * 8) + ((v & ~7) * size.u());
if (!hasAlpha) {
offs >>= 1;
}
// In-tile offsets for u/v
u &= 7;
v &= 7;
// ETC1(A4) also subdivide the 8x8 tile to 4 4x4 tiles
// Each tile is 8 bytes for ETC1, but since ETC1A4 has 4 alpha bits per pixel, that becomes 16 bytes
const u32 subTileSize = hasAlpha ? 16 : 8;
const u32 subTileIndex = (u / 4) + 2 * (v / 4); // Which of the 4 subtiles is this texel in?
// In-subtile offsets for u/v
u &= 3;
v &= 3;
offs += subTileSize * subTileIndex;
u32 alpha;
const u64* ptr = reinterpret_cast<const u64*>(inData.data() + offs); // Cast to u64*
if (hasAlpha) {
// First 64 bits of the 4x4 subtile are alpha data
const u64 alphaData = *ptr++;
alpha = Colour::convert4To8Bit((alphaData >> (4 * (u * 4 + v))) & 0xf);
} else {
alpha = 0xff; // ETC1 without alpha uses ff for every pixel
}
// Next 64 bits of the subtile are colour data
u64 colourData = *ptr;
decodeETC(u, v, colourData, alpha, outData);
}
void decodeTexelETC1ToRGBA8(OpenGL::uvec2 size, u32 u, u32 v, std::span<const u8> inData, u8* outData) {
getTexelETC<false>(size, u, v, inData, outData);
}
void decodeTexelETC1A4ToRGBA8(OpenGL::uvec2 size, u32 u, u32 v, std::span<const u8> inData, u8* outData) {
getTexelETC<true>(size, u, v, inData, outData);
}

View file

@ -885,17 +885,10 @@ void RendererVK::display() {
}
}
// DynamicLoader is in a different namespace in different versions of Vulkan-Hpp
#if VK_HEADER_VERSION >= 301
using VulkanDynamicLoader = vk::detail::DynamicLoader;
#else
using VulkanDynamicLoader = vk::DynamicLoader;
#endif
void RendererVK::initGraphicsContext(SDL_Window* window) {
targetWindow = window;
// Resolve all instance function pointers
static VulkanDynamicLoader dl;
static vk::DynamicLoader dl;
VULKAN_HPP_DEFAULT_DISPATCHER.init(dl.getProcAddress<PFN_vkGetInstanceProcAddr>("vkGetInstanceProcAddr"));
// Create Instance
@ -1595,4 +1588,4 @@ void RendererVK::deinitGraphicsContext() {
// TODO: Make it so that depth and colour buffers get written back to 3DS memory
printf("RendererVK::DeinitGraphicsContext called\n");
}
}

View file

@ -1,5 +1,4 @@
#include "services/ac.hpp"
#include "ipc.hpp"
namespace ACCommands {
@ -11,7 +10,6 @@ namespace ACCommands {
GetStatus = 0x000C0000,
GetWifiStatus = 0x000D0000,
GetConnectingInfraPriority = 0x000F0000,
GetNZoneBeaconNotFoundEvent = 0x002F0004,
RegisterDisconnectEvent = 0x00300004,
IsConnected = 0x003E0042,
SetClientVersion = 0x00400042,
@ -31,17 +29,12 @@ void ACService::handleSyncRequest(u32 messagePointer) {
case ACCommands::CreateDefaultConfig: createDefaultConfig(messagePointer); break;
case ACCommands::GetConnectingInfraPriority: getConnectingInfraPriority(messagePointer); break;
case ACCommands::GetLastErrorCode: getLastErrorCode(messagePointer); break;
case ACCommands::GetNZoneBeaconNotFoundEvent: getNZoneBeaconNotFoundEvent(messagePointer); break;
case ACCommands::GetStatus: getStatus(messagePointer); break;
case ACCommands::GetWifiStatus: getWifiStatus(messagePointer); break;
case ACCommands::IsConnected: isConnected(messagePointer); break;
case ACCommands::RegisterDisconnectEvent: registerDisconnectEvent(messagePointer); break;
case ACCommands::SetClientVersion: setClientVersion(messagePointer); break;
default:
mem.write32(messagePointer + 4, Result::Success);
Helpers::warn("AC service requested. Command: %08X\n", command);
break;
default: Helpers::panic("AC service requested. Command: %08X\n", command);
}
}
@ -79,7 +72,7 @@ void ACService::getLastErrorCode(u32 messagePointer) {
mem.write32(messagePointer, IPC::responseHeader(0x0A, 2, 0));
mem.write32(messagePointer + 4, Result::Success);
mem.write32(messagePointer + 8, 0); // Hopefully this means no error?
mem.write32(messagePointer + 8, 0); // Hopefully this means no error?
}
void ACService::getConnectingInfraPriority(u32 messagePointer) {
@ -143,13 +136,4 @@ void ACService::registerDisconnectEvent(u32 messagePointer) {
mem.write32(messagePointer, IPC::responseHeader(0x30, 1, 0));
mem.write32(messagePointer + 4, Result::Success);
}
void ACService::getNZoneBeaconNotFoundEvent(u32 messagePointer) {
const u32 processID = mem.read32(messagePointer + 8);
const Handle event = mem.read32(messagePointer + 16);
log("AC::GetNZoneBeaconNotFoundEvent (process ID = %X, event = %X) (stubbed)\n", processID, event);
mem.write32(messagePointer, IPC::responseHeader(0x2F, 1, 0));
mem.write32(messagePointer + 4, Result::Success);
}

View file

@ -391,7 +391,7 @@ void APTService::setScreencapPostPermission(u32 messagePointer) {
void APTService::getSharedFont(u32 messagePointer) {
log("APT::GetSharedFont\n");
const u32 fontVaddr = kernel.getSharedFontVaddr();
constexpr u32 fontVaddr = 0x18000000;
mem.write32(messagePointer, IPC::responseHeader(0x44, 2, 2));
mem.write32(messagePointer + 4, Result::Success);
mem.write32(messagePointer + 8, fontVaddr);

View file

@ -1,5 +1,4 @@
#include "services/boss.hpp"
#include "ipc.hpp"
namespace BOSSCommands {
@ -26,36 +25,27 @@ namespace BOSSCommands {
GetTaskState = 0x00200082,
GetTaskStatus = 0x002300C2,
GetTaskInfo = 0x00250082,
DeleteNsData = 0x00260040,
GetNsDataHeaderInfo = 0x002700C2,
ReadNsData = 0x00280102,
GetNsDataLastUpdated = 0x002D0040,
GetErrorCode = 0x002E0040,
RegisterStorageEntry = 0x002F0140,
GetStorageEntryInfo = 0x00300000,
StartBgImmediate = 0x00330042,
InitializeSessionPrivileged = 0x04010082,
GetAppNewFlag = 0x04040080,
SetAppNewFlag = 0x040500C0, // Probably
};
}
void BOSSService::reset() { optoutFlag = 0; }
void BOSSService::reset() {
optoutFlag = 0;
}
void BOSSService::handleSyncRequest(u32 messagePointer) {
const u32 command = mem.read32(messagePointer);
switch (command) {
case BOSSCommands::CancelTask: cancelTask(messagePointer); break;
case BOSSCommands::DeleteNsData: deleteNsData(messagePointer); break;
case BOSSCommands::GetAppNewFlag: getAppNewFlag(messagePointer); break;
case BOSSCommands::GetErrorCode: getErrorCode(messagePointer); break;
case BOSSCommands::GetNsDataHeaderInfo: getNsDataHeaderInfo(messagePointer); break;
case BOSSCommands::GetNewArrivalFlag: getNewArrivalFlag(messagePointer); break;
case BOSSCommands::GetNsDataIdList:
case BOSSCommands::GetNsDataIdList1:
case BOSSCommands::GetNsDataIdList2:
case BOSSCommands::GetNsDataIdList3: getNsDataIdList(messagePointer, command); break;
case BOSSCommands::GetNsDataLastUpdated: getNsDataLastUpdated(messagePointer); break;
case BOSSCommands::GetNsDataIdList3:
getNsDataIdList(messagePointer, command); break;
case BOSSCommands::GetOptoutFlag: getOptoutFlag(messagePointer); break;
case BOSSCommands::GetStorageEntryInfo: getStorageEntryInfo(messagePointer); break;
case BOSSCommands::GetTaskIdList: getTaskIdList(messagePointer); break;
@ -64,31 +54,17 @@ void BOSSService::handleSyncRequest(u32 messagePointer) {
case BOSSCommands::GetTaskState: getTaskState(messagePointer); break;
case BOSSCommands::GetTaskStatus: getTaskStatus(messagePointer); break;
case BOSSCommands::GetTaskStorageInfo: getTaskStorageInfo(messagePointer); break;
case BOSSCommands::InitializeSession:
case BOSSCommands::InitializeSessionPrivileged: initializeSession(messagePointer); break;
case BOSSCommands::ReadNsData: readNsData(messagePointer); break;
case BOSSCommands::InitializeSession: initializeSession(messagePointer); break;
case BOSSCommands::ReceiveProperty: receiveProperty(messagePointer); break;
case BOSSCommands::RegisterNewArrivalEvent: registerNewArrivalEvent(messagePointer); break;
case BOSSCommands::RegisterStorageEntry: registerStorageEntry(messagePointer); break;
case BOSSCommands::RegisterTask: registerTask(messagePointer); break;
case BOSSCommands::SendProperty: sendProperty(messagePointer); break;
case BOSSCommands::SetAppNewFlag: setAppNewFlag(messagePointer); break;
case BOSSCommands::SetOptoutFlag: setOptoutFlag(messagePointer); break;
case BOSSCommands::StartBgImmediate: startBgImmediate(messagePointer); break;
case BOSSCommands::StartTask: startTask(messagePointer); break;
case BOSSCommands::UnregisterStorage: unregisterStorage(messagePointer); break;
case BOSSCommands::UnregisterTask: unregisterTask(messagePointer); break;
case 0x04500102: // Home Menu uses this command, what is this?
Helpers::warn("BOSS command 0x04500102");
mem.write32(messagePointer, IPC::responseHeader(0x450, 1, 0));
mem.write32(messagePointer + 4, Result::Success);
break;
default:
mem.write32(messagePointer + 4, Result::Success);
Helpers::warn("BOSS service requested. Command: %08X\n", command);
break;
default: Helpers::panic("BOSS service requested. Command: %08X\n", command);
}
}
@ -123,7 +99,7 @@ void BOSSService::getTaskState(u32 messagePointer) {
mem.write32(messagePointer + 4, Result::Success);
mem.write8(messagePointer + 8, 0); // TaskStatus: Report the task finished successfully
mem.write32(messagePointer + 12, 0); // Current state value for task PropertyID 0x4
mem.write8(messagePointer + 16, 0); // TODO: Figure out what this should be
mem.write8(messagePointer + 16, 0); // TODO: Figure out what this should be
}
void BOSSService::getTaskStatus(u32 messagePointer) {
@ -174,15 +150,15 @@ void BOSSService::getErrorCode(u32 messagePointer) {
log("BOSS::GetErrorCode (stubbed)\n");
mem.write32(messagePointer, IPC::responseHeader(0x2E, 2, 0));
mem.write32(messagePointer + 4, Result::Success);
mem.write32(messagePointer + 8, Result::Success); // No error code
mem.write32(messagePointer + 8, Result::Success); // No error code
}
void BOSSService::getStorageEntryInfo(u32 messagePointer) {
log("BOSS::GetStorageEntryInfo (undocumented)\n");
mem.write32(messagePointer, IPC::responseHeader(0x30, 3, 0));
mem.write32(messagePointer + 4, Result::Success);
mem.write32(messagePointer + 8, 0); // u32, unknown meaning
mem.write16(messagePointer + 12, 0); // s16, unknown meaning
mem.write32(messagePointer + 8, 0); // u32, unknown meaning
mem.write16(messagePointer + 12, 0); // s16, unknown meaning
}
void BOSSService::sendProperty(u32 messagePointer) {
@ -197,6 +173,7 @@ void BOSSService::sendProperty(u32 messagePointer) {
// TODO: Should this do anything else?
}
void BOSSService::receiveProperty(u32 messagePointer) {
const u32 id = mem.read32(messagePointer + 4);
const u32 size = mem.read32(messagePointer + 8);
@ -205,13 +182,13 @@ void BOSSService::receiveProperty(u32 messagePointer) {
log("BOSS::ReceiveProperty (id = %d, size = %08X, ptr = %08X) (stubbed)\n", id, size, ptr);
mem.write32(messagePointer, IPC::responseHeader(0x16, 2, 2));
mem.write32(messagePointer + 4, Result::Success);
mem.write32(messagePointer + 8, 0); // Read size
mem.write32(messagePointer + 8, 0); // Read size
}
// This seems to accept a KEvent as a parameter and register it for something Spotpass related
// I need to update the 3DBrew page when it's known what it does properly
void BOSSService::registerNewArrivalEvent(u32 messagePointer) {
const Handle eventHandle = mem.read32(messagePointer + 4); // Kernel event handle to register
const Handle eventHandle = mem.read32(messagePointer + 4); // Kernel event handle to register
log("BOSS::RegisterNewArrivalEvent (handle = %X)\n", eventHandle);
mem.write32(messagePointer, IPC::responseHeader(0x8, 1, 0));
@ -275,92 +252,5 @@ void BOSSService::getNewArrivalFlag(u32 messagePointer) {
log("BOSS::GetNewArrivalFlag (stubbed)\n");
mem.write32(messagePointer, IPC::responseHeader(0x7, 2, 0));
mem.write32(messagePointer + 4, Result::Success);
mem.write8(messagePointer + 8, 0); // Flag
}
void BOSSService::startBgImmediate(u32 messagePointer) {
const u32 size = mem.read32(messagePointer + 8);
const u32 taskIDs = mem.read32(messagePointer + 12);
log("BOSS::StartBgImmediate (size = %X, task ID pointer = %X) (stubbed)\n", size, taskIDs);
mem.write32(messagePointer, IPC::responseHeader(0x33, 1, 2));
mem.write32(messagePointer + 4, Result::Success);
mem.write32(messagePointer + 8, IPC::pointerHeader(0, size, IPC::BufferType::Send));
mem.write32(messagePointer + 12, taskIDs);
}
void BOSSService::getAppNewFlag(u32 messagePointer) {
const u64 appID = mem.read64(messagePointer + 4);
log("BOSS::GetAppNewFlag (app ID = %llX)\n", appID);
mem.write32(messagePointer, IPC::responseHeader(0x404, 2, 0));
mem.write32(messagePointer + 4, Result::Success);
mem.write8(messagePointer + 8, 0); // No new content
}
void BOSSService::getNsDataHeaderInfo(u32 messagePointer) {
const u32 nsDataID = mem.read32(messagePointer + 4);
const u8 type = mem.read8(messagePointer + 8);
const u32 size = mem.read32(messagePointer + 12);
const u32 nsDataHeaderInfo = mem.read32(messagePointer + 20);
log("BOSS::GetNsDataHeaderInfo (NS data ID = %X, type = %X, size = %X, NS data header info pointer = %X) (stubbed)\n", nsDataID, type, size,
nsDataHeaderInfo);
switch (type) {
case 3:
case 5: mem.write32(nsDataHeaderInfo, 0); break; // ??
default: Helpers::panic("Unimplemented NS data header info type %X", type);
}
mem.write32(messagePointer, IPC::responseHeader(0x27, 1, 2));
mem.write32(messagePointer + 4, Result::Success);
mem.write32(messagePointer + 8, IPC::pointerHeader(0, size, IPC::BufferType::Receive));
mem.write32(messagePointer + 12, nsDataHeaderInfo);
}
void BOSSService::getNsDataLastUpdated(u32 messagePointer) {
const u32 nsDataID = mem.read32(messagePointer + 4);
log("BOSS::GetNsDataLastUpdated (NS data ID = %X) (stubbed)\n", nsDataID);
mem.write32(messagePointer, IPC::responseHeader(0x2D, 3, 0));
mem.write32(messagePointer + 4, Result::Success);
mem.write64(messagePointer + 8, 0); // Milliseconds since last update?
}
void BOSSService::readNsData(u32 messagePointer) {
const u32 nsDataID = mem.read32(messagePointer + 4);
const s64 offset = mem.read64(messagePointer + 8);
const u32 size = mem.read32(messagePointer + 20);
const u32 data = mem.read32(messagePointer + 24);
log("BOSS::ReadNsData (NS data ID = %X, offset = %llX, size = %X, data pointer = %X) (stubbed)\n", nsDataID, offset, size, data);
for (u32 i = 0; i < size; i++) {
mem.write8(data + i, 0);
}
mem.write32(messagePointer, IPC::responseHeader(0x28, 3, 2));
mem.write32(messagePointer + 4, Result::Success);
mem.write32(messagePointer + 8, size); // Technically how many bytes have been read
mem.write32(messagePointer + 12, 0); // ??
mem.write32(messagePointer + 16, IPC::pointerHeader(0, size, IPC::BufferType::Receive));
mem.write32(messagePointer + 20, data);
}
void BOSSService::deleteNsData(u32 messagePointer) {
const u32 nsDataID = mem.read32(messagePointer + 4);
log("BOSS::DeleteNsData (NS data ID = %X) (stubbed)\n", nsDataID);
mem.write32(messagePointer, IPC::responseHeader(0x26, 1, 0));
mem.write32(messagePointer + 4, Result::Success);
}
// Judging by the inputs and command number, this could very well be a "SetAppNewFlag"
void BOSSService::setAppNewFlag(u32 messagePointer) {
const u64 appID = mem.read64(messagePointer + 4);
const u8 flag = mem.read32(messagePointer + 12);
log("BOSS::SetAppNewFlag (app ID = %llX, flag = %X)\n", appID, flag);
mem.write32(messagePointer, IPC::responseHeader(0x405, 1, 0));
mem.write32(messagePointer + 4, Result::Success);
mem.write8(messagePointer + 8, 0); // Flag
}

View file

@ -17,82 +17,40 @@ namespace CFGCommands {
GetRegionCanadaUSA = 0x00040000,
GetSystemModel = 0x00050000,
TranslateCountryInfo = 0x00080080,
GetCountryCodeString = 0x00090040,
GetCountryCodeID = 0x000A0040,
IsFangateSupported = 0x000B0000,
SetConfigInfoBlk4 = 0x04020082,
UpdateConfigNANDSavegame = 0x04030000,
GetLocalFriendCodeSeed = 0x04050000,
SecureInfoGetByte101 = 0x04070000,
};
}
// cfg:i commands
namespace CFGICommands {
enum : u32 {
GetConfigInfoBlk8 = 0x08010082,
};
}
// cfg:nor commands
namespace NORCommands {
enum : u32 {
Initialize = 0x00010040,
ReadData = 0x00050082,
};
}
void CFGService::reset() {}
void CFGService::handleSyncRequest(u32 messagePointer, CFGService::Type type) {
const u32 command = mem.read32(messagePointer);
switch (command) {
case CFGCommands::GetConfigInfoBlk2: [[likely]] getConfigInfoBlk2(messagePointer); break;
case CFGCommands::GetCountryCodeID: getCountryCodeID(messagePointer); break;
case CFGCommands::GetRegionCanadaUSA: getRegionCanadaUSA(messagePointer); break;
case CFGCommands::GetSystemModel: getSystemModel(messagePointer); break;
case CFGCommands::GenHashConsoleUnique: genUniqueConsoleHash(messagePointer); break;
case CFGCommands::SecureInfoGetRegion: secureInfoGetRegion(messagePointer); break;
case CFGCommands::TranslateCountryInfo: translateCountryInfo(messagePointer); break;
if (type != Type::NOR) {
switch (command) {
case CFGCommands::GetConfigInfoBlk2: [[likely]] getConfigInfoBlk2(messagePointer); break;
case CFGCommands::GetCountryCodeString: getCountryCodeString(messagePointer); break;
case CFGCommands::GetCountryCodeID: getCountryCodeID(messagePointer); break;
case CFGCommands::GetRegionCanadaUSA: getRegionCanadaUSA(messagePointer); break;
case CFGCommands::GetSystemModel: getSystemModel(messagePointer); break;
case CFGCommands::GenHashConsoleUnique: genUniqueConsoleHash(messagePointer); break;
case CFGCommands::IsFangateSupported: isFangateSupported(messagePointer); break;
case CFGCommands::SecureInfoGetRegion: secureInfoGetRegion(messagePointer); break;
case CFGCommands::TranslateCountryInfo: translateCountryInfo(messagePointer); break;
default:
if (type == Type::S) {
// cfg:s (and cfg:i) functions only functions
switch (command) {
case CFGCommands::GetConfigInfoBlk8: getConfigInfoBlk8(messagePointer, command); break;
case CFGCommands::GetLocalFriendCodeSeed: getLocalFriendCodeSeed(messagePointer); break;
case CFGCommands::SecureInfoGetByte101: secureInfoGetByte101(messagePointer); break;
case CFGCommands::SetConfigInfoBlk4: setConfigInfoBlk4(messagePointer); break;
case CFGCommands::UpdateConfigNANDSavegame: updateConfigNANDSavegame(messagePointer); break;
default: Helpers::panic("CFG:S service requested. Command: %08X\n", command);
}
} else if (type == Type::I) {
switch (command) {
case CFGCommands::GetConfigInfoBlk8:
case CFGICommands::GetConfigInfoBlk8: getConfigInfoBlk8(messagePointer, command); break;
default: Helpers::panic("CFG:I service requested. Command: %08X\n", command);
}
} else {
Helpers::panic("CFG service requested. Command: %08X\n", command);
default:
if (type == Type::S) {
// cfg:s-only functions
switch (command) {
case CFGCommands::GetConfigInfoBlk8: getConfigInfoBlk8(messagePointer); break;
case CFGCommands::GetLocalFriendCodeSeed: getLocalFriendCodeSeed(messagePointer); break;
case CFGCommands::SecureInfoGetByte101: secureInfoGetByte101(messagePointer); break;
default: Helpers::panic("CFG:S service requested. Command: %08X\n", command);
}
} else {
Helpers::panic("CFG service requested. Command: %08X\n", command);
}
break;
}
} else {
// cfg:nor functions
switch (command) {
case NORCommands::Initialize: norInitialize(messagePointer); break;
case NORCommands::ReadData: norReadData(messagePointer); break;
default: Helpers::panic("CFG:NOR service requested. Command: %08X\n", command);
}
break;
}
}
@ -126,14 +84,14 @@ void CFGService::getConfigInfoBlk2(u32 messagePointer) {
mem.write32(messagePointer + 4, Result::Success);
}
void CFGService::getConfigInfoBlk8(u32 messagePointer, u32 commandWord) {
void CFGService::getConfigInfoBlk8(u32 messagePointer) {
u32 size = mem.read32(messagePointer + 4);
u32 blockID = mem.read32(messagePointer + 8);
u32 output = mem.read32(messagePointer + 16); // Pointer to write the output data to
log("CFG::GetConfigInfoBlk8 (size = %X, block ID = %X, output pointer = %08X\n", size, blockID, output);
getConfigInfo(output, blockID, size, 0x8);
mem.write32(messagePointer, IPC::responseHeader(commandWord >> 16, 1, 2));
mem.write32(messagePointer, IPC::responseHeader(0x401, 1, 2));
mem.write32(messagePointer + 4, Result::Success);
}
@ -142,7 +100,7 @@ void CFGService::getConfigInfo(u32 output, u32 blockID, u32 size, u32 permission
if (size == 1 && blockID == 0x70001) { // Sound output mode
mem.write8(output, static_cast<u8>(DSPService::SoundOutputMode::Stereo));
} else if (size == 1 && blockID == 0xA0002) { // System language
mem.write8(output, static_cast<u8>(settings.systemLanguage));
mem.write8(output, static_cast<u8>(LanguageCodes::English));
} else if (size == 4 && blockID == 0xB0000) { // Country info
mem.write8(output, 0); // Unknown
mem.write8(output + 1, 0); // Unknown
@ -202,37 +160,6 @@ void CFGService::getConfigInfo(u32 output, u32 blockID, u32 size, u32 permission
mem.write32(output, 0);
} else if (size == 8 && blockID == 0x00090000) {
mem.write64(output, 0); // Some sort of key used with nwm::UDS::InitializeWithVersion
} else if (size == 4 && blockID == 0x110000) {
mem.write32(output, 1); // According to 3Dbrew, 0 means system setup is required
} else if (size == 2 && blockID == 0x50001) {
// Backlight controls. Values taken from Citra
mem.write8(output, 0);
mem.write8(output + 1, 2);
} else if (size == 8 && blockID == 0x50009) {
// N3DS Backlight controls?
mem.write64(output, 0);
} else if (size == 4 && blockID == 0x180000) {
// Infrared LED related?
mem.write32(output, 0);
} else if (size == 1 && blockID == 0xE0000) {
mem.write8(output, 0);
} else if ((size == 512 && blockID == 0xC0002) || (size == 148 && blockID == 0x100001)) {
// CTR parental controls block (0xC0002) and TWL parental controls block (0x100001)
for (u32 i = 0; i < size; i++) {
mem.write8(output + i, 0);
}
} else if (size == 2 && blockID == 0x100000) {
// EULA agreed
mem.write8(output, 1); // We have agreed to the EULA
mem.write8(output + 1, 1); // EULA version = 1
} else if (size == 1 && blockID == 0x100002) {
Helpers::warn("Unimplemented TWL country code access");
mem.write8(output, 0);
} else if (size == 24 && blockID == 0x180001) {
// QTM calibration data
for (u32 i = 0; i < size; i++) {
mem.write8(output + i, 0);
}
} else {
Helpers::panic("Unhandled GetConfigInfoBlk2 configuration. Size = %d, block = %X", size, blockID);
}
@ -317,24 +244,6 @@ void CFGService::getCountryCodeID(u32 messagePointer) {
}
}
void CFGService::getCountryCodeString(u32 messagePointer) {
const u16 id = mem.read16(messagePointer + 4);
log("CFG::getCountryCodeString (id = %04X)\n", id);
mem.write32(messagePointer, IPC::responseHeader(0x09, 2, 0));
for (auto [string, code] : countryCodeToTableIDMap) {
if (code == id) {
mem.write32(messagePointer + 4, Result::Success);
mem.write32(messagePointer + 8, u32(string));
return;
}
}
// Code is not a valid country code, return an appropriate error
mem.write32(messagePointer + 4, 0xD90103FA);
}
void CFGService::secureInfoGetByte101(u32 messagePointer) {
log("CFG::SecureInfoGetByte101\n");
@ -351,25 +260,6 @@ void CFGService::getLocalFriendCodeSeed(u32 messagePointer) {
mem.write64(messagePointer + 8, 0);
}
void CFGService::setConfigInfoBlk4(u32 messagePointer) {
u32 blockID = mem.read32(messagePointer + 4);
u32 size = mem.read32(messagePointer + 8);
u32 input = mem.read32(messagePointer + 16);
log("CFG::SetConfigInfoBlk4 (block ID = %X, size = %X, input pointer = %08X)\n", blockID, size, input);
mem.write32(messagePointer, IPC::responseHeader(0x401, 1, 2));
mem.write32(messagePointer + 4, Result::Success);
mem.write32(messagePointer + 8, IPC::pointerHeader(0, size, IPC::BufferType::Receive));
mem.write32(messagePointer + 12, input);
}
void CFGService::updateConfigNANDSavegame(u32 messagePointer) {
log("CFG::UpdateConfigNANDSavegame\n");
mem.write32(messagePointer, IPC::responseHeader(0x403, 1, 0));
mem.write32(messagePointer + 4, Result::Success);
}
// https://www.3dbrew.org/wiki/Cfg:TranslateCountryInfo
void CFGService::translateCountryInfo(u32 messagePointer) {
const u32 country = mem.read32(messagePointer + 4);
@ -402,28 +292,4 @@ void CFGService::translateCountryInfo(u32 messagePointer) {
mem.write32(messagePointer, IPC::responseHeader(0x8, 2, 0));
mem.write32(messagePointer + 4, Result::Success);
mem.write32(messagePointer + 8, result);
}
void CFGService::isFangateSupported(u32 messagePointer) {
log("CFG::IsFangateSupported\n");
// TODO: What even is fangate?
mem.write32(messagePointer, IPC::responseHeader(0xB, 2, 0));
mem.write32(messagePointer + 4, Result::Success);
mem.write32(messagePointer + 8, 1);
}
void CFGService::norInitialize(u32 messagePointer) {
log("CFG::NOR::Initialize\n");
mem.write32(messagePointer, IPC::responseHeader(0x1, 1, 0));
mem.write32(messagePointer + 4, Result::Success);
}
void CFGService::norReadData(u32 messagePointer) {
log("CFG::NOR::ReadData\n");
Helpers::warn("Unimplemented CFG::NOR::ReadData");
mem.write32(messagePointer, IPC::responseHeader(0x5, 1, 0));
mem.write32(messagePointer + 4, Result::Success);
}

View file

@ -28,8 +28,7 @@ namespace DSPCommands {
RegisterInterruptEvents = 0x00150082,
GetSemaphoreEventHandle = 0x00160000,
SetSemaphoreMask = 0x00170040,
GetHeadphoneStatus = 0x001F0000,
ForceHeadphoneOut = 0x00200040,
GetHeadphoneStatus = 0x001F0000
};
}
@ -43,7 +42,6 @@ namespace Result {
void DSPService::reset() {
totalEventCount = 0;
semaphoreMask = 0;
headphonesInserted = true;
semaphoreEvent = std::nullopt;
interrupt0 = std::nullopt;
@ -62,7 +60,6 @@ void DSPService::handleSyncRequest(u32 messagePointer) {
case DSPCommands::ConvertProcessAddressFromDspDram: convertProcessAddressFromDspDram(messagePointer); break;
case DSPCommands::FlushDataCache: flushDataCache(messagePointer); break;
case DSPCommands::InvalidateDataCache: invalidateDCache(messagePointer); break;
case DSPCommands::ForceHeadphoneOut: forceHeadphoneOut(messagePointer); break;
case DSPCommands::GetHeadphoneStatus: getHeadphoneStatus(messagePointer); break;
case DSPCommands::GetSemaphoreEventHandle: getSemaphoreEventHandle(messagePointer); break;
case DSPCommands::LoadComponent: loadComponent(messagePointer); break;
@ -213,8 +210,7 @@ void DSPService::getHeadphoneStatus(u32 messagePointer) {
mem.write32(messagePointer, IPC::responseHeader(0x1F, 2, 0));
mem.write32(messagePointer + 4, Result::Success);
// This should be toggleable for shits and giggles
mem.write32(messagePointer + 8, headphonesInserted ? Result::HeadphonesInserted : Result::HeadphonesNotInserted);
mem.write32(messagePointer + 8, Result::HeadphonesInserted); // This should be toggleable for shits and giggles
}
void DSPService::getSemaphoreEventHandle(u32 messagePointer) {
@ -282,14 +278,6 @@ void DSPService::invalidateDCache(u32 messagePointer) {
mem.write32(messagePointer + 4, Result::Success);
}
void DSPService::forceHeadphoneOut(u32 messagePointer) {
headphonesInserted = mem.read8(messagePointer + 4) != 0;
log("DSP::ForceHeadphoneOut\n");
mem.write32(messagePointer, IPC::responseHeader(0x20, 1, 0));
mem.write32(messagePointer + 4, Result::Success);
}
DSPService::ComponentDumpResult DSPService::dumpComponent(const std::filesystem::path& path) {
if (loadedComponent.empty()) {
return ComponentDumpResult::NotLoaded;

View file

@ -99,11 +99,6 @@ ArchiveBase* FSService::getArchiveFromID(u32 id, const FSPath& archivePath) {
case ArchiveID::SDMC: return &sdmc;
case ArchiveID::SDMCWriteOnly: return &sdmcWriteOnly;
case ArchiveID::SavedataAndNcch: return &ncch; // This can only access NCCH outside of FSPXI
case ArchiveID::TwlPhoto: return &twlPhoto;
case ArchiveID::TwlSound: return &twlSound;
case ArchiveID::CardSPI: return &cardSpi;
default:
Helpers::panic("Unknown archive. ID: %d\n", id);
return nullptr;

View file

@ -1,5 +1,4 @@
#include "services/gsp_gpu.hpp"
#include "PICA/regs.hpp"
#include "ipc.hpp"
#include "kernel.hpp"
@ -15,7 +14,6 @@ namespace ServiceCommands {
WriteHwRegsWithMask = 0x00020084,
SetBufferSwap = 0x00050200,
FlushDataCache = 0x00080082,
InvalidateDataCache = 0x00090082,
SetLCDForceBlack = 0x000B0040,
TriggerCmdReqQueue = 0x000C0000,
ReleaseRight = 0x00170000,
@ -23,7 +21,7 @@ namespace ServiceCommands {
SaveVramSysArea = 0x00190000,
RestoreVramSysArea = 0x001A0000,
SetInternalPriorities = 0x001E0080,
StoreDataCache = 0x001F0082,
StoreDataCache = 0x001F0082
};
}
@ -40,7 +38,7 @@ namespace GXCommands {
}
void GPUService::reset() {
privilegedProcess = 0xFFFFFFFF; // Set the privileged process to an invalid handle
privilegedProcess = 0xFFFFFFFF; // Set the privileged process to an invalid handle
interruptEvent = std::nullopt;
gspThreadCount = 0;
sharedMem = nullptr;
@ -65,7 +63,6 @@ void GPUService::handleSyncRequest(u32 messagePointer) {
case ServiceCommands::ReadHwRegs: readHwRegs(messagePointer); break;
case ServiceCommands::WriteHwRegs: writeHwRegs(messagePointer); break;
case ServiceCommands::WriteHwRegsWithMask: writeHwRegsWithMask(messagePointer); break;
case ServiceCommands::InvalidateDataCache: invalidateDataCache(messagePointer); break;
default: Helpers::panic("GPU service requested. Command: %08X\n", command);
}
}
@ -114,38 +111,38 @@ void GPUService::registerInterruptRelayQueue(u32 messagePointer) {
log("GSP::GPU::RegisterInterruptRelayQueue (flags = %X, event handle = %X)\n", flags, eventHandle);
const auto event = kernel.getObject(eventHandle, KernelObjectType::Event);
if (event == nullptr) { // Check if interrupt event is invalid
if (event == nullptr) { // Check if interrupt event is invalid
Helpers::panic("Invalid event passed to GSP::GPU::RegisterInterruptRelayQueue");
} else {
interruptEvent = eventHandle;
}
mem.write32(messagePointer, IPC::responseHeader(0x13, 2, 2));
mem.write32(messagePointer + 4, Result::GSP::SuccessRegisterIRQ); // First init returns a unique result
mem.write32(messagePointer + 8, 0); // TODO: GSP module thread index
mem.write32(messagePointer + 12, 0); // Translation descriptor
mem.write32(messagePointer + 4, Result::GSP::SuccessRegisterIRQ); // First init returns a unique result
mem.write32(messagePointer + 8, 0); // TODO: GSP module thread index
mem.write32(messagePointer + 12, 0); // Translation descriptor
mem.write32(messagePointer + 16, KernelHandles::GSPSharedMemHandle);
}
void GPUService::requestInterrupt(GPUInterrupt type) {
if (sharedMem == nullptr) [[unlikely]] { // Shared memory hasn't been set up yet
if (sharedMem == nullptr) [[unlikely]] { // Shared memory hasn't been set up yet
return;
}
// TODO: Add support for multiple GSP threads
u8 index = sharedMem[0]; // The interrupt block is normally located at sharedMem + processGSPIndex*0x40
u8 index = sharedMem[0]; // The interrupt block is normally located at sharedMem + processGSPIndex*0x40
u8& interruptCount = sharedMem[1];
u8 flagIndex = (index + interruptCount) % 0x34;
interruptCount++;
sharedMem[2] = 0; // Set error code to 0
sharedMem[0xC + flagIndex] = static_cast<u8>(type); // Write interrupt type to queue
sharedMem[2] = 0; // Set error code to 0
sharedMem[0xC + flagIndex] = static_cast<u8>(type); // Write interrupt type to queue
// Update framebuffer info in shared memory
// Most new games check to make sure that the "flag" byte of the framebuffer info header is set to 0
// Not emulating this causes Yoshi's Wooly World, Captain Toad, Metroid 2 et al to hang
if (type == GPUInterrupt::VBlank0 || type == GPUInterrupt::VBlank1) {
int screen = static_cast<u32>(type) - static_cast<u32>(GPUInterrupt::VBlank0); // 0 for top screen, 1 for bottom
int screen = static_cast<u32>(type) - static_cast<u32>(GPUInterrupt::VBlank0); // 0 for top screen, 1 for bottom
FramebufferUpdate* update = getFramebufferInfo(screen);
if (update->dirtyFlag & 1) {
@ -166,6 +163,7 @@ void GPUService::readHwRegs(u32 messagePointer) {
const u32 initialDataPointer = mem.read32(messagePointer + 0x104);
u32 dataPointer = initialDataPointer;
log("GSP::GPU::ReadHwRegs (GPU address = %08X, size = %X, data address = %08X)\n", ioAddr, size, dataPointer);
// Check for alignment
if ((size & 3) || (ioAddr & 3) || (dataPointer & 3)) {
@ -197,8 +195,8 @@ void GPUService::readHwRegs(u32 messagePointer) {
}
void GPUService::writeHwRegs(u32 messagePointer) {
u32 ioAddr = mem.read32(messagePointer + 4); // GPU address based at 0x1EB00000, word aligned
const u32 size = mem.read32(messagePointer + 8); // Size in bytes
u32 ioAddr = mem.read32(messagePointer + 4); // GPU address based at 0x1EB00000, word aligned
const u32 size = mem.read32(messagePointer + 8); // Size in bytes
u32 dataPointer = mem.read32(messagePointer + 16);
log("GSP::GPU::writeHwRegs (GPU address = %08X, size = %X, data address = %08X)\n", ioAddr, size, dataPointer);
@ -230,14 +228,14 @@ void GPUService::writeHwRegs(u32 messagePointer) {
// Update sequential GPU registers using an array of data and mask values using this formula
// GPU register = (register & ~mask) | (data & mask).
void GPUService::writeHwRegsWithMask(u32 messagePointer) {
u32 ioAddr = mem.read32(messagePointer + 4); // GPU address based at 0x1EB00000, word aligned
const u32 size = mem.read32(messagePointer + 8); // Size in bytes
u32 ioAddr = mem.read32(messagePointer + 4); // GPU address based at 0x1EB00000, word aligned
const u32 size = mem.read32(messagePointer + 8); // Size in bytes
u32 dataPointer = mem.read32(messagePointer + 16); // Data pointer
u32 maskPointer = mem.read32(messagePointer + 24); // Mask pointer
u32 dataPointer = mem.read32(messagePointer + 16); // Data pointer
u32 maskPointer = mem.read32(messagePointer + 24); // Mask pointer
log("GSP::GPU::writeHwRegsWithMask (GPU address = %08X, size = %X, data address = %08X, mask address = %08X)\n", ioAddr, size, dataPointer,
maskPointer);
log("GSP::GPU::writeHwRegsWithMask (GPU address = %08X, size = %X, data address = %08X, mask address = %08X)\n",
ioAddr, size, dataPointer, maskPointer);
// Check for alignment
if ((size & 3) || (ioAddr & 3) || (dataPointer & 3) || (maskPointer & 3)) {
@ -280,16 +278,6 @@ void GPUService::flushDataCache(u32 messagePointer) {
mem.write32(messagePointer + 4, Result::Success);
}
void GPUService::invalidateDataCache(u32 messagePointer) {
u32 address = mem.read32(messagePointer + 4);
u32 size = mem.read32(messagePointer + 8);
u32 processHandle = handle = mem.read32(messagePointer + 16);
log("GSP::GPU::InvalidateDataCache(address = %08X, size = %X, process = %X)\n", address, size, processHandle);
mem.write32(messagePointer, IPC::responseHeader(0x9, 1, 0));
mem.write32(messagePointer + 4, Result::Success);
}
void GPUService::storeDataCache(u32 messagePointer) {
u32 address = mem.read32(messagePointer + 4);
u32 size = mem.read32(messagePointer + 8);
@ -351,11 +339,11 @@ void GPUService::setInternalPriorities(u32 messagePointer) {
}
void GPUService::processCommandBuffer() {
if (sharedMem == nullptr) [[unlikely]] { // Shared memory hasn't been set up yet
if (sharedMem == nullptr) [[unlikely]] { // Shared memory hasn't been set up yet
return;
}
constexpr int threadCount = 1; // TODO: More than 1 thread can have GSP commands at a time
constexpr int threadCount = 1; // TODO: More than 1 thread can have GSP commands at a time
for (int t = 0; t < threadCount; t++) {
u8* cmdBuffer = &sharedMem[0x800 + t * 0x200];
u8& commandsLeft = cmdBuffer[1];
@ -408,9 +396,9 @@ void GPUService::memoryFill(u32* cmd) {
u32 control = cmd[7];
// buf0 parameters
u32 start0 = cmd[1]; // Start address for the fill. If 0, don't fill anything
u32 value0 = cmd[2]; // Value to fill the framebuffer with
u32 end0 = cmd[3]; // End address for the fill
u32 start0 = cmd[1]; // Start address for the fill. If 0, don't fill anything
u32 value0 = cmd[2]; // Value to fill the framebuffer with
u32 end0 = cmd[3]; // End address for the fill
u32 control0 = control & 0xffff;
// buf1 parameters
@ -439,7 +427,7 @@ void GPUService::triggerDisplayTransfer(u32* cmd) {
log("GSP::GPU::TriggerDisplayTransfer (Stubbed)\n");
gpu.displayTransfer(inputAddr, outputAddr, inputSize, outputSize, flags);
requestInterrupt(GPUInterrupt::PPF); // Send "Display transfer finished" interrupt
requestInterrupt(GPUInterrupt::PPF); // Send "Display transfer finished" interrupt
}
void GPUService::triggerDMARequest(u32* cmd) {
@ -453,14 +441,22 @@ void GPUService::triggerDMARequest(u32* cmd) {
requestInterrupt(GPUInterrupt::DMA);
}
void GPUService::flushCacheRegions(u32* cmd) { log("GSP::GPU::FlushCacheRegions (Stubbed)\n"); }
void GPUService::flushCacheRegions(u32* cmd) {
log("GSP::GPU::FlushCacheRegions (Stubbed)\n");
}
void GPUService::setBufferSwapImpl(u32 screenId, const FramebufferInfo& info) {
using namespace PICA::ExternalRegs;
static constexpr std::array<u32, 8> fbAddresses = {
Framebuffer0AFirstAddr, Framebuffer0BFirstAddr, Framebuffer1AFirstAddr, Framebuffer1BFirstAddr,
Framebuffer0ASecondAddr, Framebuffer0BSecondAddr, Framebuffer1ASecondAddr, Framebuffer1BSecondAddr,
Framebuffer0AFirstAddr,
Framebuffer0BFirstAddr,
Framebuffer1AFirstAddr,
Framebuffer1BFirstAddr,
Framebuffer0ASecondAddr,
Framebuffer0BSecondAddr,
Framebuffer1ASecondAddr,
Framebuffer1BSecondAddr,
};
auto& regs = gpu.getExtRegisters();
@ -470,7 +466,12 @@ void GPUService::setBufferSwapImpl(u32 screenId, const FramebufferInfo& info) {
regs[fbAddresses[fbIndex + 1]] = VaddrToPaddr(info.rightFramebufferVaddr);
static constexpr std::array<u32, 6> configAddresses = {
Framebuffer0Config, Framebuffer0Select, Framebuffer0Stride, Framebuffer1Config, Framebuffer1Select, Framebuffer1Stride,
Framebuffer0Config,
Framebuffer0Select,
Framebuffer0Stride,
Framebuffer1Config,
Framebuffer1Select,
Framebuffer1Stride,
};
const u32 configIndex = screenId * 3;
@ -481,14 +482,14 @@ void GPUService::setBufferSwapImpl(u32 screenId, const FramebufferInfo& info) {
// Actually send command list (aka display list) to GPU
void GPUService::processCommandList(u32* cmd) {
const u32 address = cmd[1] & ~7; // Buffer address
const u32 size = cmd[2] & ~3; // Buffer size in bytes
[[maybe_unused]] const bool updateGas = cmd[3] == 1; // Update gas additive blend results (0 = don't update, 1 = update)
[[maybe_unused]] const bool flushBuffer = cmd[7] == 1; // Flush buffer (0 = don't flush, 1 = flush)
const u32 address = cmd[1] & ~7; // Buffer address
const u32 size = cmd[2] & ~3; // Buffer size in bytes
[[maybe_unused]] const bool updateGas = cmd[3] == 1; // Update gas additive blend results (0 = don't update, 1 = update)
[[maybe_unused]] const bool flushBuffer = cmd[7] == 1; // Flush buffer (0 = don't flush, 1 = flush)
log("GPU::GSP::processCommandList. Address: %08X, size in bytes: %08X\n", address, size);
gpu.startCommandList(address, size);
requestInterrupt(GPUInterrupt::P3D); // Send an IRQ when command list processing is over
requestInterrupt(GPUInterrupt::P3D); // Send an IRQ when command list processing is over
}
// TODO: Emulate the transfer engine & its registers
@ -563,4 +564,4 @@ void GPUService::importDisplayCaptureInfo(u32 messagePointer) {
mem.write32(messagePointer + 28, bottomScreenCapture.rightFramebuffer);
mem.write32(messagePointer + 32, bottomScreenCapture.format);
mem.write32(messagePointer + 36, bottomScreenCapture.stride);
}
}

View file

@ -1,10 +1,8 @@
#include "services/gsp_lcd.hpp"
#include "ipc.hpp"
namespace LCDCommands {
enum : u32 {
SetLedForceOff = 0x00130040,
};
}
@ -13,16 +11,6 @@ void LCDService::reset() {}
void LCDService::handleSyncRequest(u32 messagePointer) {
const u32 command = mem.read32(messagePointer);
switch (command) {
case LCDCommands::SetLedForceOff: setLedForceOff(messagePointer); break;
default: Helpers::panic("LCD service requested. Command: %08X\n", command);
}
}
void LCDService::setLedForceOff(u32 messagePointer) {
const u8 state = mem.read8(messagePointer + 4);
log("LCD::SetLedForceOff (state = %X)\n", state);
mem.write32(messagePointer, IPC::responseHeader(0x13, 1, 0));
mem.write32(messagePointer + 4, Result::Success);
}

View file

@ -19,11 +19,7 @@ void HTTPService::handleSyncRequest(u32 messagePointer) {
case HTTPCommands::CreateRootCertChain: createRootCertChain(messagePointer); break;
case HTTPCommands::Initialize: initialize(messagePointer); break;
case HTTPCommands::RootCertChainAddDefaultCert: rootCertChainAddDefaultCert(messagePointer); break;
default:
Helpers::warn("HTTP service requested. Command: %08X\n", command);
mem.write32(messagePointer + 4, Result::FailurePlaceholder);
break;
default: Helpers::panic("HTTP service requested. Command: %08X\n", command);
}
}

View file

@ -22,7 +22,6 @@ namespace CROHeader {
NameOffset = 0x084,
NextCRO = 0x088,
PrevCRO = 0x08C,
FixedSize = 0x98,
OnUnresolved = 0x0AC,
CodeOffset = 0x0B0,
DataOffset = 0x0B8,
@ -168,10 +167,6 @@ public:
return mem.read32(croPointer + CROHeader::PrevCRO);
}
u32 getFixedSize() {
return mem.read32(croPointer + CROHeader::FixedSize);
}
void setNextCRO(u32 nextCRO) {
mem.write32(croPointer + CROHeader::NextCRO, nextCRO);
}
@ -1253,7 +1248,8 @@ void LDRService::initialize(u32 messagePointer) {
Helpers::panic("Failed to rebase CRS");
}
kernel.clearInstructionCacheRange(mapVaddr, size);
kernel.clearInstructionCache();
loadedCRS = mapVaddr;
mem.write32(messagePointer, IPC::responseHeader(0x1, 1, 0));
@ -1282,6 +1278,8 @@ void LDRService::linkCRO(u32 messagePointer) {
Helpers::panic("Failed to link CRO");
}
kernel.clearInstructionCache();
mem.write32(messagePointer, IPC::responseHeader(0x6, 1, 0));
mem.write32(messagePointer + 4, Result::Success);
}
@ -1348,7 +1346,8 @@ void LDRService::loadCRO(u32 messagePointer, bool isNew) {
// TODO: add fixing
cro.fix(fixLevel);
kernel.clearInstructionCacheRange(mapVaddr, size);
kernel.clearInstructionCache();
if (isNew) {
mem.write32(messagePointer, IPC::responseHeader(0x9, 2, 0));
@ -1378,6 +1377,7 @@ void LDRService::unloadCRO(u32 messagePointer) {
}
CRO cro(mem, mapVaddr, true);
cro.unregisterCRO(loadedCRS);
if (!cro.unlink(loadedCRS)) {
@ -1388,7 +1388,8 @@ void LDRService::unloadCRO(u32 messagePointer) {
Helpers::panic("Failed to unrebase CRO");
}
kernel.clearInstructionCacheRange(mapVaddr, cro.getFixedSize());
kernel.clearInstructionCache();
mem.write32(messagePointer, IPC::responseHeader(0x5, 1, 0));
mem.write32(messagePointer + 4, Result::Success);
}

View file

@ -1,12 +1,10 @@
#include "services/mcu/mcu_hwc.hpp"
#include "ipc.hpp"
#include "result/result.hpp"
#include "services/mcu/mcu_hwc.hpp"
namespace MCU::HWCCommands {
enum : u32 {
GetBatteryLevel = 0x00050000,
SetInfoLedPattern = 0x000A0640,
};
}
@ -16,7 +14,6 @@ void MCU::HWCService::handleSyncRequest(u32 messagePointer) {
const u32 command = mem.read32(messagePointer);
switch (command) {
case HWCCommands::GetBatteryLevel: getBatteryLevel(messagePointer); break;
case HWCCommands::SetInfoLedPattern: setInfoLEDPattern(messagePointer); break;
default: Helpers::panic("MCU::HWC service requested. Command: %08X\n", command);
}
}
@ -27,12 +24,4 @@ void MCU::HWCService::getBatteryLevel(u32 messagePointer) {
mem.write32(messagePointer, IPC::responseHeader(0x5, 2, 0));
mem.write32(messagePointer + 4, Result::Success);
mem.write8(messagePointer + 8, config.batteryPercentage);
}
void MCU::HWCService::setInfoLEDPattern(u32 messagePointer) {
log("MCU::HWC::SetInfoLedPattern\n");
// 25 parameters to make some notification LEDs blink...
mem.write32(messagePointer, IPC::responseHeader(0xA, 1, 0));
mem.write32(messagePointer + 4, Result::Success);
}

View file

@ -1,32 +0,0 @@
#include "services/ns.hpp"
#include "ipc.hpp"
namespace NSCommands {
enum : u32 {
LaunchTitle = 0x000200C0,
};
}
void NSService::reset() {}
void NSService::handleSyncRequest(u32 messagePointer, Type type) {
const u32 command = mem.read32(messagePointer);
// ns:s commands
switch (command) {
case NSCommands::LaunchTitle: launchTitle(messagePointer); break;
default: Helpers::panic("NS service requested. Command: %08X\n", command);
}
}
void NSService::launchTitle(u32 messagePointer) {
const u64 titleID = mem.read64(messagePointer + 4);
const u32 launchFlags = mem.read32(messagePointer + 12);
Helpers::warn("NS::LaunchTitle (title ID = %llX, launch flags = %X) (stubbed)", titleID, launchFlags);
mem.write32(messagePointer, IPC::responseHeader(0x2, 2, 0));
mem.write32(messagePointer + 4, Result::Success);
mem.write32(messagePointer + 8, 0); // Process ID
}

View file

@ -1,5 +1,4 @@
#include "services/ptm.hpp"
#include "ipc.hpp"
namespace PTMCommands {
@ -13,16 +12,11 @@ namespace PTMCommands {
GetStepHistoryAll = 0x000F0084,
ConfigureNew3DSCPU = 0x08180040,
// ptm:gets functions
GetSystemTime = 0x04010000,
// ptm:play functions
GetPlayHistory = 0x08070082,
GetPlayHistoryStart = 0x08080000,
GetPlayHistoryLength = 0x08090000,
CalcPlayHistoryStart = 0x080B0080,
GetSoftwareClosedFlag = 0x080F0000,
ClearSoftwareClosedFlag = 0x08100000,
};
}
@ -33,49 +27,33 @@ void PTMService::handleSyncRequest(u32 messagePointer, PTMService::Type type) {
// ptm:play functions
switch (command) {
case PTMCommands::ConfigureNew3DSCPU: configureNew3DSCPU(messagePointer); break;
case PTMCommands::GetAdapterState: getAdapterState(messagePointer); break;
case PTMCommands::GetBatteryChargeState: getBatteryChargeState(messagePointer); break;
case PTMCommands::GetBatteryLevel: getBatteryLevel(messagePointer); break;
case PTMCommands::GetPedometerState: getPedometerState(messagePointer); break;
case PTMCommands::GetStepHistory: getStepHistory(messagePointer); break;
case PTMCommands::GetStepHistoryAll: getStepHistoryAll(messagePointer); break;
case PTMCommands::GetTotalStepCount: getTotalStepCount(messagePointer); break;
case PTMCommands::ConfigureNew3DSCPU: configureNew3DSCPU(messagePointer); break;
case PTMCommands::GetAdapterState: getAdapterState(messagePointer); break;
case PTMCommands::GetBatteryChargeState: getBatteryChargeState(messagePointer); break;
case PTMCommands::GetBatteryLevel: getBatteryLevel(messagePointer); break;
case PTMCommands::GetPedometerState: getPedometerState(messagePointer); break;
case PTMCommands::GetStepHistory: getStepHistory(messagePointer); break;
case PTMCommands::GetStepHistoryAll: getStepHistoryAll(messagePointer); break;
case PTMCommands::GetTotalStepCount: getTotalStepCount(messagePointer); break;
default:
// ptm:play-only functions
if (type == Type::PLAY) {
switch (command) {
case PTMCommands::GetPlayHistory:
case PTMCommands::GetPlayHistoryStart:
case PTMCommands::GetPlayHistoryLength:
mem.write32(messagePointer + 4, Result::Success);
mem.write64(messagePointer + 8, 0);
Helpers::warn("Stubbed PTM:PLAY service requested. Command: %08X\n", command);
break;
default:
// ptm:play-only functions
if (type == Type::PLAY) {
switch (command) {
case PTMCommands::GetPlayHistory:
case PTMCommands::GetPlayHistoryStart:
case PTMCommands::GetPlayHistoryLength:
mem.write32(messagePointer + 4, Result::Success);
mem.write64(messagePointer + 8, 0);
Helpers::warn("Stubbed PTM:PLAY service requested. Command: %08X\n", command);
break;
default: Helpers::panic("PTM PLAY service requested. Command: %08X\n", command); break;
default: Helpers::panic("PTM PLAY service requested. Command: %08X\n", command); break;
}
} else {
Helpers::panic("PTM service requested. Command: %08X\n", command);
}
} else if (type == Type::GETS) {
switch (command) {
case PTMCommands::GetSystemTime: getSystemTime(messagePointer); break;
default: Helpers::panic("PTM GETS service requested. Command: %08X\n", command); break;
}
} else if (type == Type::SYSM) {
switch (command) {
case PTMCommands::GetSoftwareClosedFlag: getSoftwareClosedFlag(messagePointer); break;
case PTMCommands::ClearSoftwareClosedFlag: clearSoftwareClosedFlag(messagePointer); break;
default:
mem.write32(messagePointer + 4, Result::Success);
Helpers::warn("PTM SYSM service requested. Command: %08X\n", command);
break;
}
} else {
Helpers::panic("PTM service requested. Command: %08X\n", command);
}
}
}
}
void PTMService::getAdapterState(u32 messagePointer) {
@ -129,33 +107,11 @@ void PTMService::getTotalStepCount(u32 messagePointer) {
log("PTM::GetTotalStepCount\n");
mem.write32(messagePointer, IPC::responseHeader(0xC, 2, 0));
mem.write32(messagePointer + 4, Result::Success);
mem.write32(messagePointer + 8, 3); // We walk a lot
mem.write32(messagePointer + 8, 3); // We walk a lot
}
void PTMService::configureNew3DSCPU(u32 messagePointer) {
log("PTM::ConfigureNew3DSCPU [stubbed]\n");
mem.write32(messagePointer, IPC::responseHeader(0x818, 1, 0));
mem.write32(messagePointer + 4, Result::Success);
}
void PTMService::getSystemTime(u32 messagePointer) {
log("PTM::GetSystemTime [stubbed]\n");
Helpers::warn("PTM::GetSystemTime called");
mem.write32(messagePointer, IPC::responseHeader(0x401, 3, 0));
mem.write32(messagePointer + 4, Result::Success);
mem.write64(messagePointer + 8, 0); // Milliseconds since 2000?
}
void PTMService::getSoftwareClosedFlag(u32 messagePointer) {
log("PTM::GetSoftwareClosedFlag\n");
mem.write32(messagePointer, IPC::responseHeader(0x80F, 2, 0));
mem.write32(messagePointer + 4, Result::Success);
mem.write8(messagePointer + 8, 0); // Show software closed dialog
}
void PTMService::clearSoftwareClosedFlag(u32 messagePointer) {
log("PTM::ClearSoftwareClosedFlag\n");
mem.write32(messagePointer, IPC::responseHeader(0x810, 1, 0));
mem.write32(messagePointer + 4, Result::Success);
}

View file

@ -6,10 +6,10 @@
#include "kernel.hpp"
ServiceManager::ServiceManager(std::span<u32, 16> regs, Memory& mem, GPU& gpu, u32& currentPID, Kernel& kernel, const EmulatorConfig& config)
: regs(regs), mem(mem), kernel(kernel), ac(mem), am(mem), boss(mem), act(mem), apt(mem, kernel), cam(mem, kernel), cecd(mem, kernel),
cfg(mem, config), csnd(mem, kernel), dlp_srvr(mem), dsp(mem, kernel, config), hid(mem, kernel), http(mem), ir_user(mem, kernel), frd(mem),
fs(mem, kernel, config), gsp_gpu(mem, gpu, kernel, currentPID), gsp_lcd(mem), ldr(mem, kernel), mcu_hwc(mem, config), mic(mem, kernel),
nfc(mem, kernel), nim(mem), ndm(mem), news_u(mem), ns(mem), nwm_uds(mem, kernel), ptm(mem, config), soc(mem), ssl(mem), y2r(mem, kernel) {}
: regs(regs), mem(mem), kernel(kernel), ac(mem), am(mem), boss(mem), act(mem), apt(mem, kernel), cam(mem, kernel), cecd(mem, kernel), cfg(mem),
csnd(mem, kernel), dlp_srvr(mem), dsp(mem, kernel, config), hid(mem, kernel), http(mem), ir_user(mem, kernel), frd(mem), fs(mem, kernel, config),
gsp_gpu(mem, gpu, kernel, currentPID), gsp_lcd(mem), ldr(mem, kernel), mcu_hwc(mem, config), mic(mem, kernel), nfc(mem, kernel), nim(mem), ndm(mem),
news_u(mem), nwm_uds(mem, kernel), ptm(mem, config), soc(mem), ssl(mem), y2r(mem, kernel) {}
static constexpr int MAX_NOTIFICATION_COUNT = 16;
@ -40,7 +40,6 @@ void ServiceManager::reset() {
news_u.reset();
nfc.reset();
nim.reset();
ns.reset();
ptm.reset();
soc.reset();
ssl.reset();
@ -96,27 +95,22 @@ void ServiceManager::registerClient(u32 messagePointer) {
// clang-format off
static std::map<std::string, HorizonHandle> serviceMap = {
{ "ac:u", KernelHandles::AC },
{ "ac:i", KernelHandles::AC },
{ "act:a", KernelHandles::ACT },
{ "act:u", KernelHandles::ACT },
{ "am:app", KernelHandles::AM },
{ "am:sys", KernelHandles::AM },
{ "APT:S", KernelHandles::APT }, // TODO: APT:A, APT:S and APT:U are slightly different
{ "APT:A", KernelHandles::APT },
{ "APT:U", KernelHandles::APT },
{ "boss:U", KernelHandles::BOSS },
{ "boss:P", KernelHandles::BOSS },
{ "cam:u", KernelHandles::CAM },
{ "cecd:u", KernelHandles::CECD },
{ "cfg:u", KernelHandles::CFG_U },
{ "cfg:i", KernelHandles::CFG_I },
{ "cfg:s", KernelHandles::CFG_S },
{ "cfg:nor", KernelHandles::CFG_NOR },
{ "csnd:SND", KernelHandles::CSND },
{ "dlp:SRVR", KernelHandles::DLP_SRVR },
{ "dsp::DSP", KernelHandles::DSP },
{ "hid:USER", KernelHandles::HID },
{ "hid:SPVR", KernelHandles::HID },
{ "http:C", KernelHandles::HTTP },
{ "ir:USER", KernelHandles::IR_USER },
{ "frd:a", KernelHandles::FRD_A },
@ -131,13 +125,11 @@ static std::map<std::string, HorizonHandle> serviceMap = {
{ "news:u", KernelHandles::NEWS_U },
{ "nfc:u", KernelHandles::NFC },
{ "ns:s", KernelHandles::NS_S },
{ "nwm::EXT", KernelHandles::NWM_EXT },
{ "nwm::UDS", KernelHandles::NWM_UDS },
{ "nim:aoc", KernelHandles::NIM },
{ "ptm:u", KernelHandles::PTM_U }, // TODO: ptm:u and ptm:sysm have very different command sets
{ "ptm:sysm", KernelHandles::PTM_SYSM },
{ "ptm:play", KernelHandles::PTM_PLAY },
{ "ptm:gets", KernelHandles::PTM_GETS },
{ "soc:U", KernelHandles::SOC },
{ "ssl:C", KernelHandles::SSL },
{ "y2r:u", KernelHandles::Y2R },
@ -220,7 +212,6 @@ void ServiceManager::sendCommandToService(u32 messagePointer, Handle handle) {
case KernelHandles::CFG_U: cfg.handleSyncRequest(messagePointer, CFGService::Type::U); break;
case KernelHandles::CFG_I: cfg.handleSyncRequest(messagePointer, CFGService::Type::I); break;
case KernelHandles::CFG_S: cfg.handleSyncRequest(messagePointer, CFGService::Type::S); break;
case KernelHandles::CFG_NOR: cfg.handleSyncRequest(messagePointer, CFGService::Type::NOR); break;
case KernelHandles::CSND: csnd.handleSyncRequest(messagePointer); break;
case KernelHandles::DLP_SRVR: dlp_srvr.handleSyncRequest(messagePointer); break;
case KernelHandles::HID: hid.handleSyncRequest(messagePointer); break;
@ -236,12 +227,11 @@ void ServiceManager::sendCommandToService(u32 messagePointer, Handle handle) {
case KernelHandles::NIM: nim.handleSyncRequest(messagePointer); break;
case KernelHandles::NDM: ndm.handleSyncRequest(messagePointer); break;
case KernelHandles::NEWS_U: news_u.handleSyncRequest(messagePointer); break;
case KernelHandles::NS_S: ns.handleSyncRequest(messagePointer, NSService::Type::S); break;
case KernelHandles::NS_S: Helpers::panic("Unimplemented SendSyncRequest to ns:s"); break;
case KernelHandles::NWM_UDS: nwm_uds.handleSyncRequest(messagePointer); break;
case KernelHandles::PTM_PLAY: ptm.handleSyncRequest(messagePointer, PTMService::Type::PLAY); break;
case KernelHandles::PTM_SYSM: ptm.handleSyncRequest(messagePointer, PTMService::Type::SYSM); break;
case KernelHandles::PTM_U: ptm.handleSyncRequest(messagePointer, PTMService::Type::U); break;
case KernelHandles::PTM_GETS: ptm.handleSyncRequest(messagePointer, PTMService::Type::GETS); break;
case KernelHandles::SOC: soc.handleSyncRequest(messagePointer); break;
case KernelHandles::SSL: ssl.handleSyncRequest(messagePointer); break;
case KernelHandles::Y2R: y2r.handleSyncRequest(messagePointer); break;

View file

@ -105,18 +105,13 @@ std::filesystem::path Emulator::getConfigPath() {
if constexpr (Helpers::isAndroid()) {
return getAndroidAppPath() / "config.toml";
} else {
std::filesystem::path localPath = std::filesystem::current_path() / "config.toml";
if (std::filesystem::exists(localPath)) {
return localPath;
} else {
return getAppDataRoot() / "config.toml";
}
return std::filesystem::current_path() / "config.toml";
}
}
#endif
void Emulator::step() {}
void Emulator::render() {}
// Only resume if a ROM is properly loaded
void Emulator::resume() {
@ -473,4 +468,4 @@ void Emulator::reloadSettings() {
}
}
#endif
}
}

View file

@ -11,7 +11,7 @@ FrontendSettings::Theme FrontendSettings::themeFromString(std::string inString)
std::transform(inString.begin(), inString.end(), inString.begin(), [](unsigned char c) { return std::tolower(c); });
static const std::unordered_map<std::string, Theme> map = {
{"system", Theme::System}, {"light", Theme::Light}, {"dark", Theme::Dark}, {"greetingscat", Theme::GreetingsCat}, {"cream", Theme::Cream}, {"oled", Theme::Oled}
{"system", Theme::System}, {"light", Theme::Light}, {"dark", Theme::Dark}, {"greetingscat", Theme::GreetingsCat}, {"cream", Theme::Cream},
};
if (auto search = map.find(inString); search != map.end()) {
@ -28,7 +28,6 @@ const char* FrontendSettings::themeToString(Theme theme) {
case Theme::Light: return "light";
case Theme::GreetingsCat: return "greetingscat";
case Theme::Cream: return "cream";
case Theme::Oled: return "oled";
case Theme::Dark:
default: return "dark";
@ -40,7 +39,7 @@ FrontendSettings::WindowIcon FrontendSettings::iconFromString(std::string inStri
static const std::unordered_map<std::string, WindowIcon> map = {
{"rpog", WindowIcon::Rpog}, {"rsyn", WindowIcon::Rsyn}, {"rcow", WindowIcon::Rcow},
{"rnap", WindowIcon::Rnap}, {"skyemu", WindowIcon::SkyEmu}, {"runpog", WindowIcon::Runpog},
{"rnap", WindowIcon::Rnap}, {"skyemu", WindowIcon::SkyEmu},
};
if (auto search = map.find(inString); search != map.end()) {
@ -57,7 +56,6 @@ const char* FrontendSettings::iconToString(WindowIcon icon) {
case WindowIcon::Rcow: return "rcow";
case WindowIcon::Rnap: return "rnap";
case WindowIcon::SkyEmu: return "skyemu";
case WindowIcon::Runpog: return "runpog";
case WindowIcon::Rpog:
default: return "rpog";

View file

@ -1,6 +1,4 @@
#include <metal_stdlib>
#include <TargetConditionals.h>
using namespace metal;
struct BasicVertexOut {
@ -221,6 +219,12 @@ struct Globals {
uint GPUREG_LIGHTING_LUTINPUT_SELECT;
uint GPUREG_LIGHTi_CONFIG;
// HACK
//bool lightingEnabled;
//uint8_t lightingNumLights;
//uint32_t lightingConfig1;
//uint16_t alphaControl;
float3 normal;
};
@ -651,16 +655,15 @@ float4 performLogicOp(LogicOp logicOp, float4 s, float4 d) {
return as_type<float4>(performLogicOpU(logicOp, as_type<uint4>(s), as_type<uint4>(d)));
}
// iOS simulator doesn't support fb fetch, so don't enable it
#ifndef TARGET_OS_SIMULATOR
#define PREVIOUS_COLOR_DECL float4 prevColor [[color(0)]],
#else
#define PREVIOUS_COLOR_DECL
#endif
fragment float4 fragmentDraw(DrawVertexOut in [[stage_in]], PREVIOUS_COLOR_DECL constant PicaRegs& picaRegs [[buffer(0)]], constant FragTEV& tev [[buffer(1)]], constant LogicOp& logicOp [[buffer(2)]], constant uint2& lutSlices [[buffer(3)]], texture2d<float> tex0 [[texture(0)]], texture2d<float> tex1 [[texture(1)]], texture2d<float> tex2 [[texture(2)]], texture2d_array<float> texLightingLut [[texture(3)]], texture1d_array<float> texFogLut [[texture(4)]], sampler samplr0 [[sampler(0)]], sampler samplr1 [[sampler(1)]], sampler samplr2 [[sampler(2)]], sampler linearSampler [[sampler(3)]]) {
fragment float4 fragmentDraw(DrawVertexOut in [[stage_in]], float4 prevColor [[color(0)]], constant PicaRegs& picaRegs [[buffer(0)]], constant FragTEV& tev [[buffer(1)]], constant LogicOp& logicOp [[buffer(2)]], constant uint2& lutSlices [[buffer(3)]], texture2d<float> tex0 [[texture(0)]], texture2d<float> tex1 [[texture(1)]], texture2d<float> tex2 [[texture(2)]], texture2d_array<float> texLightingLut [[texture(3)]], texture1d_array<float> texFogLut [[texture(4)]], sampler samplr0 [[sampler(0)]], sampler samplr1 [[sampler(1)]], sampler samplr2 [[sampler(2)]], sampler linearSampler [[sampler(3)]]) {
Globals globals;
// HACK
//globals.lightingEnabled = picaRegs.read(0x008Fu) != 0u;
//globals.lightingNumLights = picaRegs.read(0x01C2u);
//globals.lightingConfig1 = picaRegs.read(0x01C4u);
//globals.alphaControl = picaRegs.read(0x104);
globals.tevSources[0] = in.color;
if (lightingEnabled) {
calcLighting(globals, in, picaRegs, texLightingLut, lutSlices.x, linearSampler, globals.tevSources[1], globals.tevSources[2]);
@ -752,9 +755,5 @@ fragment float4 fragmentDraw(DrawVertexOut in [[stage_in]], PREVIOUS_COLOR_DECL
}
}
#ifndef TARGET_OS_SIMULATOR
return performLogicOp(logicOp, color, prevColor);
#else
return performLogicOp(logicOp, color, float4(0.0));
#endif
}
}

View file

@ -1,10 +0,0 @@
#version 310 es
precision mediump float;
in vec2 UV;
out vec4 FragColor;
uniform sampler2D u_texture;
void main() {
FragColor = texture(u_texture, UV);
}

View file

@ -1,25 +0,0 @@
#version 310 es
precision mediump float;
out vec2 UV;
void main() {
const vec4 positions[4] = vec4[](
vec4(-1.0, 1.0, 1.0, 1.0), // Top-left
vec4(1.0, 1.0, 1.0, 1.0), // Top-right
vec4(-1.0, -1.0, 1.0, 1.0), // Bottom-left
vec4(1.0, -1.0, 1.0, 1.0) // Bottom-right
);
// The 3DS displays both screens' framebuffer rotated 90 deg counter clockwise
// So we adjust our texcoords accordingly
const vec2 texcoords[4] = vec2[](
vec2(1.0, 1.0), // Top-right
vec2(1.0, 0.0), // Bottom-right
vec2(0.0, 1.0), // Top-left
vec2(0.0, 0.0) // Bottom-left
);
gl_Position = positions[gl_VertexID];
UV = texcoords[gl_VertexID];
}

View file

@ -118,7 +118,6 @@ void HydraCore::resetContext() {
if (!gladLoadGLES2Loader(reinterpret_cast<GLADloadproc>(getProcAddress))) {
Helpers::panic("OpenGL ES init failed");
}
emulator->getRenderer()->setupGLES();
#else
if (!gladLoadGLLoader(reinterpret_cast<GLADloadproc>(getProcAddress))) {
Helpers::panic("OpenGL init failed");

View file

@ -1,38 +0,0 @@
#import <Foundation/Foundation.h>
extern "C" {
#include "ios_driver.h"
}
// Apple's Foundation headers define some macros globablly that create issues with our own code, so remove the definitions
#undef ABS
#undef NO
#include <memory>
#include "emulator.hpp"
// The Objective-C++ bridge functions must be exported without name mangling in order for the SwiftUI frontend to be able to call them
#define IOS_EXPORT extern "C" __attribute__((visibility("default")))
std::unique_ptr<Emulator> emulator = nullptr;
HIDService* hidService = nullptr;
IOS_EXPORT void iosCreateEmulator() {
printf("Creating emulator\n");
emulator = std::make_unique<Emulator>();
hidService = &emulator->getServiceManager().getHID();
emulator->initGraphicsContext(nullptr);
}
IOS_EXPORT void iosRunFrame(CAMetalLayer* layer) {
void* layerBridged = (__bridge void*)layer;
emulator->getRenderer()->setMTKLayer(layerBridged);
emulator->runFrame();
}
IOS_EXPORT void iosLoadROM(NSString* pathNS) {
auto path = std::filesystem::path([pathNS UTF8String]);
emulator->loadROM(path);
}

Some files were not shown because too many files have changed in this diff Show more