mirror of
https://github.com/wheremyfoodat/Panda3DS.git
synced 2025-04-07 22:55:40 +12:00
Merge branch 'master' into shader-decomp
This commit is contained in:
commit
2b82f8b332
29 changed files with 583 additions and 204 deletions
39
.github/gles.patch
vendored
39
.github/gles.patch
vendored
|
@ -21,7 +21,7 @@ index 990e2f80..2e7842ac 100644
|
||||||
|
|
||||||
void main() {
|
void main() {
|
||||||
diff --git a/src/host_shaders/opengl_fragment_shader.frag b/src/host_shaders/opengl_fragment_shader.frag
|
diff --git a/src/host_shaders/opengl_fragment_shader.frag b/src/host_shaders/opengl_fragment_shader.frag
|
||||||
index b9f9fe4c..f1cf286f 100644
|
index 9f07df0b..96a35afa 100644
|
||||||
--- a/src/host_shaders/opengl_fragment_shader.frag
|
--- a/src/host_shaders/opengl_fragment_shader.frag
|
||||||
+++ b/src/host_shaders/opengl_fragment_shader.frag
|
+++ b/src/host_shaders/opengl_fragment_shader.frag
|
||||||
@@ -1,4 +1,5 @@
|
@@ -1,4 +1,5 @@
|
||||||
|
@ -31,6 +31,17 @@ index b9f9fe4c..f1cf286f 100644
|
||||||
|
|
||||||
in vec4 v_quaternion;
|
in vec4 v_quaternion;
|
||||||
in vec4 v_colour;
|
in vec4 v_colour;
|
||||||
|
@@ -41,8 +42,8 @@ vec3 normal;
|
||||||
|
const uint samplerEnabledBitfields[2] = uint[2](0x7170e645u, 0x7f013fefu);
|
||||||
|
|
||||||
|
bool isSamplerEnabled(uint environment_id, uint lut_id) {
|
||||||
|
- uint index = 7 * environment_id + lut_id;
|
||||||
|
- uint arrayIndex = (index >> 5);
|
||||||
|
+ uint index = 7u * environment_id + lut_id;
|
||||||
|
+ uint arrayIndex = (index >> 5u);
|
||||||
|
return (samplerEnabledBitfields[arrayIndex] & (1u << (index & 31u))) != 0u;
|
||||||
|
}
|
||||||
|
|
||||||
@@ -166,11 +167,17 @@ float lutLookup(uint lut, int index) {
|
@@ -166,11 +167,17 @@ float lutLookup(uint lut, int index) {
|
||||||
return texelFetch(u_tex_luts, ivec2(index, int(lut)), 0).r;
|
return texelFetch(u_tex_luts, ivec2(index, int(lut)), 0).r;
|
||||||
}
|
}
|
||||||
|
@ -50,6 +61,15 @@ index b9f9fe4c..f1cf286f 100644
|
||||||
}
|
}
|
||||||
|
|
||||||
// Convert an arbitrary-width floating point literal to an f32
|
// Convert an arbitrary-width floating point literal to an f32
|
||||||
|
@@ -201,7 +208,7 @@ float lightLutLookup(uint environment_id, uint lut_id, uint light_id, vec3 light
|
||||||
|
// These are the spotlight attenuation LUTs
|
||||||
|
bit_in_config1 = 8 + int(light_id & 7u);
|
||||||
|
lut_index = 8u + light_id;
|
||||||
|
- } else if (lut_id <= 6) {
|
||||||
|
+ } else if (lut_id <= 6u) {
|
||||||
|
bit_in_config1 = 16 + int(lut_id);
|
||||||
|
lut_index = lut_id;
|
||||||
|
} else {
|
||||||
@@ -210,16 +217,16 @@ float lightLutLookup(uint environment_id, uint lut_id, uint light_id, vec3 light
|
@@ -210,16 +217,16 @@ float lightLutLookup(uint environment_id, uint lut_id, uint light_id, vec3 light
|
||||||
|
|
||||||
bool current_sampler_enabled = isSamplerEnabled(environment_id, lut_id); // 7 luts per environment
|
bool current_sampler_enabled = isSamplerEnabled(environment_id, lut_id); // 7 luts per environment
|
||||||
|
@ -70,19 +90,16 @@ index b9f9fe4c..f1cf286f 100644
|
||||||
switch (input_id) {
|
switch (input_id) {
|
||||||
case 0u: {
|
case 0u: {
|
||||||
delta = dot(normal, normalize(half_vector));
|
delta = dot(normal, normalize(half_vector));
|
||||||
@@ -241,11 +248,11 @@ float lightLutLookup(uint environment_id, uint lut_id, uint light_id, vec3 light
|
@@ -243,9 +250,9 @@ float lightLutLookup(uint environment_id, uint lut_id, uint light_id, vec3 light
|
||||||
int GPUREG_LIGHTi_SPOTDIR_LOW = int(readPicaReg(0x0146u + (light_id << 4u)));
|
|
||||||
int GPUREG_LIGHTi_SPOTDIR_HIGH = int(readPicaReg(0x0147u + (light_id << 4u)));
|
|
||||||
|
|
||||||
- // Sign extend them. Normally bitfieldExtract would do that but it's missing on some versions
|
// Sign extend them. Normally bitfieldExtract would do that but it's missing on some versions
|
||||||
+ // Sign extend them. Normally bitfieldExtractCompat would do that but it's missing on some versions
|
|
||||||
// of GLSL so we do it manually
|
// of GLSL so we do it manually
|
||||||
- int se_x = bitfieldExtract(GPUREG_LIGHTi_SPOTDIR_LOW, 0, 13);
|
- int se_x = bitfieldExtract(GPUREG_LIGHTi_SPOTDIR_LOW, 0, 13);
|
||||||
- int se_y = bitfieldExtract(GPUREG_LIGHTi_SPOTDIR_LOW, 16, 13);
|
- int se_y = bitfieldExtract(GPUREG_LIGHTi_SPOTDIR_LOW, 16, 13);
|
||||||
- int se_z = bitfieldExtract(GPUREG_LIGHTi_SPOTDIR_HIGH, 0, 13);
|
- int se_z = bitfieldExtract(GPUREG_LIGHTi_SPOTDIR_HIGH, 0, 13);
|
||||||
+ int se_x = bitfieldExtractCompat(GPUREG_LIGHTi_SPOTDIR_LOW, 0, 13);
|
+ int se_x = int(bitfieldExtractCompat(uint(GPUREG_LIGHTi_SPOTDIR_LOW), 0, 13));
|
||||||
+ int se_y = bitfieldExtractCompat(GPUREG_LIGHTi_SPOTDIR_LOW, 16, 13);
|
+ int se_y = int(bitfieldExtractCompat(uint(GPUREG_LIGHTi_SPOTDIR_LOW), 16, 13));
|
||||||
+ int se_z = bitfieldExtractCompat(GPUREG_LIGHTi_SPOTDIR_HIGH, 0, 13);
|
+ int se_z = int(bitfieldExtractCompat(uint(GPUREG_LIGHTi_SPOTDIR_HIGH), 0, 13));
|
||||||
|
|
||||||
if ((se_x & 0x1000) == 0x1000) se_x |= 0xffffe000;
|
if ((se_x & 0x1000) == 0x1000) se_x |= 0xffffe000;
|
||||||
if ((se_y & 0x1000) == 0x1000) se_y |= 0xffffe000;
|
if ((se_y & 0x1000) == 0x1000) se_y |= 0xffffe000;
|
||||||
|
@ -225,10 +242,10 @@ index 057f9a88..dc735ced 100644
|
||||||
v_quaternion = a_quaternion;
|
v_quaternion = a_quaternion;
|
||||||
}
|
}
|
||||||
diff --git a/third_party/opengl/opengl.hpp b/third_party/opengl/opengl.hpp
|
diff --git a/third_party/opengl/opengl.hpp b/third_party/opengl/opengl.hpp
|
||||||
index 4a08650a..21af37e3 100644
|
index 607815fa..cbfcc096 100644
|
||||||
--- a/third_party/opengl/opengl.hpp
|
--- a/third_party/opengl/opengl.hpp
|
||||||
+++ b/third_party/opengl/opengl.hpp
|
+++ b/third_party/opengl/opengl.hpp
|
||||||
@@ -583,22 +583,22 @@ namespace OpenGL {
|
@@ -602,22 +602,22 @@ namespace OpenGL {
|
||||||
static void disableScissor() { glDisable(GL_SCISSOR_TEST); }
|
static void disableScissor() { glDisable(GL_SCISSOR_TEST); }
|
||||||
static void enableBlend() { glEnable(GL_BLEND); }
|
static void enableBlend() { glEnable(GL_BLEND); }
|
||||||
static void disableBlend() { glDisable(GL_BLEND); }
|
static void disableBlend() { glDisable(GL_BLEND); }
|
||||||
|
|
2
.github/workflows/HTTP_Build.yml
vendored
2
.github/workflows/HTTP_Build.yml
vendored
|
@ -19,7 +19,7 @@ jobs:
|
||||||
runs-on: ubuntu-latest
|
runs-on: ubuntu-latest
|
||||||
|
|
||||||
steps:
|
steps:
|
||||||
- uses: actions/checkout@v2
|
- uses: actions/checkout@v4
|
||||||
- name: Fetch submodules
|
- name: Fetch submodules
|
||||||
run: git submodule update --init --recursive
|
run: git submodule update --init --recursive
|
||||||
|
|
||||||
|
|
8
.github/workflows/Hydra_Build.yml
vendored
8
.github/workflows/Hydra_Build.yml
vendored
|
@ -15,7 +15,7 @@ jobs:
|
||||||
runs-on: windows-latest
|
runs-on: windows-latest
|
||||||
|
|
||||||
steps:
|
steps:
|
||||||
- uses: actions/checkout@v2
|
- uses: actions/checkout@v4
|
||||||
- name: Fetch submodules
|
- name: Fetch submodules
|
||||||
run: git submodule update --init --recursive
|
run: git submodule update --init --recursive
|
||||||
|
|
||||||
|
@ -58,7 +58,7 @@ jobs:
|
||||||
runs-on: macos-13
|
runs-on: macos-13
|
||||||
|
|
||||||
steps:
|
steps:
|
||||||
- uses: actions/checkout@v2
|
- uses: actions/checkout@v4
|
||||||
- name: Fetch submodules
|
- name: Fetch submodules
|
||||||
run: git submodule update --init --recursive
|
run: git submodule update --init --recursive
|
||||||
|
|
||||||
|
@ -101,7 +101,7 @@ jobs:
|
||||||
runs-on: ubuntu-latest
|
runs-on: ubuntu-latest
|
||||||
|
|
||||||
steps:
|
steps:
|
||||||
- uses: actions/checkout@v2
|
- uses: actions/checkout@v4
|
||||||
- name: Fetch submodules
|
- name: Fetch submodules
|
||||||
run: git submodule update --init --recursive
|
run: git submodule update --init --recursive
|
||||||
|
|
||||||
|
@ -154,7 +154,7 @@ jobs:
|
||||||
runs-on: ubuntu-latest
|
runs-on: ubuntu-latest
|
||||||
|
|
||||||
steps:
|
steps:
|
||||||
- uses: actions/checkout@v2
|
- uses: actions/checkout@v4
|
||||||
- name: Fetch submodules
|
- name: Fetch submodules
|
||||||
run: git submodule update --init --recursive
|
run: git submodule update --init --recursive
|
||||||
|
|
||||||
|
|
4
.github/workflows/Linux_AppImage_Build.yml
vendored
4
.github/workflows/Linux_AppImage_Build.yml
vendored
|
@ -19,7 +19,7 @@ jobs:
|
||||||
runs-on: ubuntu-latest
|
runs-on: ubuntu-latest
|
||||||
|
|
||||||
steps:
|
steps:
|
||||||
- uses: actions/checkout@v2
|
- uses: actions/checkout@v4
|
||||||
- name: Fetch submodules
|
- name: Fetch submodules
|
||||||
run: git submodule update --init --recursive
|
run: git submodule update --init --recursive
|
||||||
|
|
||||||
|
@ -52,7 +52,7 @@ jobs:
|
||||||
run: ./.github/linux-appimage.sh
|
run: ./.github/linux-appimage.sh
|
||||||
|
|
||||||
- name: Upload executable
|
- name: Upload executable
|
||||||
uses: actions/upload-artifact@v2
|
uses: actions/upload-artifact@v4
|
||||||
with:
|
with:
|
||||||
name: Linux executable
|
name: Linux executable
|
||||||
path: './Alber-x86_64.AppImage'
|
path: './Alber-x86_64.AppImage'
|
||||||
|
|
4
.github/workflows/Linux_Build.yml
vendored
4
.github/workflows/Linux_Build.yml
vendored
|
@ -19,7 +19,7 @@ jobs:
|
||||||
runs-on: ubuntu-latest
|
runs-on: ubuntu-latest
|
||||||
|
|
||||||
steps:
|
steps:
|
||||||
- uses: actions/checkout@v2
|
- uses: actions/checkout@v4
|
||||||
- name: Fetch submodules
|
- name: Fetch submodules
|
||||||
run: git submodule update --init --recursive
|
run: git submodule update --init --recursive
|
||||||
|
|
||||||
|
@ -49,7 +49,7 @@ jobs:
|
||||||
run: cmake --build ${{github.workspace}}/build --config ${{env.BUILD_TYPE}}
|
run: cmake --build ${{github.workspace}}/build --config ${{env.BUILD_TYPE}}
|
||||||
|
|
||||||
- name: Upload executable
|
- name: Upload executable
|
||||||
uses: actions/upload-artifact@v2
|
uses: actions/upload-artifact@v4
|
||||||
with:
|
with:
|
||||||
name: Linux executable
|
name: Linux executable
|
||||||
path: './build/Alber'
|
path: './build/Alber'
|
||||||
|
|
6
.github/workflows/MacOS_Build.yml
vendored
6
.github/workflows/MacOS_Build.yml
vendored
|
@ -19,7 +19,7 @@ jobs:
|
||||||
runs-on: macos-13
|
runs-on: macos-13
|
||||||
|
|
||||||
steps:
|
steps:
|
||||||
- uses: actions/checkout@v2
|
- uses: actions/checkout@v4
|
||||||
- name: Fetch submodules
|
- name: Fetch submodules
|
||||||
run: git submodule update --init --recursive
|
run: git submodule update --init --recursive
|
||||||
|
|
||||||
|
@ -40,7 +40,7 @@ jobs:
|
||||||
run: cmake --build ${{github.workspace}}/build --config ${{env.BUILD_TYPE}}
|
run: cmake --build ${{github.workspace}}/build --config ${{env.BUILD_TYPE}}
|
||||||
|
|
||||||
- name: Install bundle dependencies
|
- name: Install bundle dependencies
|
||||||
run: brew install --overwrite python@3.12 && brew install dylibbundler imagemagick
|
run: brew install dylibbundler imagemagick
|
||||||
|
|
||||||
- name: Run bundle script
|
- name: Run bundle script
|
||||||
run: ./.github/mac-bundle.sh
|
run: ./.github/mac-bundle.sh
|
||||||
|
@ -52,7 +52,7 @@ jobs:
|
||||||
run: zip -r Alber Alber.app
|
run: zip -r Alber Alber.app
|
||||||
|
|
||||||
- name: Upload MacOS App
|
- name: Upload MacOS App
|
||||||
uses: actions/upload-artifact@v2
|
uses: actions/upload-artifact@v4
|
||||||
with:
|
with:
|
||||||
name: MacOS Alber App Bundle
|
name: MacOS Alber App Bundle
|
||||||
path: 'Alber.zip'
|
path: 'Alber.zip'
|
||||||
|
|
14
.github/workflows/Qt_Build.yml
vendored
14
.github/workflows/Qt_Build.yml
vendored
|
@ -15,7 +15,7 @@ jobs:
|
||||||
runs-on: windows-latest
|
runs-on: windows-latest
|
||||||
|
|
||||||
steps:
|
steps:
|
||||||
- uses: actions/checkout@v2
|
- uses: actions/checkout@v4
|
||||||
- name: Fetch submodules
|
- name: Fetch submodules
|
||||||
run: git submodule update --init --recursive
|
run: git submodule update --init --recursive
|
||||||
|
|
||||||
|
@ -45,7 +45,7 @@ jobs:
|
||||||
windeployqt --dir upload upload/Alber.exe
|
windeployqt --dir upload upload/Alber.exe
|
||||||
|
|
||||||
- name: Upload executable
|
- name: Upload executable
|
||||||
uses: actions/upload-artifact@v2
|
uses: actions/upload-artifact@v4
|
||||||
with:
|
with:
|
||||||
name: Windows executable
|
name: Windows executable
|
||||||
path: upload
|
path: upload
|
||||||
|
@ -54,7 +54,7 @@ jobs:
|
||||||
runs-on: macos-13
|
runs-on: macos-13
|
||||||
|
|
||||||
steps:
|
steps:
|
||||||
- uses: actions/checkout@v2
|
- uses: actions/checkout@v4
|
||||||
- name: Fetch submodules
|
- name: Fetch submodules
|
||||||
run: git submodule update --init --recursive
|
run: git submodule update --init --recursive
|
||||||
|
|
||||||
|
@ -67,7 +67,7 @@ jobs:
|
||||||
|
|
||||||
- name: Install bundle dependencies
|
- name: Install bundle dependencies
|
||||||
run: |
|
run: |
|
||||||
brew install --overwrite python@3.12 && brew install dylibbundler imagemagick
|
brew install dylibbundler imagemagick
|
||||||
|
|
||||||
- name: Install qt
|
- name: Install qt
|
||||||
run: brew install qt && which macdeployqt
|
run: brew install qt && which macdeployqt
|
||||||
|
@ -90,7 +90,7 @@ jobs:
|
||||||
run: zip -r Alber Alber.app
|
run: zip -r Alber Alber.app
|
||||||
|
|
||||||
- name: Upload MacOS App
|
- name: Upload MacOS App
|
||||||
uses: actions/upload-artifact@v2
|
uses: actions/upload-artifact@v4
|
||||||
with:
|
with:
|
||||||
name: MacOS Alber App Bundle
|
name: MacOS Alber App Bundle
|
||||||
path: 'Alber.zip'
|
path: 'Alber.zip'
|
||||||
|
@ -99,7 +99,7 @@ jobs:
|
||||||
runs-on: ubuntu-latest
|
runs-on: ubuntu-latest
|
||||||
|
|
||||||
steps:
|
steps:
|
||||||
- uses: actions/checkout@v2
|
- uses: actions/checkout@v4
|
||||||
- name: Fetch submodules
|
- name: Fetch submodules
|
||||||
run: git submodule update --init --recursive
|
run: git submodule update --init --recursive
|
||||||
|
|
||||||
|
@ -135,7 +135,7 @@ jobs:
|
||||||
./.github/linux-appimage-qt.sh
|
./.github/linux-appimage-qt.sh
|
||||||
|
|
||||||
- name: Upload executable
|
- name: Upload executable
|
||||||
uses: actions/upload-artifact@v2
|
uses: actions/upload-artifact@v4
|
||||||
with:
|
with:
|
||||||
name: Linux executable
|
name: Linux executable
|
||||||
path: './Alber-x86_64.AppImage'
|
path: './Alber-x86_64.AppImage'
|
||||||
|
|
4
.github/workflows/Windows_Build.yml
vendored
4
.github/workflows/Windows_Build.yml
vendored
|
@ -19,7 +19,7 @@ jobs:
|
||||||
runs-on: windows-latest
|
runs-on: windows-latest
|
||||||
|
|
||||||
steps:
|
steps:
|
||||||
- uses: actions/checkout@v2
|
- uses: actions/checkout@v4
|
||||||
- name: Fetch submodules
|
- name: Fetch submodules
|
||||||
run: git submodule update --init --recursive
|
run: git submodule update --init --recursive
|
||||||
|
|
||||||
|
@ -40,7 +40,7 @@ jobs:
|
||||||
run: cmake --build ${{github.workspace}}/build --config ${{env.BUILD_TYPE}}
|
run: cmake --build ${{github.workspace}}/build --config ${{env.BUILD_TYPE}}
|
||||||
|
|
||||||
- name: Upload executable
|
- name: Upload executable
|
||||||
uses: actions/upload-artifact@v2
|
uses: actions/upload-artifact@v4
|
||||||
with:
|
with:
|
||||||
name: Windows executable
|
name: Windows executable
|
||||||
path: './build/${{ env.BUILD_TYPE }}/Alber.exe'
|
path: './build/${{ env.BUILD_TYPE }}/Alber.exe'
|
||||||
|
|
3
.gitmodules
vendored
3
.gitmodules
vendored
|
@ -79,3 +79,6 @@
|
||||||
[submodule "third_party/fmt"]
|
[submodule "third_party/fmt"]
|
||||||
path = third_party/fmt
|
path = third_party/fmt
|
||||||
url = https://github.com/fmtlib/fmt
|
url = https://github.com/fmtlib/fmt
|
||||||
|
[submodule "third_party/fdk-aac"]
|
||||||
|
path = third_party/fdk-aac
|
||||||
|
url = https://github.com/Panda3DS-emu/fdk-aac/
|
||||||
|
|
|
@ -33,6 +33,12 @@ if(CMAKE_CXX_COMPILER_ID STREQUAL "GNU")
|
||||||
set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -Wno-interference-size")
|
set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -Wno-interference-size")
|
||||||
endif()
|
endif()
|
||||||
|
|
||||||
|
if(ANDROID)
|
||||||
|
set(DEFAULT_OPENGL_PROFILE OpenGLES)
|
||||||
|
else()
|
||||||
|
set(DEFAULT_OPENGL_PROFILE OpenGL)
|
||||||
|
endif()
|
||||||
|
|
||||||
option(DISABLE_PANIC_DEV "Make a build with fewer and less intrusive asserts" ON)
|
option(DISABLE_PANIC_DEV "Make a build with fewer and less intrusive asserts" ON)
|
||||||
option(GPU_DEBUG_INFO "Enable additional GPU debugging info" OFF)
|
option(GPU_DEBUG_INFO "Enable additional GPU debugging info" OFF)
|
||||||
option(ENABLE_OPENGL "Enable OpenGL rendering backend" ON)
|
option(ENABLE_OPENGL "Enable OpenGL rendering backend" ON)
|
||||||
|
@ -49,6 +55,14 @@ option(BUILD_HYDRA_CORE "Build a Hydra core" OFF)
|
||||||
option(BUILD_LIBRETRO_CORE "Build a Libretro 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(ENABLE_RENDERDOC_API "Build with support for Renderdoc's capture API for graphics debugging" ON)
|
||||||
|
|
||||||
|
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)
|
||||||
|
|
||||||
|
if(ENABLE_OPENGL AND (OPENGL_PROFILE STREQUAL "OpenGLES"))
|
||||||
|
message(STATUS "Building with OpenGLES support")
|
||||||
|
add_compile_definitions(USING_GLES)
|
||||||
|
endif()
|
||||||
|
|
||||||
if(BUILD_HYDRA_CORE)
|
if(BUILD_HYDRA_CORE)
|
||||||
set(CMAKE_POSITION_INDEPENDENT_CODE ON)
|
set(CMAKE_POSITION_INDEPENDENT_CODE ON)
|
||||||
endif()
|
endif()
|
||||||
|
@ -213,6 +227,7 @@ else()
|
||||||
endif()
|
endif()
|
||||||
|
|
||||||
add_subdirectory(third_party/teakra EXCLUDE_FROM_ALL)
|
add_subdirectory(third_party/teakra EXCLUDE_FROM_ALL)
|
||||||
|
add_subdirectory(third_party/fdk-aac)
|
||||||
|
|
||||||
set(CAPSTONE_ARCHITECTURE_DEFAULT OFF)
|
set(CAPSTONE_ARCHITECTURE_DEFAULT OFF)
|
||||||
set(CAPSTONE_ARM_SUPPORT ON)
|
set(CAPSTONE_ARM_SUPPORT ON)
|
||||||
|
@ -263,7 +278,7 @@ set(APPLET_SOURCE_FILES src/core/applets/applet.cpp src/core/applets/mii_selecto
|
||||||
src/core/applets/error_applet.cpp
|
src/core/applets/error_applet.cpp
|
||||||
)
|
)
|
||||||
set(AUDIO_SOURCE_FILES src/core/audio/dsp_core.cpp src/core/audio/null_core.cpp src/core/audio/teakra_core.cpp
|
set(AUDIO_SOURCE_FILES src/core/audio/dsp_core.cpp src/core/audio/null_core.cpp src/core/audio/teakra_core.cpp
|
||||||
src/core/audio/miniaudio_device.cpp src/core/audio/hle_core.cpp
|
src/core/audio/miniaudio_device.cpp src/core/audio/hle_core.cpp src/core/audio/aac_decoder.cpp
|
||||||
)
|
)
|
||||||
set(RENDERER_SW_SOURCE_FILES src/core/renderer_sw/renderer_sw.cpp)
|
set(RENDERER_SW_SOURCE_FILES src/core/renderer_sw/renderer_sw.cpp)
|
||||||
|
|
||||||
|
@ -303,7 +318,7 @@ set(HEADER_FILES include/emulator.hpp include/helpers.hpp include/termcolor.hpp
|
||||||
include/audio/hle_core.hpp include/capstone.hpp include/audio/aac.hpp include/PICA/pica_frag_config.hpp
|
include/audio/hle_core.hpp include/capstone.hpp include/audio/aac.hpp include/PICA/pica_frag_config.hpp
|
||||||
include/PICA/pica_frag_uniforms.hpp include/PICA/shader_gen_types.hpp include/PICA/shader_decompiler.hpp
|
include/PICA/pica_frag_uniforms.hpp include/PICA/shader_gen_types.hpp include/PICA/shader_decompiler.hpp
|
||||||
include/PICA/pica_vert_config.hpp include/sdl_sensors.hpp include/PICA/draw_acceleration.hpp include/renderdoc.hpp
|
include/PICA/pica_vert_config.hpp include/sdl_sensors.hpp include/PICA/draw_acceleration.hpp include/renderdoc.hpp
|
||||||
include/align.hpp
|
include/align.hpp include/audio/aac_decoder.hpp
|
||||||
)
|
)
|
||||||
|
|
||||||
cmrc_add_resource_library(
|
cmrc_add_resource_library(
|
||||||
|
@ -468,7 +483,7 @@ set(ALL_SOURCES ${SOURCE_FILES} ${FS_SOURCE_FILES} ${CRYPTO_SOURCE_FILES} ${KERN
|
||||||
${AUDIO_SOURCE_FILES} ${HEADER_FILES} ${FRONTEND_HEADER_FILES})
|
${AUDIO_SOURCE_FILES} ${HEADER_FILES} ${FRONTEND_HEADER_FILES})
|
||||||
target_sources(AlberCore PRIVATE ${ALL_SOURCES})
|
target_sources(AlberCore PRIVATE ${ALL_SOURCES})
|
||||||
|
|
||||||
target_link_libraries(AlberCore PRIVATE dynarmic cryptopp glad resources_console_fonts teakra)
|
target_link_libraries(AlberCore PRIVATE dynarmic cryptopp glad resources_console_fonts teakra fdk-aac)
|
||||||
target_link_libraries(AlberCore PUBLIC glad capstone fmt::fmt)
|
target_link_libraries(AlberCore PUBLIC glad capstone fmt::fmt)
|
||||||
|
|
||||||
if(ENABLE_DISCORD_RPC AND NOT ANDROID)
|
if(ENABLE_DISCORD_RPC AND NOT ANDROID)
|
||||||
|
@ -509,6 +524,9 @@ if(NOT BUILD_HYDRA_CORE AND NOT BUILD_LIBRETRO_CORE)
|
||||||
message(FATAL_ERROR "Qt frontend requires OpenGL")
|
message(FATAL_ERROR "Qt frontend requires OpenGL")
|
||||||
endif()
|
endif()
|
||||||
|
|
||||||
|
option(GENERATE_QT_TRANSLATION "Generate Qt translation file" OFF)
|
||||||
|
set(QT_LANGUAGES docs/translations)
|
||||||
|
|
||||||
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
|
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/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
|
||||||
|
@ -545,6 +563,17 @@ if(NOT BUILD_HYDRA_CORE AND NOT BUILD_LIBRETRO_CORE)
|
||||||
endif()
|
endif()
|
||||||
endif()
|
endif()
|
||||||
|
|
||||||
|
# Generates an en.ts file for translations
|
||||||
|
# To update the file, use cmake --build --target Alber_lupdate
|
||||||
|
if(GENERATE_QT_TRANSLATION)
|
||||||
|
find_package(Qt6 REQUIRED COMPONENTS LinguistTools)
|
||||||
|
qt_add_lupdate(Alber TS_FILES ${QT_LANGUAGES}/en.ts
|
||||||
|
SOURCES ${FRONTEND_SOURCE_FILES}
|
||||||
|
INCLUDE_DIRECTORIES ${FRONTEND_HEADER_FILES}
|
||||||
|
NO_GLOBAL_TARGET
|
||||||
|
)
|
||||||
|
endif()
|
||||||
|
|
||||||
qt_add_resources(AlberCore "app_images"
|
qt_add_resources(AlberCore "app_images"
|
||||||
PREFIX "/"
|
PREFIX "/"
|
||||||
FILES
|
FILES
|
||||||
|
|
|
@ -54,6 +54,15 @@ namespace Audio::AAC {
|
||||||
u32_le sampleCount;
|
u32_le sampleCount;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
struct DecodeRequest {
|
||||||
|
u32_le address; // Address of input AAC stream
|
||||||
|
u32_le size; // Size of input AAC stream
|
||||||
|
u32_le destAddrLeft; // Output address for left channel samples
|
||||||
|
u32_le destAddrRight; // Output address for right channel samples
|
||||||
|
u32_le unknown1;
|
||||||
|
u32_le unknown2;
|
||||||
|
};
|
||||||
|
|
||||||
struct Message {
|
struct Message {
|
||||||
u16_le mode = Mode::None; // Encode or decode AAC?
|
u16_le mode = Mode::None; // Encode or decode AAC?
|
||||||
u16_le command = Command::Init;
|
u16_le command = Command::Init;
|
||||||
|
@ -62,7 +71,9 @@ namespace Audio::AAC {
|
||||||
// Info on the AAC request
|
// Info on the AAC request
|
||||||
union {
|
union {
|
||||||
std::array<u8, 24> commandData{};
|
std::array<u8, 24> commandData{};
|
||||||
|
|
||||||
DecodeResponse decodeResponse;
|
DecodeResponse decodeResponse;
|
||||||
|
DecodeRequest decodeRequest;
|
||||||
};
|
};
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
24
include/audio/aac_decoder.hpp
Normal file
24
include/audio/aac_decoder.hpp
Normal file
|
@ -0,0 +1,24 @@
|
||||||
|
#pragma once
|
||||||
|
#include <functional>
|
||||||
|
|
||||||
|
#include "audio/aac.hpp"
|
||||||
|
#include "helpers.hpp"
|
||||||
|
|
||||||
|
struct AAC_DECODER_INSTANCE;
|
||||||
|
|
||||||
|
namespace Audio::AAC {
|
||||||
|
class Decoder {
|
||||||
|
using DecoderHandle = AAC_DECODER_INSTANCE*;
|
||||||
|
using PaddrCallback = std::function<u8*(u32)>;
|
||||||
|
|
||||||
|
DecoderHandle decoderHandle = nullptr;
|
||||||
|
|
||||||
|
bool isInitialized() { return decoderHandle != nullptr; }
|
||||||
|
void initialize();
|
||||||
|
|
||||||
|
public:
|
||||||
|
// Decode function. Takes in a reference to the AAC response & request, and a callback for paddr -> pointer conversions
|
||||||
|
void decode(AAC::Message& response, const AAC::Message& request, PaddrCallback paddrCallback);
|
||||||
|
~Decoder();
|
||||||
|
};
|
||||||
|
} // namespace Audio::AAC
|
|
@ -324,8 +324,8 @@ namespace Audio::HLE {
|
||||||
BitField<15, 1, u32> outputBufferCountDirty;
|
BitField<15, 1, u32> outputBufferCountDirty;
|
||||||
BitField<16, 1, u32> masterVolumeDirty;
|
BitField<16, 1, u32> masterVolumeDirty;
|
||||||
|
|
||||||
BitField<24, 1, u32> auxReturnVolume0Dirty;
|
BitField<24, 1, u32> auxVolume0Dirty;
|
||||||
BitField<25, 1, u32> auxReturnVolume1Dirty;
|
BitField<25, 1, u32> auxVolume1Dirty;
|
||||||
BitField<26, 1, u32> outputFormatDirty;
|
BitField<26, 1, u32> outputFormatDirty;
|
||||||
BitField<27, 1, u32> clippingModeDirty;
|
BitField<27, 1, u32> clippingModeDirty;
|
||||||
BitField<28, 1, u32> headphonesConnectedDirty;
|
BitField<28, 1, u32> headphonesConnectedDirty;
|
||||||
|
@ -337,7 +337,7 @@ namespace Audio::HLE {
|
||||||
/// The DSP has three intermediate audio mixers. This controls the volume level (0.0-1.0) for
|
/// The DSP has three intermediate audio mixers. This controls the volume level (0.0-1.0) for
|
||||||
/// each at the final mixer.
|
/// each at the final mixer.
|
||||||
float_le masterVolume;
|
float_le masterVolume;
|
||||||
std::array<float_le, 2> auxReturnVolume;
|
std::array<float_le, 2> auxVolumes;
|
||||||
|
|
||||||
u16_le outputBufferCount;
|
u16_le outputBufferCount;
|
||||||
u16 pad1[2];
|
u16 pad1[2];
|
||||||
|
@ -422,7 +422,7 @@ namespace Audio::HLE {
|
||||||
|
|
||||||
struct DspStatus {
|
struct DspStatus {
|
||||||
u16_le unknown;
|
u16_le unknown;
|
||||||
u16_le dropped_frames;
|
u16_le droppedFrames;
|
||||||
u16 pad0[0xE];
|
u16 pad0[0xE];
|
||||||
};
|
};
|
||||||
ASSERT_DSP_STRUCT(DspStatus, 32);
|
ASSERT_DSP_STRUCT(DspStatus, 32);
|
||||||
|
|
|
@ -2,10 +2,12 @@
|
||||||
#include <array>
|
#include <array>
|
||||||
#include <cassert>
|
#include <cassert>
|
||||||
#include <deque>
|
#include <deque>
|
||||||
|
#include <memory>
|
||||||
#include <queue>
|
#include <queue>
|
||||||
#include <vector>
|
#include <vector>
|
||||||
|
|
||||||
#include "audio/aac.hpp"
|
#include "audio/aac.hpp"
|
||||||
|
#include "audio/aac_decoder.hpp"
|
||||||
#include "audio/dsp_core.hpp"
|
#include "audio/dsp_core.hpp"
|
||||||
#include "audio/dsp_shared_mem.hpp"
|
#include "audio/dsp_shared_mem.hpp"
|
||||||
#include "memory.hpp"
|
#include "memory.hpp"
|
||||||
|
@ -93,8 +95,7 @@ namespace Audio {
|
||||||
DSPSource() { reset(); }
|
DSPSource() { reset(); }
|
||||||
};
|
};
|
||||||
|
|
||||||
class HLE_DSP : public DSPCore {
|
class DSPMixer {
|
||||||
// The audio frame types are public in case we want to use them for unit tests
|
|
||||||
public:
|
public:
|
||||||
template <typename T, usize channelCount = 1>
|
template <typename T, usize channelCount = 1>
|
||||||
using Sample = std::array<T, channelCount>;
|
using Sample = std::array<T, channelCount>;
|
||||||
|
@ -111,6 +112,43 @@ namespace Audio {
|
||||||
template <typename T>
|
template <typename T>
|
||||||
using QuadFrame = Frame<T, 4>;
|
using QuadFrame = Frame<T, 4>;
|
||||||
|
|
||||||
|
private:
|
||||||
|
using ChannelFormat = HLE::DspConfiguration::OutputFormat;
|
||||||
|
// The audio from each DSP voice is converted to quadraphonic and then fed into 3 intermediate mixing stages
|
||||||
|
// Two of these intermediate mixers (second and third) are used for effects, including custom effects done on the CPU
|
||||||
|
static constexpr usize mixerStageCount = 3;
|
||||||
|
|
||||||
|
public:
|
||||||
|
ChannelFormat channelFormat = ChannelFormat::Stereo;
|
||||||
|
std::array<float, mixerStageCount> volumes;
|
||||||
|
std::array<bool, 2> enableAuxStages;
|
||||||
|
|
||||||
|
void reset() {
|
||||||
|
channelFormat = ChannelFormat::Stereo;
|
||||||
|
|
||||||
|
volumes.fill(0.0);
|
||||||
|
enableAuxStages.fill(false);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
class HLE_DSP : public DSPCore {
|
||||||
|
// The audio frame types are public in case we want to use them for unit tests
|
||||||
|
public:
|
||||||
|
template <typename T, usize channelCount = 1>
|
||||||
|
using Sample = DSPMixer::Sample<T, channelCount>;
|
||||||
|
|
||||||
|
template <typename T, usize channelCount>
|
||||||
|
using Frame = DSPMixer::Frame<T, channelCount>;
|
||||||
|
|
||||||
|
template <typename T>
|
||||||
|
using MonoFrame = DSPMixer::MonoFrame<T>;
|
||||||
|
|
||||||
|
template <typename T>
|
||||||
|
using StereoFrame = DSPMixer::StereoFrame<T>;
|
||||||
|
|
||||||
|
template <typename T>
|
||||||
|
using QuadFrame = DSPMixer::QuadFrame<T>;
|
||||||
|
|
||||||
using Source = Audio::DSPSource;
|
using Source = Audio::DSPSource;
|
||||||
using SampleBuffer = Source::SampleBuffer;
|
using SampleBuffer = Source::SampleBuffer;
|
||||||
|
|
||||||
|
@ -129,6 +167,9 @@ namespace Audio {
|
||||||
std::array<Source, Audio::HLE::sourceCount> sources; // DSP voices
|
std::array<Source, Audio::HLE::sourceCount> sources; // DSP voices
|
||||||
Audio::HLE::DspMemory dspRam;
|
Audio::HLE::DspMemory dspRam;
|
||||||
|
|
||||||
|
Audio::DSPMixer mixer;
|
||||||
|
std::unique_ptr<Audio::AAC::Decoder> aacDecoder;
|
||||||
|
|
||||||
void resetAudioPipe();
|
void resetAudioPipe();
|
||||||
bool loaded = false; // Have we loaded a component?
|
bool loaded = false; // Have we loaded a component?
|
||||||
|
|
||||||
|
@ -171,9 +212,12 @@ namespace Audio {
|
||||||
|
|
||||||
void handleAACRequest(const AAC::Message& request);
|
void handleAACRequest(const AAC::Message& request);
|
||||||
void updateSourceConfig(Source& source, HLE::SourceConfiguration::Configuration& config, s16_le* adpcmCoefficients);
|
void updateSourceConfig(Source& source, HLE::SourceConfiguration::Configuration& config, s16_le* adpcmCoefficients);
|
||||||
|
void updateMixerConfig(HLE::SharedMemory& sharedMem);
|
||||||
void generateFrame(StereoFrame<s16>& frame);
|
void generateFrame(StereoFrame<s16>& frame);
|
||||||
void generateFrame(DSPSource& source);
|
void generateFrame(DSPSource& source);
|
||||||
void outputFrame();
|
void outputFrame();
|
||||||
|
// Perform the final mix, mixing the quadraphonic samples from all voices into the output audio frame
|
||||||
|
void performMix(Audio::HLE::SharedMemory& readRegion, Audio::HLE::SharedMemory& writeRegion);
|
||||||
|
|
||||||
// Decode an entire buffer worth of audio
|
// Decode an entire buffer worth of audio
|
||||||
void decodeBuffer(DSPSource& source);
|
void decodeBuffer(DSPSource& source);
|
||||||
|
|
|
@ -1,6 +1,13 @@
|
||||||
#pragma once
|
#pragma once
|
||||||
|
|
||||||
#include <QAction>
|
#include <QAction>
|
||||||
|
#include <QCheckBox>
|
||||||
|
#include <QDialog>
|
||||||
|
#include <QLabel>
|
||||||
|
#include <QLineEdit>
|
||||||
|
#include <QListWidget>
|
||||||
|
#include <QPushButton>
|
||||||
|
#include <QTextEdit>
|
||||||
#include <QWidget>
|
#include <QWidget>
|
||||||
#include <filesystem>
|
#include <filesystem>
|
||||||
#include <memory>
|
#include <memory>
|
||||||
|
@ -24,3 +31,60 @@ class CheatsWindow final : public QWidget {
|
||||||
std::filesystem::path cheatPath;
|
std::filesystem::path cheatPath;
|
||||||
Emulator* emu;
|
Emulator* emu;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
struct CheatMetadata {
|
||||||
|
u32 handle = Cheats::badCheatHandle;
|
||||||
|
std::string name = "New cheat";
|
||||||
|
std::string code;
|
||||||
|
bool enabled = true;
|
||||||
|
};
|
||||||
|
|
||||||
|
class CheatEntryWidget : public QWidget {
|
||||||
|
Q_OBJECT
|
||||||
|
|
||||||
|
public:
|
||||||
|
CheatEntryWidget(Emulator* emu, CheatMetadata metadata, QListWidget* parent);
|
||||||
|
|
||||||
|
void Update() {
|
||||||
|
name->setText(metadata.name.c_str());
|
||||||
|
enabled->setChecked(metadata.enabled);
|
||||||
|
update();
|
||||||
|
}
|
||||||
|
|
||||||
|
void Remove() {
|
||||||
|
emu->getCheats().removeCheat(metadata.handle);
|
||||||
|
cheatList->takeItem(cheatList->row(listItem));
|
||||||
|
deleteLater();
|
||||||
|
}
|
||||||
|
|
||||||
|
const CheatMetadata& getMetadata() { return metadata; }
|
||||||
|
void setMetadata(const CheatMetadata& metadata) { this->metadata = metadata; }
|
||||||
|
|
||||||
|
private:
|
||||||
|
void checkboxChanged(int state);
|
||||||
|
void editClicked();
|
||||||
|
|
||||||
|
Emulator* emu;
|
||||||
|
CheatMetadata metadata;
|
||||||
|
u32 handle;
|
||||||
|
QLabel* name;
|
||||||
|
QCheckBox* enabled;
|
||||||
|
QListWidget* cheatList;
|
||||||
|
QListWidgetItem* listItem;
|
||||||
|
};
|
||||||
|
|
||||||
|
class CheatEditDialog : public QDialog {
|
||||||
|
Q_OBJECT
|
||||||
|
|
||||||
|
public:
|
||||||
|
CheatEditDialog(Emulator* emu, CheatEntryWidget& cheatEntry);
|
||||||
|
|
||||||
|
void accepted();
|
||||||
|
void rejected();
|
||||||
|
|
||||||
|
private:
|
||||||
|
Emulator* emu;
|
||||||
|
CheatEntryWidget& cheatEntry;
|
||||||
|
QTextEdit* codeEdit;
|
||||||
|
QLineEdit* nameEdit;
|
||||||
|
};
|
|
@ -140,7 +140,7 @@ class MainWindow : public QMainWindow {
|
||||||
MainWindow(QApplication* app, QWidget* parent = nullptr);
|
MainWindow(QApplication* app, QWidget* parent = nullptr);
|
||||||
~MainWindow();
|
~MainWindow();
|
||||||
|
|
||||||
void closeEvent(QCloseEvent *event) override;
|
void closeEvent(QCloseEvent* event) override;
|
||||||
void keyPressEvent(QKeyEvent* event) override;
|
void keyPressEvent(QKeyEvent* event) override;
|
||||||
void keyReleaseEvent(QKeyEvent* event) override;
|
void keyReleaseEvent(QKeyEvent* event) override;
|
||||||
void mousePressEvent(QMouseEvent* event) override;
|
void mousePressEvent(QMouseEvent* event) override;
|
||||||
|
|
|
@ -111,6 +111,7 @@ std::string FragmentGenerator::generate(const FragmentConfig& config) {
|
||||||
if (api == API::GLES) {
|
if (api == API::GLES) {
|
||||||
ret += R"(
|
ret += R"(
|
||||||
#define USING_GLES 1
|
#define USING_GLES 1
|
||||||
|
#define fma(a, b, c) ((a) * (b) + (c))
|
||||||
|
|
||||||
precision mediump int;
|
precision mediump int;
|
||||||
precision mediump float;
|
precision mediump float;
|
||||||
|
@ -506,7 +507,7 @@ void FragmentGenerator::compileLights(std::string& shader, const PICA::FragmentC
|
||||||
"].distanceAttenuationScale + lightSources[" + std::to_string(lightID) + "].distanceAttenuationBias, 0.0, 1.0);\n";
|
"].distanceAttenuationScale + lightSources[" + std::to_string(lightID) + "].distanceAttenuationBias, 0.0, 1.0);\n";
|
||||||
|
|
||||||
shader += "distance_attenuation = lutLookup(" + std::to_string(16 + lightID) +
|
shader += "distance_attenuation = lutLookup(" + std::to_string(16 + lightID) +
|
||||||
", int(clamp(floor(distance_att_delta * 256.0), 0.0, 255.0)));\n";
|
"u, int(clamp(floor(distance_att_delta * 256.0), 0.0, 255.0)));\n";
|
||||||
}
|
}
|
||||||
|
|
||||||
compileLUTLookup(shader, config, i, spotlightLutIndex);
|
compileLUTLookup(shader, config, i, spotlightLutIndex);
|
||||||
|
@ -641,7 +642,7 @@ void FragmentGenerator::compileLUTLookup(std::string& shader, const PICA::Fragme
|
||||||
if (absEnabled) {
|
if (absEnabled) {
|
||||||
bool twoSidedDiffuse = config.lighting.lights[lightIndex].twoSidedDiffuse;
|
bool twoSidedDiffuse = config.lighting.lights[lightIndex].twoSidedDiffuse;
|
||||||
shader += twoSidedDiffuse ? "lut_lookup_delta = abs(lut_lookup_delta);\n" : "lut_lookup_delta = max(lut_lookup_delta, 0.0);\n";
|
shader += twoSidedDiffuse ? "lut_lookup_delta = abs(lut_lookup_delta);\n" : "lut_lookup_delta = max(lut_lookup_delta, 0.0);\n";
|
||||||
shader += "lut_lookup_result = lutLookup(" + std::to_string(lutIndex) + ", int(clamp(floor(lut_lookup_delta * 256.0), 0.0, 255.0)));\n";
|
shader += "lut_lookup_result = lutLookup(" + std::to_string(lutIndex) + "u, int(clamp(floor(lut_lookup_delta * 256.0), 0.0, 255.0)));\n";
|
||||||
if (scale != 0) {
|
if (scale != 0) {
|
||||||
shader += "lut_lookup_result *= " + std::to_string(scales[scale]) + ";\n";
|
shader += "lut_lookup_result *= " + std::to_string(scales[scale]) + ";\n";
|
||||||
}
|
}
|
||||||
|
@ -649,7 +650,7 @@ void FragmentGenerator::compileLUTLookup(std::string& shader, const PICA::Fragme
|
||||||
// Range is [-1, 1] so we need to map it to [0, 1]
|
// Range is [-1, 1] so we need to map it to [0, 1]
|
||||||
shader += "lut_lookup_index = int(clamp(floor(lut_lookup_delta * 128.0), -128.f, 127.f));\n";
|
shader += "lut_lookup_index = int(clamp(floor(lut_lookup_delta * 128.0), -128.f, 127.f));\n";
|
||||||
shader += "if (lut_lookup_index < 0) lut_lookup_index += 256;\n";
|
shader += "if (lut_lookup_index < 0) lut_lookup_index += 256;\n";
|
||||||
shader += "lut_lookup_result = lutLookup(" + std::to_string(lutIndex) + ", lut_lookup_index);\n";
|
shader += "lut_lookup_result = lutLookup(" + std::to_string(lutIndex) + "u, lut_lookup_index);\n";
|
||||||
if (scale != 0) {
|
if (scale != 0) {
|
||||||
shader += "lut_lookup_result *= " + std::to_string(scales[scale]) + ";\n";
|
shader += "lut_lookup_result *= " + std::to_string(scales[scale]) + ";\n";
|
||||||
}
|
}
|
||||||
|
|
139
src/core/audio/aac_decoder.cpp
Normal file
139
src/core/audio/aac_decoder.cpp
Normal file
|
@ -0,0 +1,139 @@
|
||||||
|
#include "audio/aac_decoder.hpp"
|
||||||
|
|
||||||
|
#include <aacdecoder_lib.h>
|
||||||
|
|
||||||
|
#include <vector>
|
||||||
|
using namespace Audio;
|
||||||
|
|
||||||
|
void AAC::Decoder::decode(AAC::Message& response, const AAC::Message& request, AAC::Decoder::PaddrCallback paddrCallback) {
|
||||||
|
// Copy the command and mode fields of the request to the response
|
||||||
|
response.command = request.command;
|
||||||
|
response.mode = request.mode;
|
||||||
|
response.decodeResponse.size = request.decodeRequest.size;
|
||||||
|
|
||||||
|
// Write a dummy response at first. We'll be overwriting it later if decoding goes well
|
||||||
|
response.resultCode = AAC::ResultCode::Success;
|
||||||
|
response.decodeResponse.channelCount = 2;
|
||||||
|
response.decodeResponse.sampleCount = 1024;
|
||||||
|
response.decodeResponse.sampleRate = AAC::SampleRate::Rate48000;
|
||||||
|
|
||||||
|
if (!isInitialized()) {
|
||||||
|
initialize();
|
||||||
|
|
||||||
|
// AAC decoder failed to initialize, return dummy data and return without decoding
|
||||||
|
if (!isInitialized()) {
|
||||||
|
Helpers::warn("Failed to initialize AAC decoder");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
u8* input = paddrCallback(request.decodeRequest.address);
|
||||||
|
const u8* inputEnd = paddrCallback(request.decodeRequest.address + request.decodeRequest.size);
|
||||||
|
u8* outputLeft = paddrCallback(request.decodeRequest.destAddrLeft);
|
||||||
|
u8* outputRight = nullptr;
|
||||||
|
|
||||||
|
if (input == nullptr || inputEnd == nullptr || outputLeft == nullptr) {
|
||||||
|
Helpers::warn("Invalid pointers passed to AAC decoder");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
u32 bytesValid = request.decodeRequest.size;
|
||||||
|
u32 bufferSize = request.decodeRequest.size;
|
||||||
|
|
||||||
|
// Each frame is 2048 samples with 2 channels
|
||||||
|
static constexpr usize frameSize = 2048 * 2;
|
||||||
|
std::array<s16, frameSize> frame;
|
||||||
|
std::array<std::vector<s16>, 2> audioStreams;
|
||||||
|
|
||||||
|
bool queriedStreamInfo = false;
|
||||||
|
|
||||||
|
while (bytesValid != 0) {
|
||||||
|
if (aacDecoder_Fill(decoderHandle, &input, &bufferSize, &bytesValid) != AAC_DEC_OK) {
|
||||||
|
Helpers::warn("Failed to fill AAC decoder with samples");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
auto decodeResult = aacDecoder_DecodeFrame(decoderHandle, frame.data(), frameSize, 0);
|
||||||
|
|
||||||
|
if (decodeResult == AAC_DEC_TRANSPORT_SYNC_ERROR) {
|
||||||
|
// https://android.googlesource.com/platform/external/aac/+/2ddc922/libAACdec/include/aacdecoder_lib.h#362
|
||||||
|
// According to the above, if we get a sync error, we're not meant to stop decoding, but rather just continue feeding data
|
||||||
|
} else if (decodeResult == AAC_DEC_OK) {
|
||||||
|
auto getSampleRate = [](u32 rate) {
|
||||||
|
switch (rate) {
|
||||||
|
case 8000: return AAC::SampleRate::Rate8000;
|
||||||
|
case 11025: return AAC::SampleRate::Rate11025;
|
||||||
|
case 12000: return AAC::SampleRate::Rate12000;
|
||||||
|
case 16000: return AAC::SampleRate::Rate16000;
|
||||||
|
case 22050: return AAC::SampleRate::Rate22050;
|
||||||
|
case 24000: return AAC::SampleRate::Rate24000;
|
||||||
|
case 32000: return AAC::SampleRate::Rate32000;
|
||||||
|
case 44100: return AAC::SampleRate::Rate44100;
|
||||||
|
case 48000:
|
||||||
|
default: return AAC::SampleRate::Rate48000;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
auto info = aacDecoder_GetStreamInfo(decoderHandle);
|
||||||
|
response.decodeResponse.sampleCount = info->frameSize;
|
||||||
|
response.decodeResponse.channelCount = info->numChannels;
|
||||||
|
response.decodeResponse.sampleRate = getSampleRate(info->sampleRate);
|
||||||
|
|
||||||
|
int channels = info->numChannels;
|
||||||
|
// Reserve space in our output stream vectors so push_back doesn't do allocations
|
||||||
|
for (int i = 0; i < channels; i++) {
|
||||||
|
audioStreams[i].reserve(audioStreams[i].size() + info->frameSize);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Fetch output pointer for right output channel if we've got > 1 channel
|
||||||
|
if (channels > 1 && outputRight == nullptr) {
|
||||||
|
outputRight = paddrCallback(request.decodeRequest.destAddrRight);
|
||||||
|
// If the right output channel doesn't point to a proper padddr, return
|
||||||
|
if (outputRight == nullptr) {
|
||||||
|
Helpers::warn("Right AAC output channel doesn't point to valid physical address");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
for (int sample = 0; sample < info->frameSize; sample++) {
|
||||||
|
for (int stream = 0; stream < channels; stream++) {
|
||||||
|
audioStreams[stream].push_back(frame[(sample * channels) + stream]);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
Helpers::warn("Failed to decode AAC frame");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
for (int i = 0; i < 2; i++) {
|
||||||
|
auto& stream = audioStreams[i];
|
||||||
|
u8* pointer = (i == 0) ? outputLeft : outputRight;
|
||||||
|
|
||||||
|
if (!stream.empty() && pointer != nullptr) {
|
||||||
|
std::memcpy(pointer, stream.data(), stream.size() * sizeof(s16));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void AAC::Decoder::initialize() {
|
||||||
|
decoderHandle = aacDecoder_Open(TRANSPORT_TYPE::TT_MP4_ADTS, 1);
|
||||||
|
|
||||||
|
if (decoderHandle == nullptr) [[unlikely]] {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Cap output channel count to 2
|
||||||
|
if (aacDecoder_SetParam(decoderHandle, AAC_PCM_MAX_OUTPUT_CHANNELS, 2) != AAC_DEC_OK) [[unlikely]] {
|
||||||
|
aacDecoder_Close(decoderHandle);
|
||||||
|
decoderHandle = nullptr;
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
AAC::Decoder::~Decoder() {
|
||||||
|
if (isInitialized()) {
|
||||||
|
aacDecoder_Close(decoderHandle);
|
||||||
|
decoderHandle = nullptr;
|
||||||
|
}
|
||||||
|
}
|
|
@ -6,6 +6,7 @@
|
||||||
#include <thread>
|
#include <thread>
|
||||||
#include <utility>
|
#include <utility>
|
||||||
|
|
||||||
|
#include "audio/aac_decoder.hpp"
|
||||||
#include "services/dsp.hpp"
|
#include "services/dsp.hpp"
|
||||||
|
|
||||||
namespace Audio {
|
namespace Audio {
|
||||||
|
@ -23,6 +24,8 @@ namespace Audio {
|
||||||
for (int i = 0; i < sources.size(); i++) {
|
for (int i = 0; i < sources.size(); i++) {
|
||||||
sources[i].index = i;
|
sources[i].index = i;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
aacDecoder.reset(new Audio::AAC::Decoder());
|
||||||
}
|
}
|
||||||
|
|
||||||
void HLE_DSP::resetAudioPipe() {
|
void HLE_DSP::resetAudioPipe() {
|
||||||
|
@ -73,6 +76,7 @@ namespace Audio {
|
||||||
source.reset();
|
source.reset();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
mixer.reset();
|
||||||
// Note: Reset audio pipe AFTER resetting all pipes, otherwise the new data will be yeeted
|
// Note: Reset audio pipe AFTER resetting all pipes, otherwise the new data will be yeeted
|
||||||
resetAudioPipe();
|
resetAudioPipe();
|
||||||
}
|
}
|
||||||
|
@ -247,6 +251,8 @@ namespace Audio {
|
||||||
|
|
||||||
source.isBufferIDDirty = false;
|
source.isBufferIDDirty = false;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
performMix(read, write);
|
||||||
}
|
}
|
||||||
|
|
||||||
void HLE_DSP::updateSourceConfig(Source& source, HLE::SourceConfiguration::Configuration& config, s16_le* adpcmCoefficients) {
|
void HLE_DSP::updateSourceConfig(Source& source, HLE::SourceConfiguration::Configuration& config, s16_le* adpcmCoefficients) {
|
||||||
|
@ -462,6 +468,50 @@ namespace Audio {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void HLE_DSP::performMix(Audio::HLE::SharedMemory& readRegion, Audio::HLE::SharedMemory& writeRegion) {
|
||||||
|
updateMixerConfig(readRegion);
|
||||||
|
// TODO: Do the actual audio mixing
|
||||||
|
|
||||||
|
auto& dspStatus = writeRegion.dspStatus;
|
||||||
|
// Stub the DSP status. It's unknown what the "unknown" field is but Citra sets it to 0, so we do too to be safe
|
||||||
|
dspStatus.droppedFrames = 0;
|
||||||
|
dspStatus.unknown = 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
void HLE_DSP::updateMixerConfig(Audio::HLE::SharedMemory& sharedMem) {
|
||||||
|
auto& config = sharedMem.dspConfiguration;
|
||||||
|
// No configs have been changed, so there's nothing to update
|
||||||
|
if (config.dirtyRaw == 0) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (config.outputFormatDirty) {
|
||||||
|
mixer.channelFormat = config.outputFormat;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (config.masterVolumeDirty) {
|
||||||
|
mixer.volumes[0] = config.masterVolume;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (config.auxVolume0Dirty) {
|
||||||
|
mixer.volumes[1] = config.auxVolumes[0];
|
||||||
|
}
|
||||||
|
|
||||||
|
if (config.auxVolume1Dirty) {
|
||||||
|
mixer.volumes[2] = config.auxVolumes[1];
|
||||||
|
}
|
||||||
|
|
||||||
|
if (config.auxBusEnable0Dirty) {
|
||||||
|
mixer.enableAuxStages[0] = config.auxBusEnable[0] != 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (config.auxBusEnable1Dirty) {
|
||||||
|
mixer.enableAuxStages[1] = config.auxBusEnable[1] != 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
config.dirtyRaw = 0;
|
||||||
|
}
|
||||||
|
|
||||||
HLE_DSP::SampleBuffer HLE_DSP::decodePCM8(const u8* data, usize sampleCount, Source& source) {
|
HLE_DSP::SampleBuffer HLE_DSP::decodePCM8(const u8* data, usize sampleCount, Source& source) {
|
||||||
SampleBuffer decodedSamples(sampleCount);
|
SampleBuffer decodedSamples(sampleCount);
|
||||||
|
|
||||||
|
@ -584,7 +634,6 @@ namespace Audio {
|
||||||
switch (request.command) {
|
switch (request.command) {
|
||||||
case AAC::Command::EncodeDecode:
|
case AAC::Command::EncodeDecode:
|
||||||
// Dummy response to stop games from hanging
|
// Dummy response to stop games from hanging
|
||||||
// TODO: Fix this when implementing AAC
|
|
||||||
response.resultCode = AAC::ResultCode::Success;
|
response.resultCode = AAC::ResultCode::Success;
|
||||||
response.decodeResponse.channelCount = 2;
|
response.decodeResponse.channelCount = 2;
|
||||||
response.decodeResponse.sampleCount = 1024;
|
response.decodeResponse.sampleCount = 1024;
|
||||||
|
@ -593,6 +642,10 @@ namespace Audio {
|
||||||
|
|
||||||
response.command = request.command;
|
response.command = request.command;
|
||||||
response.mode = request.mode;
|
response.mode = request.mode;
|
||||||
|
|
||||||
|
// We've already got an AAC decoder but it's currently disabled until mixing & output is properly implemented
|
||||||
|
// TODO: Uncomment this when the time comes
|
||||||
|
// aacDecoder->decode(response, request, [this](u32 paddr) { return getPointerPhys<u8>(paddr); });
|
||||||
break;
|
break;
|
||||||
|
|
||||||
case AAC::Command::Init:
|
case AAC::Command::Init:
|
||||||
|
|
|
@ -90,16 +90,17 @@ void MiniAudioDevice::init(Samples& samples, bool safe) {
|
||||||
deviceConfig.dataCallback = [](ma_device* device, void* out, const void* input, ma_uint32 frameCount) {
|
deviceConfig.dataCallback = [](ma_device* device, void* out, const void* input, ma_uint32 frameCount) {
|
||||||
auto self = reinterpret_cast<MiniAudioDevice*>(device->pUserData);
|
auto self = reinterpret_cast<MiniAudioDevice*>(device->pUserData);
|
||||||
s16* output = reinterpret_cast<ma_int16*>(out);
|
s16* output = reinterpret_cast<ma_int16*>(out);
|
||||||
|
const usize maxSamples = std::min(self->samples->Capacity(), usize(frameCount * channelCount));
|
||||||
|
|
||||||
// Wait until there's enough samples to pop
|
// Wait until there's enough samples to pop
|
||||||
while (self->samples->size() < frameCount * channelCount) {
|
while (self->samples->size() < maxSamples) {
|
||||||
// If audio output is disabled from the emulator thread, make sure that this callback will return and not hang
|
// If audio output is disabled from the emulator thread, make sure that this callback will return and not hang
|
||||||
if (!self->running) {
|
if (!self->running) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
self->samples->pop(output, frameCount * channelCount);
|
self->samples->pop(output, maxSamples);
|
||||||
};
|
};
|
||||||
|
|
||||||
if (ma_device_init(&context, &deviceConfig, &device) != MA_SUCCESS) {
|
if (ma_device_init(&context, &deviceConfig, &device) != MA_SUCCESS) {
|
||||||
|
|
|
@ -51,7 +51,7 @@ void RendererGL::reset() {
|
||||||
gl.useProgram(oldProgram); // Switch to old GL program
|
gl.useProgram(oldProgram); // Switch to old GL program
|
||||||
}
|
}
|
||||||
|
|
||||||
#ifdef __ANDROID__
|
#ifdef USING_GLES
|
||||||
fragShaderGen.setTarget(PICA::ShaderGen::API::GLES, PICA::ShaderGen::Language::GLSL);
|
fragShaderGen.setTarget(PICA::ShaderGen::API::GLES, PICA::ShaderGen::Language::GLSL);
|
||||||
#endif
|
#endif
|
||||||
}
|
}
|
||||||
|
|
|
@ -114,7 +114,7 @@ hydra::Size HydraCore::getNativeSize() { return {400, 480}; }
|
||||||
void HydraCore::setOutputSize(hydra::Size size) {}
|
void HydraCore::setOutputSize(hydra::Size size) {}
|
||||||
|
|
||||||
void HydraCore::resetContext() {
|
void HydraCore::resetContext() {
|
||||||
#ifdef __ANDROID__
|
#ifdef USING_GLES
|
||||||
if (!gladLoadGLES2Loader(reinterpret_cast<GLADloadproc>(getProcAddress))) {
|
if (!gladLoadGLES2Loader(reinterpret_cast<GLADloadproc>(getProcAddress))) {
|
||||||
Helpers::panic("OpenGL ES init failed");
|
Helpers::panic("OpenGL ES init failed");
|
||||||
}
|
}
|
||||||
|
|
|
@ -8,13 +8,13 @@
|
||||||
#include <emulator.hpp>
|
#include <emulator.hpp>
|
||||||
#include <renderer_gl/renderer_gl.hpp>
|
#include <renderer_gl/renderer_gl.hpp>
|
||||||
|
|
||||||
static retro_environment_t envCallbacks;
|
static retro_environment_t envCallback;
|
||||||
static retro_video_refresh_t videoCallbacks;
|
static retro_video_refresh_t videoCallback;
|
||||||
static retro_audio_sample_batch_t audioBatchCallback;
|
static retro_audio_sample_batch_t audioBatchCallback;
|
||||||
static retro_input_poll_t inputPollCallback;
|
static retro_input_poll_t inputPollCallback;
|
||||||
static retro_input_state_t inputStateCallback;
|
static retro_input_state_t inputStateCallback;
|
||||||
|
|
||||||
static retro_hw_render_callback hw_render;
|
static retro_hw_render_callback hwRender;
|
||||||
static std::filesystem::path savePath;
|
static std::filesystem::path savePath;
|
||||||
|
|
||||||
static bool screenTouched;
|
static bool screenTouched;
|
||||||
|
@ -30,17 +30,17 @@ std::filesystem::path Emulator::getAppDataRoot() {
|
||||||
return std::filesystem::path(savePath / "Emulator Files");
|
return std::filesystem::path(savePath / "Emulator Files");
|
||||||
}
|
}
|
||||||
|
|
||||||
static void* GetGLProcAddress(const char* name) {
|
static void* getGLProcAddress(const char* name) {
|
||||||
return (void*)hw_render.get_proc_address(name);
|
return (void*)hwRender.get_proc_address(name);
|
||||||
}
|
}
|
||||||
|
|
||||||
static void VideoResetContext() {
|
static void videoResetContext() {
|
||||||
#ifdef USING_GLES
|
#ifdef USING_GLES
|
||||||
if (!gladLoadGLES2Loader(reinterpret_cast<GLADloadproc>(GetGLProcAddress))) {
|
if (!gladLoadGLES2Loader(reinterpret_cast<GLADloadproc>(getGLProcAddress))) {
|
||||||
Helpers::panic("OpenGL ES init failed");
|
Helpers::panic("OpenGL ES init failed");
|
||||||
}
|
}
|
||||||
#else
|
#else
|
||||||
if (!gladLoadGLLoader(reinterpret_cast<GLADloadproc>(GetGLProcAddress))) {
|
if (!gladLoadGLLoader(reinterpret_cast<GLADloadproc>(getGLProcAddress))) {
|
||||||
Helpers::panic("OpenGL init failed");
|
Helpers::panic("OpenGL init failed");
|
||||||
}
|
}
|
||||||
#endif
|
#endif
|
||||||
|
@ -48,31 +48,31 @@ static void VideoResetContext() {
|
||||||
emulator->initGraphicsContext(nullptr);
|
emulator->initGraphicsContext(nullptr);
|
||||||
}
|
}
|
||||||
|
|
||||||
static void VideoDestroyContext() {
|
static void videoDestroyContext() {
|
||||||
emulator->deinitGraphicsContext();
|
emulator->deinitGraphicsContext();
|
||||||
}
|
}
|
||||||
|
|
||||||
static bool SetHWRender(retro_hw_context_type type) {
|
static bool setHWRender(retro_hw_context_type type) {
|
||||||
hw_render.context_type = type;
|
hwRender.context_type = type;
|
||||||
hw_render.context_reset = VideoResetContext;
|
hwRender.context_reset = videoResetContext;
|
||||||
hw_render.context_destroy = VideoDestroyContext;
|
hwRender.context_destroy = videoDestroyContext;
|
||||||
hw_render.bottom_left_origin = true;
|
hwRender.bottom_left_origin = true;
|
||||||
|
|
||||||
switch (type) {
|
switch (type) {
|
||||||
case RETRO_HW_CONTEXT_OPENGL_CORE:
|
case RETRO_HW_CONTEXT_OPENGL_CORE:
|
||||||
hw_render.version_major = 4;
|
hwRender.version_major = 4;
|
||||||
hw_render.version_minor = 1;
|
hwRender.version_minor = 1;
|
||||||
|
|
||||||
if (envCallbacks(RETRO_ENVIRONMENT_SET_HW_RENDER, &hw_render)) {
|
if (envCallback(RETRO_ENVIRONMENT_SET_HW_RENDER, &hwRender)) {
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
case RETRO_HW_CONTEXT_OPENGLES3:
|
case RETRO_HW_CONTEXT_OPENGLES3:
|
||||||
case RETRO_HW_CONTEXT_OPENGL:
|
case RETRO_HW_CONTEXT_OPENGL:
|
||||||
hw_render.version_major = 3;
|
hwRender.version_major = 3;
|
||||||
hw_render.version_minor = 1;
|
hwRender.version_minor = 1;
|
||||||
|
|
||||||
if (envCallbacks(RETRO_ENVIRONMENT_SET_HW_RENDER, &hw_render)) {
|
if (envCallback(RETRO_ENVIRONMENT_SET_HW_RENDER, &hwRender)) {
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
|
@ -84,18 +84,18 @@ static bool SetHWRender(retro_hw_context_type type) {
|
||||||
|
|
||||||
static void videoInit() {
|
static void videoInit() {
|
||||||
retro_hw_context_type preferred = RETRO_HW_CONTEXT_NONE;
|
retro_hw_context_type preferred = RETRO_HW_CONTEXT_NONE;
|
||||||
envCallbacks(RETRO_ENVIRONMENT_GET_PREFERRED_HW_RENDER, &preferred);
|
envCallback(RETRO_ENVIRONMENT_GET_PREFERRED_HW_RENDER, &preferred);
|
||||||
|
|
||||||
if (preferred && SetHWRender(preferred)) return;
|
if (preferred && setHWRender(preferred)) return;
|
||||||
if (SetHWRender(RETRO_HW_CONTEXT_OPENGL_CORE)) return;
|
if (setHWRender(RETRO_HW_CONTEXT_OPENGL_CORE)) return;
|
||||||
if (SetHWRender(RETRO_HW_CONTEXT_OPENGL)) return;
|
if (setHWRender(RETRO_HW_CONTEXT_OPENGL)) return;
|
||||||
if (SetHWRender(RETRO_HW_CONTEXT_OPENGLES3)) return;
|
if (setHWRender(RETRO_HW_CONTEXT_OPENGLES3)) return;
|
||||||
|
|
||||||
hw_render.context_type = RETRO_HW_CONTEXT_NONE;
|
hwRender.context_type = RETRO_HW_CONTEXT_NONE;
|
||||||
}
|
}
|
||||||
|
|
||||||
static bool GetButtonState(uint id) { return inputStateCallback(0, RETRO_DEVICE_JOYPAD, 0, id); }
|
static bool getButtonState(uint id) { return inputStateCallback(0, RETRO_DEVICE_JOYPAD, 0, id); }
|
||||||
static float GetAxisState(uint index, uint id) { return inputStateCallback(0, RETRO_DEVICE_ANALOG, index, id); }
|
static float getAxisState(uint index, uint id) { return inputStateCallback(0, RETRO_DEVICE_ANALOG, index, id); }
|
||||||
|
|
||||||
static void inputInit() {
|
static void inputInit() {
|
||||||
static const retro_controller_description controllers[] = {
|
static const retro_controller_description controllers[] = {
|
||||||
|
@ -108,7 +108,7 @@ static void inputInit() {
|
||||||
{NULL, 0},
|
{NULL, 0},
|
||||||
};
|
};
|
||||||
|
|
||||||
envCallbacks(RETRO_ENVIRONMENT_SET_CONTROLLER_INFO, (void*)ports);
|
envCallback(RETRO_ENVIRONMENT_SET_CONTROLLER_INFO, (void*)ports);
|
||||||
|
|
||||||
retro_input_descriptor desc[] = {
|
retro_input_descriptor desc[] = {
|
||||||
{0, RETRO_DEVICE_JOYPAD, 0, RETRO_DEVICE_ID_JOYPAD_LEFT, "Left"},
|
{0, RETRO_DEVICE_JOYPAD, 0, RETRO_DEVICE_ID_JOYPAD_LEFT, "Left"},
|
||||||
|
@ -128,14 +128,14 @@ static void inputInit() {
|
||||||
{0},
|
{0},
|
||||||
};
|
};
|
||||||
|
|
||||||
envCallbacks(RETRO_ENVIRONMENT_SET_INPUT_DESCRIPTORS, &desc);
|
envCallback(RETRO_ENVIRONMENT_SET_INPUT_DESCRIPTORS, &desc);
|
||||||
}
|
}
|
||||||
|
|
||||||
static std::string FetchVariable(std::string key, std::string def) {
|
static std::string fetchVariable(std::string key, std::string def) {
|
||||||
retro_variable var = {nullptr};
|
retro_variable var = {nullptr};
|
||||||
var.key = key.c_str();
|
var.key = key.c_str();
|
||||||
|
|
||||||
if (!envCallbacks(RETRO_ENVIRONMENT_GET_VARIABLE, &var) || var.value == nullptr) {
|
if (!envCallback(RETRO_ENVIRONMENT_GET_VARIABLE, &var) || var.value == nullptr) {
|
||||||
Helpers::warn("Fetching variable %s failed.", key.c_str());
|
Helpers::warn("Fetching variable %s failed.", key.c_str());
|
||||||
return def;
|
return def;
|
||||||
}
|
}
|
||||||
|
@ -143,13 +143,27 @@ static std::string FetchVariable(std::string key, std::string def) {
|
||||||
return std::string(var.value);
|
return std::string(var.value);
|
||||||
}
|
}
|
||||||
|
|
||||||
static bool FetchVariableBool(std::string key, bool def) {
|
static int fetchVariableInt(std::string key, int def) {
|
||||||
return FetchVariable(key, def ? "enabled" : "disabled") == "enabled";
|
std::string value = fetchVariable(key, std::to_string(def));
|
||||||
|
|
||||||
|
if (!value.empty() && std::isdigit(value[0])) {
|
||||||
|
return std::stoi(value);
|
||||||
|
}
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
static bool fetchVariableBool(std::string key, bool def) {
|
||||||
|
return fetchVariable(key, def ? "enabled" : "disabled") == "enabled";
|
||||||
|
}
|
||||||
|
|
||||||
|
static int fetchVariableRange(std::string key, int min, int max) {
|
||||||
|
return std::clamp(fetchVariableInt(key, min), min, max);
|
||||||
}
|
}
|
||||||
|
|
||||||
static void configInit() {
|
static void configInit() {
|
||||||
static const retro_variable values[] = {
|
static const retro_variable values[] = {
|
||||||
{"panda3ds_use_shader_jit", "Enable shader JIT; enabled|disabled"},
|
{"panda3ds_use_shader_jit", EmulatorConfig::shaderJitDefault ? "Enable shader JIT; enabled|disabled" : "Enable shader JIT; disabled|enabled"},
|
||||||
{"panda3ds_accelerate_shaders",
|
{"panda3ds_accelerate_shaders",
|
||||||
EmulatorConfig::accelerateShadersDefault ? "Run 3DS shaders on the GPU; enabled|disabled" : "Run 3DS shaders on the GPU; disabled|enabled"},
|
EmulatorConfig::accelerateShadersDefault ? "Run 3DS shaders on the GPU; enabled|disabled" : "Run 3DS shaders on the GPU; disabled|enabled"},
|
||||||
{"panda3ds_accurate_shader_mul", "Enable accurate shader multiplication; disabled|enabled"},
|
{"panda3ds_accurate_shader_mul", "Enable accurate shader multiplication; disabled|enabled"},
|
||||||
|
@ -167,35 +181,35 @@ static void configInit() {
|
||||||
{nullptr, nullptr},
|
{nullptr, nullptr},
|
||||||
};
|
};
|
||||||
|
|
||||||
envCallbacks(RETRO_ENVIRONMENT_SET_VARIABLES, (void*)values);
|
envCallback(RETRO_ENVIRONMENT_SET_VARIABLES, (void*)values);
|
||||||
}
|
}
|
||||||
|
|
||||||
static void configUpdate() {
|
static void configUpdate() {
|
||||||
EmulatorConfig& config = emulator->getConfig();
|
EmulatorConfig& config = emulator->getConfig();
|
||||||
|
|
||||||
config.rendererType = RendererType::OpenGL;
|
config.rendererType = RendererType::OpenGL;
|
||||||
config.vsyncEnabled = FetchVariableBool("panda3ds_use_vsync", true);
|
config.vsyncEnabled = fetchVariableBool("panda3ds_use_vsync", true);
|
||||||
config.shaderJitEnabled = FetchVariableBool("panda3ds_use_shader_jit", true);
|
config.shaderJitEnabled = fetchVariableBool("panda3ds_use_shader_jit", EmulatorConfig::shaderJitDefault);
|
||||||
config.chargerPlugged = FetchVariableBool("panda3ds_use_charger", true);
|
config.chargerPlugged = fetchVariableBool("panda3ds_use_charger", true);
|
||||||
config.batteryPercentage = std::clamp(std::stoi(FetchVariable("panda3ds_battery_level", "5")), 0, 100);
|
config.batteryPercentage = fetchVariableRange("panda3ds_battery_level", 5, 100);
|
||||||
config.dspType = Audio::DSPCore::typeFromString(FetchVariable("panda3ds_dsp_emulation", "null"));
|
config.dspType = Audio::DSPCore::typeFromString(fetchVariable("panda3ds_dsp_emulation", "null"));
|
||||||
config.audioEnabled = FetchVariableBool("panda3ds_use_audio", false);
|
config.audioEnabled = fetchVariableBool("panda3ds_use_audio", false);
|
||||||
config.sdCardInserted = FetchVariableBool("panda3ds_use_virtual_sd", true);
|
config.sdCardInserted = fetchVariableBool("panda3ds_use_virtual_sd", true);
|
||||||
config.sdWriteProtected = FetchVariableBool("panda3ds_write_protect_virtual_sd", false);
|
config.sdWriteProtected = fetchVariableBool("panda3ds_write_protect_virtual_sd", false);
|
||||||
config.accurateShaderMul = FetchVariableBool("panda3ds_accurate_shader_mul", false);
|
config.accurateShaderMul = fetchVariableBool("panda3ds_accurate_shader_mul", false);
|
||||||
config.useUbershaders = FetchVariableBool("panda3ds_use_ubershader", EmulatorConfig::ubershaderDefault);
|
config.useUbershaders = fetchVariableBool("panda3ds_use_ubershader", EmulatorConfig::ubershaderDefault);
|
||||||
config.accelerateShaders = FetchVariableBool("panda3ds_accelerate_shaders", EmulatorConfig::accelerateShadersDefault);
|
config.accelerateShaders = FetchVariableBool("panda3ds_accelerate_shaders", EmulatorConfig::accelerateShadersDefault);
|
||||||
|
|
||||||
config.forceShadergenForLights = FetchVariableBool("panda3ds_ubershader_lighting_override", true);
|
config.forceShadergenForLights = fetchVariableBool("panda3ds_ubershader_lighting_override", true);
|
||||||
config.lightShadergenThreshold = std::clamp(std::stoi(FetchVariable("panda3ds_ubershader_lighting_override_threshold", "1")), 1, 8);
|
config.lightShadergenThreshold = fetchVariableRange("panda3ds_ubershader_lighting_override_threshold", 1, 8);
|
||||||
config.discordRpcEnabled = false;
|
config.discordRpcEnabled = false;
|
||||||
|
|
||||||
config.save();
|
config.save();
|
||||||
}
|
}
|
||||||
|
|
||||||
static void ConfigCheckVariables() {
|
static void configCheckVariables() {
|
||||||
bool updated = false;
|
bool updated = false;
|
||||||
envCallbacks(RETRO_ENVIRONMENT_GET_VARIABLE_UPDATE, &updated);
|
envCallback(RETRO_ENVIRONMENT_GET_VARIABLE_UPDATE, &updated);
|
||||||
|
|
||||||
if (updated) {
|
if (updated) {
|
||||||
configUpdate();
|
configUpdate();
|
||||||
|
@ -207,7 +221,7 @@ void retro_get_system_info(retro_system_info* info) {
|
||||||
info->valid_extensions = "3ds|3dsx|elf|axf|cci|cxi|app";
|
info->valid_extensions = "3ds|3dsx|elf|axf|cci|cxi|app";
|
||||||
info->library_version = PANDA3DS_VERSION;
|
info->library_version = PANDA3DS_VERSION;
|
||||||
info->library_name = "Panda3DS";
|
info->library_name = "Panda3DS";
|
||||||
info->block_extract = true;
|
info->block_extract = false;
|
||||||
}
|
}
|
||||||
|
|
||||||
void retro_get_system_av_info(retro_system_av_info* info) {
|
void retro_get_system_av_info(retro_system_av_info* info) {
|
||||||
|
@ -223,11 +237,11 @@ void retro_get_system_av_info(retro_system_av_info* info) {
|
||||||
}
|
}
|
||||||
|
|
||||||
void retro_set_environment(retro_environment_t cb) {
|
void retro_set_environment(retro_environment_t cb) {
|
||||||
envCallbacks = cb;
|
envCallback = cb;
|
||||||
}
|
}
|
||||||
|
|
||||||
void retro_set_video_refresh(retro_video_refresh_t cb) {
|
void retro_set_video_refresh(retro_video_refresh_t cb) {
|
||||||
videoCallbacks = cb;
|
videoCallback = cb;
|
||||||
}
|
}
|
||||||
|
|
||||||
void retro_set_audio_sample_batch(retro_audio_sample_batch_t cb) {
|
void retro_set_audio_sample_batch(retro_audio_sample_batch_t cb) {
|
||||||
|
@ -246,15 +260,15 @@ void retro_set_input_state(retro_input_state_t cb) {
|
||||||
|
|
||||||
void retro_init() {
|
void retro_init() {
|
||||||
enum retro_pixel_format xrgb888 = RETRO_PIXEL_FORMAT_XRGB8888;
|
enum retro_pixel_format xrgb888 = RETRO_PIXEL_FORMAT_XRGB8888;
|
||||||
envCallbacks(RETRO_ENVIRONMENT_SET_PIXEL_FORMAT, &xrgb888);
|
envCallback(RETRO_ENVIRONMENT_SET_PIXEL_FORMAT, &xrgb888);
|
||||||
|
|
||||||
char* save_dir = nullptr;
|
char* saveDir = nullptr;
|
||||||
|
|
||||||
if (!envCallbacks(RETRO_ENVIRONMENT_GET_SAVE_DIRECTORY, &save_dir) || save_dir == nullptr) {
|
if (!envCallback(RETRO_ENVIRONMENT_GET_SAVE_DIRECTORY, &saveDir) || saveDir == nullptr) {
|
||||||
Helpers::warn("No save directory provided by LibRetro.");
|
Helpers::warn("No save directory provided by LibRetro.");
|
||||||
savePath = std::filesystem::current_path();
|
savePath = std::filesystem::current_path();
|
||||||
} else {
|
} else {
|
||||||
savePath = std::filesystem::path(save_dir);
|
savePath = std::filesystem::path(saveDir);
|
||||||
}
|
}
|
||||||
|
|
||||||
emulator = std::make_unique<Emulator>();
|
emulator = std::make_unique<Emulator>();
|
||||||
|
@ -293,31 +307,31 @@ void retro_reset() {
|
||||||
}
|
}
|
||||||
|
|
||||||
void retro_run() {
|
void retro_run() {
|
||||||
ConfigCheckVariables();
|
configCheckVariables();
|
||||||
|
|
||||||
renderer->setFBO(hw_render.get_current_framebuffer());
|
renderer->setFBO(hwRender.get_current_framebuffer());
|
||||||
renderer->resetStateManager();
|
renderer->resetStateManager();
|
||||||
|
|
||||||
inputPollCallback();
|
inputPollCallback();
|
||||||
|
|
||||||
HIDService& hid = emulator->getServiceManager().getHID();
|
HIDService& hid = emulator->getServiceManager().getHID();
|
||||||
|
|
||||||
hid.setKey(HID::Keys::A, GetButtonState(RETRO_DEVICE_ID_JOYPAD_A));
|
hid.setKey(HID::Keys::A, getButtonState(RETRO_DEVICE_ID_JOYPAD_A));
|
||||||
hid.setKey(HID::Keys::B, GetButtonState(RETRO_DEVICE_ID_JOYPAD_B));
|
hid.setKey(HID::Keys::B, getButtonState(RETRO_DEVICE_ID_JOYPAD_B));
|
||||||
hid.setKey(HID::Keys::X, GetButtonState(RETRO_DEVICE_ID_JOYPAD_X));
|
hid.setKey(HID::Keys::X, getButtonState(RETRO_DEVICE_ID_JOYPAD_X));
|
||||||
hid.setKey(HID::Keys::Y, GetButtonState(RETRO_DEVICE_ID_JOYPAD_Y));
|
hid.setKey(HID::Keys::Y, getButtonState(RETRO_DEVICE_ID_JOYPAD_Y));
|
||||||
hid.setKey(HID::Keys::L, GetButtonState(RETRO_DEVICE_ID_JOYPAD_L));
|
hid.setKey(HID::Keys::L, getButtonState(RETRO_DEVICE_ID_JOYPAD_L));
|
||||||
hid.setKey(HID::Keys::R, GetButtonState(RETRO_DEVICE_ID_JOYPAD_R));
|
hid.setKey(HID::Keys::R, getButtonState(RETRO_DEVICE_ID_JOYPAD_R));
|
||||||
hid.setKey(HID::Keys::Start, GetButtonState(RETRO_DEVICE_ID_JOYPAD_START));
|
hid.setKey(HID::Keys::Start, getButtonState(RETRO_DEVICE_ID_JOYPAD_START));
|
||||||
hid.setKey(HID::Keys::Select, GetButtonState(RETRO_DEVICE_ID_JOYPAD_SELECT));
|
hid.setKey(HID::Keys::Select, getButtonState(RETRO_DEVICE_ID_JOYPAD_SELECT));
|
||||||
hid.setKey(HID::Keys::Up, GetButtonState(RETRO_DEVICE_ID_JOYPAD_UP));
|
hid.setKey(HID::Keys::Up, getButtonState(RETRO_DEVICE_ID_JOYPAD_UP));
|
||||||
hid.setKey(HID::Keys::Down, GetButtonState(RETRO_DEVICE_ID_JOYPAD_DOWN));
|
hid.setKey(HID::Keys::Down, getButtonState(RETRO_DEVICE_ID_JOYPAD_DOWN));
|
||||||
hid.setKey(HID::Keys::Left, GetButtonState(RETRO_DEVICE_ID_JOYPAD_LEFT));
|
hid.setKey(HID::Keys::Left, getButtonState(RETRO_DEVICE_ID_JOYPAD_LEFT));
|
||||||
hid.setKey(HID::Keys::Right, GetButtonState(RETRO_DEVICE_ID_JOYPAD_RIGHT));
|
hid.setKey(HID::Keys::Right, getButtonState(RETRO_DEVICE_ID_JOYPAD_RIGHT));
|
||||||
|
|
||||||
// Get analog values for the left analog stick (Right analog stick is N3DS-only and unimplemented)
|
// Get analog values for the left analog stick (Right analog stick is N3DS-only and unimplemented)
|
||||||
float xLeft = GetAxisState(RETRO_DEVICE_INDEX_ANALOG_LEFT, RETRO_DEVICE_ID_ANALOG_X);
|
float xLeft = getAxisState(RETRO_DEVICE_INDEX_ANALOG_LEFT, RETRO_DEVICE_ID_ANALOG_X);
|
||||||
float yLeft = GetAxisState(RETRO_DEVICE_INDEX_ANALOG_LEFT, RETRO_DEVICE_ID_ANALOG_Y);
|
float yLeft = getAxisState(RETRO_DEVICE_INDEX_ANALOG_LEFT, RETRO_DEVICE_ID_ANALOG_Y);
|
||||||
|
|
||||||
hid.setCirclepadX((xLeft / +32767) * 0x9C);
|
hid.setCirclepadX((xLeft / +32767) * 0x9C);
|
||||||
hid.setCirclepadY((yLeft / -32767) * 0x9C);
|
hid.setCirclepadY((yLeft / -32767) * 0x9C);
|
||||||
|
@ -355,7 +369,7 @@ void retro_run() {
|
||||||
hid.updateInputs(emulator->getTicks());
|
hid.updateInputs(emulator->getTicks());
|
||||||
emulator->runFrame();
|
emulator->runFrame();
|
||||||
|
|
||||||
videoCallbacks(RETRO_HW_FRAME_BUFFER_VALID, emulator->width, emulator->height, 0);
|
videoCallback(RETRO_HW_FRAME_BUFFER_VALID, emulator->width, emulator->height, 0);
|
||||||
}
|
}
|
||||||
|
|
||||||
void retro_set_controller_port_device(uint port, uint device) {}
|
void retro_set_controller_port_device(uint port, uint device) {}
|
||||||
|
|
35
src/lua.cpp
35
src/lua.cpp
|
@ -130,6 +130,32 @@ MAKE_MEMORY_FUNCTIONS(32)
|
||||||
MAKE_MEMORY_FUNCTIONS(64)
|
MAKE_MEMORY_FUNCTIONS(64)
|
||||||
#undef MAKE_MEMORY_FUNCTIONS
|
#undef MAKE_MEMORY_FUNCTIONS
|
||||||
|
|
||||||
|
static int readFloatThunk(lua_State* L) {
|
||||||
|
const u32 vaddr = (u32)lua_tonumber(L, 1);
|
||||||
|
lua_pushnumber(L, (lua_Number)Helpers::bit_cast<float, u32>(LuaManager::g_emulator->getMemory().read32(vaddr)));
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
static int writeFloatThunk(lua_State* L) {
|
||||||
|
const u32 vaddr = (u32)lua_tonumber(L, 1);
|
||||||
|
const float value = (float)lua_tonumber(L, 2);
|
||||||
|
LuaManager::g_emulator->getMemory().write32(vaddr, Helpers::bit_cast<u32, float>(value));
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
static int readDoubleThunk(lua_State* L) {
|
||||||
|
const u32 vaddr = (u32)lua_tonumber(L, 1);
|
||||||
|
lua_pushnumber(L, (lua_Number)Helpers::bit_cast<double, u64>(LuaManager::g_emulator->getMemory().read64(vaddr)));
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
static int writeDoubleThunk(lua_State* L) {
|
||||||
|
const u32 vaddr = (u32)lua_tonumber(L, 1);
|
||||||
|
const double value = (double)lua_tonumber(L, 2);
|
||||||
|
LuaManager::g_emulator->getMemory().write64(vaddr, Helpers::bit_cast<u64, double>(value));
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
static int getAppIDThunk(lua_State* L) {
|
static int getAppIDThunk(lua_State* L) {
|
||||||
std::optional<u64> id = LuaManager::g_emulator->getMemory().getProgramID();
|
std::optional<u64> id = LuaManager::g_emulator->getMemory().getProgramID();
|
||||||
|
|
||||||
|
@ -248,10 +274,14 @@ static constexpr luaL_Reg functions[] = {
|
||||||
{ "__read16", read16Thunk },
|
{ "__read16", read16Thunk },
|
||||||
{ "__read32", read32Thunk },
|
{ "__read32", read32Thunk },
|
||||||
{ "__read64", read64Thunk },
|
{ "__read64", read64Thunk },
|
||||||
|
{ "__readFloat", readFloatThunk },
|
||||||
|
{ "__readDouble", readDoubleThunk },
|
||||||
{ "__write8", write8Thunk} ,
|
{ "__write8", write8Thunk} ,
|
||||||
{ "__write16", write16Thunk },
|
{ "__write16", write16Thunk },
|
||||||
{ "__write32", write32Thunk },
|
{ "__write32", write32Thunk },
|
||||||
{ "__write64", write64Thunk },
|
{ "__write64", write64Thunk },
|
||||||
|
{ "__writeFloat", writeFloatThunk },
|
||||||
|
{ "__writeDouble", writeDoubleThunk },
|
||||||
{ "__getAppID", getAppIDThunk },
|
{ "__getAppID", getAppIDThunk },
|
||||||
{ "__pause", pauseThunk },
|
{ "__pause", pauseThunk },
|
||||||
{ "__resume", resumeThunk },
|
{ "__resume", resumeThunk },
|
||||||
|
@ -273,10 +303,15 @@ void LuaManager::initializeThunks() {
|
||||||
read16 = function(addr) return GLOBALS.__read16(addr) end,
|
read16 = function(addr) return GLOBALS.__read16(addr) end,
|
||||||
read32 = function(addr) return GLOBALS.__read32(addr) end,
|
read32 = function(addr) return GLOBALS.__read32(addr) end,
|
||||||
read64 = function(addr) return GLOBALS.__read64(addr) end,
|
read64 = function(addr) return GLOBALS.__read64(addr) end,
|
||||||
|
readFloat = function(addr) return GLOBALS.__readFloat(addr) end,
|
||||||
|
readDouble = function(addr) return GLOBALS.__readDouble(addr) end,
|
||||||
|
|
||||||
write8 = function(addr, value) GLOBALS.__write8(addr, value) end,
|
write8 = function(addr, value) GLOBALS.__write8(addr, value) end,
|
||||||
write16 = function(addr, value) GLOBALS.__write16(addr, value) end,
|
write16 = function(addr, value) GLOBALS.__write16(addr, value) end,
|
||||||
write32 = function(addr, value) GLOBALS.__write32(addr, value) end,
|
write32 = function(addr, value) GLOBALS.__write32(addr, value) end,
|
||||||
write64 = function(addr, value) GLOBALS.__write64(addr, value) end,
|
write64 = function(addr, value) GLOBALS.__write64(addr, value) end,
|
||||||
|
writeFloat = function(addr, value) GLOBALS.__writeFloat(addr, value) end,
|
||||||
|
writeDouble = function(addr, value) GLOBALS.__writeDouble(addr, value) end,
|
||||||
|
|
||||||
getAppID = function()
|
getAppID = function()
|
||||||
local ffi = require("ffi")
|
local ffi = require("ffi")
|
||||||
|
|
|
@ -1,11 +1,13 @@
|
||||||
#include "panda_qt/about_window.hpp"
|
#include "panda_qt/about_window.hpp"
|
||||||
#include "version.hpp"
|
|
||||||
|
|
||||||
|
#include <QHBoxLayout>
|
||||||
#include <QLabel>
|
#include <QLabel>
|
||||||
#include <QTextEdit>
|
#include <QTextEdit>
|
||||||
#include <QVBoxLayout>
|
#include <QVBoxLayout>
|
||||||
#include <QtGlobal>
|
#include <QtGlobal>
|
||||||
|
|
||||||
|
#include "version.hpp"
|
||||||
|
|
||||||
// Based on https://github.com/dolphin-emu/dolphin/blob/master/Source/Core/DolphinQt/AboutDialog.cpp
|
// Based on https://github.com/dolphin-emu/dolphin/blob/master/Source/Core/DolphinQt/AboutDialog.cpp
|
||||||
|
|
||||||
AboutWindow::AboutWindow(QWidget* parent) : QDialog(parent) {
|
AboutWindow::AboutWindow(QWidget* parent) : QDialog(parent) {
|
||||||
|
|
|
@ -1,15 +1,9 @@
|
||||||
#include "panda_qt/cheats_window.hpp"
|
#include "panda_qt/cheats_window.hpp"
|
||||||
|
|
||||||
#include <QCheckBox>
|
|
||||||
#include <QDialog>
|
|
||||||
#include <QDialogButtonBox>
|
#include <QDialogButtonBox>
|
||||||
#include <QLabel>
|
#include <QHBoxLayout>
|
||||||
#include <QLineEdit>
|
|
||||||
#include <QListWidget>
|
|
||||||
#include <QPushButton>
|
|
||||||
#include <QTextEdit>
|
|
||||||
#include <QVBoxLayout>
|
|
||||||
#include <QTimer>
|
#include <QTimer>
|
||||||
|
#include <QVBoxLayout>
|
||||||
#include <functional>
|
#include <functional>
|
||||||
|
|
||||||
#include "cheats.hpp"
|
#include "cheats.hpp"
|
||||||
|
@ -18,71 +12,17 @@
|
||||||
|
|
||||||
MainWindow* mainWindow = nullptr;
|
MainWindow* mainWindow = nullptr;
|
||||||
|
|
||||||
struct CheatMetadata {
|
|
||||||
u32 handle = Cheats::badCheatHandle;
|
|
||||||
std::string name = "New cheat";
|
|
||||||
std::string code;
|
|
||||||
bool enabled = true;
|
|
||||||
};
|
|
||||||
|
|
||||||
void dispatchToMainThread(std::function<void()> callback) {
|
void dispatchToMainThread(std::function<void()> callback) {
|
||||||
QTimer* timer = new QTimer();
|
QTimer* timer = new QTimer();
|
||||||
timer->moveToThread(qApp->thread());
|
timer->moveToThread(qApp->thread());
|
||||||
timer->setSingleShot(true);
|
timer->setSingleShot(true);
|
||||||
QObject::connect(timer, &QTimer::timeout, [=]()
|
QObject::connect(timer, &QTimer::timeout, [=]() {
|
||||||
{
|
|
||||||
callback();
|
callback();
|
||||||
timer->deleteLater();
|
timer->deleteLater();
|
||||||
});
|
});
|
||||||
QMetaObject::invokeMethod(timer, "start", Qt::QueuedConnection, Q_ARG(int, 0));
|
QMetaObject::invokeMethod(timer, "start", Qt::QueuedConnection, Q_ARG(int, 0));
|
||||||
}
|
}
|
||||||
|
|
||||||
class CheatEntryWidget : public QWidget {
|
|
||||||
public:
|
|
||||||
CheatEntryWidget(Emulator* emu, CheatMetadata metadata, QListWidget* parent);
|
|
||||||
|
|
||||||
void Update() {
|
|
||||||
name->setText(metadata.name.c_str());
|
|
||||||
enabled->setChecked(metadata.enabled);
|
|
||||||
update();
|
|
||||||
}
|
|
||||||
|
|
||||||
void Remove() {
|
|
||||||
emu->getCheats().removeCheat(metadata.handle);
|
|
||||||
cheatList->takeItem(cheatList->row(listItem));
|
|
||||||
deleteLater();
|
|
||||||
}
|
|
||||||
|
|
||||||
const CheatMetadata& getMetadata() { return metadata; }
|
|
||||||
void setMetadata(const CheatMetadata& metadata) { this->metadata = metadata; }
|
|
||||||
|
|
||||||
private:
|
|
||||||
void checkboxChanged(int state);
|
|
||||||
void editClicked();
|
|
||||||
|
|
||||||
Emulator* emu;
|
|
||||||
CheatMetadata metadata;
|
|
||||||
u32 handle;
|
|
||||||
QLabel* name;
|
|
||||||
QCheckBox* enabled;
|
|
||||||
QListWidget* cheatList;
|
|
||||||
QListWidgetItem* listItem;
|
|
||||||
};
|
|
||||||
|
|
||||||
class CheatEditDialog : public QDialog {
|
|
||||||
public:
|
|
||||||
CheatEditDialog(Emulator* emu, CheatEntryWidget& cheatEntry);
|
|
||||||
|
|
||||||
void accepted();
|
|
||||||
void rejected();
|
|
||||||
|
|
||||||
private:
|
|
||||||
Emulator* emu;
|
|
||||||
CheatEntryWidget& cheatEntry;
|
|
||||||
QTextEdit* codeEdit;
|
|
||||||
QLineEdit* nameEdit;
|
|
||||||
};
|
|
||||||
|
|
||||||
CheatEntryWidget::CheatEntryWidget(Emulator* emu, CheatMetadata metadata, QListWidget* parent)
|
CheatEntryWidget::CheatEntryWidget(Emulator* emu, CheatMetadata metadata, QListWidget* parent)
|
||||||
: QWidget(), emu(emu), metadata(metadata), cheatList(parent) {
|
: QWidget(), emu(emu), metadata(metadata), cheatList(parent) {
|
||||||
QHBoxLayout* layout = new QHBoxLayout;
|
QHBoxLayout* layout = new QHBoxLayout;
|
||||||
|
|
|
@ -1,7 +1,7 @@
|
||||||
#include "input_mappings.hpp"
|
|
||||||
|
|
||||||
#include <QKeyEvent>
|
#include <QKeyEvent>
|
||||||
|
|
||||||
|
#include "input_mappings.hpp"
|
||||||
|
|
||||||
InputMappings InputMappings::defaultKeyboardMappings() {
|
InputMappings InputMappings::defaultKeyboardMappings() {
|
||||||
InputMappings mappings;
|
InputMappings mappings;
|
||||||
mappings.setMapping(Qt::Key_L, HID::Keys::A);
|
mappings.setMapping(Qt::Key_L, HID::Keys::A);
|
||||||
|
|
|
@ -1,8 +1,9 @@
|
||||||
|
#include "panda_qt/shader_editor.hpp"
|
||||||
|
|
||||||
#include <QPushButton>
|
#include <QPushButton>
|
||||||
#include <QVBoxLayout>
|
#include <QVBoxLayout>
|
||||||
|
|
||||||
#include "panda_qt/main_window.hpp"
|
#include "panda_qt/main_window.hpp"
|
||||||
#include "panda_qt/shader_editor.hpp"
|
|
||||||
|
|
||||||
using namespace Zep;
|
using namespace Zep;
|
||||||
|
|
||||||
|
|
1
third_party/fdk-aac
vendored
Submodule
1
third_party/fdk-aac
vendored
Submodule
|
@ -0,0 +1 @@
|
||||||
|
Subproject commit 5559136bb53ce38f6f07dac4f47674dd4f032d03
|
Loading…
Add table
Reference in a new issue