mirror of
https://github.com/wheremyfoodat/Panda3DS.git
synced 2025-04-06 14:15:41 +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() {
|
||||
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
|
||||
+++ b/src/host_shaders/opengl_fragment_shader.frag
|
||||
@@ -1,4 +1,5 @@
|
||||
|
@ -31,6 +31,17 @@ index b9f9fe4c..f1cf286f 100644
|
|||
|
||||
in vec4 v_quaternion;
|
||||
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) {
|
||||
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
|
||||
@@ -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
|
||||
|
||||
bool current_sampler_enabled = isSamplerEnabled(environment_id, lut_id); // 7 luts per environment
|
||||
|
@ -70,19 +90,16 @@ index b9f9fe4c..f1cf286f 100644
|
|||
switch (input_id) {
|
||||
case 0u: {
|
||||
delta = dot(normal, normalize(half_vector));
|
||||
@@ -241,11 +248,11 @@ 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)));
|
||||
@@ -243,9 +250,9 @@ float lightLutLookup(uint environment_id, uint lut_id, uint light_id, vec3 light
|
||||
|
||||
- // 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
|
||||
// Sign extend them. Normally bitfieldExtract would do that but it's missing on some versions
|
||||
// of GLSL so we do it manually
|
||||
- int se_x = bitfieldExtract(GPUREG_LIGHTi_SPOTDIR_LOW, 0, 13);
|
||||
- int se_y = bitfieldExtract(GPUREG_LIGHTi_SPOTDIR_LOW, 16, 13);
|
||||
- int se_z = bitfieldExtract(GPUREG_LIGHTi_SPOTDIR_HIGH, 0, 13);
|
||||
+ int se_x = bitfieldExtractCompat(GPUREG_LIGHTi_SPOTDIR_LOW, 0, 13);
|
||||
+ int se_y = bitfieldExtractCompat(GPUREG_LIGHTi_SPOTDIR_LOW, 16, 13);
|
||||
+ int se_z = bitfieldExtractCompat(GPUREG_LIGHTi_SPOTDIR_HIGH, 0, 13);
|
||||
+ int se_x = int(bitfieldExtractCompat(uint(GPUREG_LIGHTi_SPOTDIR_LOW), 0, 13));
|
||||
+ int se_y = int(bitfieldExtractCompat(uint(GPUREG_LIGHTi_SPOTDIR_LOW), 16, 13));
|
||||
+ int se_z = int(bitfieldExtractCompat(uint(GPUREG_LIGHTi_SPOTDIR_HIGH), 0, 13));
|
||||
|
||||
if ((se_x & 0x1000) == 0x1000) se_x |= 0xffffe000;
|
||||
if ((se_y & 0x1000) == 0x1000) se_y |= 0xffffe000;
|
||||
|
@ -225,10 +242,10 @@ index 057f9a88..dc735ced 100644
|
|||
v_quaternion = a_quaternion;
|
||||
}
|
||||
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
|
||||
+++ 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 enableBlend() { glEnable(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
|
||||
|
||||
steps:
|
||||
- uses: actions/checkout@v2
|
||||
- uses: actions/checkout@v4
|
||||
- name: Fetch submodules
|
||||
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
|
||||
|
||||
steps:
|
||||
- uses: actions/checkout@v2
|
||||
- uses: actions/checkout@v4
|
||||
- name: Fetch submodules
|
||||
run: git submodule update --init --recursive
|
||||
|
||||
|
@ -58,7 +58,7 @@ jobs:
|
|||
runs-on: macos-13
|
||||
|
||||
steps:
|
||||
- uses: actions/checkout@v2
|
||||
- uses: actions/checkout@v4
|
||||
- name: Fetch submodules
|
||||
run: git submodule update --init --recursive
|
||||
|
||||
|
@ -101,7 +101,7 @@ jobs:
|
|||
runs-on: ubuntu-latest
|
||||
|
||||
steps:
|
||||
- uses: actions/checkout@v2
|
||||
- uses: actions/checkout@v4
|
||||
- name: Fetch submodules
|
||||
run: git submodule update --init --recursive
|
||||
|
||||
|
@ -154,7 +154,7 @@ jobs:
|
|||
runs-on: ubuntu-latest
|
||||
|
||||
steps:
|
||||
- uses: actions/checkout@v2
|
||||
- uses: actions/checkout@v4
|
||||
- name: Fetch submodules
|
||||
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
|
||||
|
||||
steps:
|
||||
- uses: actions/checkout@v2
|
||||
- uses: actions/checkout@v4
|
||||
- name: Fetch submodules
|
||||
run: git submodule update --init --recursive
|
||||
|
||||
|
@ -52,7 +52,7 @@ jobs:
|
|||
run: ./.github/linux-appimage.sh
|
||||
|
||||
- name: Upload executable
|
||||
uses: actions/upload-artifact@v2
|
||||
uses: actions/upload-artifact@v4
|
||||
with:
|
||||
name: Linux executable
|
||||
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
|
||||
|
||||
steps:
|
||||
- uses: actions/checkout@v2
|
||||
- uses: actions/checkout@v4
|
||||
- name: Fetch submodules
|
||||
run: git submodule update --init --recursive
|
||||
|
||||
|
@ -49,7 +49,7 @@ jobs:
|
|||
run: cmake --build ${{github.workspace}}/build --config ${{env.BUILD_TYPE}}
|
||||
|
||||
- name: Upload executable
|
||||
uses: actions/upload-artifact@v2
|
||||
uses: actions/upload-artifact@v4
|
||||
with:
|
||||
name: Linux executable
|
||||
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
|
||||
|
||||
steps:
|
||||
- uses: actions/checkout@v2
|
||||
- uses: actions/checkout@v4
|
||||
- name: Fetch submodules
|
||||
run: git submodule update --init --recursive
|
||||
|
||||
|
@ -40,7 +40,7 @@ jobs:
|
|||
run: cmake --build ${{github.workspace}}/build --config ${{env.BUILD_TYPE}}
|
||||
|
||||
- name: Install bundle dependencies
|
||||
run: brew install --overwrite python@3.12 && brew install dylibbundler imagemagick
|
||||
run: brew install dylibbundler imagemagick
|
||||
|
||||
- name: Run bundle script
|
||||
run: ./.github/mac-bundle.sh
|
||||
|
@ -52,7 +52,7 @@ jobs:
|
|||
run: zip -r Alber Alber.app
|
||||
|
||||
- name: Upload MacOS App
|
||||
uses: actions/upload-artifact@v2
|
||||
uses: actions/upload-artifact@v4
|
||||
with:
|
||||
name: MacOS Alber App Bundle
|
||||
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
|
||||
|
||||
steps:
|
||||
- uses: actions/checkout@v2
|
||||
- uses: actions/checkout@v4
|
||||
- name: Fetch submodules
|
||||
run: git submodule update --init --recursive
|
||||
|
||||
|
@ -45,7 +45,7 @@ jobs:
|
|||
windeployqt --dir upload upload/Alber.exe
|
||||
|
||||
- name: Upload executable
|
||||
uses: actions/upload-artifact@v2
|
||||
uses: actions/upload-artifact@v4
|
||||
with:
|
||||
name: Windows executable
|
||||
path: upload
|
||||
|
@ -54,7 +54,7 @@ jobs:
|
|||
runs-on: macos-13
|
||||
|
||||
steps:
|
||||
- uses: actions/checkout@v2
|
||||
- uses: actions/checkout@v4
|
||||
- name: Fetch submodules
|
||||
run: git submodule update --init --recursive
|
||||
|
||||
|
@ -67,7 +67,7 @@ jobs:
|
|||
|
||||
- name: Install bundle dependencies
|
||||
run: |
|
||||
brew install --overwrite python@3.12 && brew install dylibbundler imagemagick
|
||||
brew install dylibbundler imagemagick
|
||||
|
||||
- name: Install qt
|
||||
run: brew install qt && which macdeployqt
|
||||
|
@ -90,7 +90,7 @@ jobs:
|
|||
run: zip -r Alber Alber.app
|
||||
|
||||
- name: Upload MacOS App
|
||||
uses: actions/upload-artifact@v2
|
||||
uses: actions/upload-artifact@v4
|
||||
with:
|
||||
name: MacOS Alber App Bundle
|
||||
path: 'Alber.zip'
|
||||
|
@ -99,7 +99,7 @@ jobs:
|
|||
runs-on: ubuntu-latest
|
||||
|
||||
steps:
|
||||
- uses: actions/checkout@v2
|
||||
- uses: actions/checkout@v4
|
||||
- name: Fetch submodules
|
||||
run: git submodule update --init --recursive
|
||||
|
||||
|
@ -135,7 +135,7 @@ jobs:
|
|||
./.github/linux-appimage-qt.sh
|
||||
|
||||
- name: Upload executable
|
||||
uses: actions/upload-artifact@v2
|
||||
uses: actions/upload-artifact@v4
|
||||
with:
|
||||
name: Linux executable
|
||||
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
|
||||
|
||||
steps:
|
||||
- uses: actions/checkout@v2
|
||||
- uses: actions/checkout@v4
|
||||
- name: Fetch submodules
|
||||
run: git submodule update --init --recursive
|
||||
|
||||
|
@ -40,7 +40,7 @@ jobs:
|
|||
run: cmake --build ${{github.workspace}}/build --config ${{env.BUILD_TYPE}}
|
||||
|
||||
- name: Upload executable
|
||||
uses: actions/upload-artifact@v2
|
||||
uses: actions/upload-artifact@v4
|
||||
with:
|
||||
name: Windows executable
|
||||
path: './build/${{ env.BUILD_TYPE }}/Alber.exe'
|
||||
|
|
3
.gitmodules
vendored
3
.gitmodules
vendored
|
@ -79,3 +79,6 @@
|
|||
[submodule "third_party/fmt"]
|
||||
path = third_party/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")
|
||||
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(GPU_DEBUG_INFO "Enable additional GPU debugging info" OFF)
|
||||
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(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)
|
||||
set(CMAKE_POSITION_INDEPENDENT_CODE ON)
|
||||
endif()
|
||||
|
@ -213,6 +227,7 @@ else()
|
|||
endif()
|
||||
|
||||
add_subdirectory(third_party/teakra EXCLUDE_FROM_ALL)
|
||||
add_subdirectory(third_party/fdk-aac)
|
||||
|
||||
set(CAPSTONE_ARCHITECTURE_DEFAULT OFF)
|
||||
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
|
||||
)
|
||||
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)
|
||||
|
||||
|
@ -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/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/align.hpp
|
||||
include/align.hpp include/audio/aac_decoder.hpp
|
||||
)
|
||||
|
||||
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})
|
||||
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)
|
||||
|
||||
if(ENABLE_DISCORD_RPC AND NOT ANDROID)
|
||||
|
@ -508,6 +523,9 @@ if(NOT BUILD_HYDRA_CORE AND NOT BUILD_LIBRETRO_CORE)
|
|||
if(NOT ENABLE_OPENGL)
|
||||
message(FATAL_ERROR "Qt frontend requires OpenGL")
|
||||
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
|
||||
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
|
||||
|
@ -545,6 +563,17 @@ if(NOT BUILD_HYDRA_CORE AND NOT BUILD_LIBRETRO_CORE)
|
|||
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"
|
||||
PREFIX "/"
|
||||
FILES
|
||||
|
|
|
@ -54,6 +54,15 @@ namespace Audio::AAC {
|
|||
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 {
|
||||
u16_le mode = Mode::None; // Encode or decode AAC?
|
||||
u16_le command = Command::Init;
|
||||
|
@ -62,7 +71,9 @@ namespace Audio::AAC {
|
|||
// Info on the AAC request
|
||||
union {
|
||||
std::array<u8, 24> commandData{};
|
||||
|
||||
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<16, 1, u32> masterVolumeDirty;
|
||||
|
||||
BitField<24, 1, u32> auxReturnVolume0Dirty;
|
||||
BitField<25, 1, u32> auxReturnVolume1Dirty;
|
||||
BitField<24, 1, u32> auxVolume0Dirty;
|
||||
BitField<25, 1, u32> auxVolume1Dirty;
|
||||
BitField<26, 1, u32> outputFormatDirty;
|
||||
BitField<27, 1, u32> clippingModeDirty;
|
||||
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
|
||||
/// each at the final mixer.
|
||||
float_le masterVolume;
|
||||
std::array<float_le, 2> auxReturnVolume;
|
||||
std::array<float_le, 2> auxVolumes;
|
||||
|
||||
u16_le outputBufferCount;
|
||||
u16 pad1[2];
|
||||
|
@ -422,7 +422,7 @@ namespace Audio::HLE {
|
|||
|
||||
struct DspStatus {
|
||||
u16_le unknown;
|
||||
u16_le dropped_frames;
|
||||
u16_le droppedFrames;
|
||||
u16 pad0[0xE];
|
||||
};
|
||||
ASSERT_DSP_STRUCT(DspStatus, 32);
|
||||
|
|
|
@ -2,10 +2,12 @@
|
|||
#include <array>
|
||||
#include <cassert>
|
||||
#include <deque>
|
||||
#include <memory>
|
||||
#include <queue>
|
||||
#include <vector>
|
||||
|
||||
#include "audio/aac.hpp"
|
||||
#include "audio/aac_decoder.hpp"
|
||||
#include "audio/dsp_core.hpp"
|
||||
#include "audio/dsp_shared_mem.hpp"
|
||||
#include "memory.hpp"
|
||||
|
@ -33,8 +35,8 @@ namespace Audio {
|
|||
SampleFormat format;
|
||||
SourceType sourceType;
|
||||
|
||||
bool fromQueue = false; // Is this buffer from the buffer queue or an embedded buffer?
|
||||
bool hasPlayedOnce = false; // Has the buffer been played at least once before?
|
||||
bool fromQueue = false; // Is this buffer from the buffer queue or an embedded buffer?
|
||||
bool hasPlayedOnce = false; // Has the buffer been played at least once before?
|
||||
|
||||
bool operator<(const Buffer& other) const {
|
||||
// Lower ID = Higher priority
|
||||
|
@ -93,8 +95,7 @@ namespace Audio {
|
|||
DSPSource() { reset(); }
|
||||
};
|
||||
|
||||
class HLE_DSP : public DSPCore {
|
||||
// The audio frame types are public in case we want to use them for unit tests
|
||||
class DSPMixer {
|
||||
public:
|
||||
template <typename T, usize channelCount = 1>
|
||||
using Sample = std::array<T, channelCount>;
|
||||
|
@ -111,6 +112,43 @@ namespace Audio {
|
|||
template <typename T>
|
||||
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 SampleBuffer = Source::SampleBuffer;
|
||||
|
||||
|
@ -129,6 +167,9 @@ namespace Audio {
|
|||
std::array<Source, Audio::HLE::sourceCount> sources; // DSP voices
|
||||
Audio::HLE::DspMemory dspRam;
|
||||
|
||||
Audio::DSPMixer mixer;
|
||||
std::unique_ptr<Audio::AAC::Decoder> aacDecoder;
|
||||
|
||||
void resetAudioPipe();
|
||||
bool loaded = false; // Have we loaded a component?
|
||||
|
||||
|
@ -171,10 +212,13 @@ namespace Audio {
|
|||
|
||||
void handleAACRequest(const AAC::Message& request);
|
||||
void updateSourceConfig(Source& source, HLE::SourceConfiguration::Configuration& config, s16_le* adpcmCoefficients);
|
||||
void updateMixerConfig(HLE::SharedMemory& sharedMem);
|
||||
void generateFrame(StereoFrame<s16>& frame);
|
||||
void generateFrame(DSPSource& source);
|
||||
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
|
||||
void decodeBuffer(DSPSource& source);
|
||||
|
||||
|
|
|
@ -1,6 +1,13 @@
|
|||
#pragma once
|
||||
|
||||
#include <QAction>
|
||||
#include <QCheckBox>
|
||||
#include <QDialog>
|
||||
#include <QLabel>
|
||||
#include <QLineEdit>
|
||||
#include <QListWidget>
|
||||
#include <QPushButton>
|
||||
#include <QTextEdit>
|
||||
#include <QWidget>
|
||||
#include <filesystem>
|
||||
#include <memory>
|
||||
|
@ -24,3 +31,60 @@ class CheatsWindow final : public QWidget {
|
|||
std::filesystem::path cheatPath;
|
||||
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();
|
||||
|
||||
void closeEvent(QCloseEvent *event) override;
|
||||
void closeEvent(QCloseEvent* event) override;
|
||||
void keyPressEvent(QKeyEvent* event) override;
|
||||
void keyReleaseEvent(QKeyEvent* event) override;
|
||||
void mousePressEvent(QMouseEvent* event) override;
|
||||
|
|
|
@ -111,6 +111,7 @@ std::string FragmentGenerator::generate(const FragmentConfig& config) {
|
|||
if (api == API::GLES) {
|
||||
ret += R"(
|
||||
#define USING_GLES 1
|
||||
#define fma(a, b, c) ((a) * (b) + (c))
|
||||
|
||||
precision mediump int;
|
||||
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";
|
||||
|
||||
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);
|
||||
|
@ -641,7 +642,7 @@ void FragmentGenerator::compileLUTLookup(std::string& shader, const PICA::Fragme
|
|||
if (absEnabled) {
|
||||
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 += "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) {
|
||||
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]
|
||||
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 += "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) {
|
||||
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 <utility>
|
||||
|
||||
#include "audio/aac_decoder.hpp"
|
||||
#include "services/dsp.hpp"
|
||||
|
||||
namespace Audio {
|
||||
|
@ -23,6 +24,8 @@ namespace Audio {
|
|||
for (int i = 0; i < sources.size(); i++) {
|
||||
sources[i].index = i;
|
||||
}
|
||||
|
||||
aacDecoder.reset(new Audio::AAC::Decoder());
|
||||
}
|
||||
|
||||
void HLE_DSP::resetAudioPipe() {
|
||||
|
@ -73,6 +76,7 @@ namespace Audio {
|
|||
source.reset();
|
||||
}
|
||||
|
||||
mixer.reset();
|
||||
// Note: Reset audio pipe AFTER resetting all pipes, otherwise the new data will be yeeted
|
||||
resetAudioPipe();
|
||||
}
|
||||
|
@ -247,6 +251,8 @@ namespace Audio {
|
|||
|
||||
source.isBufferIDDirty = false;
|
||||
}
|
||||
|
||||
performMix(read, write);
|
||||
}
|
||||
|
||||
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) {
|
||||
SampleBuffer decodedSamples(sampleCount);
|
||||
|
||||
|
@ -584,7 +634,6 @@ namespace Audio {
|
|||
switch (request.command) {
|
||||
case AAC::Command::EncodeDecode:
|
||||
// Dummy response to stop games from hanging
|
||||
// TODO: Fix this when implementing AAC
|
||||
response.resultCode = AAC::ResultCode::Success;
|
||||
response.decodeResponse.channelCount = 2;
|
||||
response.decodeResponse.sampleCount = 1024;
|
||||
|
@ -593,6 +642,10 @@ namespace Audio {
|
|||
|
||||
response.command = request.command;
|
||||
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;
|
||||
|
||||
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) {
|
||||
auto self = reinterpret_cast<MiniAudioDevice*>(device->pUserData);
|
||||
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
|
||||
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 (!self->running) {
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
self->samples->pop(output, frameCount * channelCount);
|
||||
self->samples->pop(output, maxSamples);
|
||||
};
|
||||
|
||||
if (ma_device_init(&context, &deviceConfig, &device) != MA_SUCCESS) {
|
||||
|
|
|
@ -51,7 +51,7 @@ void RendererGL::reset() {
|
|||
gl.useProgram(oldProgram); // Switch to old GL program
|
||||
}
|
||||
|
||||
#ifdef __ANDROID__
|
||||
#ifdef USING_GLES
|
||||
fragShaderGen.setTarget(PICA::ShaderGen::API::GLES, PICA::ShaderGen::Language::GLSL);
|
||||
#endif
|
||||
}
|
||||
|
|
|
@ -114,7 +114,7 @@ hydra::Size HydraCore::getNativeSize() { return {400, 480}; }
|
|||
void HydraCore::setOutputSize(hydra::Size size) {}
|
||||
|
||||
void HydraCore::resetContext() {
|
||||
#ifdef __ANDROID__
|
||||
#ifdef USING_GLES
|
||||
if (!gladLoadGLES2Loader(reinterpret_cast<GLADloadproc>(getProcAddress))) {
|
||||
Helpers::panic("OpenGL ES init failed");
|
||||
}
|
||||
|
|
|
@ -8,13 +8,13 @@
|
|||
#include <emulator.hpp>
|
||||
#include <renderer_gl/renderer_gl.hpp>
|
||||
|
||||
static retro_environment_t envCallbacks;
|
||||
static retro_video_refresh_t videoCallbacks;
|
||||
static retro_environment_t envCallback;
|
||||
static retro_video_refresh_t videoCallback;
|
||||
static retro_audio_sample_batch_t audioBatchCallback;
|
||||
static retro_input_poll_t inputPollCallback;
|
||||
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 bool screenTouched;
|
||||
|
@ -30,17 +30,17 @@ std::filesystem::path Emulator::getAppDataRoot() {
|
|||
return std::filesystem::path(savePath / "Emulator Files");
|
||||
}
|
||||
|
||||
static void* GetGLProcAddress(const char* name) {
|
||||
return (void*)hw_render.get_proc_address(name);
|
||||
static void* getGLProcAddress(const char* name) {
|
||||
return (void*)hwRender.get_proc_address(name);
|
||||
}
|
||||
|
||||
static void VideoResetContext() {
|
||||
static void videoResetContext() {
|
||||
#ifdef USING_GLES
|
||||
if (!gladLoadGLES2Loader(reinterpret_cast<GLADloadproc>(GetGLProcAddress))) {
|
||||
if (!gladLoadGLES2Loader(reinterpret_cast<GLADloadproc>(getGLProcAddress))) {
|
||||
Helpers::panic("OpenGL ES init failed");
|
||||
}
|
||||
#else
|
||||
if (!gladLoadGLLoader(reinterpret_cast<GLADloadproc>(GetGLProcAddress))) {
|
||||
if (!gladLoadGLLoader(reinterpret_cast<GLADloadproc>(getGLProcAddress))) {
|
||||
Helpers::panic("OpenGL init failed");
|
||||
}
|
||||
#endif
|
||||
|
@ -48,31 +48,31 @@ static void VideoResetContext() {
|
|||
emulator->initGraphicsContext(nullptr);
|
||||
}
|
||||
|
||||
static void VideoDestroyContext() {
|
||||
emulator->deinitGraphicsContext();
|
||||
static void videoDestroyContext() {
|
||||
emulator->deinitGraphicsContext();
|
||||
}
|
||||
|
||||
static bool SetHWRender(retro_hw_context_type type) {
|
||||
hw_render.context_type = type;
|
||||
hw_render.context_reset = VideoResetContext;
|
||||
hw_render.context_destroy = VideoDestroyContext;
|
||||
hw_render.bottom_left_origin = true;
|
||||
static bool setHWRender(retro_hw_context_type type) {
|
||||
hwRender.context_type = type;
|
||||
hwRender.context_reset = videoResetContext;
|
||||
hwRender.context_destroy = videoDestroyContext;
|
||||
hwRender.bottom_left_origin = true;
|
||||
|
||||
switch (type) {
|
||||
case RETRO_HW_CONTEXT_OPENGL_CORE:
|
||||
hw_render.version_major = 4;
|
||||
hw_render.version_minor = 1;
|
||||
hwRender.version_major = 4;
|
||||
hwRender.version_minor = 1;
|
||||
|
||||
if (envCallbacks(RETRO_ENVIRONMENT_SET_HW_RENDER, &hw_render)) {
|
||||
if (envCallback(RETRO_ENVIRONMENT_SET_HW_RENDER, &hwRender)) {
|
||||
return true;
|
||||
}
|
||||
break;
|
||||
case RETRO_HW_CONTEXT_OPENGLES3:
|
||||
case RETRO_HW_CONTEXT_OPENGL:
|
||||
hw_render.version_major = 3;
|
||||
hw_render.version_minor = 1;
|
||||
hwRender.version_major = 3;
|
||||
hwRender.version_minor = 1;
|
||||
|
||||
if (envCallbacks(RETRO_ENVIRONMENT_SET_HW_RENDER, &hw_render)) {
|
||||
if (envCallback(RETRO_ENVIRONMENT_SET_HW_RENDER, &hwRender)) {
|
||||
return true;
|
||||
}
|
||||
break;
|
||||
|
@ -84,18 +84,18 @@ static bool SetHWRender(retro_hw_context_type type) {
|
|||
|
||||
static void videoInit() {
|
||||
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 (SetHWRender(RETRO_HW_CONTEXT_OPENGL_CORE)) return;
|
||||
if (SetHWRender(RETRO_HW_CONTEXT_OPENGL)) return;
|
||||
if (SetHWRender(RETRO_HW_CONTEXT_OPENGLES3)) return;
|
||||
if (preferred && setHWRender(preferred)) return;
|
||||
if (setHWRender(RETRO_HW_CONTEXT_OPENGL_CORE)) return;
|
||||
if (setHWRender(RETRO_HW_CONTEXT_OPENGL)) 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 float GetAxisState(uint index, uint id) { return inputStateCallback(0, RETRO_DEVICE_ANALOG, index, 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 void inputInit() {
|
||||
static const retro_controller_description controllers[] = {
|
||||
|
@ -108,7 +108,7 @@ static void inputInit() {
|
|||
{NULL, 0},
|
||||
};
|
||||
|
||||
envCallbacks(RETRO_ENVIRONMENT_SET_CONTROLLER_INFO, (void*)ports);
|
||||
envCallback(RETRO_ENVIRONMENT_SET_CONTROLLER_INFO, (void*)ports);
|
||||
|
||||
retro_input_descriptor desc[] = {
|
||||
{0, RETRO_DEVICE_JOYPAD, 0, RETRO_DEVICE_ID_JOYPAD_LEFT, "Left"},
|
||||
|
@ -128,14 +128,14 @@ static void inputInit() {
|
|||
{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};
|
||||
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());
|
||||
return def;
|
||||
}
|
||||
|
@ -143,13 +143,27 @@ static std::string FetchVariable(std::string key, std::string def) {
|
|||
return std::string(var.value);
|
||||
}
|
||||
|
||||
static bool FetchVariableBool(std::string key, bool def) {
|
||||
return FetchVariable(key, def ? "enabled" : "disabled") == "enabled";
|
||||
static int fetchVariableInt(std::string key, int def) {
|
||||
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 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",
|
||||
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"},
|
||||
|
@ -167,35 +181,35 @@ static void configInit() {
|
|||
{nullptr, nullptr},
|
||||
};
|
||||
|
||||
envCallbacks(RETRO_ENVIRONMENT_SET_VARIABLES, (void*)values);
|
||||
envCallback(RETRO_ENVIRONMENT_SET_VARIABLES, (void*)values);
|
||||
}
|
||||
|
||||
static void configUpdate() {
|
||||
EmulatorConfig& config = emulator->getConfig();
|
||||
|
||||
config.rendererType = RendererType::OpenGL;
|
||||
config.vsyncEnabled = FetchVariableBool("panda3ds_use_vsync", true);
|
||||
config.shaderJitEnabled = FetchVariableBool("panda3ds_use_shader_jit", true);
|
||||
config.chargerPlugged = FetchVariableBool("panda3ds_use_charger", true);
|
||||
config.batteryPercentage = std::clamp(std::stoi(FetchVariable("panda3ds_battery_level", "5")), 0, 100);
|
||||
config.dspType = Audio::DSPCore::typeFromString(FetchVariable("panda3ds_dsp_emulation", "null"));
|
||||
config.audioEnabled = FetchVariableBool("panda3ds_use_audio", false);
|
||||
config.sdCardInserted = FetchVariableBool("panda3ds_use_virtual_sd", true);
|
||||
config.sdWriteProtected = FetchVariableBool("panda3ds_write_protect_virtual_sd", false);
|
||||
config.accurateShaderMul = FetchVariableBool("panda3ds_accurate_shader_mul", false);
|
||||
config.useUbershaders = FetchVariableBool("panda3ds_use_ubershader", EmulatorConfig::ubershaderDefault);
|
||||
config.vsyncEnabled = fetchVariableBool("panda3ds_use_vsync", true);
|
||||
config.shaderJitEnabled = fetchVariableBool("panda3ds_use_shader_jit", EmulatorConfig::shaderJitDefault);
|
||||
config.chargerPlugged = fetchVariableBool("panda3ds_use_charger", true);
|
||||
config.batteryPercentage = fetchVariableRange("panda3ds_battery_level", 5, 100);
|
||||
config.dspType = Audio::DSPCore::typeFromString(fetchVariable("panda3ds_dsp_emulation", "null"));
|
||||
config.audioEnabled = fetchVariableBool("panda3ds_use_audio", false);
|
||||
config.sdCardInserted = fetchVariableBool("panda3ds_use_virtual_sd", true);
|
||||
config.sdWriteProtected = fetchVariableBool("panda3ds_write_protect_virtual_sd", false);
|
||||
config.accurateShaderMul = fetchVariableBool("panda3ds_accurate_shader_mul", false);
|
||||
config.useUbershaders = fetchVariableBool("panda3ds_use_ubershader", EmulatorConfig::ubershaderDefault);
|
||||
config.accelerateShaders = FetchVariableBool("panda3ds_accelerate_shaders", EmulatorConfig::accelerateShadersDefault);
|
||||
|
||||
config.forceShadergenForLights = FetchVariableBool("panda3ds_ubershader_lighting_override", true);
|
||||
config.lightShadergenThreshold = std::clamp(std::stoi(FetchVariable("panda3ds_ubershader_lighting_override_threshold", "1")), 1, 8);
|
||||
config.forceShadergenForLights = fetchVariableBool("panda3ds_ubershader_lighting_override", true);
|
||||
config.lightShadergenThreshold = fetchVariableRange("panda3ds_ubershader_lighting_override_threshold", 1, 8);
|
||||
config.discordRpcEnabled = false;
|
||||
|
||||
config.save();
|
||||
}
|
||||
|
||||
static void ConfigCheckVariables() {
|
||||
static void configCheckVariables() {
|
||||
bool updated = false;
|
||||
envCallbacks(RETRO_ENVIRONMENT_GET_VARIABLE_UPDATE, &updated);
|
||||
envCallback(RETRO_ENVIRONMENT_GET_VARIABLE_UPDATE, &updated);
|
||||
|
||||
if (updated) {
|
||||
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->library_version = PANDA3DS_VERSION;
|
||||
info->library_name = "Panda3DS";
|
||||
info->block_extract = true;
|
||||
info->block_extract = false;
|
||||
}
|
||||
|
||||
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) {
|
||||
envCallbacks = cb;
|
||||
envCallback = 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) {
|
||||
|
@ -246,15 +260,15 @@ void retro_set_input_state(retro_input_state_t cb) {
|
|||
|
||||
void retro_init() {
|
||||
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.");
|
||||
savePath = std::filesystem::current_path();
|
||||
} else {
|
||||
savePath = std::filesystem::path(save_dir);
|
||||
savePath = std::filesystem::path(saveDir);
|
||||
}
|
||||
|
||||
emulator = std::make_unique<Emulator>();
|
||||
|
@ -293,31 +307,31 @@ void retro_reset() {
|
|||
}
|
||||
|
||||
void retro_run() {
|
||||
ConfigCheckVariables();
|
||||
configCheckVariables();
|
||||
|
||||
renderer->setFBO(hw_render.get_current_framebuffer());
|
||||
renderer->setFBO(hwRender.get_current_framebuffer());
|
||||
renderer->resetStateManager();
|
||||
|
||||
inputPollCallback();
|
||||
|
||||
HIDService& hid = emulator->getServiceManager().getHID();
|
||||
|
||||
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::X, GetButtonState(RETRO_DEVICE_ID_JOYPAD_X));
|
||||
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::R, GetButtonState(RETRO_DEVICE_ID_JOYPAD_R));
|
||||
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::Up, GetButtonState(RETRO_DEVICE_ID_JOYPAD_UP));
|
||||
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::Right, GetButtonState(RETRO_DEVICE_ID_JOYPAD_RIGHT));
|
||||
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::X, getButtonState(RETRO_DEVICE_ID_JOYPAD_X));
|
||||
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::R, getButtonState(RETRO_DEVICE_ID_JOYPAD_R));
|
||||
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::Up, getButtonState(RETRO_DEVICE_ID_JOYPAD_UP));
|
||||
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::Right, getButtonState(RETRO_DEVICE_ID_JOYPAD_RIGHT));
|
||||
|
||||
// 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 yLeft = GetAxisState(RETRO_DEVICE_INDEX_ANALOG_LEFT, RETRO_DEVICE_ID_ANALOG_Y);
|
||||
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);
|
||||
|
||||
hid.setCirclepadX((xLeft / +32767) * 0x9C);
|
||||
hid.setCirclepadY((yLeft / -32767) * 0x9C);
|
||||
|
@ -355,7 +369,7 @@ void retro_run() {
|
|||
hid.updateInputs(emulator->getTicks());
|
||||
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) {}
|
||||
|
|
35
src/lua.cpp
35
src/lua.cpp
|
@ -130,6 +130,32 @@ MAKE_MEMORY_FUNCTIONS(32)
|
|||
MAKE_MEMORY_FUNCTIONS(64)
|
||||
#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) {
|
||||
std::optional<u64> id = LuaManager::g_emulator->getMemory().getProgramID();
|
||||
|
||||
|
@ -248,10 +274,14 @@ static constexpr luaL_Reg functions[] = {
|
|||
{ "__read16", read16Thunk },
|
||||
{ "__read32", read32Thunk },
|
||||
{ "__read64", read64Thunk },
|
||||
{ "__readFloat", readFloatThunk },
|
||||
{ "__readDouble", readDoubleThunk },
|
||||
{ "__write8", write8Thunk} ,
|
||||
{ "__write16", write16Thunk },
|
||||
{ "__write32", write32Thunk },
|
||||
{ "__write64", write64Thunk },
|
||||
{ "__writeFloat", writeFloatThunk },
|
||||
{ "__writeDouble", writeDoubleThunk },
|
||||
{ "__getAppID", getAppIDThunk },
|
||||
{ "__pause", pauseThunk },
|
||||
{ "__resume", resumeThunk },
|
||||
|
@ -273,10 +303,15 @@ void LuaManager::initializeThunks() {
|
|||
read16 = function(addr) return GLOBALS.__read16(addr) end,
|
||||
read32 = function(addr) return GLOBALS.__read32(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,
|
||||
write16 = function(addr, value) GLOBALS.__write16(addr, value) end,
|
||||
write32 = function(addr, value) GLOBALS.__write32(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()
|
||||
local ffi = require("ffi")
|
||||
|
|
|
@ -1,11 +1,13 @@
|
|||
#include "panda_qt/about_window.hpp"
|
||||
#include "version.hpp"
|
||||
|
||||
#include <QHBoxLayout>
|
||||
#include <QLabel>
|
||||
#include <QTextEdit>
|
||||
#include <QVBoxLayout>
|
||||
#include <QtGlobal>
|
||||
|
||||
#include "version.hpp"
|
||||
|
||||
// Based on https://github.com/dolphin-emu/dolphin/blob/master/Source/Core/DolphinQt/AboutDialog.cpp
|
||||
|
||||
AboutWindow::AboutWindow(QWidget* parent) : QDialog(parent) {
|
||||
|
|
|
@ -1,15 +1,9 @@
|
|||
#include "panda_qt/cheats_window.hpp"
|
||||
|
||||
#include <QCheckBox>
|
||||
#include <QDialog>
|
||||
#include <QDialogButtonBox>
|
||||
#include <QLabel>
|
||||
#include <QLineEdit>
|
||||
#include <QListWidget>
|
||||
#include <QPushButton>
|
||||
#include <QTextEdit>
|
||||
#include <QVBoxLayout>
|
||||
#include <QHBoxLayout>
|
||||
#include <QTimer>
|
||||
#include <QVBoxLayout>
|
||||
#include <functional>
|
||||
|
||||
#include "cheats.hpp"
|
||||
|
@ -18,71 +12,17 @@
|
|||
|
||||
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) {
|
||||
QTimer* timer = new QTimer();
|
||||
timer->moveToThread(qApp->thread());
|
||||
timer->setSingleShot(true);
|
||||
QObject::connect(timer, &QTimer::timeout, [=]()
|
||||
{
|
||||
callback();
|
||||
timer->deleteLater();
|
||||
});
|
||||
QMetaObject::invokeMethod(timer, "start", Qt::QueuedConnection, Q_ARG(int, 0));
|
||||
QTimer* timer = new QTimer();
|
||||
timer->moveToThread(qApp->thread());
|
||||
timer->setSingleShot(true);
|
||||
QObject::connect(timer, &QTimer::timeout, [=]() {
|
||||
callback();
|
||||
timer->deleteLater();
|
||||
});
|
||||
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)
|
||||
: QWidget(), emu(emu), metadata(metadata), cheatList(parent) {
|
||||
QHBoxLayout* layout = new QHBoxLayout;
|
||||
|
@ -219,7 +159,7 @@ void CheatEditDialog::rejected() {
|
|||
|
||||
CheatsWindow::CheatsWindow(Emulator* emu, const std::filesystem::path& cheatPath, QWidget* parent)
|
||||
: QWidget(parent, Qt::Window), emu(emu), cheatPath(cheatPath) {
|
||||
mainWindow = static_cast<MainWindow*>(parent);
|
||||
mainWindow = static_cast<MainWindow*>(parent);
|
||||
|
||||
QVBoxLayout* layout = new QVBoxLayout;
|
||||
layout->setContentsMargins(6, 6, 6, 6);
|
||||
|
@ -265,4 +205,4 @@ void CheatsWindow::removeClicked() {
|
|||
|
||||
CheatEntryWidget* entry = static_cast<CheatEntryWidget*>(cheatList->itemWidget(item));
|
||||
entry->Remove();
|
||||
}
|
||||
}
|
|
@ -1,7 +1,7 @@
|
|||
#include "input_mappings.hpp"
|
||||
|
||||
#include <QKeyEvent>
|
||||
|
||||
#include "input_mappings.hpp"
|
||||
|
||||
InputMappings InputMappings::defaultKeyboardMappings() {
|
||||
InputMappings mappings;
|
||||
mappings.setMapping(Qt::Key_L, HID::Keys::A);
|
||||
|
|
|
@ -1,8 +1,9 @@
|
|||
#include "panda_qt/shader_editor.hpp"
|
||||
|
||||
#include <QPushButton>
|
||||
#include <QVBoxLayout>
|
||||
|
||||
#include "panda_qt/main_window.hpp"
|
||||
#include "panda_qt/shader_editor.hpp"
|
||||
|
||||
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