diff --git a/.github/workflows/Hydra_Build.yml b/.github/workflows/Hydra_Build.yml
index 785e0e4a2..6827fffe7 100644
--- a/.github/workflows/Hydra_Build.yml
+++ b/.github/workflows/Hydra_Build.yml
@@ -70,7 +70,7 @@ jobs:
vulkan-components: Vulkan-Headers, Vulkan-Loader, SPIRV-Tools, Glslang
- name: Configure CMake
- run: cmake -B ${{github.workspace}}/build -DCMAKE_BUILD_TYPE=${{env.BUILD_TYPE}} -DENABLE_USER_BUILD=ON -DBUILD_HYDRA_CORE=ON
+ run: cmake -B ${{github.workspace}}/build -DCMAKE_BUILD_TYPE=${{env.BUILD_TYPE}} -DENABLE_USER_BUILD=ON -DBUILD_HYDRA_CORE=ON -DCMAKE_OSX_ARCHITECTURE=x86_64
- name: Build
run: cmake --build ${{github.workspace}}/build --config ${{env.BUILD_TYPE}}
@@ -84,7 +84,7 @@ jobs:
- name: Configure CMake (Again)
run: |
rm -rf ${{github.workspace}}/build
- cmake -B ${{github.workspace}}/build -DCMAKE_BUILD_TYPE=${{env.BUILD_TYPE}} -DENABLE_USER_BUILD=ON -DBUILD_LIBRETRO_CORE=ON
+ cmake -B ${{github.workspace}}/build -DCMAKE_BUILD_TYPE=${{env.BUILD_TYPE}} -DENABLE_USER_BUILD=ON -DBUILD_LIBRETRO_CORE=ON -DCMAKE_OSX_ARCHITECTURE=x86_64
- name: Build (Again)
run: cmake --build ${{github.workspace}}/build --config ${{env.BUILD_TYPE}} && ls -R ${{github.workspace}}/build
diff --git a/.github/workflows/MacOS_Build.yml b/.github/workflows/MacOS_Build.yml
index 76b75bd4b..a0c09bbfe 100644
--- a/.github/workflows/MacOS_Build.yml
+++ b/.github/workflows/MacOS_Build.yml
@@ -12,10 +12,11 @@ env:
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
+ strategy:
+ matrix:
+ arch: [x86_64, arm64]
+
+ name: MacOS-${{ matrix.arch }}
runs-on: macos-13
steps:
@@ -33,7 +34,7 @@ jobs:
- name: Configure CMake
# Configure CMake in a 'build' subdirectory. `CMAKE_BUILD_TYPE` is only required if you are using a single-configuration generator such as make.
# See https://cmake.org/cmake/help/latest/variable/CMAKE_BUILD_TYPE.html?highlight=cmake_build_type
- run: cmake -B ${{github.workspace}}/build -DCMAKE_BUILD_TYPE=${{env.BUILD_TYPE}} -DENABLE_USER_BUILD=ON
+ run: cmake -B ${{github.workspace}}/build -DCMAKE_BUILD_TYPE=${{env.BUILD_TYPE}} -DENABLE_USER_BUILD=ON -DCMAKE_OSX_ARCHITECTURES=${{ matrix.arch }}
- name: Build
# Build your program with the given configuration
@@ -49,10 +50,45 @@ jobs:
run: codesign --force -s - -vvvv Alber.app
- name: Zip it up
- run: zip -r Alber Alber.app
+ run: zip -r Alber-${{ matrix.arch }} Alber.app
- name: Upload MacOS App
uses: actions/upload-artifact@v4
with:
- name: MacOS Alber App Bundle
- path: 'Alber.zip'
+ name: MacOS Alber App Bundle (${{ matrix.arch }})
+ path: Alber-${{ matrix.arch }}.zip
+
+ MacOS-Universal:
+ name: MacOS-Universal
+ needs: [build]
+ runs-on: macos-13
+
+ steps:
+ - name: Download x86_64
+ uses: actions/download-artifact@v4
+ with:
+ name: MacOS Alber App Bundle (x86_64)
+ path: x86_64
+ - name: Download ARM64
+ uses: actions/download-artifact@v4
+ with:
+ name: MacOS Alber App Bundle (arm64)
+ path: arm64
+ - name: Combine app bundles
+ shell: bash
+ run: |
+ set -x
+ unzip x86_64/*.zip -d x86_64
+ unzip arm64/*.zip -d arm64
+ lipo {x86_64,arm64}/Alber.app/Contents/MacOS/Alber -create -output Alber
+ cp -v -a arm64/Alber.app Alber.app
+ cp -v Alber Alber.app/Contents/MacOS/Alber
+ # Mix in x86_64 files that do not appear in the ARM64 build (e.g. libvulkan)
+ cp -v -R -n x86_64/Alber.app/* Alber.app/ || true
+ codesign --force -s - -vvvv Alber.app
+ zip -r -y Alber-universal.zip Alber.app
+ - name: Upload artifact
+ uses: actions/upload-artifact@v4
+ with:
+ name: MacOS Alber App Bundle (universal)
+ path: Alber-universal.zip
diff --git a/.github/workflows/Qt_Build.yml b/.github/workflows/Qt_Build.yml
index 3b846a275..ebd856c6a 100644
--- a/.github/workflows/Qt_Build.yml
+++ b/.github/workflows/Qt_Build.yml
@@ -51,6 +51,11 @@ jobs:
path: upload
MacOS:
+ strategy:
+ matrix:
+ arch: [x86_64, arm64]
+
+ name: MacOS-${{ matrix.arch }}
runs-on: macos-13
steps:
@@ -69,11 +74,17 @@ jobs:
run: |
brew install dylibbundler imagemagick
- - name: Install qt
- run: brew install qt && which macdeployqt
+ - name: Install Qt
+ uses: jurplel/install-qt-action@v3
+ with:
+ aqtversion: '==3.1.*'
+ version: '6.8.1'
+ host: 'mac'
+ target: 'desktop'
+ arch: 'clang_64'
- name: Configure CMake
- run: cmake -B ${{github.workspace}}/build -DCMAKE_BUILD_TYPE=${{env.BUILD_TYPE}} -DENABLE_USER_BUILD=ON -DENABLE_QT_GUI=ON
+ run: cmake -B ${{github.workspace}}/build -DCMAKE_BUILD_TYPE=${{env.BUILD_TYPE}} -DENABLE_USER_BUILD=ON -DENABLE_QT_GUI=ON -DCMAKE_OSX_ARCHITECTURES=${{ matrix.arch }}
- name: Build
run: cmake --build ${{github.workspace}}/build --config ${{env.BUILD_TYPE}}
@@ -87,13 +98,48 @@ jobs:
run: codesign --force -s - -vvvv Alber.app
- name: Zip it up
- run: zip -r Alber Alber.app
+ run: zip -r Alber-${{ matrix.arch }} Alber.app
- name: Upload MacOS App
uses: actions/upload-artifact@v4
with:
- name: MacOS Alber App Bundle
- path: 'Alber.zip'
+ name: MacOS Alber App Bundle (${{ matrix.arch }})
+ path: Alber-${{ matrix.arch }}.zip
+
+ MacOS-Universal:
+ name: MacOS-Universal
+ needs: [MacOS]
+ runs-on: macos-13
+
+ steps:
+ - name: Download x86_64
+ uses: actions/download-artifact@v4
+ with:
+ name: MacOS Alber App Bundle (x86_64)
+ path: x86_64
+ - name: Download ARM64
+ uses: actions/download-artifact@v4
+ with:
+ name: MacOS Alber App Bundle (arm64)
+ path: arm64
+ - name: Combine app bundles
+ shell: bash
+ run: |
+ set -x
+ unzip x86_64/*.zip -d x86_64
+ unzip arm64/*.zip -d arm64
+ lipo {x86_64,arm64}/Alber.app/Contents/MacOS/Alber -create -output Alber
+ cp -v -a arm64/Alber.app Alber.app
+ cp -v Alber Alber.app/Contents/MacOS/Alber
+ # Mix in x86_64 files that do not appear in the ARM64 build (e.g. libvulkan)
+ cp -v -R -n x86_64/Alber.app/* Alber.app/ || true
+ codesign --force -s - -vvvv Alber.app
+ zip -r -y Alber-universal.zip Alber.app
+ - name: Upload artifact
+ uses: actions/upload-artifact@v4
+ with:
+ name: MacOS Alber App Bundle (universal)
+ path: Alber-universal.zip
Linux:
runs-on: ubuntu-24.04
@@ -107,7 +153,7 @@ jobs:
run: |
sudo apt-get update && sudo apt install libx11-dev libgl1 libglx-mesa0 mesa-common-dev libfuse2 libwayland-dev libgl1-mesa-dev
sudo apt update
- sudo apt install qt6-base-dev qt6-base-private-dev
+ sudo apt install qt6-base-dev qt6-base-private-dev qt6-tools-dev
- name: Install newer Clang
run: |
diff --git a/.gitmodules b/.gitmodules
index b1e054237..9fcd8d60a 100644
--- a/.gitmodules
+++ b/.gitmodules
@@ -40,9 +40,6 @@
[submodule "third_party/zep"]
path = third_party/zep
url = https://github.com/Panda3DS-emu/zep
-[submodule "third_party/oaknut"]
- path = third_party/oaknut
- url = https://github.com/merryhime/oaknut
[submodule "third_party/luv"]
path = third_party/luv
url = https://github.com/luvit/luv
@@ -85,3 +82,6 @@
[submodule "third_party/cryptoppwin"]
path = third_party/cryptoppwin
url = https://github.com/shadps4-emu/ext-cryptoppwin
+[submodule "third_party/oaknut"]
+ path = third_party/oaknut
+ url = https://github.com/panda3ds-emu/oaknut
diff --git a/CMakeLists.txt b/CMakeLists.txt
index 16ad1c411..8df42ff95 100644
--- a/CMakeLists.txt
+++ b/CMakeLists.txt
@@ -219,26 +219,51 @@ if(ENABLE_LUAJIT)
target_link_libraries(AlberCore PRIVATE libluajit)
endif()
-# Check for x64
-if (CMAKE_SYSTEM_PROCESSOR STREQUAL "AMD64" OR CMAKE_SYSTEM_PROCESSOR STREQUAL "x86-64" OR CMAKE_SYSTEM_PROCESSOR STREQUAL "x86_64")
+# Detect target architecture
+if (NOT APPLE OR "${CMAKE_OSX_ARCHITECTURES}" STREQUAL "")
+ # Normal target detection
+ if (CMAKE_SYSTEM_PROCESSOR STREQUAL "AMD64" OR CMAKE_SYSTEM_PROCESSOR STREQUAL "x86-64" OR CMAKE_SYSTEM_PROCESSOR STREQUAL "x86_64")
set(HOST_X64 TRUE)
+ else()
+ set(HOST_X64 FALSE)
+ endif()
+
+ if (CMAKE_SYSTEM_PROCESSOR STREQUAL "aarch64" OR CMAKE_SYSTEM_PROCESSOR STREQUAL "arm64")
+ set(HOST_ARM64 TRUE)
+ else()
+ set(HOST_ARM64 FALSE)
+ endif()
+else()
+ # Apple target detection
+ if("x86_64" IN_LIST CMAKE_OSX_ARCHITECTURES)
+ set(HOST_X64 TRUE)
+ else()
+ set(HOST_X64 FALSE)
+ endif()
+
+ if("arm64" IN_LIST CMAKE_OSX_ARCHITECTURES)
+ set(HOST_ARM64 TRUE)
+ else()
+ set(HOST_ARM64 FALSE)
+ endif()
+
+ if (HOST_ARM64 AND HOST_X64)
+ message(FATAL_ERROR "Universal builds not supported like this! Please compile separately and stitch together")
+ endif()
+endif()
+
+if (HOST_X64)
add_subdirectory(third_party/xbyak) # Add xbyak submodule for x86 JITs
include_directories(third_party/xbyak)
add_compile_definitions(PANDA3DS_DYNAPICA_SUPPORTED)
add_compile_definitions(PANDA3DS_X64_HOST)
-else()
- set(HOST_X64 FALSE)
endif()
-# Check for arm64
-if (CMAKE_SYSTEM_PROCESSOR STREQUAL "aarch64" OR CMAKE_SYSTEM_PROCESSOR STREQUAL "arm64")
- set(HOST_ARM64 TRUE)
+if (HOST_ARM64)
add_subdirectory(third_party/oaknut) # Add Oaknut submodule for arm64 JITs
include_directories(third_party/oaknut/include)
add_compile_definitions(PANDA3DS_DYNAPICA_SUPPORTED)
add_compile_definitions(PANDA3DS_ARM64_HOST)
-else()
- set(HOST_ARM64 FALSE)
endif()
# Enable SSE4.1 if it's not explicitly disabled
@@ -315,6 +340,7 @@ 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
@@ -364,8 +390,8 @@ 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/enum_flag_ops.hpp include/services/dsp_firmware_db.hpp include/frontend_settings.hpp
- include/dynamic_library.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/enum_flag_ops.hpp include/dynamic_library.hpp
)
cmrc_add_resource_library(
@@ -647,7 +673,7 @@ if(NOT BUILD_HYDRA_CORE AND NOT BUILD_LIBRETRO_CORE)
add_executable(Alber)
if(ENABLE_QT_GUI)
- find_package(Qt6 REQUIRED COMPONENTS Widgets)
+ find_package(Qt6 REQUIRED COMPONENTS Widgets LinguistTools)
if(NOT ENABLE_OPENGL)
message(FATAL_ERROR "Qt frontend requires OpenGL")
endif()
@@ -657,7 +683,7 @@ if(NOT BUILD_HYDRA_CORE AND NOT BUILD_LIBRETRO_CORE)
set(FRONTEND_SOURCE_FILES src/panda_qt/main.cpp src/panda_qt/screen.cpp src/panda_qt/main_window.cpp src/panda_qt/about_window.cpp
src/panda_qt/config_window.cpp src/panda_qt/zep.cpp src/panda_qt/text_editor.cpp src/panda_qt/cheats_window.cpp src/panda_qt/mappings.cpp
- src/panda_qt/patch_window.cpp src/panda_qt/elided_label.cpp src/panda_qt/shader_editor.cpp
+ src/panda_qt/patch_window.cpp src/panda_qt/elided_label.cpp src/panda_qt/shader_editor.cpp src/panda_qt/translations.cpp
)
set(FRONTEND_HEADER_FILES include/panda_qt/screen.hpp include/panda_qt/main_window.hpp include/panda_qt/about_window.hpp
include/panda_qt/config_window.hpp include/panda_qt/text_editor.hpp include/panda_qt/cheats_window.hpp
@@ -710,6 +736,23 @@ if(NOT BUILD_HYDRA_CORE AND NOT BUILD_LIBRETRO_CORE)
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
)
+
+ # Translation files in Qt's .ts format. Will be converted into binary files and embedded into the executable
+ set(TRANSLATIONS_TS docs/translations/en.ts docs/translations/el.ts docs/translations/es.ts docs/translations/pt_br.ts docs/translations/nl.ts)
+ set_source_files_properties(${TRANSLATIONS_TS} PROPERTIES OUTPUT_LOCATION "${CMAKE_CURRENT_BINARY_DIR}/translations")
+ qt_add_translation(TRANSLATIONS_QM ${TRANSLATIONS_TS})
+
+ set(TRANSLATIONS_QRC ${CMAKE_CURRENT_BINARY_DIR}/translations/translations.qrc)
+ file(WRITE ${TRANSLATIONS_QRC} "\n")
+ foreach (QM ${TRANSLATIONS_QM})
+ message("${QM}")
+ get_filename_component(QM_FILE ${QM} NAME)
+ file(APPEND ${TRANSLATIONS_QRC} "${QM_FILE}\n")
+ endforeach (QM)
+ file(APPEND ${TRANSLATIONS_QRC} "")
+
+ qt_add_resources(TRANSLATIONS ${TRANSLATIONS_QRC})
+ set(APP_RESOURCES ${APP_RESOURCES} ${TRANSLATIONS})
else()
set(FRONTEND_SOURCE_FILES src/panda_sdl/main.cpp src/panda_sdl/frontend_sdl.cpp src/panda_sdl/mappings.cpp)
set(FRONTEND_HEADER_FILES "include/panda_sdl/frontend_sdl.hpp")
diff --git a/docs/translations/el.ts b/docs/translations/el.ts
new file mode 100644
index 000000000..0368d8bda
--- /dev/null
+++ b/docs/translations/el.ts
@@ -0,0 +1,763 @@
+
+
+ AboutWindow
+
+
+
+ Σχετικά με το Panda3DS
+
+
+
+
+ Τo Panda3DS είναι ένας δωρεάν και open source εξομοιωτής του Nintendo 3DS, για Windows, MacOS και Linux
+
+
+
+
+ Επισκεφτείται το panda3ds.com για βοήθεια με το Panda3DS και συνδέσμους στις επίσημες σελίδες υποστήριξης μας.
+
+
+
+
+
+
+
+
+ CheatEditDialog
+
+
+
+ Επεξεργασία Κωδικού
+
+
+
+
+ Όνομα κωδικού
+
+
+
+ CheatEntryWidget
+
+
+
+ Επεξεργασία
+
+
+
+ CheatsWindow
+
+
+
+ Κωδικοί
+
+
+
+
+ Προσθήκη
+
+
+
+
+ Αφαίρεση
+
+
+
+ ConfigWindow
+
+
+
+ Ρυθμίσεις
+
+
+
+
+ Ρυθμίσεις Διεπαφής
+
+
+
+
+ Σύστημα
+
+
+
+
+ Φωτεινό
+
+
+
+
+ Σκοτεινό
+
+
+
+
+ Γεια Σου Γάτα
+
+
+
+
+ Κρέμα
+
+
+
+
+ Χρώματα
+
+
+
+
+ Χαρούμενο Πάντα
+
+
+
+
+ Χαρούμενο Πάντα (χρωματιστό)
+
+
+
+
+ Πάντα που νυστάζει
+
+
+
+
+ Αγελάδα πάντα
+
+
+
+
+ Ο πιγκουίνος από το SkyEmu
+
+
+
+
+ Εικονίδιο Παραθύρου
+
+
+
+
+ Γλώσσα
+
+
+
+
+ Εμφάνιση έκδοσης στον τίτλο του παραθύρου
+
+
+
+
+ Αλβέρτος v%1
+
+
+
+
+ Αλβέρτος
+
+
+
+
+ Αποθήκευση θέσης παραθύρου
+
+
+
+
+ Γενικές Ρυθμίσεις
+
+
+
+
+ Περιήγηση
+
+
+
+
+ Επιλογή φακέλου
+
+
+
+
+ Προεπιλεγμένος φάκελος ROM
+
+
+
+
+ Ενεργοποίηση Discord RPC
+
+
+
+
+ Ενεργοποίηση φορητής εγκατάστασης
+
+
+
+
+ Εκτύπωση έκδοσης στην κονσόλα
+
+
+
+
+ Ρυθμίσεις Γραφικών
+
+
+
+
+
+ Κανένα
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ Πυρήνας GPU
+
+
+
+
+ Ενεργοποίηση Renderdoc
+
+
+
+
+ Ενεργοποίηση μεταγλωττιστή shaders
+
+
+
+
+ Ενεργοποίηση VSync
+
+
+
+
+ Χρήση ubershaders (Χωρίς stutter, ίσως πιο αργό)
+
+
+
+
+ Ακριβής πολλαπλασιασμός στα shaders
+
+
+
+
+ Επιτάχυνση shaders
+
+
+
+
+ Εξαναγκασμός shadergen όταν υπάρχουν φώτα
+
+
+
+
+ Αριθμός φωτών για εξαναγκασμό shadergen
+
+
+
+
+ Ρυθμίσεις Ήχου
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ Εξομοίωση DSP
+
+
+
+
+ Ενεργοποίηση ήχου
+
+
+
+
+ Ενεργοποίηση ήχου AAC
+
+
+
+
+ Εκτύπωση λογισμικού DSP
+
+
+
+
+ Σίγαση συσκευής ήχου
+
+
+
+
+ Κυβική
+
+
+
+
+ Γραμμική
+
+
+
+
+ Κλίμακα ήχου
+
+
+
+
+ Ένταση ήχου
+
+
+
+
+ Ρυθμίσεις μπαταρίας
+
+
+
+
+ Ποσοστό μπαταρίας
+
+
+
+
+ Φορτιστής
+
+
+
+
+ Ρυθμίσης κάρτας SD
+
+
+
+
+ Ενεργοποίηση εικονικής SD
+
+
+
+
+ Προστασία της SD από εγγραφή
+
+
+
+
+ Διεπαφή
+
+
+
+
+ Ρυθμίσεις διεπαφής
+
+
+
+
+ Γενικά
+
+
+
+
+ Γενικές ρυθμίσεις εξομοιωτή
+
+
+
+
+ Γραφικά
+
+
+
+
+ Ρυθμίσεις εξομοίωσης γραφικών
+
+
+
+
+ Ήχος
+
+
+
+
+ Ρυθμίσεις εξομοίωσης ήχου
+
+
+
+
+ Μπαταρία
+
+
+
+
+ Ρυθμίσεις εξομοίωσης μπαταρίας
+
+
+
+
+ Κάρτα SD
+
+
+
+
+ Ρυθμίσεις εξομοίωσης κάρτας SD
+
+
+
+
+ Επιτυχία αλλαγής γλώσσας
+
+
+
+
+ Επανεκκινήστε το Panda3DS για να εφαρμοστεί η νέα γλώσσα.
+
+
+
+
+ Αποτυχία αλλαγής γλώσσας
+
+
+
+
+ Το Panda3DS δεν υποστηρίζει τον γλώσσα που επιλέξατε. Αν το βλέπετε αυτό, κάποιος έκανε λάθος στον κώδικα, κατηγορήστε τον Πάρη...
+
+
+
+ MainWindow
+
+
+
+ Αλβέρτος
+
+
+
+
+ Αρχεία
+
+
+
+
+ Εξομοίωση
+
+
+
+
+ Εργαλεία
+
+
+
+
+ Σχετικά
+
+
+
+
+ Φόρτωση παιχνιδιού
+
+
+
+
+ Φόρτωση αρχείου Lua
+
+
+
+
+ Άνοιγμα φακέλου Panda3DS
+
+
+
+
+ Παύση
+
+
+
+
+ Συνέχεια
+
+
+
+
+ Επανέναρξη
+
+
+
+
+ Ρύθμιση
+
+
+
+
+
+
+
+
+
+ Άνοιγμα Lua Editor
+
+
+
+
+ Άνοιγμα Editor κωδικών
+
+
+
+
+ Άνοιγμα παραθύρου για patching
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ Σχετικά με το Panda3DS
+
+
+
+
+ Επιλέξτε 3DS ROM για να φορτώσετε
+
+
+
+
+
+
+
+
+
+ Επιλέξτε αρχείο Lua για να φορτώσετε
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ ΟΚ
+
+
+
+
+ Δεν βρέθηκε RomFS
+
+
+
+
+ Δεν βρέθηκε RomFS στην εφαρμογή που έχει φορτωθεί
+
+
+
+
+ Επιλέξτε αρχείο
+
+
+
+
+
+
+
+
+
+ Δεν έχει φορτωθεί DSP firmware
+
+
+
+
+
+
+
+
+
+ Αποτυχία ανοίγματος του αρχείου εξόδου
+
+
+
+
+
+
+
+
+ PatchWindow
+
+
+
+
+
+
+
+
+ Επιλογή αρχείου εισόδου
+
+
+
+
+
+ Επιλέξτε
+
+
+
+
+
+ Επιλογή αρχείου patch
+
+
+
+
+ Εφαρμογή patch
+
+
+
+
+ Επιλέξτε αρχείο να κάνετε patch
+
+
+
+
+
+ Όλα τα αρχεία (*.*)
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ Παρακαλούμε διαλέξτε και αρχείο εισόδου και αρχείο patch
+
+
+
+
+ Επιλογή αρχείου
+
+
+
+
+ Δεν επιλέχθηκε φάκελος εξόδου
+
+
+
+
+ Δεν επιλέχθηκε αρχείο εξόδου. Δεν έγινε patching
+
+
+
+
+ Άγνωστο είδος patch
+
+
+
+
+ Άγνωστο είδος αρχείου patch. Υποστηρίζονται αρχεία IPS, UPS και BPS
+
+
+
+
+ Αποτυχία ανοίγματος των αρχείων εισόδου
+
+
+
+
+ Βεβαιωθείτε ότι είναι σε φάκελο που έχει πρόσβαση το Panda3DS
+
+
+
+
+ Επιτυχής Εφαρμογή Patch
+
+
+
+
+ To αρχείο σας έγινε patch με επιτυχία
+
+
+
+
+
+
+
+
+
+ Το patch εφαρμόστηκε με επιτυχία αλλά ανιχνεύτηκε σφάλμα στο checksum. Ενδέχεται τα αρχεία εισόδου η εξόδου να είναι λανθασμένα
+
+
+
+
+ Σφάλμα στο patching
+
+
+
+
+ Προέκυψε σφάλμα στο patching
+
+
+
+ PatchWindow::PatchWindow
+
+
+
+ ΟΚ
+
+
+
+ ShaderEditorWindow
+
+
+
+ Επαναφόρτωση shader
+
+
+
+ TextEditorWindow
+
+
+
+
+
+
+
+
+ Φόρτωση αρχείου
+
+
+
\ No newline at end of file
diff --git a/docs/translations/en.ts b/docs/translations/en.ts
new file mode 100644
index 000000000..4a70fabb3
--- /dev/null
+++ b/docs/translations/en.ts
@@ -0,0 +1,766 @@
+
+
+
+
+ AboutWindow
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ CheatEditDialog
+
+
+
+
+
+
+
+
+
+
+
+
+ CheatEntryWidget
+
+
+
+
+
+
+
+ CheatsWindow
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ ConfigWindow
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ MainWindow
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ PatchWindow
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ PatchWindow::PatchWindow
+
+
+
+
+
+
+
+ ShaderEditorWindow
+
+
+
+
+
+
+
+ TextEditorWindow
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/docs/translations/es.ts b/docs/translations/es.ts
new file mode 100644
index 000000000..6f3452614
--- /dev/null
+++ b/docs/translations/es.ts
@@ -0,0 +1,763 @@
+
+
+ AboutWindow
+
+
+
+ Acerca de Panda3DS
+
+
+
+
+ Panda3DS es un emulador libre y de código abierto de Nintendo 3DS para Windows, MacOS y Linux
+
+
+
+
+ Visita panda3ds.com para obtener ayuda con Panda3DS y los links a nuestras páginas oficiales de soporte.
+
+
+
+
+ Panda3DS es desarrollado por voluntarios en su tiempo libre. Debajo se encuentran los voluntarios que están de acuerdo con ser listados aquí, en ningún orden en particular.<br>Si piensas que deberías ser listado, por favor infórmanos<br><br>- Peach (wheremyfoodat)<br>- noumidev<br>- liuk707<br>- Wunk<br>- marysaka<br>- Sky<br>- merryhime<br>- TGP17<br>- Shadow<br>
+
+
+
+ CheatEditDialog
+
+
+
+ Editar Truco
+
+
+
+
+ Nombre del truco
+
+
+
+ CheatEntryWidget
+
+
+
+ Editar
+
+
+
+ CheatsWindow
+
+
+
+ Trucos
+
+
+
+
+ Añadir
+
+
+
+
+ Quitar
+
+
+
+ ConfigWindow
+
+
+
+ Configuración
+
+
+
+
+ Configuración de Interfaz
+
+
+
+
+ Sistema
+
+
+
+
+ Claro
+
+
+
+
+ Oscuro
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ Tema de color
+
+
+
+
+ Panda feliz
+
+
+
+
+ Panda feliz (colorido)
+
+
+
+
+ Panda somnoliento
+
+
+
+
+ Panda vaca
+
+
+
+
+ El pungüino de SkyEmu
+
+
+
+
+ Icono de ventana
+
+
+
+
+ Idioma
+
+
+
+
+ Mostrar versión en la barra de título de la ventana
+
+
+
+
+ Alber v%1
+
+
+
+
+ Alber
+
+
+
+
+ Recordar posición de la ventana
+
+
+
+
+ Configuración General
+
+
+
+
+ Examinar...
+
+
+
+
+ Seleccionar Directorio
+
+
+
+
+ Ruta predeterminada de ROMs
+
+
+
+
+ Activar Discord RPC
+
+
+
+
+ Usar build portable
+
+
+
+
+ Imprimir versión en consola
+
+
+
+
+ Configuración de Gráficos
+
+
+
+
+
+ Nulo
+
+
+
+
+ OpenGL
+
+
+
+
+ Vulkan
+
+
+
+
+ Renderizador GPU
+
+
+
+
+ Activar Renderdoc
+
+
+
+
+ Activar JIT de shaders
+
+
+
+
+ Activar VSync
+
+
+
+
+ Usar ubershaders (No stuttering, puede ser más lento)
+
+
+
+
+ Multiplicación precisa de shaders
+
+
+
+
+ Acelerar shaders
+
+
+
+
+ Forzar shadergen al renderizar luces
+
+
+
+
+ Umbral de luz para forzar shadergen
+
+
+
+
+ Configuración de Audio
+
+
+
+
+ LLE
+
+
+
+
+ HLE
+
+
+
+
+ Emulación de DSP
+
+
+
+
+ Activar audio
+
+
+
+
+ Activar audio AAC
+
+
+
+
+ Imprimir firmware DSP
+
+
+
+
+ Silenciar dispositivo de audio
+
+
+
+
+ Cúbico
+
+
+
+
+ Linear
+
+
+
+
+ Curva del volumen
+
+
+
+
+ Volumen del dispositivo de audio
+
+
+
+
+ Configuración de Batería
+
+
+
+
+ Porcentaje de batería
+
+
+
+
+ Cargador conectado
+
+
+
+
+ Configuración de Tarjeta SD
+
+
+
+
+ Activar tarjeta SD virtual
+
+
+
+
+ Proteger tarjeta SD de escritura
+
+
+
+
+ Interfaz
+
+
+
+
+ Configuración de Interfaz de Usuario
+
+
+
+
+ General
+
+
+
+
+ Configuración general del emulador
+
+
+
+
+ Gráficos
+
+
+
+
+ Configuración de emulación de gráficos y salida de vídeo
+
+
+
+
+ Audio
+
+
+
+
+ Configuración de emulación y salida de audio
+
+
+
+
+ Batería
+
+
+
+
+ Configuración de emulación de la batería
+
+
+
+
+ Tarjeta SD
+
+
+
+
+ Configuración de emulación de la tarjeta SD
+
+
+
+
+ Idioma cambiado correctamente
+
+
+
+
+ Reinicie Panda3DS para utilizar el nuevo idioma.
+
+
+
+
+ Cambio de idioma fallido
+
+
+
+
+ El idioma que ha seleccionado no está incluido en Panda3DS. Si está viendo esto, alguien cometió un error en el código...
+
+
+
+ MainWindow
+
+
+
+ Alber
+
+
+
+
+ Archivo
+
+
+
+
+ Emulación
+
+
+
+
+ Herramientas
+
+
+
+
+ Acerca de
+
+
+
+
+ Cargar juego
+
+
+
+
+ Cargar script Lua
+
+
+
+
+ Abrir carpeta Panda3DS
+
+
+
+
+ Pausar
+
+
+
+
+ Reanudar
+
+
+
+
+ Reiniciar
+
+
+
+
+ Configurar
+
+
+
+
+ Volcar RomFS
+
+
+
+
+ Abrir Editor Lua
+
+
+
+
+ Abrir Editor de Trucos
+
+
+
+
+ Abrir Ventana de Parches
+
+
+
+
+ Abrir Editor de Shaders
+
+
+
+
+ Volcar firmware DSP cargado
+
+
+
+
+ Acerca de Panda3DS
+
+
+
+
+ Seleccione el ROM de 3DS a cargar
+
+
+
+
+ ROMs de Nintendo 3DS (*.3ds *.cci *.cxi *.app *.ncch *.3dsx *.elf *.axf)
+
+
+
+
+ Seleccione el script Lua a cargar
+
+
+
+
+ Scripts Lua (*.lua *.txt)
+
+
+
+
+ Seleccione la carpeta donde volcar los archivos del RomFS
+
+
+
+
+ Formato inváido para volcado de RomFS
+
+
+
+
+ La aplicación cargada no tiene un formato que soporta RomFS
+
+
+
+
+
+
+ OK
+
+
+
+
+ RomFS no encontrado
+
+
+
+
+ No se encontró una partición RomFS en la aplicación cargada
+
+
+
+
+ Seleccionar archivo
+
+
+
+
+ Archivo de firmware DSP (*.cdc)
+
+
+
+
+ Firmware DSP no cargado
+
+
+
+
+ La aplicación cargada no ha subido un firmware al DSP
+
+
+
+
+ Error al abrir el archivo de salida
+
+
+
+
+ No se pudo escribir el firmware DSP cargado al archivo seleccionado. Por favor asegure que tiene los permisos necesarios para acceder a este archivo
+
+
+
+ PatchWindow
+
+
+
+ Parcheador de ROM
+
+
+
+
+ Seleccione el archivo de entrada
+
+
+
+
+
+ Seleccionar
+
+
+
+
+
+ Seleccione el archivo de parche
+
+
+
+
+ Aplicar parche
+
+
+
+
+ Seleccione el archivo a parchear
+
+
+
+
+
+ Todos los archivos (*.*)
+
+
+
+
+ Archivos de parche (*.ips *.ups *.bps)
+
+
+
+
+ Rutas no proporcionadas correctamente
+
+
+
+
+ Por favor proporcione rutas para el archivo de entrada y el parche
+
+
+
+
+ Seleccionar archivo
+
+
+
+
+ No hay archivo de salida
+
+
+
+
+ No se ha proporcionado una ruta para el archivo de salida, no se ha aplicado el parche
+
+
+
+
+ Formato del parche desconocido
+
+
+
+
+ Formato desconocido del archivo de parche. Actualmente son soportados IPS, UPS y BPS
+
+
+
+
+ Error al abrir archivos de entrada
+
+
+
+
+ Asegure que estén en un directorio al que Panda3DS tenga acceso
+
+
+
+
+ Parche exitoso
+
+
+
+
+ Su archivo fue parcheado con éxito.
+
+
+
+
+ Discrepancia en la suma de verificación
+
+
+
+
+ El parche se aplicó con éxito pero se detectó una discrepancia en la suma de verificación. Los archivos de entrada o salida pueden no ser correctos
+
+
+
+
+ Error de parcheo
+
+
+
+
+ Ha ocurrido un error en el parcheo
+
+
+
+ PatchWindow::PatchWindow
+
+
+
+ OK
+
+
+
+ ShaderEditorWindow
+
+
+
+ Recargar shader
+
+
+
+ TextEditorWindow
+
+
+
+ Editor Lua
+
+
+
+
+ Cargar script
+
+
+
\ No newline at end of file
diff --git a/docs/translations/nl.ts b/docs/translations/nl.ts
new file mode 100644
index 000000000..4e6d3e9e5
--- /dev/null
+++ b/docs/translations/nl.ts
@@ -0,0 +1,763 @@
+
+
+ AboutWindow
+
+
+
+ Over Panda3DS
+
+
+
+
+ Panda3DS is een gratis, open source Nintendo 3DS-emulator voor Windows, MacOS en Linux
+
+
+
+
+ Bezoek panda3ds.com voor ondersteuning van Panda3DS en links naar onze officiële ondersteuningskanalen.
+
+
+
+
+ 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.<br>Als jij vindt dat je op deze lijst zou moeten staan, laat het ons dan weten<br><br>- Peach (wheremyfoodat)<br>- noumidev<br>- liuk707<br>- Wunk<br>- marysaka<br>- Sky<br>- merryhime<br>- TGP17<br>- Shadow<br>
+
+
+
+ CheatEditDialog
+
+
+
+ Cheat bewerken
+
+
+
+
+ Cheatnaam
+
+
+
+ CheatEntryWidget
+
+
+
+ Bewerken
+
+
+
+ CheatsWindow
+
+
+
+ Cheats
+
+
+
+
+ Toevoegen
+
+
+
+
+ Verwijderen
+
+
+
+ ConfigWindow
+
+
+
+ Instellingen
+
+
+
+
+ Interfaceinstellingen
+
+
+
+
+ Systeem
+
+
+
+
+ Licht
+
+
+
+
+ Donker
+
+
+
+
+ Begroetingskat
+
+
+
+
+ Crème
+
+
+
+
+ Kleurenthema
+
+
+
+
+ Blije panda
+
+
+
+
+ Blije panda (kleurrijk)
+
+
+
+
+ Slaperige panda
+
+
+
+
+ Koeienpanda
+
+
+
+
+ De pinguïn van SkyEmu
+
+
+
+
+ Venstericoon
+
+
+
+
+ Taal
+
+
+
+
+ Toon versie in venstertitel
+
+
+
+
+ Alber v%1
+
+
+
+
+ Alber
+
+
+
+
+ Vensterpositie onthouden
+
+
+
+
+ Algemene instellingen
+
+
+
+
+ Bladeren...
+
+
+
+
+ Kies map
+
+
+
+
+ Standaard pad voor ROMs
+
+
+
+
+ Discord RPC inschakelen
+
+
+
+
+ Portable build gebruiken
+
+
+
+
+ Versie afdrukken in consoleuitvoer
+
+
+
+
+ Grafische instellingen
+
+
+
+
+
+ Null
+
+
+
+
+ OpenGL
+
+
+
+
+ Vulkan
+
+
+
+
+ Renderen op videokaart
+
+
+
+
+ Renderdoc inschakelen
+
+
+
+
+ Shader JIT inschakelen
+
+
+
+
+ VSync inschakelen
+
+
+
+
+ Ubershaders gebruiken (geen haperingen, mogelijk langzamer)
+
+
+
+
+ Nauwkeurige vermenigvuldigen in shaders
+
+
+
+
+ Shaders versnellen
+
+
+
+
+ Shadergen afdwingen bij tekenen licht
+
+
+
+
+ Lichtgrens voor afdwingen shadergen
+
+
+
+
+ Audioinstellingen
+
+
+
+
+ LLE
+
+
+
+
+ HLE
+
+
+
+
+ DSP-emulatie
+
+
+
+
+ Audio inschakelen
+
+
+
+
+ AAC-audio inschakelen
+
+
+
+
+ DSP-firmware afdrukken
+
+
+
+
+ Audioapparaat dempen
+
+
+
+
+ Kubiek
+
+
+
+
+ Lineair
+
+
+
+
+ Volumecurve
+
+
+
+
+ Volume audioapparaat
+
+
+
+
+ Batterij-instellingen
+
+
+
+
+ Batterijpercentage
+
+
+
+
+ Oplader aangesloten
+
+
+
+
+ Instellingen SD-kaart
+
+
+
+
+ Virtuele SD-kaart inschakelen
+
+
+
+
+ Virtuele SD-kaart schrijfbeveiligen
+
+
+
+
+ Interface
+
+
+
+
+ Instellingen gebruikersinterface
+
+
+
+
+ Algemeen
+
+
+
+
+ Algemene emulatorinstellingen
+
+
+
+
+ Weergave
+
+
+
+
+ Instellingen grafische emulatie en weergave
+
+
+
+
+ Audio
+
+
+
+
+ Instellingen audioemulatie en weergave
+
+
+
+
+ Batterij
+
+
+
+
+ Instellingen batterijemulatie
+
+
+
+
+ SD-kaart
+
+
+
+
+ Instellingen SD-kaart-emulatie
+
+
+
+
+ Taal succesvol ingesteld
+
+
+
+
+ Herstart Panda3DS om de nieuw gekozen taal te gebruiken.
+
+
+
+
+ Wijzigen van taal mislukt
+
+
+
+
+ De gekozen taal is niet beschikbaar in Panda3DS. Als je dit leest heeft iemand de taalcode verprutst...
+
+
+
+ MainWindow
+
+
+
+ Alber
+
+
+
+
+ Bestand
+
+
+
+
+ Emulatie
+
+
+
+
+ Hulpmiddelen
+
+
+
+
+ Over
+
+
+
+
+ Spel laden
+
+
+
+
+ LUA-script laden
+
+
+
+
+ Open Panda3DS-map
+
+
+
+
+ Pauzeren
+
+
+
+
+ Hervatten
+
+
+
+
+ Reset
+
+
+
+
+ Instellingen
+
+
+
+
+ RomFS dumpen
+
+
+
+
+ Open LUA-editor
+
+
+
+
+ Open cheats-editor
+
+
+
+
+ Open patchvenster
+
+
+
+
+ Open shader-editor
+
+
+
+
+ Geladen DSP-firmware dumpen
+
+
+
+
+ Over Panda3DS
+
+
+
+
+ Kies 3DS ROM om te laden
+
+
+
+
+ Nintendo 3DS ROMs (*.3ds *.cci *.cxi *.app *.ncch *.3dsx *.elf *.axf)
+
+
+
+
+ Kies LUA-script om te laden
+
+
+
+
+ LUA-scripts (*.lua *.txt)
+
+
+
+
+ Kies map om RomFS-bestanden heen te dumpen
+
+
+
+
+ Ongeldig formaat voor RomFS dump
+
+
+
+
+ Het formaat van de momenteel geladen applicatie ondersteunt geen RomFS
+
+
+
+
+
+
+ OK
+
+
+
+
+ Geen RomFS gevonden
+
+
+
+
+ Geen RomFS-partitie gevonden in de geladen applicatie
+
+
+
+
+ Selecteer bestand
+
+
+
+
+ DSP-firmware-bestand (*.cdc)
+
+
+
+
+ Geen DSP-firmware geladen
+
+
+
+
+ De momenteel geladen applicatie heeft geen firmware geüpload naar de DSP
+
+
+
+
+ Uitvoerbestand openen mislukt
+
+
+
+
+ De momenteel geladen DSP-firmware kan niet worden geschreven naar het gekozen bestand. Controleer de permissies van het gekozen bestand
+
+
+
+ PatchWindow
+
+
+
+ ROM-patcher
+
+
+
+
+ Kies invoerbestand
+
+
+
+
+
+ Kies
+
+
+
+
+
+ Kies patchbestand
+
+
+
+
+ Patch toepassen
+
+
+
+
+ Kies bestand om te patchen
+
+
+
+
+
+ Alle bestanden (*.*)
+
+
+
+
+ Patch-bestanden (*.ips *.ups *.bps)
+
+
+
+
+ Paden incorrect meegegeven
+
+
+
+
+ Geef paden van invoerbestand en patchbestand op
+
+
+
+
+ Kies bestand
+
+
+
+
+ Geen uitvoerpad
+
+
+
+
+ Geen pad opgegeven voor uitvoerbestand, patch niet toegepast
+
+
+
+
+ Onbekend patchformaat
+
+
+
+
+ Ongeldig formaat van patchbestand. Momenteel wordt IPS, UPS en BPS ondersteund
+
+
+
+
+ Openen van invoerbestanden mislukt
+
+
+
+
+ Zorg ervoor dat ze in een map staan waar Panda3DS toegang toe heeft
+
+
+
+
+ Patch succesvol
+
+
+
+
+ Het bestand is succesvol gepatcht.
+
+
+
+
+ Checksum komt niet overeen
+
+
+
+
+ Patch is succesvol toegepast maar de checksum komt niet overeen. Invoer- of uitvoerbestand is mogelijk ongeldig
+
+
+
+
+ Fout tijdens patchen
+
+
+
+
+ Er is bij het patchen een fout opgetreden
+
+
+
+ PatchWindow::PatchWindow
+
+
+
+ OK
+
+
+
+ ShaderEditorWindow
+
+
+
+ Shader herladen
+
+
+
+ TextEditorWindow
+
+
+
+ LUA-editor
+
+
+
+
+ Script laden
+
+
+
\ No newline at end of file
diff --git a/docs/translations/pt_br.ts b/docs/translations/pt_br.ts
new file mode 100644
index 000000000..e2afe3a97
--- /dev/null
+++ b/docs/translations/pt_br.ts
@@ -0,0 +1,764 @@
+
+
+ AboutWindow
+
+
+
+ Sobre o Panda3DS
+
+
+
+
+ Panda3DS é um emulador gratuito e open-source para Windows, MacOS e Linux
+
+
+
+
+ Visite panda3ds.com para obter ajuda e links de suporte oficial.
+
+
+
+
+ Panda3DS é desenvolvido por voluntários em seu tempo livre. Abaixo a lista de alguns volutário<br>(Lista sem nenhuma ordem específica)<br>Se acha que seu nome deveria estar listado aqui por favor informe-nos<br><br>- Peach (wheremyfoodat)<br>- noumidev<br>- liuk707<br>- Wunk<br>- marysaka<br>- Sky<br>- merryhime<br>- TGP17<br>- Shadow<br>
+
+
+
+ CheatEditDialog
+
+
+
+ Editar Trapaças
+
+
+
+
+ Nome da trapaça
+
+
+
+ CheatEntryWidget
+
+
+
+ Editar
+
+
+
+ CheatsWindow
+
+
+
+ Trapaça
+
+
+
+
+ Adicionar
+
+
+
+
+ Sair
+
+
+
+ ConfigWindow
+
+
+
+ Configurações
+
+
+
+
+ Configurações da interface
+
+
+
+
+ Sistema
+
+
+
+
+ Claro
+
+
+
+
+ Escuro
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ Tema de color
+
+
+
+
+ Panda feliz
+
+
+
+
+ Panda feliz (colorido)
+
+
+
+
+ Panda sonolento
+
+
+
+
+ Panda vaca
+
+
+
+
+ O pinguim do SkyEmu
+
+
+
+
+ Icone da janela
+
+
+
+
+ Idioma
+
+
+
+
+ Mostrar versão na barra de título
+
+
+
+
+ Alber v%1
+
+
+
+
+ Alber
+
+
+
+
+ Lembrar posição da janela
+
+
+
+
+ Configurações gerais
+
+
+
+
+ Navegar...
+
+
+
+
+ Selecionar o diretório
+
+
+
+
+ Diretório padrão das ROMs
+
+
+
+
+ Ativar Discord RPC
+
+
+
+
+ Usar build portatil
+
+
+
+
+ Imprimir versão no console
+
+
+
+
+ Configurações gráficas
+
+
+
+
+
+ Nulo
+
+
+
+
+ OpenGL
+
+
+
+
+ Vulkan
+
+
+
+
+ Renderizador GPU
+
+
+
+
+ Ativar Renderdoc
+
+
+
+
+ Ativar JIT de shaders
+
+
+
+
+ Ativar VSync
+
+
+
+
+ Usar ubershaders (No stuttering, puede ser más lento)
+
+
+
+
+ Multiplicação precisa de shaders
+
+
+
+
+ Graficos acelerados
+
+
+
+
+ Forçar shadergen ao renderizar luzes.
+
+
+
+
+ Limear de luzes para forçar shadergen
+
+
+
+
+ Configurações de audio
+
+
+
+
+ LLE
+
+
+
+
+ HLE
+
+
+
+
+ Emulação DSP
+
+
+
+
+ Ativar audio
+
+
+
+
+ Ativar audio AAC
+
+
+
+
+ Imprimir firmware DSP
+
+
+
+
+ Silenciar dispositivo de audio
+
+
+
+
+ Cúbico
+
+
+
+
+ Linear
+
+
+
+
+ Curva de volume
+
+
+
+
+ Volume do dispositivo de audio
+
+
+
+
+ Configurações de bateria
+
+
+
+
+ Porcentagem da bateria
+
+
+
+
+ Carregador conectado
+
+
+
+
+ Configurações do cartão de memoria
+
+
+
+
+ Ativar cartão de memoria virtual
+
+
+
+
+ Proteger cartão de memoria virtual contra escrita
+
+
+
+
+ Interface
+
+
+
+
+ Configurações da interface de usuario
+
+
+
+
+ Geral
+
+
+
+
+ Configurações gerais do emulador
+
+
+
+
+ Gráficos
+
+
+
+
+ Configurações da emulação e saida de video
+
+
+
+
+ Audio
+
+
+
+
+ Configurações da emulação e saida de audio
+
+
+
+
+ Bateria
+
+
+
+
+ Configuração da emulação da bateria
+
+
+
+
+ Cartão de memoria
+
+
+
+
+ Configurar a emulação do cartão de memoria
+
+
+
+
+ Idioma alterado com sucesso
+
+
+
+
+ Para aplicar o novo idioma feche e abra o emulador.
+
+
+
+
+ A mudaça de idioma falhou
+
+
+
+
+ O idioma selecionado não existe no Panda3DS. Se você esta vendo esse erro existe um erro no codigo...
+
+
+
+ MainWindow
+
+
+
+ Alber
+
+
+
+
+ Arquivo
+
+
+
+
+ Emulação
+
+
+
+
+ Ferramentas
+
+
+
+
+ Sobre
+
+
+
+
+ Carregar jogo
+
+
+
+
+ Carregar Script Lua
+
+
+
+
+ Abrir pasta do Panda3DS
+
+
+
+
+ Pausar
+
+
+
+
+ Continuar
+
+
+
+
+ Reiniciar
+
+
+
+
+ Configurar
+
+
+
+
+ Extrair RomFS
+
+
+
+
+ Abrir editor Lua
+
+
+
+
+ Abrir editor de trapaças
+
+
+
+
+ Abrir janela de trapaças
+
+
+
+
+ Abrir editor de shaders
+
+
+
+
+ Extrair firmware DSP carregado
+
+
+
+
+ Sobre o Panda3DS
+
+
+
+
+ Selecione uma ROM de 3DS para carregar
+
+
+
+
+ ROMs de Nintendo 3DS (*.3ds *.cci *.cxi *.app *.ncch *.3dsx *.elf *.axf)
+
+
+
+
+ Selecione uma ROM de 3DS para carregar
+
+
+
+
+ Scripts Lua (*.lua *.txt)
+
+
+
+
+ Selecione onde a RomFS será extraida
+
+
+
+
+ Formato de RomFS inválido
+
+
+
+
+ O aplicativo carregado não suporta RomFS
+
+
+
+
+
+
+ OK
+
+
+
+
+ RomFS no encontrado
+
+
+
+
+ A partição RomFS não foi encontrada no aplicativo
+
+
+
+
+ Selecionar arquivo
+
+
+
+
+ Arquivo do firmware DSP (*.cdc)
+
+
+
+
+ Nenhum firmware DSP carregado
+
+
+
+
+ A aplicação não carregou um firmware DSP
+
+
+
+
+ Erro ao abrir arquvio de destino
+
+
+
+
+ O firmware DSP carregado não pode escrever no arquivo selecionado. Porfavor veja se você tem permissão para modificalo-lo.
+
+
+
+ PatchWindow
+
+
+
+ Editor de ROM
+
+
+
+
+ Selecione o arquivo de entrada
+
+
+
+
+
+ Selecionar
+
+
+
+
+
+ Seleciona um arquivo de patch
+
+
+
+
+ Aplicar patch
+
+
+
+
+ Selecione um arquivo para editar
+
+
+
+
+
+ Todos os arquivos (*.*)
+
+
+
+
+ Arquivos de patch (*.ips *.ups *.bps)
+
+
+
+
+ Diretórios não fornecidos corretamente
+
+
+
+
+ Por favor selecione os diretórios tanto para o arquivo de origem como para o patch
+
+
+
+
+ Selecionar arquivo
+
+
+
+
+ Sem diretório de saida
+
+
+
+
+ Nenhum diretorio de destino foi fornecido, patch não aplicado.
+
+
+
+
+ Formato de patch desconhecido
+
+
+
+
+ Arquivo de patch inválido. Atualmete são suportado patches nos formatos IPS, UPS e BPS
+
+
+
+
+ Falha ao abrir os arquivos
+
+
+
+
+ Certifique-se de que eles estejam em um diretório ao qual o Panda3DS tenha acesso
+
+
+
+
+ Patch aplicado
+
+
+
+
+ O patch foi aplicado com sucesso ao arquivo
+
+
+
+
+ Checagem inválido
+
+
+
+
+ O patch foi aplicado com sucesso porem a checagem falhou. O arquivo de origem ou destino pode não estar correto.
+
+
+
+
+ Erro de patch
+
+
+
+
+ Um erro ocorreu ao aplicar o patch
+
+
+
+ PatchWindow::PatchWindow
+
+
+
+ OK
+
+
+
+ ShaderEditorWindow
+
+
+
+ Recargar shader
+
+
+
+ TextEditorWindow
+
+
+
+ Editor Lua
+
+
+
+
+ Cargar script
+
+
+
+
diff --git a/include/audio/miniaudio_device.hpp b/include/audio/miniaudio_device.hpp
index 516c16025..0363aa441 100644
--- a/include/audio/miniaudio_device.hpp
+++ b/include/audio/miniaudio_device.hpp
@@ -36,4 +36,6 @@ class MiniAudioDevice {
void start();
void stop();
+
+ bool isInitialized() const { return initialized; }
};
\ No newline at end of file
diff --git a/include/frontend_settings.hpp b/include/frontend_settings.hpp
index 1a78ab66f..ae9678792 100644
--- a/include/frontend_settings.hpp
+++ b/include/frontend_settings.hpp
@@ -24,6 +24,7 @@ struct FrontendSettings {
Theme theme = Theme::Dark;
WindowIcon icon = WindowIcon::Rpog;
+ std::string language = "en";
static Theme themeFromString(std::string inString);
static const char* themeToString(Theme theme);
diff --git a/include/fs/archive_base.hpp b/include/fs/archive_base.hpp
index 2843be687..d899d1a6e 100644
--- a/include/fs/archive_base.hpp
+++ b/include/fs/archive_base.hpp
@@ -7,6 +7,7 @@
#include
#include
#include
+
#include "helpers.hpp"
#include "memory.hpp"
#include "result.hpp"
@@ -15,13 +16,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 {
@@ -34,91 +35,95 @@ 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)";
- default: return "Unknown archive";
- }
- }
-}
+ 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
struct FSPath {
- u32 type = PathType::Invalid;
-
- std::vector binary; // Path data for binary paths
- std::string string; // Path data for ASCII paths
- std::u16string utf16_string;
-
- FSPath() {}
-
- FSPath(u32 type, const std::vector& 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::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;
- }
-; }
- }
+ u32 type = PathType::Invalid;
+
+ std::vector binary; // Path data for binary paths
+ std::string string; // Path data for ASCII paths
+ std::u16string utf16_string;
+
+ FSPath() {}
+
+ FSPath(u32 type, const std::vector& 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::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;
-
- 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) {}
+ 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) {}
+
+ // 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 {
@@ -156,106 +161,104 @@ struct DirectorySession {
using FileDescriptor = std::optional;
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
- };
-
-protected:
- using Handle = u32;
-
- 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
- bool isPathSafe(const FSPath& path) {
- static_assert(format == PathType::ASCII || format == PathType::UTF16);
- using String = typename std::conditional::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()) {
- 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;
-
- // 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);
-
- if (token == dots) {
- level--;
- if (level < 0) return false;
- } else {
- level++;
- }
- }
-
- 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;
-
- virtual Rust::Result 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;
- }
-
- // 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 openArchive(const FSPath& path) = 0;
-
- virtual Rust::Result 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 HorizonResult renameFile(const FSPath& oldPath, const FSPath& newPath) {
+ 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;
+
+ 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
+ bool isPathSafe(const FSPath& path) {
+ static_assert(format == PathType::ASCII || format == PathType::UTF16);
+ using String = typename std::conditional::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()) {
+ 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;
+
+ // 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);
+
+ if (token == dots) {
+ level--;
+ if (level < 0) return false;
+ } else {
+ level++;
+ }
+ }
+
+ 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;
+
+ virtual Rust::Result 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;
+ }
+
+ // 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 openArchive(const FSPath& path) = 0;
+
+ virtual Rust::Result 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 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 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 readFile(FileSession* file, u64 offset, u32 size, u32 dataPointer) = 0;
- ArchiveBase(Memory& mem) : mem(mem) {}
+ ArchiveBase(Memory& mem) : mem(mem) {}
};
struct ArchiveResource {
diff --git a/include/fs/archive_card_spi.hpp b/include/fs/archive_card_spi.hpp
new file mode 100644
index 000000000..fefa9933f
--- /dev/null
+++ b/include/fs/archive_card_spi.hpp
@@ -0,0 +1,30 @@
+#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 openArchive(const FSPath& path) override;
+ Rust::Result openDirectory(const FSPath& path) override;
+
+ FileDescriptor openFile(const FSPath& path, const FilePerms& perms) override;
+
+ std::optional readFile(FileSession* file, u64 offset, u32 size, u32 dataPointer) override {
+ Helpers::panic("Unimplemented ReadFile for Card SPI archive");
+ return {};
+ };
+};
\ No newline at end of file
diff --git a/include/fs/archive_twl_photo.hpp b/include/fs/archive_twl_photo.hpp
new file mode 100644
index 000000000..23111e2b2
--- /dev/null
+++ b/include/fs/archive_twl_photo.hpp
@@ -0,0 +1,30 @@
+#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 openArchive(const FSPath& path) override;
+ Rust::Result openDirectory(const FSPath& path) override;
+
+ FileDescriptor openFile(const FSPath& path, const FilePerms& perms) override;
+
+ std::optional readFile(FileSession* file, u64 offset, u32 size, u32 dataPointer) override {
+ Helpers::panic("Unimplemented ReadFile for TWL_PHOTO archive");
+ return {};
+ };
+};
\ No newline at end of file
diff --git a/include/fs/archive_twl_sound.hpp b/include/fs/archive_twl_sound.hpp
new file mode 100644
index 000000000..cc8fc866c
--- /dev/null
+++ b/include/fs/archive_twl_sound.hpp
@@ -0,0 +1,30 @@
+#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 openArchive(const FSPath& path) override;
+ Rust::Result openDirectory(const FSPath& path) override;
+
+ FileDescriptor openFile(const FSPath& path, const FilePerms& perms) override;
+
+ std::optional readFile(FileSession* file, u64 offset, u32 size, u32 dataPointer) override {
+ Helpers::panic("Unimplemented ReadFile for TWL_SOUND archive");
+ return {};
+ };
+};
\ No newline at end of file
diff --git a/include/panda_qt/config_window.hpp b/include/panda_qt/config_window.hpp
index 9c0037534..1d37a8caa 100644
--- a/include/panda_qt/config_window.hpp
+++ b/include/panda_qt/config_window.hpp
@@ -45,6 +45,8 @@ class ConfigWindow : public QDialog {
void setTheme(FrontendSettings::Theme theme);
void setIcon(FrontendSettings::WindowIcon icon);
+ QComboBox* createLanguageSelect();
+
public:
ConfigWindow(ConfigCallback configCallback, MainWindowCallback windowCallback, const EmulatorConfig& config, QWidget* parent = nullptr);
~ConfigWindow();
diff --git a/include/panda_qt/main_window.hpp b/include/panda_qt/main_window.hpp
index eb1cfb16f..ed54ad84d 100644
--- a/include/panda_qt/main_window.hpp
+++ b/include/panda_qt/main_window.hpp
@@ -126,6 +126,7 @@ class MainWindow : public QMainWindow {
void setupControllerSensors(SDL_GameController* controller);
void sendMessage(const EmulatorMessage& message);
void dispatchMessage(const EmulatorMessage& message);
+ void loadTranslation();
// Tracks whether we are using an OpenGL-backed renderer or a Vulkan-backed renderer
bool usingGL = false;
diff --git a/include/services/dsp.hpp b/include/services/dsp.hpp
index 7eb9b28b0..3013c94d4 100644
--- a/include/services/dsp.hpp
+++ b/include/services/dsp.hpp
@@ -44,9 +44,12 @@ class DSPService {
size_t totalEventCount;
std::vector 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);
diff --git a/include/services/fs.hpp b/include/services/fs.hpp
index 3b3b3d44b..82f07077a 100644
--- a/include/services/fs.hpp
+++ b/include/services/fs.hpp
@@ -1,11 +1,14 @@
#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"
@@ -39,6 +42,10 @@ class FSService {
ExtSaveDataArchive sharedExtSaveData_nand;
SystemSaveDataArchive systemSaveData;
+ TWLPhotoArchive twlPhoto;
+ TWLSoundArchive twlSound;
+ CardSPIArchive cardSpi;
+
ArchiveBase* getArchiveFromID(u32 id, const FSPath& archivePath);
Rust::Result openArchiveHandle(u32 archiveID, const FSPath& path);
Rust::Result openDirectoryHandle(ArchiveBase* archive, const FSPath& path);
@@ -87,7 +94,8 @@ 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), kernel(kernel), config(config), systemSaveData(mem) {}
+ userSaveData2(mem, ArchiveID::UserSaveData2), systemSaveData(mem), twlPhoto(mem), twlSound(mem), cardSpi(mem), kernel(kernel),
+ config(config) {}
void reset();
void handleSyncRequest(u32 messagePointer);
diff --git a/include/services/gsp_gpu.hpp b/include/services/gsp_gpu.hpp
index d72446097..8294565b1 100644
--- a/include/services/gsp_gpu.hpp
+++ b/include/services/gsp_gpu.hpp
@@ -1,6 +1,7 @@
#pragma once
#include
#include
+
#include "PICA/gpu.hpp"
#include "helpers.hpp"
#include "kernel_types.hpp"
@@ -9,12 +10,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
};
@@ -28,8 +29,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
@@ -64,8 +65,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;
};
@@ -74,6 +75,7 @@ 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);
@@ -108,15 +110,14 @@ 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);
}
}
diff --git a/include/services/mcu/mcu_hwc.hpp b/include/services/mcu/mcu_hwc.hpp
index 4c6a88305..c2fada28e 100644
--- a/include/services/mcu/mcu_hwc.hpp
+++ b/include/services/mcu/mcu_hwc.hpp
@@ -17,6 +17,7 @@ namespace MCU {
// Service commands
void getBatteryLevel(u32 messagePointer);
+ void setInfoLEDPattern(u32 messagePointer);
public:
HWCService(Memory& mem, const EmulatorConfig& config) : mem(mem), config(config) {}
diff --git a/readme.md b/readme.md
index 35715ac23..7ffb7384f 100644
--- a/readme.md
+++ b/readme.md
@@ -19,14 +19,14 @@ SDL builds (No GUI):
|Platform|Status|Download|
|--------|------------|--------|
|Windows build|[![Windows Build](https://github.com/wheremyfoodat/Panda3DS/actions/workflows/Windows_Build.yml/badge.svg?branch=master)](https://github.com/wheremyfoodat/Panda3DS/actions/workflows/Windows_Build.yml)|[Windows Executable](https://nightly.link/wheremyfoodat/Panda3DS/workflows/Windows_Build/master/Windows%20executable.zip)|
-|MacOS build|[![MacOS Build](https://github.com/wheremyfoodat/Panda3DS/actions/workflows/MacOS_Build.yml/badge.svg?branch=master)](https://github.com/wheremyfoodat/Panda3DS/actions/workflows/MacOS_Build.yml)|[MacOS App Bundle](https://nightly.link/wheremyfoodat/Panda3DS/workflows/MacOS_Build/master/MacOS%20Alber%20App%20Bundle.zip)|
+|MacOS build|[![MacOS Build](https://github.com/wheremyfoodat/Panda3DS/actions/workflows/MacOS_Build.yml/badge.svg?branch=master)](https://github.com/wheremyfoodat/Panda3DS/actions/workflows/MacOS_Build.yml)|[MacOS App Bundle](https://nightly.link/wheremyfoodat/Panda3DS/workflows/MacOS_Build/master/MacOS%20Alber%20App%20Bundle%20(universal).zip)|
|Linux build|[![Linux Build](https://github.com/wheremyfoodat/Panda3DS/actions/workflows/Linux_Build.yml/badge.svg?branch=master)](https://github.com/wheremyfoodat/Panda3DS/actions/workflows/Linux_Build.yml)|[Linux AppImage](https://nightly.link/wheremyfoodat/Panda3DS/workflows/Linux_AppImage_Build/master/Linux%20executable.zip)|
Qt and Android builds:
|Platform|Status|Download|
|--------|------------|--------|
|Windows build|[![Qt Build](https://github.com/wheremyfoodat/Panda3DS/actions/workflows/Qt_Build.yml/badge.svg)](https://github.com/wheremyfoodat/Panda3DS/actions/workflows/Qt_Build.yml)|[Windows Executable](https://nightly.link/wheremyfoodat/Panda3DS/workflows/Qt_Build/master/Windows%20executable.zip)|
-|MacOS build|[![Qt Build](https://github.com/wheremyfoodat/Panda3DS/actions/workflows/Qt_Build.yml/badge.svg)](https://github.com/wheremyfoodat/Panda3DS/actions/workflows/Qt_Build.yml)|[MacOS App Bundle](https://nightly.link/wheremyfoodat/Panda3DS/workflows/Qt_Build/master/MacOS%20Alber%20App%20Bundle.zip)|
+|MacOS build|[![Qt Build](https://github.com/wheremyfoodat/Panda3DS/actions/workflows/Qt_Build.yml/badge.svg)](https://github.com/wheremyfoodat/Panda3DS/actions/workflows/Qt_Build.yml)|[MacOS App Bundle](https://nightly.link/wheremyfoodat/Panda3DS/workflows/Qt_Build/master/MacOS%20Alber%20App%20Bundle%20(universal).zip)|
|Linux build|[![Qt Build](https://github.com/wheremyfoodat/Panda3DS/actions/workflows/Qt_Build.yml/badge.svg)](https://github.com/wheremyfoodat/Panda3DS/actions/workflows/Qt_Build.yml)|[Linux AppImage](https://nightly.link/wheremyfoodat/Panda3DS/workflows/Qt_Build/master/Linux%20executable.zip)|
|Android build (arm64)|[![Android Build](https://github.com/wheremyfoodat/Panda3DS/actions/workflows/Android_Build.yml/badge.svg)](https://github.com/wheremyfoodat/Panda3DS/actions/workflows/Android_Build.yml)|[Android APK](https://nightly.link/wheremyfoodat/Panda3DS/workflows/Android_Build/master/Android%20APKs%20(arm64).zip)|
@@ -118,7 +118,7 @@ Panda3DS also supports controller input using the SDL2 GameController API.
- [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)
diff --git a/src/config.cpp b/src/config.cpp
index fd4d969d4..73b5d6644 100644
--- a/src/config.cpp
+++ b/src/config.cpp
@@ -141,6 +141,7 @@ void EmulatorConfig::load() {
frontendSettings.theme = FrontendSettings::themeFromString(toml::find_or(ui, "Theme", "dark"));
frontendSettings.icon = FrontendSettings::iconFromString(toml::find_or(ui, "WindowIcon", "rpog"));
+ frontendSettings.language = toml::find_or(ui, "Language", "en");
}
}
}
@@ -202,6 +203,7 @@ void EmulatorConfig::save() {
data["UI"]["Theme"] = std::string(FrontendSettings::themeToString(frontendSettings.theme));
data["UI"]["WindowIcon"] = std::string(FrontendSettings::iconToString(frontendSettings.icon));
+ data["UI"]["Language"] = frontendSettings.language;
std::ofstream file(path, std::ios::out);
file << data;
diff --git a/src/core/fs/archive_card_spi.cpp b/src/core/fs/archive_card_spi.cpp
new file mode 100644
index 000000000..db1b5fded
--- /dev/null
+++ b/src/core/fs/archive_card_spi.cpp
@@ -0,0 +1,40 @@
+#include
+#include
+
+#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 CardSPIArchive::openArchive(const FSPath& path) {
+ if (path.type != PathType::Empty) {
+ Helpers::panic("Unimplemented path type for CardSPIArchive::OpenArchive");
+ }
+
+ Helpers::warn("Unimplemented: Card SPI archive");
+ return Err(Result::FailurePlaceholder);
+}
+
+Rust::Result CardSPIArchive::openDirectory(const FSPath& path) {
+ Helpers::panic("[Card SPI] OpenDirectory not yet supported");
+ return Err(Result::FailurePlaceholder);
+}
diff --git a/src/core/fs/archive_system_save_data.cpp b/src/core/fs/archive_system_save_data.cpp
index 37afb431f..7cb454a25 100644
--- a/src/core/fs/archive_system_save_data.cpp
+++ b/src/core/fs/archive_system_save_data.cpp
@@ -87,7 +87,7 @@ HorizonResult SystemSaveDataArchive::createFile(const FSPath& path, u64 size) {
HorizonResult SystemSaveDataArchive::createDirectory(const FSPath& path) {
if (path.type == PathType::UTF16) {
if (!isPathSafe(path)) {
- Helpers::panic("Unsafe path in SystemSaveData::OpenFile");
+ Helpers::panic("Unsafe path in SystemSaveData::CreateDirectory");
}
fs::path p = IOFile::getAppData() / ".." / "SharedFiles" / "SystemSaveData";
diff --git a/src/core/fs/archive_twl_photo.cpp b/src/core/fs/archive_twl_photo.cpp
new file mode 100644
index 000000000..a1833c5cf
--- /dev/null
+++ b/src/core/fs/archive_twl_photo.cpp
@@ -0,0 +1,40 @@
+#include
+#include
+
+#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 TWLPhotoArchive::openArchive(const FSPath& path) {
+ if (path.type != PathType::Empty) {
+ Helpers::panic("Unimplemented path type for TWLPhotoArchive::OpenArchive");
+ }
+
+ Helpers::warn("Unimplemented: TWL_PHOTO archive");
+ return Err(Result::FailurePlaceholder);
+}
+
+Rust::Result TWLPhotoArchive::openDirectory(const FSPath& path) {
+ Helpers::panic("[TWL_PHOTO] OpenDirectory not yet supported");
+ return Err(Result::FailurePlaceholder);
+}
diff --git a/src/core/fs/archive_twl_sound.cpp b/src/core/fs/archive_twl_sound.cpp
new file mode 100644
index 000000000..fbe98caf5
--- /dev/null
+++ b/src/core/fs/archive_twl_sound.cpp
@@ -0,0 +1,40 @@
+#include
+#include
+
+#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 TWLSoundArchive::openArchive(const FSPath& path) {
+ if (path.type != PathType::Empty) {
+ Helpers::panic("Unimplemented path type for TWLSoundArchive::OpenArchive");
+ }
+
+ Helpers::warn("Unimplemented: TWL_SOUND archive");
+ return Err(Result::FailurePlaceholder);
+}
+
+Rust::Result TWLSoundArchive::openDirectory(const FSPath& path) {
+ Helpers::panic("[TWL_SOUND] OpenDirectory not yet supported");
+ return Err(Result::FailurePlaceholder);
+}
diff --git a/src/core/services/dsp.cpp b/src/core/services/dsp.cpp
index 5d7956b51..93de78da1 100644
--- a/src/core/services/dsp.cpp
+++ b/src/core/services/dsp.cpp
@@ -28,7 +28,8 @@ namespace DSPCommands {
RegisterInterruptEvents = 0x00150082,
GetSemaphoreEventHandle = 0x00160000,
SetSemaphoreMask = 0x00170040,
- GetHeadphoneStatus = 0x001F0000
+ GetHeadphoneStatus = 0x001F0000,
+ ForceHeadphoneOut = 0x00200040,
};
}
@@ -42,6 +43,7 @@ namespace Result {
void DSPService::reset() {
totalEventCount = 0;
semaphoreMask = 0;
+ headphonesInserted = true;
semaphoreEvent = std::nullopt;
interrupt0 = std::nullopt;
@@ -60,6 +62,7 @@ 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;
@@ -210,7 +213,8 @@ void DSPService::getHeadphoneStatus(u32 messagePointer) {
mem.write32(messagePointer, IPC::responseHeader(0x1F, 2, 0));
mem.write32(messagePointer + 4, Result::Success);
- mem.write32(messagePointer + 8, Result::HeadphonesInserted); // This should be toggleable for shits and giggles
+ // This should be toggleable for shits and giggles
+ mem.write32(messagePointer + 8, headphonesInserted ? Result::HeadphonesInserted : Result::HeadphonesNotInserted);
}
void DSPService::getSemaphoreEventHandle(u32 messagePointer) {
@@ -278,6 +282,14 @@ 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;
diff --git a/src/core/services/fs.cpp b/src/core/services/fs.cpp
index e81db6cd3..54a4241dd 100644
--- a/src/core/services/fs.cpp
+++ b/src/core/services/fs.cpp
@@ -99,6 +99,11 @@ 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;
diff --git a/src/core/services/gsp_gpu.cpp b/src/core/services/gsp_gpu.cpp
index 8cf77a7e5..96364e736 100644
--- a/src/core/services/gsp_gpu.cpp
+++ b/src/core/services/gsp_gpu.cpp
@@ -14,6 +14,7 @@ namespace ServiceCommands {
WriteHwRegsWithMask = 0x00020084,
SetBufferSwap = 0x00050200,
FlushDataCache = 0x00080082,
+ InvalidateDataCache = 0x00090082,
SetLCDForceBlack = 0x000B0040,
TriggerCmdReqQueue = 0x000C0000,
ReleaseRight = 0x00170000,
@@ -21,7 +22,7 @@ namespace ServiceCommands {
SaveVramSysArea = 0x00190000,
RestoreVramSysArea = 0x001A0000,
SetInternalPriorities = 0x001E0080,
- StoreDataCache = 0x001F0082
+ StoreDataCache = 0x001F0082,
};
}
@@ -63,6 +64,7 @@ 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);
}
}
@@ -278,6 +280,16 @@ 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);
diff --git a/src/core/services/mcu/mcu_hwc.cpp b/src/core/services/mcu/mcu_hwc.cpp
index 2873adf5c..0e4e6ed35 100644
--- a/src/core/services/mcu/mcu_hwc.cpp
+++ b/src/core/services/mcu/mcu_hwc.cpp
@@ -1,10 +1,12 @@
+#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,
};
}
@@ -14,6 +16,7 @@ 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);
}
}
@@ -24,4 +27,12 @@ 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);
}
\ No newline at end of file
diff --git a/src/emulator.cpp b/src/emulator.cpp
index ac9007550..84b638f3f 100644
--- a/src/emulator.cpp
+++ b/src/emulator.cpp
@@ -428,6 +428,10 @@ RomFS::DumpingResult Emulator::dumpRomFS(const std::filesystem::path& path) {
}
void Emulator::setAudioEnabled(bool enable) {
+ // Don't enable audio if we didn't manage to find an audio device and initialize it properly, otherwise audio sync will break,
+ // because the emulator will expect the audio device to drain the sample buffer, but there's no audio device running...
+ enable = enable && audioDevice.isInitialized();
+
if (!enable) {
audioDevice.stop();
} else if (enable && romType != ROMType::None && running) {
diff --git a/src/panda_qt/config_window.cpp b/src/panda_qt/config_window.cpp
index 7f42acab9..519a4a227 100644
--- a/src/panda_qt/config_window.cpp
+++ b/src/panda_qt/config_window.cpp
@@ -12,7 +12,7 @@ ConfigWindow::ConfigWindow(ConfigCallback configCallback, MainWindowCallback win
// Set the window title of the main window appropriately if we enable showing the app version on the window
if (config.windowSettings.showAppVersion) {
- getMainWindow()->setWindowTitle("Alber v" PANDA3DS_VERSION);
+ getMainWindow()->setWindowTitle(tr("Alber v%1").arg(PANDA3DS_VERSION));
}
// Initialize the widget list and the widget container widgets
@@ -96,6 +96,9 @@ ConfigWindow::ConfigWindow(ConfigCallback configCallback, MainWindowCallback win
});
guiLayout->addRow(tr("Window icon"), iconSelect);
+ QComboBox* languageSelect = createLanguageSelect();
+ guiLayout->addRow(tr("Language"), languageSelect);
+
QCheckBox* showAppVersion = new QCheckBox(tr("Show version on window title"));
showAppVersion->setChecked(config.windowSettings.showAppVersion);
connect(showAppVersion, &QCheckBox::toggled, this, [&](bool checked) {
@@ -103,7 +106,7 @@ ConfigWindow::ConfigWindow(ConfigCallback configCallback, MainWindowCallback win
updateConfig();
// Update main window title
- getMainWindow()->setWindowTitle(checked ? "Alber v" PANDA3DS_VERSION : "Alber");
+ getMainWindow()->setWindowTitle(checked ? tr("Alber v%1").arg(PANDA3DS_VERSION) : tr("Alber"));
});
connectCheckbox(showAppVersion, config.windowSettings.showAppVersion);
guiLayout->addRow(showAppVersion);
diff --git a/src/panda_qt/main_window.cpp b/src/panda_qt/main_window.cpp
index 5af31d737..c30d23ca2 100644
--- a/src/panda_qt/main_window.cpp
+++ b/src/panda_qt/main_window.cpp
@@ -14,6 +14,9 @@
#include "version.hpp"
MainWindow::MainWindow(QApplication* app, QWidget* parent) : QMainWindow(parent), keyboardMappings(InputMappings::defaultKeyboardMappings()) {
+ emu = new Emulator();
+
+ loadTranslation();
setWindowTitle(tr("Alber"));
// Enable drop events for loading ROMs
@@ -28,8 +31,7 @@ MainWindow::MainWindow(QApplication* app, QWidget* parent) : QMainWindow(parent)
appRunning = true;
// Set our menu bar up
- menuBar = new QMenuBar(this);
- setMenuBar(menuBar);
+ menuBar = new QMenuBar(nullptr);
// Create menu bar menus
auto fileMenu = menuBar->addMenu(tr("File"));
@@ -74,8 +76,8 @@ MainWindow::MainWindow(QApplication* app, QWidget* parent) : QMainWindow(parent)
auto aboutAction = aboutMenu->addAction(tr("About Panda3DS"));
connect(aboutAction, &QAction::triggered, this, &MainWindow::showAboutMenu);
+ setMenuBar(menuBar);
- emu = new Emulator();
emu->setOutputSize(screen->surfaceWidth, screen->surfaceHeight);
// Set up misc objects
@@ -678,4 +680,4 @@ void MainWindow::setupControllerSensors(SDL_GameController* controller) {
if (haveAccelerometer) {
SDL_GameControllerSetSensorEnabled(controller, SDL_SENSOR_ACCEL, SDL_TRUE);
}
-}
+}
\ No newline at end of file
diff --git a/src/panda_qt/translations.cpp b/src/panda_qt/translations.cpp
new file mode 100644
index 000000000..bfadd5700
--- /dev/null
+++ b/src/panda_qt/translations.cpp
@@ -0,0 +1,94 @@
+#include
+#include
+#include
+#include
+
+#include "panda_qt/config_window.hpp"
+#include "panda_qt/main_window.hpp"
+
+void MainWindow::loadTranslation() {
+ // TODO: This should become a member variable when we allow changing language at runtime.
+ QTranslator* translator = nullptr;
+
+ // Fetch the .qm file for our language and load it
+ auto language = QString::fromStdString(emu->getConfig().frontendSettings.language);
+ const QString baseDir = QStringLiteral(":/translations");
+ const QString basePath = QStringLiteral("%1/%2.qm").arg(baseDir).arg(language);
+
+ if (QFile::exists(basePath)) {
+ if (translator != nullptr) {
+ qApp->removeTranslator(translator);
+ }
+
+ translator = new QTranslator(qApp);
+ if (!translator->load(basePath)) {
+ QMessageBox::warning(
+ nullptr, QStringLiteral("Translation Error"),
+ QStringLiteral("Failed to find load translation file for '%1':\n%2").arg(language).arg(basePath)
+ );
+ delete translator;
+ } else {
+ qApp->installTranslator(translator);
+ }
+ } else {
+ printf("Language file %s does not exist. Defaulting to English\n", basePath.toStdString().c_str());
+ }
+}
+
+struct LanguageInfo {
+ QString name; // Full name of the language (for example "English (US)")
+ const char* code; // ISO 639 language code (for example "en_us")
+
+ explicit LanguageInfo(const QString& name, const char* code) : name(name), code(code) {}
+};
+
+// List of languages in the order they should appear in the menu
+// Please keep this list mostly in alphabetical order.
+// Also, for Unicode characters in language names, use Unicode keycodes instead of writing out the name,
+// as some compilers/toolchains may not enjoy Unicode in source files.
+static std::array languages = {
+ LanguageInfo(QStringLiteral(u"English"), "en"), // English
+ LanguageInfo(QStringLiteral(u"\u0395\u03BB\u03BB\u03B7\u03BD\u03B9\u03BA\u03AC"), "el"), // Greek
+ LanguageInfo(QStringLiteral(u"Espa\u00F1ol"), "es"), // Spanish
+ LanguageInfo(QStringLiteral(u"Nederlands"), "nl"), // Dutch
+ LanguageInfo(QStringLiteral(u"Portugu\u00EAs (Brasil)"), "pt_br") // Portuguese (Brazilian)
+};
+
+QComboBox* ConfigWindow::createLanguageSelect() {
+ QComboBox* select = new QComboBox();
+
+ for (usize i = 0; i < languages.size(); i++) {
+ const auto& lang = languages[i];
+ select->addItem(lang.name);
+
+ if (config.frontendSettings.language == lang.code) {
+ select->setCurrentIndex(i);
+ }
+ }
+
+ connect(select, &QComboBox::currentIndexChanged, this, [&](int index) {
+ const QString baseDir = QStringLiteral(":/translations");
+ const QString basePath = QStringLiteral("%1/%2.qm").arg(baseDir).arg(languages[index].code);
+
+ if (QFile::exists(basePath)) {
+ config.frontendSettings.language = languages[index].code;
+ updateConfig();
+
+ QMessageBox messageBox(
+ QMessageBox::Icon::Information, tr("Language change successful"),
+ tr("Restart Panda3DS for the new language to be used.")
+ );
+
+ messageBox.exec();
+ } else {
+ QMessageBox messageBox(
+ QMessageBox::Icon::Warning, tr("Language change failed"),
+ tr("The language you selected is not included in Panda3DS. If you're seeing this, someone messed up the language UI code...")
+ );
+
+ messageBox.exec();
+ }
+ });
+
+ return select;
+}
diff --git a/src/pandroid/app/src/main/java/com/panda3ds/pandroid/app/GameActivity.java b/src/pandroid/app/src/main/java/com/panda3ds/pandroid/app/GameActivity.java
index 503684acc..c6764d382 100644
--- a/src/pandroid/app/src/main/java/com/panda3ds/pandroid/app/GameActivity.java
+++ b/src/pandroid/app/src/main/java/com/panda3ds/pandroid/app/GameActivity.java
@@ -152,6 +152,10 @@ protected void onPause() {
@Override
public boolean dispatchKeyEvent(KeyEvent event) {
+ if (event.getKeyCode() == KeyEvent.KEYCODE_VOLUME_UP || event.getKeyCode() == KeyEvent.KEYCODE_VOLUME_DOWN) {
+ return super.dispatchKeyEvent(event);
+ }
+
if ((!drawerFragment.isOpened()) && InputHandler.processKeyEvent(event)) {
return true;
}
diff --git a/third_party/LuaJIT b/third_party/LuaJIT
index 41edf0959..8bf7686d8 160000
--- a/third_party/LuaJIT
+++ b/third_party/LuaJIT
@@ -1 +1 @@
-Subproject commit 41edf0959b9504d36dd85f5f16893c004ea7d7ba
+Subproject commit 8bf7686d820f868eae1a522c481fee09c18c90b9
diff --git a/third_party/oaknut b/third_party/oaknut
index 94c726ce0..790374d7e 160000
--- a/third_party/oaknut
+++ b/third_party/oaknut
@@ -1 +1 @@
-Subproject commit 94c726ce0338b054eb8cb5ea91de8fe6c19f4392
+Subproject commit 790374d7e66257b1f8ed89d798e5dcfb5363af05