├── .gitmodules ├── .github └── FUNDING.yml ├── .gitignore ├── source ├── hardware │ ├── dsp.cpp │ └── dsp.hpp ├── framework │ ├── tests │ │ ├── test_main.cpp │ │ └── config.cpp │ ├── ranges.hpp │ ├── CMakeLists.txt │ ├── color.hpp │ ├── logging.hpp │ ├── exceptions.cpp │ ├── console.hpp │ ├── console.cpp │ └── exceptions.hpp ├── video_core │ ├── externals │ │ └── nihstro │ │ │ ├── examples │ │ │ ├── inline_assembler │ │ │ │ └── simple │ │ │ │ │ └── CMakeLists.txt │ │ │ └── assembler │ │ │ │ ├── cube │ │ │ │ ├── data │ │ │ │ │ ├── texture.bin │ │ │ │ │ └── test.vsh │ │ │ │ ├── source │ │ │ │ │ ├── _gs.s │ │ │ │ │ ├── gs.h │ │ │ │ │ └── math.c │ │ │ │ └── README.md │ │ │ │ └── cube_lighting │ │ │ │ ├── data │ │ │ │ ├── texture.bin │ │ │ │ └── test.vsh │ │ │ │ ├── source │ │ │ │ ├── _gs.s │ │ │ │ ├── gs.h │ │ │ │ └── math.c │ │ │ │ └── README.md │ │ │ ├── .travis.yml │ │ │ ├── include │ │ │ └── nihstro │ │ │ │ ├── float24.h │ │ │ │ └── preprocessor.h │ │ │ ├── license.txt │ │ │ └── CMakeLists.txt │ ├── src │ │ ├── support │ │ │ └── common │ │ │ │ ├── common_funcs.h │ │ │ │ ├── common_types.h │ │ │ │ └── log.h │ │ ├── video_core │ │ │ ├── context.cpp │ │ │ ├── clipper.h │ │ │ ├── vulkan │ │ │ │ ├── shader_gen.hpp │ │ │ │ └── pipeline_cache.hpp │ │ │ ├── rasterizer.h │ │ │ ├── command_processor.h │ │ │ ├── primitive_assembly.h │ │ │ ├── context.h │ │ │ ├── shader_private.hpp │ │ │ ├── primitive_assembly.cpp │ │ │ ├── renderer.hpp │ │ │ └── shader_analysis.hpp │ │ ├── interrupt_listener.hpp │ │ └── debug │ │ │ └── gpu.hpp │ ├── utils │ │ └── vulkan_utils │ │ │ ├── glsl_helper.hpp │ │ │ ├── CMakeLists.txt │ │ │ ├── memory_types.cpp │ │ │ ├── device_manager.hpp │ │ │ ├── glsl_helper.cpp │ │ │ ├── memory_types.hpp │ │ │ └── debug_markers.hpp │ └── CMakeLists.txt ├── ui │ ├── audio_frontend.hpp │ ├── key_database.hpp │ └── key_database.cpp ├── loader │ ├── host_file.hpp │ ├── firm.cpp │ ├── firm.hpp │ ├── host_file.cpp │ └── gamecard.hpp ├── processes │ ├── http.hpp │ ├── news.hpp │ ├── friend.hpp │ ├── errdisp.hpp │ ├── ps.hpp │ ├── cdc.hpp │ ├── pdn.hpp │ ├── act.hpp │ ├── ssl.hpp │ ├── hid.hpp │ ├── csnd.hpp │ ├── cam.hpp │ ├── am.hpp │ ├── dlp.hpp │ ├── fs_paths.hpp │ ├── mic.hpp │ ├── ndm.hpp │ ├── cecd.hpp │ ├── am_hpv.hpp │ ├── ro_hpv.hpp │ ├── cfg_hpv.hpp │ ├── dummy.hpp │ ├── ns_hpv.hpp │ ├── sm_hpv.hpp │ ├── nwm.hpp │ ├── dummy.cpp │ ├── dsp_hpv.hpp │ ├── ptm.hpp │ ├── gpio.hpp │ ├── pxi_fs_file_buffer_emu.hpp │ ├── ns.hpp │ ├── cfg_hpv.cpp │ ├── dlp.cpp │ ├── mcu.hpp │ ├── i2c.hpp │ ├── ssl.cpp │ ├── dsp.hpp │ ├── fs_hpv.hpp │ ├── http.cpp │ ├── errdisp.cpp │ ├── am_hpv.cpp │ ├── ro_hpv.cpp │ ├── act.cpp │ ├── pxi.hpp │ ├── news.cpp │ └── fs_common.hpp ├── gui-sdl │ └── audio_frontend_sdl.hpp ├── os_serialization.cpp ├── platform │ ├── CMakeLists.txt │ ├── csnd.hpp │ ├── cdc.hpp │ ├── crypto.hpp │ ├── gpio.hpp │ ├── hid.hpp │ ├── gpu │ │ └── zorder.hpp │ ├── mcu.hpp │ ├── config.hpp │ ├── i2c.hpp │ ├── dsp.hpp │ ├── file_formats │ │ ├── bcfnt.hpp │ │ ├── dsp1.hpp │ │ ├── smdh.hpp │ │ └── formats.cpp │ └── am.hpp ├── utility │ ├── simple_tcp.hpp │ └── simple_tcp.cpp ├── os_console.hpp ├── arm │ ├── thumb.hpp │ └── processor_interpreter.hpp ├── debug │ ├── os.hpp │ └── jit.hpp ├── pica.hpp ├── session.hpp ├── os_serialization.hpp ├── debug_server.hpp ├── pica.cpp ├── settings.cpp ├── input.hpp ├── debug_server.cpp ├── os_hypervisor.hpp ├── gdb_stub.h ├── os_types.hpp └── input.cpp ├── .gitlab-ci.yml ├── data └── nand_archives │ ├── 0004009b │ ├── 00010202 │ │ └── content │ │ │ └── 00000000.app │ ├── 00010402 │ │ └── content │ │ │ └── 00000000.app │ └── 00014002 │ │ └── content │ │ └── 00000000.app │ ├── 000400db │ ├── 00010302 │ │ └── content │ │ │ └── 00000000.app │ └── 00016102 │ │ └── content │ │ └── 00000000.app │ └── CMakeLists.txt ├── tools ├── embed_title │ ├── Readme.md │ ├── CMakeLists.txt │ └── main.cpp ├── 3dsx_to_cia │ └── CMakeLists.txt └── install_cia │ └── CMakeLists.txt ├── .gitlab └── build.yml ├── conanfile.py └── README.md /.gitmodules: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /.github/FUNDING.yml: -------------------------------------------------------------------------------- 1 | patreon: mikage 2 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | build/* 2 | aes_keys.txt 3 | *.tracy -------------------------------------------------------------------------------- /source/hardware/dsp.cpp: -------------------------------------------------------------------------------- 1 | #include "dsp.hpp" 2 | -------------------------------------------------------------------------------- /.gitlab-ci.yml: -------------------------------------------------------------------------------- 1 | include: 2 | - local: '.gitlab/build.yml' 3 | -------------------------------------------------------------------------------- /source/framework/tests/test_main.cpp: -------------------------------------------------------------------------------- 1 | #define CATCH_CONFIG_MAIN 2 | #include 3 | -------------------------------------------------------------------------------- /source/video_core/externals/nihstro/examples/inline_assembler/simple/CMakeLists.txt: -------------------------------------------------------------------------------- 1 | add_executable(simple simple.cpp) 2 | -------------------------------------------------------------------------------- /data/nand_archives/0004009b/00010202/content/00000000.app: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mikage-emu/mikage-dev/HEAD/data/nand_archives/0004009b/00010202/content/00000000.app -------------------------------------------------------------------------------- /data/nand_archives/0004009b/00010402/content/00000000.app: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mikage-emu/mikage-dev/HEAD/data/nand_archives/0004009b/00010402/content/00000000.app -------------------------------------------------------------------------------- /data/nand_archives/0004009b/00014002/content/00000000.app: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mikage-emu/mikage-dev/HEAD/data/nand_archives/0004009b/00014002/content/00000000.app -------------------------------------------------------------------------------- /data/nand_archives/000400db/00010302/content/00000000.app: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mikage-emu/mikage-dev/HEAD/data/nand_archives/000400db/00010302/content/00000000.app -------------------------------------------------------------------------------- /data/nand_archives/000400db/00016102/content/00000000.app: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mikage-emu/mikage-dev/HEAD/data/nand_archives/000400db/00016102/content/00000000.app -------------------------------------------------------------------------------- /tools/embed_title/Readme.md: -------------------------------------------------------------------------------- 1 | ## embed_title 2 | 3 | Copies an NCCH from the given path to the given NAND location, storing it in the directory corresponding to its title id. 4 | -------------------------------------------------------------------------------- /source/video_core/src/support/common/common_funcs.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #ifndef FORCE_INLINE 4 | // TODO: Properly implement this. 5 | #define FORCE_INLINE inline 6 | #endif 7 | -------------------------------------------------------------------------------- /tools/3dsx_to_cia/CMakeLists.txt: -------------------------------------------------------------------------------- 1 | add_executable(3dsx_to_cia main.cpp) 2 | target_link_libraries(3dsx_to_cia PRIVATE framework platform cryptopp::cryptopp Boost::boost Boost::program_options) 3 | -------------------------------------------------------------------------------- /tools/embed_title/CMakeLists.txt: -------------------------------------------------------------------------------- 1 | add_executable(embed_title main.cpp) 2 | target_link_libraries(embed_title framework platform boost_program_options boost_filesystem boost_system fmt_includes) 3 | -------------------------------------------------------------------------------- /source/video_core/externals/nihstro/examples/assembler/cube/data/texture.bin: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mikage-emu/mikage-dev/HEAD/source/video_core/externals/nihstro/examples/assembler/cube/data/texture.bin -------------------------------------------------------------------------------- /source/video_core/src/video_core/context.cpp: -------------------------------------------------------------------------------- 1 | #include "context.h" 2 | 3 | namespace Pica { 4 | 5 | Context::Context() = default; 6 | 7 | Context::~Context() = default; 8 | 9 | } // namespace Pica 10 | -------------------------------------------------------------------------------- /source/ui/audio_frontend.hpp: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include 4 | #include 5 | 6 | struct AudioFrontend { 7 | virtual void OutputSamples(std::array samples) = 0; 8 | 9 | }; 10 | -------------------------------------------------------------------------------- /source/video_core/externals/nihstro/examples/assembler/cube_lighting/data/texture.bin: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mikage-emu/mikage-dev/HEAD/source/video_core/externals/nihstro/examples/assembler/cube_lighting/data/texture.bin -------------------------------------------------------------------------------- /source/loader/host_file.hpp: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include "../processes/pxi_fs.hpp" 4 | 5 | namespace FileSystem { 6 | 7 | std::unique_ptr OpenNativeFile(int file_desctiptor); 8 | 9 | } // namespace FileSystem 10 | -------------------------------------------------------------------------------- /source/processes/http.hpp: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | namespace HLE { 4 | 5 | namespace OS { 6 | 7 | class FakeThread; 8 | 9 | struct FakeHTTP { 10 | FakeHTTP(FakeThread& thread); 11 | }; 12 | 13 | } // namespace OS 14 | 15 | } // namespace HLE 16 | -------------------------------------------------------------------------------- /source/processes/news.hpp: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | namespace HLE { 4 | 5 | namespace OS { 6 | 7 | class FakeThread; 8 | 9 | struct FakeNEWS { 10 | FakeNEWS(FakeThread& thread); 11 | }; 12 | 13 | } // namespace OS 14 | 15 | } // namespace HLE 16 | -------------------------------------------------------------------------------- /source/processes/friend.hpp: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | namespace HLE { 4 | 5 | namespace OS { 6 | 7 | class FakeThread; 8 | 9 | struct FakeFRIEND { 10 | FakeFRIEND(FakeThread& thread); 11 | }; 12 | 13 | } // namespace OS 14 | 15 | } // namespace HLE 16 | -------------------------------------------------------------------------------- /source/ui/key_database.hpp: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include 4 | 5 | #include 6 | 7 | namespace spdlog { 8 | class logger; 9 | } 10 | 11 | KeyDatabase LoadKeyDatabase(spdlog::logger&, const std::filesystem::path& filename); 12 | -------------------------------------------------------------------------------- /source/video_core/src/interrupt_listener.hpp: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | // TODO: Instead of allowing any interrupt to be reported here, just allow triggering a single one 4 | class InterruptListener { 5 | public: 6 | virtual void NotifyInterrupt(uint32_t index) = 0; 7 | }; 8 | -------------------------------------------------------------------------------- /source/gui-sdl/audio_frontend_sdl.hpp: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include 4 | 5 | class SDLAudioFrontend : public AudioFrontend { 6 | public: 7 | void OutputSamples(std::array samples) override {} 8 | SDLAudioFrontend() = default; 9 | }; 10 | -------------------------------------------------------------------------------- /source/os_serialization.cpp: -------------------------------------------------------------------------------- 1 | #define FORMATS_IMPL_EXPLICIT_FORMAT_INSTANTIATIONS_INTENDED 2 | #include 3 | 4 | #include "os.hpp" 5 | 6 | namespace FileFormat { 7 | 8 | template struct SerializationInterface; 9 | 10 | } // namespace FileFormat 11 | -------------------------------------------------------------------------------- /source/processes/errdisp.hpp: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include "fake_process.hpp" 4 | 5 | namespace HLE::OS { 6 | 7 | template<> std::shared_ptr CreateFakeProcessViaContext(OS&, Interpreter::Setup&, uint32_t, const std::string&); 8 | 9 | } // namespace HLE::OS 10 | -------------------------------------------------------------------------------- /tools/install_cia/CMakeLists.txt: -------------------------------------------------------------------------------- 1 | add_executable(install_cia main.cpp) 2 | target_link_libraries(install_cia PRIVATE framework platform cryptopp boost_program_options boost_filesystem boost_system) 3 | target_link_libraries(install_cia PRIVATE fmt::fmt) 4 | target_link_libraries(install_cia PRIVATE cryptopp::cryptopp-static) 5 | -------------------------------------------------------------------------------- /source/processes/ps.hpp: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include "fake_process.hpp" 4 | 5 | namespace HLE { 6 | 7 | namespace OS { 8 | 9 | template<> std::shared_ptr CreateFakeProcessViaContext(OS&, Interpreter::Setup&, uint32_t, const std::string&); 10 | 11 | } // namespace OS 12 | 13 | } // namespace HLE 14 | -------------------------------------------------------------------------------- /source/video_core/utils/vulkan_utils/glsl_helper.hpp: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include 4 | 5 | #include 6 | #include 7 | 8 | /** 9 | * Compiles the given GLSL shader source to SPIR-V 10 | */ 11 | std::vector CompileShader(EShLanguage, const char* code, const char* entrypoint); 12 | -------------------------------------------------------------------------------- /source/processes/cdc.hpp: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include "fake_process.hpp" 4 | 5 | namespace HLE { 6 | 7 | namespace OS { 8 | 9 | template<> std::shared_ptr CreateFakeProcessViaContext(OS&, Interpreter::Setup&, uint32_t, const std::string&); 10 | 11 | } // namespace OS 12 | 13 | } // namespace HLE 14 | -------------------------------------------------------------------------------- /source/processes/pdn.hpp: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include "fake_process.hpp" 4 | 5 | namespace HLE { 6 | 7 | namespace OS { 8 | 9 | template<> std::shared_ptr CreateFakeProcessViaContext(OS&, Interpreter::Setup&, uint32_t, const std::string&); 10 | 11 | } // namespace OS 12 | 13 | } // namespace HLE 14 | -------------------------------------------------------------------------------- /source/platform/CMakeLists.txt: -------------------------------------------------------------------------------- 1 | add_library(platform STATIC file_formats/formats.cpp) 2 | #target_include_directories(platform PUBLIC $) 3 | target_include_directories(platform PUBLIC ${CMAKE_CURRENT_SOURCE_DIR}/..) 4 | target_link_libraries(platform framework) 5 | target_link_libraries(platform range-v3::range-v3) 6 | -------------------------------------------------------------------------------- /source/processes/act.hpp: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include "fake_process.hpp" 4 | 5 | namespace HLE { 6 | 7 | namespace OS { 8 | 9 | template<> std::shared_ptr CreateFakeProcessViaContext(OS&, Interpreter::Setup&, uint32_t pid, const std::string& name); 10 | 11 | } // namespace OS 12 | 13 | } // namespace HLE 14 | -------------------------------------------------------------------------------- /source/processes/ssl.hpp: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include "fake_process.hpp" 4 | 5 | namespace HLE { 6 | 7 | namespace OS { 8 | 9 | template<> std::shared_ptr CreateFakeProcessViaContext(OS&, Interpreter::Setup&, uint32_t pid, const std::string& name); 10 | 11 | } // namespace OS 12 | 13 | } // namespace HLE 14 | -------------------------------------------------------------------------------- /source/processes/hid.hpp: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include "fake_process.hpp" 4 | 5 | namespace HLE { 6 | 7 | namespace OS { 8 | 9 | template<> std::shared_ptr CreateFakeProcessViaContext(OS& os, Interpreter::Setup& setup, uint32_t pid, const std::string& name); 10 | 11 | } // namespace OS 12 | 13 | } // namespace HLE 14 | -------------------------------------------------------------------------------- /source/processes/csnd.hpp: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include "fake_process.hpp" 4 | 5 | namespace HLE { 6 | 7 | namespace OS { 8 | 9 | template<> std::shared_ptr CreateFakeProcessViaContext(OS& os, Interpreter::Setup& setup, uint32_t pid, const std::string& name); 10 | 11 | } // namespace OS 12 | 13 | } // namespace HLE 14 | -------------------------------------------------------------------------------- /source/video_core/src/support/common/common_types.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include 4 | 5 | using u8 = uint8_t; 6 | using u16 = uint16_t; 7 | using u32 = uint32_t; 8 | using u64 = uint64_t; 9 | using s8 = int8_t; 10 | using s16 = int16_t; 11 | using s32 = int32_t; 12 | using s64 = int64_t; 13 | 14 | using PAddr = uint32_t; 15 | using VAddr = uint32_t; 16 | -------------------------------------------------------------------------------- /source/processes/cam.hpp: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include "fake_process.hpp" 4 | 5 | namespace HLE { 6 | 7 | namespace OS { 8 | 9 | class FakeThread; 10 | 11 | template<> std::shared_ptr CreateFakeProcessViaContext(OS&, Interpreter::Setup&, uint32_t, const std::string&); 12 | 13 | } // namespace OS 14 | 15 | } // namespace HLE 16 | -------------------------------------------------------------------------------- /source/processes/am.hpp: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include "os.hpp" 4 | 5 | #include 6 | 7 | namespace HLE { 8 | 9 | namespace OS { 10 | 11 | template<> std::shared_ptr CreateFakeProcessViaContext(OS& os, Interpreter::Setup& setup, uint32_t pid, const std::string& name); 12 | 13 | } // namespace OS 14 | 15 | } // namespace HLE 16 | -------------------------------------------------------------------------------- /source/processes/dlp.hpp: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include "os.hpp" 4 | 5 | #include 6 | 7 | namespace HLE { 8 | 9 | namespace OS { 10 | 11 | template<> std::shared_ptr CreateFakeProcessViaContext(OS& os, Interpreter::Setup& setup, uint32_t pid, const std::string& name); 12 | 13 | } // namespace OS 14 | 15 | } // namespace HLE 16 | -------------------------------------------------------------------------------- /source/processes/fs_paths.hpp: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include 4 | 5 | namespace HLE { 6 | 7 | class FakeFS; 8 | using FSContext = FakeFS; 9 | 10 | inline uint64_t GetId0(FSContext&) { 11 | // TODO 12 | return 0; 13 | } 14 | 15 | inline uint64_t GetId1(FSContext&) { 16 | // TODO 17 | return 0; 18 | } 19 | 20 | } // namespace HLE 21 | -------------------------------------------------------------------------------- /source/processes/mic.hpp: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include "os.hpp" 4 | 5 | #include 6 | 7 | namespace HLE { 8 | 9 | namespace OS { 10 | 11 | template<> std::shared_ptr CreateFakeProcessViaContext(OS& os, Interpreter::Setup& setup, uint32_t pid, const std::string& name); 12 | 13 | } // namespace OS 14 | 15 | } // namespace HLE 16 | -------------------------------------------------------------------------------- /source/processes/ndm.hpp: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include "os.hpp" 4 | 5 | #include 6 | 7 | namespace HLE { 8 | 9 | namespace OS { 10 | 11 | template<> std::shared_ptr CreateFakeProcessViaContext(OS&, Interpreter::Setup&, uint32_t pid, const std::string& name); 12 | 13 | } // namespace OS 14 | 15 | } // namespace HLE 16 | 17 | -------------------------------------------------------------------------------- /source/processes/cecd.hpp: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include "os.hpp" 4 | 5 | #include 6 | 7 | namespace HLE { 8 | 9 | namespace OS { 10 | 11 | template<> std::shared_ptr CreateFakeProcessViaContext(OS& os, Interpreter::Setup& setup, uint32_t pid, const std::string& name); 12 | 13 | } // namespace OS 14 | 15 | } // namespace HLE 16 | -------------------------------------------------------------------------------- /source/video_core/externals/nihstro/examples/assembler/cube/source/_gs.s: -------------------------------------------------------------------------------- 1 | .section ".text" 2 | .arm 3 | .align 4 4 | .global _vboMemcpy50 5 | 6 | # r0 : dst 7 | # r1 : src 8 | # fixed size 0x50 9 | _vboMemcpy50: 10 | push {r4-r11} 11 | ldmia r1!, {r2-r12} 12 | stmia r0!, {r2-r12} 13 | ldmia r1!, {r2-r12} 14 | stmia r0!, {r2-r12} 15 | pop {r4-r11} 16 | bx lr 17 | -------------------------------------------------------------------------------- /source/processes/am_hpv.hpp: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include "os_hypervisor_private.hpp" 4 | 5 | namespace HLE { 6 | 7 | namespace OS { 8 | 9 | namespace HPV { 10 | 11 | struct AMContext : SessionContext { 12 | }; 13 | 14 | HPV::RefCounted CreateAmService(RefCounted port, AMContext&); 15 | 16 | } // namespace HPV 17 | 18 | } // namespace HOS 19 | 20 | } // namespace HLE 21 | -------------------------------------------------------------------------------- /source/processes/ro_hpv.hpp: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include "os_hypervisor_private.hpp" 4 | 5 | namespace HLE { 6 | 7 | namespace OS { 8 | 9 | namespace HPV { 10 | 11 | struct ROContext : SessionContext { 12 | }; 13 | 14 | HPV::RefCounted CreateRoService(RefCounted port, ROContext&); 15 | 16 | } // namespace HPV 17 | 18 | } // namespace HOS 19 | 20 | } // namespace HLE 21 | -------------------------------------------------------------------------------- /source/video_core/externals/nihstro/examples/assembler/cube_lighting/source/_gs.s: -------------------------------------------------------------------------------- 1 | .section ".text" 2 | .arm 3 | .align 4 4 | .global _vboMemcpy50 5 | 6 | # r0 : dst 7 | # r1 : src 8 | # fixed size 0x50 9 | _vboMemcpy50: 10 | push {r4-r11} 11 | ldmia r1!, {r2-r12} 12 | stmia r0!, {r2-r12} 13 | ldmia r1!, {r2-r12} 14 | stmia r0!, {r2-r12} 15 | pop {r4-r11} 16 | bx lr 17 | -------------------------------------------------------------------------------- /source/processes/cfg_hpv.hpp: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include "os_hypervisor_private.hpp" 4 | 5 | namespace HLE { 6 | 7 | namespace OS { 8 | 9 | namespace HPV { 10 | 11 | struct CFGContext : SessionContext { 12 | }; 13 | 14 | HPV::RefCounted CreateCfgService(RefCounted port, CFGContext&); 15 | 16 | } // namespace HPV 17 | 18 | } // namespace HOS 19 | 20 | } // namespace HLE 21 | -------------------------------------------------------------------------------- /source/hardware/dsp.hpp: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include 4 | 5 | #include 6 | 7 | #include 8 | 9 | namespace Memory { 10 | 11 | struct DSPDevice : MemoryAccessHandler { 12 | std::shared_ptr logger; 13 | 14 | DSPDevice(LogManager& log_manager); 15 | 16 | // Teakra::Teakra teakra; 17 | }; 18 | 19 | } // namespace Memory 20 | -------------------------------------------------------------------------------- /source/video_core/src/video_core/clipper.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | namespace Pica { 4 | 5 | struct Context; 6 | 7 | namespace VertexShader { 8 | struct OutputVertex; 9 | } 10 | 11 | namespace Clipper { 12 | 13 | using VertexShader::OutputVertex; 14 | 15 | void ProcessTriangle(Context& context, OutputVertex& v0, OutputVertex& v1, OutputVertex& v2); 16 | 17 | } // namespace 18 | 19 | } // namespace 20 | -------------------------------------------------------------------------------- /source/processes/dummy.hpp: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include "fake_process.hpp" 4 | 5 | namespace HLE { 6 | 7 | namespace OS { 8 | 9 | // Creates a dummy process that immediately exits 10 | template<> std::shared_ptr CreateFakeProcessViaContext(OS&, Interpreter::Setup&, uint32_t pid, const std::string& name); 11 | 12 | } // namespace OS 13 | 14 | } // namespace HLE 15 | -------------------------------------------------------------------------------- /source/video_core/src/video_core/vulkan/shader_gen.hpp: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include 4 | 5 | namespace Pica { 6 | 7 | struct Context; 8 | 9 | namespace VertexShader { 10 | struct ShaderEngine; 11 | } 12 | 13 | namespace Vulkan { 14 | 15 | std::string GenerateVertexShader(Context&, const VertexShader::ShaderEngine&); 16 | 17 | std::string GenerateFragmentShader(Context&); 18 | 19 | } // namespace Vulkan 20 | 21 | } // namespace Pica 22 | -------------------------------------------------------------------------------- /source/framework/ranges.hpp: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include 4 | #include // for std::size of C arrays 5 | 6 | namespace ranges::views { 7 | 8 | /// Returns a view of indexes into the given range 9 | template 10 | inline auto indexes(Rng&& rng) noexcept { 11 | using std::size; 12 | using index_t = decltype(size(rng)); 13 | return ranges::views::iota(index_t { 0 }, size(rng)); 14 | } 15 | 16 | } 17 | -------------------------------------------------------------------------------- /source/video_core/src/support/common/log.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #define LOG_CRITICAL(chan, ...) printf(__VA_ARGS__) 4 | #define LOG_ERROR(...) 5 | #define LOG_TRACE(...) 6 | #define LOG_WARNING(...) 7 | 8 | #define ERROR_LOG(...) 9 | 10 | #define HW_GPU 11 | 12 | #define UNIMPLEMENTED() do { throw std::runtime_error(fmt::format("Unimplemented in file {} at line {}", __FILE__, __LINE__)); } while(0) 13 | 14 | #define _dbg_assert_(...) 15 | #define _dbg_assert_msg_(...) 16 | -------------------------------------------------------------------------------- /source/utility/simple_tcp.hpp: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include 4 | 5 | class SimpleTCPServer { 6 | protected: 7 | boost::asio::io_context io_context; 8 | boost::asio::ip::tcp::acceptor acceptor; 9 | 10 | void SetupAsyncAccept(); 11 | 12 | public: 13 | SimpleTCPServer(uint16_t port); 14 | 15 | virtual void OnClientConnected(boost::asio::ip::tcp::socket) = 0; 16 | 17 | void RunTCPServer(); 18 | void StopTCPServer(); 19 | }; 20 | -------------------------------------------------------------------------------- /source/os_console.hpp: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include "framework/console.hpp" 4 | 5 | namespace HLE { 6 | 7 | namespace OS { 8 | 9 | class OS; 10 | 11 | class ConsoleModule : public ::ConsoleModule { 12 | OS& os; 13 | 14 | public: 15 | ConsoleModule(OS& os); 16 | virtual ~ConsoleModule() = default; 17 | 18 | virtual std::optional HandleCommand(const std::string& command) override; 19 | }; 20 | 21 | } // namespace OS 22 | 23 | } // namespace HLE 24 | -------------------------------------------------------------------------------- /source/platform/csnd.hpp: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include "ipc.hpp" 4 | 5 | namespace Platform::CSND { 6 | 7 | using Initialize = IPC::IPCCommand<0x1>::add_uint32::add_uint32::add_uint32::add_uint32::add_uint32 8 | ::response::add_handles; 9 | 10 | using FlushDataCache = IPC::IPCCommand<0x9>::add_uint32::add_uint32::add_handle 11 | ::response; 12 | 13 | } // namespace Platform::CSND 14 | -------------------------------------------------------------------------------- /source/processes/ns_hpv.hpp: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include "os_hypervisor_private.hpp" 4 | 5 | namespace HLE { 6 | 7 | namespace OS { 8 | 9 | namespace HPV { 10 | 11 | struct NSContext : SessionContext { 12 | }; 13 | 14 | HPV::RefCounted CreateAPTService(RefCounted port, NSContext& context); 15 | HPV::RefCounted CreateNSService(RefCounted port, NSContext& context); 16 | 17 | } // namespace HPV 18 | 19 | } // namespace HOS 20 | 21 | } // namespace HLE 22 | -------------------------------------------------------------------------------- /source/video_core/externals/nihstro/.travis.yml: -------------------------------------------------------------------------------- 1 | os: 2 | - linux 3 | 4 | language: cpp 5 | sudo: false 6 | 7 | addons: 8 | apt: 9 | sources: 10 | - ubuntu-toolchain-r-test 11 | - kalakris-cmake 12 | - boost-latest 13 | packages: 14 | - gcc-4.9 15 | - g++-4.9 16 | - cmake 17 | - libboost1.55-all-dev 18 | 19 | script: 20 | - export CC=gcc-4.9 21 | - export CXX=g++-4.9 22 | - mkdir build 23 | - cd build 24 | - cmake .. 25 | - make 26 | -------------------------------------------------------------------------------- /source/processes/sm_hpv.hpp: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include "os_hypervisor_private.hpp" 4 | 5 | namespace HLE { 6 | 7 | namespace OS { 8 | 9 | namespace HPV { 10 | 11 | struct SMContext : SessionContext { 12 | }; 13 | 14 | HPV::RefCounted CreateSMSrvService(RefCounted port, SMContext& context); 15 | HPV::RefCounted CreateSMSrvPmService(RefCounted port, SMContext& context); 16 | 17 | } // namespace HPV 18 | 19 | } // namespace HOS 20 | 21 | } // namespace HLE 22 | -------------------------------------------------------------------------------- /source/video_core/src/video_core/rasterizer.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | namespace Pica { 4 | 5 | struct Context; 6 | 7 | namespace VertexShader { 8 | struct OutputVertex; 9 | } 10 | 11 | namespace Rasterizer { 12 | 13 | void ProcessTriangle(Context& context, 14 | const VertexShader::OutputVertex& v0, 15 | const VertexShader::OutputVertex& v1, 16 | const VertexShader::OutputVertex& v2); 17 | 18 | } // namespace Rasterizer 19 | 20 | } // namespace Pica 21 | -------------------------------------------------------------------------------- /source/framework/CMakeLists.txt: -------------------------------------------------------------------------------- 1 | # Adding an interface library to propagate include directories 2 | add_library(framework exceptions.cpp profiler.cpp) 3 | target_include_directories(framework INTERFACE $) 4 | 5 | target_link_libraries(framework PUBLIC fmt::fmt) 6 | target_link_libraries(framework INTERFACE range-v3::range-v3) 7 | target_link_libraries(framework PUBLIC Boost::boost) 8 | if(libunwind_FOUND) 9 | target_link_libraries(framework PRIVATE libunwind::unwind) 10 | endif () 11 | -------------------------------------------------------------------------------- /source/platform/cdc.hpp: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include "ipc.hpp" 4 | 5 | namespace Platform { 6 | 7 | /** 8 | * CDC = Communication Device Class. 9 | */ 10 | namespace CDC { 11 | 12 | namespace HID { 13 | 14 | // NOTE: Response headers for these aren't verified! 15 | using GetTouchData = IPC::IPCCommand<0x1> 16 | ::response::add_uint32::add_uint32; 17 | using Initialize = IPC::IPCCommand<0x2> 18 | ::response; 19 | 20 | } 21 | 22 | } // namespace CDC 23 | 24 | } // namespace Platform 25 | -------------------------------------------------------------------------------- /source/video_core/externals/nihstro/examples/assembler/cube_lighting/README.md: -------------------------------------------------------------------------------- 1 | cube lighting example 2 | ===================== 3 | 4 | An example similar to cube, but with some rudimentary vertex lighting effects. The shader used is somewhat more complex and involves a LOOP to implement multiple light sources. 5 | 6 | Before trying to compile, make sure your NIHSTRO environment variable points to the directory nihstro-assemble resides in. Additionally, ctrulib in revision 1f52ac344d or similar is required, plus some patches to implement proper uniform setters. 7 | -------------------------------------------------------------------------------- /source/processes/nwm.hpp: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include "ipc.hpp" 4 | #include "os.hpp" 5 | 6 | namespace HLE { 7 | 8 | namespace OS { 9 | 10 | class FakeThread; 11 | 12 | struct FakeNWM { 13 | FakeNWM(FakeThread& thread); 14 | 15 | const uint32_t shared_mem_size = 0x22000; 16 | uint32_t shared_mem_vaddr; 17 | HandleTable::Entry shared_memory; 18 | HandleTable::Entry shared_memory_event; 19 | 20 | IPC::StaticBuffer soc_static_buffer; 21 | }; 22 | 23 | } // namespace OS 24 | 25 | } // namespace HLE 26 | -------------------------------------------------------------------------------- /source/loader/firm.cpp: -------------------------------------------------------------------------------- 1 | #include "firm.hpp" 2 | 3 | namespace Loader { 4 | 5 | bool IsFirm(std::istream& str) { 6 | auto file_begin = str.tellg(); 7 | 8 | unsigned char magic[4]; 9 | str.read(reinterpret_cast(magic), sizeof(magic)); 10 | str.seekg(file_begin); 11 | 12 | if (str.gcount() != sizeof(magic) || 13 | magic[0] != 'F' || 14 | magic[1] != 'I' || 15 | magic[2] != 'R' || 16 | magic[3] != 'M') { 17 | return false; 18 | } 19 | 20 | return true; 21 | } 22 | 23 | } // namespace Loader 24 | -------------------------------------------------------------------------------- /source/processes/dummy.cpp: -------------------------------------------------------------------------------- 1 | #include "dummy.hpp" 2 | 3 | namespace HLE { 4 | 5 | namespace OS { 6 | 7 | struct DummyProcess { 8 | DummyProcess(FakeThread& thread) { 9 | thread.CallSVC(&OS::SVCExitThread); 10 | } 11 | }; 12 | 13 | template<> std::shared_ptr CreateFakeProcessViaContext(OS& os, Interpreter::Setup& setup, uint32_t pid, const std::string& name) { 14 | return WrappedFakeProcess::CreateWithContext(os, setup, pid, name); 15 | } 16 | 17 | } // namespace OS 18 | 19 | } // namespace HLE 20 | -------------------------------------------------------------------------------- /source/processes/dsp_hpv.hpp: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include "os_hypervisor_private.hpp" 4 | 5 | struct AudioFrontend; 6 | 7 | namespace Settings { 8 | struct Settings; 9 | } 10 | 11 | namespace HLE { 12 | 13 | namespace OS { 14 | 15 | namespace HPV { 16 | 17 | struct DSPContext : SessionContext { 18 | Settings::Settings* settings = nullptr; 19 | AudioFrontend* frontend = nullptr; 20 | }; 21 | 22 | HPV::RefCounted CreateDspService(RefCounted port, DSPContext&); 23 | 24 | } // namespace HPV 25 | 26 | } // namespace HOS 27 | 28 | } // namespace HLE 29 | -------------------------------------------------------------------------------- /source/video_core/externals/nihstro/examples/assembler/cube/README.md: -------------------------------------------------------------------------------- 1 | cube example 2 | ============ 3 | 4 | Simple port of ctrulib's gpu example to nihstro shaders. The C program code is mostly unchanged from the original, however the example shader in the data subdirectory should give you a good idea of the basic nihcode shader syntax. 5 | 6 | Before trying to compile, make sure your NIHSTRO environment variable points to the directory nihstro-assemble resides in. Additionally, ctrulib in revision 1f52ac344d or similar is required, plus some patches to implement proper uniform setters. 7 | -------------------------------------------------------------------------------- /source/platform/crypto.hpp: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include 4 | #include 5 | #include 6 | 7 | struct KeyDatabase { 8 | // Each AES slot has three entries called KeyX, KeyY, and KeyN ("normal key") 9 | static constexpr int num_aes_slots = 0x40; 10 | 11 | using KeyType = std::array; 12 | 13 | struct { 14 | std::optional x; 15 | std::optional y; 16 | std::optional n; 17 | } aes_slots[num_aes_slots]; 18 | 19 | // KeyYs used to decrypt title keys (required to decrypt CDN contents and update CIAs) 20 | std::array, 6> common_y; 21 | }; 22 | -------------------------------------------------------------------------------- /source/framework/color.hpp: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include 4 | 5 | namespace Color { 6 | 7 | /// Convert a 1-bit color component to 8 bit 8 | static inline uint8_t Convert1To8(uint8_t value) { 9 | return value * 255; 10 | } 11 | 12 | /// Convert a 4-bit color component to 8 bit 13 | static inline uint8_t Convert4To8(uint8_t value) { 14 | return (value << 4) | value; 15 | } 16 | 17 | /// Convert a 5-bit color component to 8 bit 18 | static inline uint8_t Convert5To8(uint8_t value) { 19 | return (value << 3) | (value >> 2); 20 | } 21 | 22 | /// Convert a 6-bit color component to 8 bit 23 | static inline uint8_t Convert6To8(uint8_t value) { 24 | return (value << 2) | (value >> 4); 25 | } 26 | 27 | } // namespace 28 | -------------------------------------------------------------------------------- /source/processes/ptm.hpp: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include "fake_process.hpp" 4 | #include "ipc.hpp" 5 | 6 | #include 7 | 8 | namespace HLE { 9 | 10 | namespace OS { 11 | 12 | struct Handle; 13 | 14 | class FakePTM final { 15 | OS& os; 16 | spdlog::logger& logger; 17 | 18 | public: 19 | FakePTM(FakeThread& thread); 20 | 21 | void IPCThread(FakeThread& thread, const char* service_name); 22 | void IPCThread_gets(FakeThread& thread, const char* service_name); 23 | 24 | void CommandHandler(FakeThread& thread, const IPC::CommandHeader& header); 25 | void CommandHandler_gets(FakeThread& thread, const IPC::CommandHeader& header); 26 | }; 27 | 28 | } // namespace OS 29 | 30 | } // namespace HLE 31 | 32 | #pragma once 33 | -------------------------------------------------------------------------------- /data/nand_archives/CMakeLists.txt: -------------------------------------------------------------------------------- 1 | # This directory contains pre-built NCCH archives that provide freely licensed 2 | # alternatives to system archives not included in game update partitions. 3 | # 4 | # The build scripts are located at https://github.com/mikage-emu/conan-3ds/tree/mikage/packages/citra_system_archives, 5 | # which is based on https://github.com/B3n30/citra_system_archives. 6 | 7 | set(TITLES 8 | "0004009b/00010202" 9 | "0004009b/00010402" 10 | "0004009b/00014002" 11 | "000400db/00010302" 12 | "000400db/00016102") 13 | 14 | include(GNUInstallDirs) 15 | foreach(TITLE ${TITLES}) 16 | install(FILES "${TITLE}/content/00000000.app" RENAME "00000000.cxi" DESTINATION "${CMAKE_INSTALL_DATAROOTDIR}/mikage/nand/title/${TITLE}/content") 17 | endforeach() 18 | -------------------------------------------------------------------------------- /source/platform/gpio.hpp: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include "ipc.hpp" 4 | 5 | namespace Platform { 6 | 7 | /** 8 | * GPIO: Interface for system processes to hardware interrupts. 9 | */ 10 | namespace GPIO { 11 | 12 | // All IPC commands are common between the services in this module, hence they 13 | // are not put in a nested namespace 14 | 15 | namespace IPC = Platform::IPC; 16 | 17 | // TODO: Verify the input handle type for Unb/BindInterrupt 18 | using BindInterrupt = IPC::IPCCommand<0x9>::add_uint32::add_uint32::add_handle 19 | ::response; 20 | using UnbindInterrupt = IPC::IPCCommand<0xa>::add_uint32::add_handle 21 | ::response; 22 | 23 | } // namespace GPIO 24 | 25 | } // namespace Platform 26 | -------------------------------------------------------------------------------- /source/utility/simple_tcp.cpp: -------------------------------------------------------------------------------- 1 | #include "simple_tcp.hpp" 2 | 3 | #include 4 | 5 | void SimpleTCPServer::SetupAsyncAccept() { 6 | acceptor.async_accept([&](boost::system::error_code ec, boost::asio::ip::tcp::socket socket) { 7 | if (!ec) { 8 | OnClientConnected(std::move(socket)); 9 | } 10 | SetupAsyncAccept(); 11 | }); 12 | } 13 | 14 | SimpleTCPServer::SimpleTCPServer(uint16_t port) 15 | : acceptor(io_context, boost::asio::ip::tcp::endpoint(boost::asio::ip::tcp::v4(), port)) { 16 | SetupAsyncAccept(); 17 | } 18 | 19 | void SimpleTCPServer::RunTCPServer() { 20 | while (io_context.run()) {} 21 | } 22 | 23 | void SimpleTCPServer::StopTCPServer() { 24 | boost::asio::post(io_context, [this]() { io_context.stop(); }); 25 | } 26 | -------------------------------------------------------------------------------- /source/processes/gpio.hpp: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include "fake_process.hpp" 4 | 5 | #include 6 | 7 | namespace HLE { 8 | 9 | namespace OS { 10 | 11 | struct Handle; 12 | 13 | class FakeGPIO final { 14 | OS& os; 15 | spdlog::logger& logger; 16 | 17 | public: 18 | FakeGPIO(FakeThread& thread); 19 | 20 | void GPIOThread(FakeThread& thread, const char* service_name, uint32_t interrupt_mask); 21 | 22 | void CommandHandler(FakeThread& thread, const IPC::CommandHeader& header, uint32_t interrupt_mask); 23 | 24 | OS::ResultAnd<> HandleBindInterrupt(FakeThread& thread, uint32_t service_interrupt_mask, uint32_t interrupt_mask, 25 | uint32_t priority, Handle event); 26 | }; 27 | 28 | } // namespace OS 29 | 30 | } // namespace HLE 31 | -------------------------------------------------------------------------------- /source/processes/pxi_fs_file_buffer_emu.hpp: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include "pxi_fs.hpp" 4 | 5 | namespace HLE { 6 | 7 | namespace PXI { 8 | 9 | namespace FS { 10 | 11 | class FileBufferInEmulatedMemory : public FileBuffer { 12 | OS::FakeThread& thread; 13 | PXIBuffer buffer; 14 | 15 | public: 16 | FileBufferInEmulatedMemory(OS::FakeThread& thread, const PXIBuffer& buffer) : thread(thread), buffer(buffer) { 17 | } 18 | 19 | void Write(const char* source, uint32_t num_bytes) override { 20 | for (uint32_t buffer_offset = 0; buffer_offset < num_bytes; ++buffer_offset) { 21 | buffer.Write(thread, buffer_offset, source[buffer_offset]); 22 | } 23 | } 24 | }; 25 | 26 | } // namespace FS 27 | 28 | } // namespace PXI 29 | 30 | } // namespace HLE 31 | -------------------------------------------------------------------------------- /source/video_core/utils/vulkan_utils/CMakeLists.txt: -------------------------------------------------------------------------------- 1 | find_package(SPIRV-Tools REQUIRED) 2 | 3 | add_library(vulkan-util STATIC 4 | device_manager.cpp 5 | glsl_helper.cpp 6 | memory_types.cpp 7 | ) 8 | target_include_directories(vulkan-util INTERFACE ..) 9 | target_link_libraries(vulkan-util PUBLIC Vulkan::Vulkan) 10 | target_link_libraries(vulkan-util PUBLIC glslang::glslang) 11 | target_link_libraries(vulkan-util PUBLIC glslang::glslang-default-resource-limits) 12 | target_link_libraries(vulkan-util PUBLIC glslang::SPIRV) 13 | target_link_libraries(vulkan-util PRIVATE SPIRV-Tools) # not linked by upstream glslang 14 | target_link_libraries(vulkan-util PRIVATE range-v3::range-v3) 15 | target_link_libraries(vulkan-util PRIVATE spdlog::spdlog) 16 | set_property(TARGET vulkan-util PROPERTY POSITION_INDEPENDENT_CODE ON) 17 | -------------------------------------------------------------------------------- /source/arm/thumb.hpp: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include "../arm.h" 4 | 5 | #include 6 | 7 | namespace ARM { 8 | 9 | struct DecodedThumbInstr { 10 | // Equivalent ARM instruction, if any 11 | std::optional arm_equivalent; 12 | bool may_read_pc = false; 13 | bool may_modify_pc = false; 14 | 15 | DecodedThumbInstr& SetMayReadPC() { 16 | may_read_pc = true; 17 | return *this; 18 | } 19 | 20 | DecodedThumbInstr& SetMayModifyPC() { 21 | may_modify_pc = true; 22 | return *this; 23 | } 24 | }; 25 | 26 | /** 27 | * Translates the given THUMB instruction to an equivalent ARM encoding. 28 | * If this is not possible, the caller is responsible for interpreting 29 | * the instruction manually. 30 | */ 31 | DecodedThumbInstr DecodeThumb(ARM::ThumbInstr); 32 | 33 | } // namespace ARM 34 | -------------------------------------------------------------------------------- /source/video_core/utils/vulkan_utils/memory_types.cpp: -------------------------------------------------------------------------------- 1 | #include "memory_types.hpp" 2 | 3 | #include 4 | 5 | namespace Pica::Vulkan { 6 | 7 | MemoryTypeDatabase::MemoryTypeDatabase(spdlog::logger& logger, vk::PhysicalDevice physical_device) : physical_device(physical_device) { 8 | // Output list of memory types for debug purposes 9 | auto properties = physical_device.getMemoryProperties(); 10 | for (uint32_t type_index = 0; type_index < properties.memoryTypeCount; ++type_index) { 11 | auto& type = properties.memoryTypes[type_index]; 12 | auto& heap = properties.memoryHeaps[type.heapIndex]; 13 | logger.info("Memory type {}: {}, using heap {} ({} bytes, {})", type_index, vk::to_string(type.propertyFlags), type.heapIndex, heap.size, vk::to_string(heap.flags)); 14 | } 15 | } 16 | 17 | } // namespace Pica::Vulkan 18 | -------------------------------------------------------------------------------- /source/framework/logging.hpp: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include 4 | 5 | #include 6 | 7 | class LogManager { 8 | spdlog::sink_ptr sink; 9 | std::unordered_map> loggers; 10 | 11 | public: 12 | LogManager(spdlog::sink_ptr sink_) : sink(sink_) { 13 | } 14 | 15 | void ChangeSink(spdlog::sink_ptr new_sink) { 16 | sink = new_sink; 17 | } 18 | 19 | std::shared_ptr GetLogger(const std::string& name) { 20 | return loggers.at(name); 21 | } 22 | 23 | std::shared_ptr RegisterLogger(std::string name) { 24 | auto logger = std::make_shared(name, sink); 25 | logger->set_pattern("[%n] [%l] %v"); 26 | auto ret = loggers.emplace(std::move(name), std::move(logger)); 27 | return ret.first->second; 28 | } 29 | }; 30 | -------------------------------------------------------------------------------- /source/processes/ns.hpp: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include "fake_process.hpp" 4 | 5 | namespace FileFormat { 6 | struct ExHeader; 7 | } 8 | 9 | namespace HLE { 10 | 11 | namespace OS { 12 | 13 | class OS; 14 | class FakeThread; 15 | 16 | // TODO: Remove this function once BootThread doesn't need access to it anymore 17 | OS::ResultAnd LaunchTitleInternal(FakeThread& source, bool from_firm, uint64_t title_id, uint32_t flags); 18 | 19 | /** 20 | * Creates a process from the code in the given NCCH file. 21 | * 22 | * Performs minimal setup, but doesn't register the process to any services. 23 | */ 24 | HandleTable::Entry LoadProcessFromFile(FakeThread&, 25 | bool from_firm, 26 | const FileFormat::ExHeader&, 27 | std::unique_ptr ncch_file, bool is_exefs = false /* TODO: Get rid of this */); 28 | 29 | } // namespace OS 30 | 31 | } // namespace HLE 32 | -------------------------------------------------------------------------------- /source/video_core/utils/vulkan_utils/device_manager.hpp: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include 4 | 5 | #include 6 | 7 | namespace spdlog { 8 | class logger; 9 | } 10 | 11 | struct VulkanInstanceManager { 12 | #if VK_HEADER_VERSION >= 301 13 | vk::detail::DynamicLoader vulkan_loader; 14 | #else 15 | vk::DynamicLoader vulkan_loader; 16 | #endif 17 | vk::UniqueInstance instance; 18 | 19 | VulkanInstanceManager(spdlog::logger&, const char* app_name, const std::vector& required_instance_extensions); 20 | }; 21 | 22 | struct VulkanDeviceManager { 23 | vk::PhysicalDevice physical_device; 24 | vk::UniqueDevice device; 25 | 26 | uint32_t present_queue_index; 27 | uint32_t graphics_queue_index; 28 | vk::Queue present_queue; 29 | vk::Queue graphics_queue; 30 | 31 | VulkanDeviceManager(VulkanInstanceManager&, spdlog::logger&, vk::SurfaceKHR, bool is_wayland); 32 | }; 33 | 34 | /** 35 | * Check if we're running inside of RenderDoc 36 | */ 37 | bool IsRenderDocActive(); 38 | -------------------------------------------------------------------------------- /source/debug/os.hpp: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include 4 | 5 | #include 6 | #include 7 | 8 | namespace HLE::OS { 9 | class OS; 10 | class Process; 11 | class Thread; 12 | using ProcessId = uint32_t; 13 | using ThreadId = uint32_t; 14 | } 15 | 16 | namespace Debugger { 17 | 18 | struct OSService : Service { 19 | struct ProcessContext { 20 | HLE::OS::Process* process; 21 | std::unordered_map threads; 22 | }; 23 | 24 | std::unordered_map processes; 25 | std::mutex access_mutex; 26 | 27 | void RegisterProcess(HLE::OS::ProcessId, HLE::OS::Process&); 28 | void UnregisterProcess(HLE::OS::ProcessId); 29 | 30 | void RegisterThread(HLE::OS::ProcessId, HLE::OS::ThreadId, HLE::OS::Thread&); 31 | void UnregisterThread(HLE::OS::ProcessId, HLE::OS::ThreadId); 32 | 33 | void Shutdown(); 34 | 35 | void RegisterRoutes(Pistache::Rest::Router&) override; 36 | }; 37 | 38 | } // namespace Debugger 39 | -------------------------------------------------------------------------------- /source/video_core/src/video_core/command_processor.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include "common/bit_field.h" 4 | #include "common/common_types.h" 5 | 6 | namespace Pica { 7 | 8 | struct Context; 9 | 10 | namespace CommandProcessor { 11 | 12 | union CommandHeader { 13 | u32 hex; 14 | 15 | BitFieldLegacy< 0, 16, u32> cmd_id; 16 | 17 | // parameter_mask: 18 | // Mask applied to the input value to make it possible to update 19 | // parts of a register without overwriting its other fields. 20 | // first bit: 0x000000FF 21 | // second bit: 0x0000FF00 22 | // third bit: 0x00FF0000 23 | // fourth bit: 0xFF000000 24 | BitFieldLegacy<16, 4, u32> parameter_mask; 25 | 26 | BitFieldLegacy<20, 8, u32> extra_data_length; 27 | 28 | BitFieldLegacy<31, 1, u32> group_commands; 29 | }; 30 | static_assert(std::is_standard_layout::value == true, 31 | "CommandHeader does not use standard layout"); 32 | static_assert(sizeof(CommandHeader) == sizeof(u32), "CommandHeader has incorrect size!"); 33 | 34 | void ProcessCommandList(Context&, PAddr list, u32 size); 35 | 36 | } // namespace 37 | 38 | } // namespace 39 | -------------------------------------------------------------------------------- /source/pica.hpp: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include 4 | 5 | namespace spdlog { 6 | class logger; 7 | } 8 | 9 | namespace vk { 10 | class PhysicalDevice; 11 | class Device; 12 | class Queue; 13 | } 14 | 15 | namespace Pica { 16 | struct Context; 17 | class Renderer; 18 | } 19 | 20 | namespace Debugger { 21 | class DebugServer; 22 | } 23 | 24 | namespace Settings { 25 | struct Settings; 26 | } 27 | 28 | namespace Profiler { 29 | class Profiler; 30 | } 31 | 32 | class InterruptListener; 33 | 34 | namespace Memory { 35 | struct PhysicalMemory; 36 | } 37 | 38 | class PicaContext { 39 | public: 40 | PicaContext(std::shared_ptr, Debugger::DebugServer&, 41 | Settings::Settings&, Memory::PhysicalMemory&, 42 | Profiler::Profiler&, vk::PhysicalDevice, vk::Device, 43 | uint32_t graphics_queue_index, vk::Queue render_graphics_queue); 44 | ~PicaContext(); 45 | 46 | void InjectDependency(InterruptListener& listener); 47 | void InjectDependency(Memory::PhysicalMemory& memory); 48 | 49 | std::unique_ptr context; 50 | std::unique_ptr renderer; 51 | }; 52 | 53 | // Interface functions so that we don't need to define Pica::Context in here... 54 | Pica::Renderer* GetRenderer(Pica::Context&); 55 | -------------------------------------------------------------------------------- /source/session.hpp: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include "debug_server.hpp" 4 | #include "input.hpp" 5 | #include "pica.hpp" 6 | #include "interpreter.h" 7 | 8 | #include 9 | 10 | #include 11 | 12 | #include 13 | 14 | struct AudioFrontend; 15 | 16 | class NetworkConsole; 17 | 18 | namespace Loader { 19 | class GameCard; 20 | } 21 | 22 | struct KeyDatabase; 23 | 24 | struct EmuSession { 25 | Profiler::Profiler profiler; 26 | Debugger::DebugServer debug_server; 27 | 28 | // TODO: Does this still need to be a shared_ptr? 29 | std::shared_ptr setup; 30 | 31 | PicaContext pica; 32 | 33 | InputSource input; 34 | std::pair circle_pad { }; 35 | 36 | std::thread emuthread; 37 | std::exception_ptr emuthread_exception = nullptr; 38 | 39 | std::unique_ptr network_console; 40 | std::thread console_thread; 41 | 42 | EmuSession( LogManager&, Settings::Settings&, 43 | AudioFrontend&, VulkanDeviceManager&, EmuDisplay::EmuDisplay&, 44 | const KeyDatabase&, std::unique_ptr); 45 | 46 | void Run(); 47 | 48 | ~EmuSession(); 49 | }; 50 | 51 | std::unique_ptr LoadGameCard(spdlog::logger& logger, Settings::Settings& settings); 52 | -------------------------------------------------------------------------------- /source/loader/firm.hpp: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include 4 | 5 | #include 6 | 7 | namespace FIRM { 8 | 9 | using boost::endian::little_uint32_t; 10 | 11 | struct SectionHeader { 12 | little_uint32_t file_offset; // offset within file to section data 13 | little_uint32_t load_address; // physical memory address to load section data to 14 | little_uint32_t size; // size in bytes (may be 0) 15 | little_uint32_t type; // 0 = ARM9, 1 = ARM11 16 | uint8_t hash[0x20]; // SHA-256 hash of the section data 17 | }; 18 | static_assert(sizeof(SectionHeader) == 0x30, "Incorrect FIRM section header size"); 19 | 20 | struct Header { 21 | char magic[4]; // "FIRM" 22 | 23 | char reserved1[4]; 24 | 25 | little_uint32_t entry_arm11; // physical memory address 26 | little_uint32_t entry_arm9; // physical memory address 27 | 28 | char reserved2[0x30]; 29 | 30 | SectionHeader sections[4]; 31 | 32 | uint8_t signature[0x100]; // RSA-2048 signature of the header 33 | }; 34 | static_assert(sizeof(Header) == 0x200, "Incorrect FIRM header size"); 35 | 36 | } // namespace FIRM 37 | 38 | namespace Loader { 39 | 40 | /** 41 | * @note The stream read cursor is restored when this function returns 42 | */ 43 | bool IsFirm(std::istream& str); 44 | 45 | } // namespace Loader 46 | -------------------------------------------------------------------------------- /source/video_core/src/video_core/primitive_assembly.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include 4 | 5 | #include 6 | 7 | namespace Pica { 8 | 9 | struct Context; 10 | 11 | /* 12 | * Utility class to build triangles from a series of vertices, 13 | * according to a given triangle topology. 14 | */ 15 | template 16 | struct PrimitiveAssembler { 17 | using TriangleHandler = std::function; 21 | 22 | PrimitiveAssembler(); 23 | 24 | void Configure(Regs::TriangleTopology topology); 25 | 26 | /* 27 | * Queues a vertex, builds primitives from the vertex queue according to the given 28 | * triangle topology, and calls triangle_handler for each generated primitive. 29 | * NOTE: We could specify the triangle handler in the constructor, but this way we can 30 | * keep event and handler code next to each other. 31 | */ 32 | void SubmitVertex(Context&, VertexType& vtx, TriangleHandler triangle_handler); 33 | 34 | private: 35 | Regs::TriangleTopology topology; 36 | 37 | int buffer_index; 38 | VertexType buffer[2]; 39 | bool strip_ready; 40 | }; 41 | 42 | 43 | } // namespace 44 | -------------------------------------------------------------------------------- /source/framework/exceptions.cpp: -------------------------------------------------------------------------------- 1 | #include "exceptions.hpp" 2 | 3 | #if __has_include() 4 | #define UNW_LOCAL_ONLY 5 | #include 6 | #endif 7 | 8 | #include 9 | 10 | #include 11 | 12 | namespace Mikage::Exceptions { 13 | 14 | std::string generate_backtrace() { 15 | std::string ret; 16 | 17 | #if __has_include() 18 | unw_cursor_t cursor; unw_context_t uc; 19 | unw_word_t ip, sp; 20 | 21 | char name[256]; 22 | 23 | unw_getcontext(&uc); 24 | unw_init_local(&cursor, &uc); 25 | 26 | int frame = 0; 27 | while (unw_step(&cursor) > 0) { 28 | unw_get_reg(&cursor, UNW_REG_IP, &ip); 29 | unw_get_reg(&cursor, UNW_REG_SP, &sp); 30 | unw_word_t offp; 31 | unw_get_proc_name(&cursor, name, sizeof(name), &offp); 32 | ret += fmt::format("{:>3} {:#014x} in {} (sp={:#014x})\n", "#" + std::to_string(frame++), ip, boost::core::demangle(name).c_str(), sp); 33 | } 34 | #endif 35 | return ret; 36 | } 37 | 38 | std::string ContractViolated::FormatMessage(std::string_view condition, std::string_view function, std::string_view file, int line) { 39 | return fmt::format( "Failed assertion '{}' in {} ({}:{})\n", 40 | condition, function, std::filesystem::path(file).filename().c_str(), line); 41 | } 42 | 43 | } // namespace Mikage::Exceptions 44 | -------------------------------------------------------------------------------- /source/os_serialization.hpp: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include "os.hpp" 4 | #include "framework/formats.hpp" // TODO: Rename contents to generic Serialization namespace 5 | 6 | namespace Serialization { 7 | 8 | inline auto Loader(HLE::OS::Thread& thread, /*VAddr*/ uint32_t vaddr) { 9 | return [&thread, vaddr](char* dest, size_t size) mutable { 10 | for (auto data_ptr = dest; data_ptr - dest < size; ++data_ptr) { 11 | *data_ptr = thread.ReadMemory(vaddr++); 12 | } 13 | }; 14 | }; 15 | 16 | // TODO: Move to common framework 17 | template 18 | inline auto LoadVia(T&&... ts) { 19 | return FileFormat::SerializationInterface::Load(Loader(std::forward(ts)...)); 20 | } 21 | 22 | // TODO: We can't implement this currently: Load() doesn't take a reference, but copy, hence the state of loader isn't carried on! 23 | //template 24 | //inline auto LoadVia(T&&... ts) { 25 | // auto loader = Loader(std::forward(ts)...); 26 | // if constexpr (sizeof... (Datas) == 1) { 27 | // // Return the element directly 28 | // return (FileFormat::SerializationInterface::Load(loader), ...); 29 | // } else { 30 | // // Return a tuple of loaded elements 31 | // return std::tuple { FileFormat::SerializationInterface::Load(loader)... }; 32 | // } 33 | //} 34 | 35 | } // namespace Serialization 36 | -------------------------------------------------------------------------------- /source/debug_server.hpp: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #if ENABLE_PISTACHE 4 | #define DEBUG_SERVER_AVAILABLE 5 | #endif 6 | 7 | #include 8 | #include 9 | 10 | #ifdef DEBUG_SERVER_AVAILABLE 11 | namespace Pistache { 12 | namespace Rest { 13 | class Request; 14 | class Router; 15 | } 16 | namespace Http { 17 | class ResponseWriter; 18 | } 19 | } 20 | #endif 21 | 22 | namespace Debugger { 23 | 24 | #ifdef DEBUG_SERVER_AVAILABLE 25 | struct Service { 26 | virtual ~Service() = default; 27 | 28 | virtual void RegisterRoutes(Pistache::Rest::Router& router) = 0; 29 | }; 30 | 31 | /** 32 | * Factory function to create a service of the given type. 33 | * Must be specialized by service implementors 34 | */ 35 | template 36 | std::unique_ptr CreateService(); 37 | 38 | class DebugServerImpl; 39 | #endif 40 | 41 | class DebugServer { 42 | #ifdef DEBUG_SERVER_AVAILABLE 43 | std::unique_ptr impl; 44 | 45 | Service& GetService(const std::type_info& service_type) const; 46 | #endif 47 | 48 | public: 49 | #ifdef DEBUG_SERVER_AVAILABLE 50 | DebugServer(); 51 | ~DebugServer(); 52 | 53 | void Run(unsigned port); 54 | 55 | template 56 | ConcreteService& GetService() const { 57 | return static_cast(GetService(typeid(ConcreteService))); 58 | } 59 | #endif 60 | }; 61 | 62 | } // namespace Debugger 63 | -------------------------------------------------------------------------------- /source/processes/cfg_hpv.cpp: -------------------------------------------------------------------------------- 1 | #include "cfg_hpv.hpp" 2 | #include "os_hypervisor_private.hpp" 3 | #include "os.hpp" 4 | 5 | #include 6 | 7 | #include 8 | 9 | namespace HLE { 10 | 11 | namespace OS { 12 | 13 | namespace HPV { 14 | 15 | namespace { 16 | 17 | struct CfgService : SessionToPort { 18 | CfgService(RefCounted port_, CFGContext& context_) : SessionToPort(port_, context_) { 19 | } 20 | 21 | void OnRequest(Hypervisor& hypervisor, Thread& thread, Handle session) override { 22 | const uint32_t command_header = thread.ReadTLS(0x80); 23 | auto dispatcher = RequestDispatcher<> { thread, *this, command_header }; 24 | 25 | namespace Cmd = Platform::Config; 26 | 27 | dispatcher.DecodeRequest([&](auto& response, uint32_t size, uint32_t block_id, IPC::MappedBuffer output) { 28 | auto description = fmt::format( "GetConfigInfoBlk2, size={:#x}, block_id={:#x}", 29 | size, block_id); 30 | Session::OnRequest(hypervisor, thread, session, description); 31 | }); 32 | } 33 | }; 34 | 35 | } // anonymous namespace 36 | 37 | HPV::RefCounted CreateCfgService(RefCounted port, CFGContext& context) { 38 | return HPV::RefCounted(new CfgService(port, context)); 39 | } 40 | 41 | } // namespace HPV 42 | 43 | } // namespace OS 44 | 45 | } // namespace HLE 46 | -------------------------------------------------------------------------------- /source/video_core/externals/nihstro/include/nihstro/float24.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include 4 | #include 5 | 6 | #include "bit_field.h" 7 | 8 | namespace nihstro { 9 | 10 | inline uint32_t to_float24(float val) { 11 | static_assert(std::numeric_limits::is_iec559, "Compiler does not adhere to IEEE 754"); 12 | 13 | union Float32 { 14 | BitFieldNihstro< 0, 23, uint32_t> mant; 15 | BitFieldNihstro<23, 8, uint32_t> biased_exp; 16 | BitFieldNihstro<31, 1, uint32_t> sign; 17 | 18 | static int ExponentBias() { 19 | return 127; 20 | } 21 | } f32 = reinterpret_cast(val); 22 | 23 | union Float24 { 24 | uint32_t hex; 25 | 26 | BitFieldNihstro< 0, 16, uint32_t> mant; 27 | BitFieldNihstro<16, 7, uint32_t> biased_exp; 28 | BitFieldNihstro<23, 1, uint32_t> sign; 29 | 30 | static int ExponentBias() { 31 | return 63; 32 | } 33 | } f24 = { 0 }; 34 | 35 | int biased_exp = (int)f32.biased_exp - Float32::ExponentBias() + Float24::ExponentBias(); 36 | unsigned mant = (biased_exp >= 0) ? (f32.mant >> (f32.mant.NumBits() - f24.mant.NumBits())) : 0; 37 | if (biased_exp >= (1 << f24.biased_exp.NumBits())) { 38 | // TODO: Return +inf or -inf 39 | } 40 | 41 | f24.biased_exp = std::max(0, biased_exp); 42 | f24.mant = mant; 43 | f24.sign = f32.sign.Value(); 44 | 45 | return f24.hex; 46 | } 47 | 48 | } // namespace 49 | -------------------------------------------------------------------------------- /source/video_core/externals/nihstro/examples/assembler/cube/source/gs.h: -------------------------------------------------------------------------------- 1 | #ifndef GS_H 2 | #define GS_H 3 | 4 | #include <3ds.h> 5 | #include "math.h" 6 | 7 | #define GS_MATRIXSTACK_SIZE (8) 8 | 9 | typedef enum 10 | { 11 | GS_PROJECTION = 0, 12 | GS_MODELVIEW = 1, 13 | GS_MATRIXTYPES 14 | }GS_MATRIX; 15 | 16 | typedef struct 17 | { 18 | u8* data; 19 | u32 currentSize; // in bytes 20 | u32 maxSize; // in bytes 21 | u32 numVertices; 22 | u32* commands; 23 | u32 commandsSize; 24 | }gsVbo_s; 25 | 26 | 27 | void gsInit(shaderProgram_s* shader); 28 | void gsExit(void); 29 | 30 | void gsStartFrame(void); 31 | void gsAdjustBufferMatrices(mtx44 transformation); 32 | 33 | void* gsLinearAlloc(size_t size); 34 | void gsLinearFree(void* mem); 35 | 36 | float* gsGetMatrix(GS_MATRIX m); 37 | int gsLoadMatrix(GS_MATRIX m, float* data); 38 | int gsPushMatrix(); 39 | int gsPopMatrix(); 40 | int gsMatrixMode(GS_MATRIX m); 41 | 42 | void gsLoadIdentity(); 43 | void gsProjectionMatrix(float fovy, float aspect, float near, float far); 44 | void gsRotateX(float x); 45 | void gsRotateY(float y); 46 | void gsRotateZ(float z); 47 | void gsScale(float x, float y, float z); 48 | void gsTranslate(float x, float y, float z); 49 | int gsMultMatrix(float* data); 50 | 51 | int gsVboInit(gsVbo_s* vbo); 52 | int gsVboCreate(gsVbo_s* vbo, u32 size); 53 | int gsVboFlushData(gsVbo_s* vbo); 54 | int gsVboDestroy(gsVbo_s* vbo); 55 | int gsVboDraw(gsVbo_s* vbo); 56 | void* gsVboGetOffset(gsVbo_s* vbo); 57 | int gsVboAddData(gsVbo_s* vbo, void* data, u32 size, u32 units); 58 | 59 | #endif 60 | -------------------------------------------------------------------------------- /source/video_core/externals/nihstro/examples/assembler/cube_lighting/source/gs.h: -------------------------------------------------------------------------------- 1 | #ifndef GS_H 2 | #define GS_H 3 | 4 | #include <3ds.h> 5 | #include "math.h" 6 | 7 | #define GS_MATRIXSTACK_SIZE (8) 8 | 9 | typedef enum 10 | { 11 | GS_PROJECTION = 0, 12 | GS_MODELVIEW = 1, 13 | GS_MATRIXTYPES 14 | }GS_MATRIX; 15 | 16 | typedef struct 17 | { 18 | u8* data; 19 | u32 currentSize; // in bytes 20 | u32 maxSize; // in bytes 21 | u32 numVertices; 22 | u32* commands; 23 | u32 commandsSize; 24 | }gsVbo_s; 25 | 26 | 27 | void gsInit(shaderProgram_s* shader); 28 | void gsExit(void); 29 | 30 | void gsStartFrame(void); 31 | void gsAdjustBufferMatrices(mtx44 transformation); 32 | 33 | void* gsLinearAlloc(size_t size); 34 | void gsLinearFree(void* mem); 35 | 36 | float* gsGetMatrix(GS_MATRIX m); 37 | int gsLoadMatrix(GS_MATRIX m, float* data); 38 | int gsPushMatrix(); 39 | int gsPopMatrix(); 40 | int gsMatrixMode(GS_MATRIX m); 41 | 42 | void gsLoadIdentity(); 43 | void gsProjectionMatrix(float fovy, float aspect, float near, float far); 44 | void gsRotateX(float x); 45 | void gsRotateY(float y); 46 | void gsRotateZ(float z); 47 | void gsScale(float x, float y, float z); 48 | void gsTranslate(float x, float y, float z); 49 | int gsMultMatrix(float* data); 50 | 51 | int gsVboInit(gsVbo_s* vbo); 52 | int gsVboCreate(gsVbo_s* vbo, u32 size); 53 | int gsVboFlushData(gsVbo_s* vbo); 54 | int gsVboDestroy(gsVbo_s* vbo); 55 | int gsVboDraw(gsVbo_s* vbo); 56 | void* gsVboGetOffset(gsVbo_s* vbo); 57 | int gsVboAddData(gsVbo_s* vbo, void* data, u32 size, u32 units); 58 | 59 | #endif 60 | -------------------------------------------------------------------------------- /source/video_core/src/debug/gpu.hpp: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include "../video_core/shader.hpp" 4 | 5 | #include 6 | 7 | #include 8 | 9 | #include 10 | #include 11 | #include 12 | #include 13 | 14 | #include 15 | 16 | namespace Debugger { 17 | 18 | namespace detail { 19 | struct identity_hash { 20 | XXH64_hash_t operator()(const XXH64_hash_t& hash) const noexcept { 21 | return hash; 22 | } 23 | }; 24 | } 25 | 26 | // TODO: Rename to ShaderService 27 | struct GPUService : Service { 28 | struct ShaderData { 29 | std::vector instructions; 30 | std::vector swizzle_data; 31 | uint16_t bool_uniforms; 32 | uint32_t entry_point; 33 | 34 | Pica::Regs::VSOutputAttributes::Semantic output_semantics[4 * std::tuple_size_v]; 35 | }; 36 | 37 | std::mutex access_mutex; 38 | std::unordered_map shaders; 39 | 40 | void RegisterShader(const XXH64_hash_t& key, 41 | std::vector instructions, 42 | std::vector swizzle_data, 43 | uint32_t entry_point, 44 | uint16_t bool_uniforms, 45 | const decltype(Pica::Regs::shader_output_semantics)&); 46 | 47 | void Shutdown(); 48 | 49 | void RegisterRoutes(Pistache::Rest::Router&) override; 50 | }; 51 | 52 | } // namespace Debugger 53 | -------------------------------------------------------------------------------- /source/video_core/externals/nihstro/examples/assembler/cube/data/test.vsh: -------------------------------------------------------------------------------- 1 | // setup constants 2 | .alias myconst c32 as (1.0, 0.0, 0.5, 1.0) 3 | 4 | // setup output map 5 | .alias outpos o0 as position 6 | .alias outcol o1 as color 7 | .alias outtex0 o2.xyzw as texcoord0 // Would like to use .xy instead, but this is not supported by ctrulib currently 8 | .alias outtex1 o3.xyzw as texcoord1 9 | .alias outtex2 o4.xyzw as texcoord2 10 | 11 | // setup uniform map, for use with SHDR_GetUniformRegister 12 | .alias projection c0 // -c3 13 | .alias modelview c4 // -c7 14 | .alias lightDirection c8 15 | .alias lightAmbient c9 16 | 17 | main: 18 | mov r1.xyz, v0.xyz 19 | mov r1.w, myconst.w 20 | 21 | mdvl: // tempreg = mdlvMtx * in.pos 22 | dp4 r0.x, modelview[0], r1 23 | dp4 r0.y, modelview[1], r1 24 | dp4 r0.z, modelview[2], r1 25 | mov r0.w, myconst.w 26 | 27 | proj: // result.pos = projMtx * tempreg 28 | dp4 outpos.x, projection[0], r0 29 | dp4 outpos.y, projection[1], r0 30 | dp4 outpos.z, projection[2], r0 31 | dp4 outpos.w, projection[3], r0 32 | 33 | tex: // result.texcoord = in.texcoord 34 | mov outtex0, v1 35 | mov outtex1, myconst.yyyw 36 | mov outtex2, myconst.yyyw 37 | 38 | col: // Hacky lighting: color = ambient.xyz + clamp(dot(L,N), 1.0) * ambient.www 39 | dp3 r0.xyz, lightDirection.xyz, v2.xyz 40 | max r0.xyz, myconst.yyy, r0.xyz 41 | mul r0.xyz, lightAmbient.www, r0.xyz 42 | add outcol.xyz, lightAmbient.xyz, r0.xyz 43 | mov outcol.w, myconst.w 44 | 45 | nop 46 | end 47 | 48 | endmain: 49 | -------------------------------------------------------------------------------- /source/pica.cpp: -------------------------------------------------------------------------------- 1 | // video_core has its own version of BitField that collides with our BitField. Hence, we initialize the video_core context in this separate translation unit, where we don't include BitField. 2 | 3 | #include "pica.hpp" 4 | #include "video_core/src/video_core/vulkan/renderer.hpp" 5 | 6 | #include "video_core/src/video_core/context.h" 7 | 8 | #include 9 | 10 | #include 11 | 12 | PicaContext::PicaContext( std::shared_ptr logger, Debugger::DebugServer& debug_server, 13 | Settings::Settings& settings, Memory::PhysicalMemory& mem, 14 | Profiler::Profiler& profiler,vk::PhysicalDevice physical_device, vk::Device device, 15 | uint32_t graphics_queue_index, vk::Queue render_graphics_queue) 16 | : context(std::make_unique()) { 17 | renderer = std::make_unique(mem, logger, profiler, physical_device, device, graphics_queue_index, render_graphics_queue); 18 | context->debug_server = &debug_server; 19 | context->settings = &settings; 20 | context->renderer = renderer.get(); 21 | context->activity = &profiler.GetActivity("GPU"); 22 | context->logger = logger; 23 | } 24 | 25 | PicaContext::~PicaContext() = default; 26 | 27 | void PicaContext::InjectDependency(InterruptListener& os) { 28 | context->os = &os; 29 | } 30 | 31 | void PicaContext::InjectDependency(Memory::PhysicalMemory& memory) { 32 | context->mem = &memory; 33 | } 34 | 35 | Pica::Renderer* GetRenderer(Pica::Context& context) { 36 | return context.renderer; 37 | } 38 | -------------------------------------------------------------------------------- /source/video_core/externals/nihstro/license.txt: -------------------------------------------------------------------------------- 1 | Copyright 2014 Tony Wasserka 2 | All rights reserved. 3 | 4 | Redistribution and use in source and binary forms, with or without 5 | modification, are permitted provided that the following conditions are met: 6 | 7 | * Redistributions of source code must retain the above copyright 8 | notice, this list of conditions and the following disclaimer. 9 | * Redistributions in binary form must reproduce the above copyright 10 | notice, this list of conditions and the following disclaimer in the 11 | documentation and/or other materials provided with the distribution. 12 | * Neither the name of the owner nor the names of its contributors may 13 | be used to endorse or promote products derived from this software 14 | without specific prior written permission. 15 | 16 | THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS 17 | "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT 18 | LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR 19 | A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT 20 | OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, 21 | SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT 22 | LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, 23 | DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY 24 | THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT 25 | (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE 26 | OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 27 | 28 | 29 | -------------------------------------------------------------------------------- /source/framework/tests/config.cpp: -------------------------------------------------------------------------------- 1 | #include "framework/config_framework.hpp" 2 | 3 | #include 4 | 5 | #include 6 | 7 | struct IntegerTag : Config::Option { 8 | static constexpr const char* name = "IntegerTag"; 9 | using type = int; 10 | static type default_value() { return -1; } 11 | }; 12 | 13 | struct BooleanOptionTagTrue : Config::BooleanOption { 14 | static constexpr const char* name = "BooleanTagTrue"; 15 | }; 16 | 17 | struct BooleanOptionTagFalse : Config::BooleanOption { 18 | static constexpr const char* name = "BooleanTagFalse"; 19 | }; 20 | 21 | template<> 22 | bool Config::BooleanOption::default_val = true; 23 | template<> 24 | bool Config::BooleanOption::default_val = false; 25 | 26 | struct StructTag : Config::Option { 27 | struct Data { 28 | std::string string; 29 | bool operator == (const Data& d) const { 30 | return string == d.string; 31 | } 32 | }; 33 | static constexpr const char* name = "StructTag"; 34 | using type = Data; 35 | static type default_value() { return {"Hello World"}; } 36 | }; 37 | 38 | using Settings = Config::Options; 39 | 40 | TEST_CASE("Default Settings") { 41 | Settings settings; 42 | 43 | REQUIRE(settings.get() == -1); 44 | REQUIRE(settings.get() == true); 45 | REQUIRE(settings.get() == false); 46 | REQUIRE(settings.get() == StructTag::Data { "Hello World" }); 47 | } 48 | -------------------------------------------------------------------------------- /source/video_core/src/video_core/context.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include "primitive_assembly.h" 4 | #include "shader.hpp" 5 | 6 | #include 7 | 8 | #include 9 | #include 10 | 11 | namespace spdlog { 12 | class logger; 13 | } 14 | 15 | struct InterruptListener; 16 | 17 | namespace Memory { 18 | struct PhysicalMemory; 19 | } 20 | 21 | namespace Profiler { 22 | class Activity; 23 | } 24 | 25 | namespace Settings { 26 | struct Settings; 27 | } 28 | 29 | namespace Debugger { 30 | class DebugServer; 31 | } 32 | 33 | namespace Pica { 34 | 35 | class Renderer; 36 | 37 | struct Context { 38 | Regs registers; 39 | 40 | // Vertex attribute defaults as configured by the application 41 | std::array, 12> vertex_attribute_defaults; 42 | 43 | PrimitiveAssembler primitive_assembler; 44 | PrimitiveAssembler primitive_assembler_new; 45 | 46 | VertexShader::EnginePool shader_engines; 47 | 48 | VertexShader::ShaderUniforms shader_uniforms; 49 | 50 | std::array shader_memory; 51 | std::array swizzle_data; 52 | 53 | // Data for each of the 24 light LUTs 54 | std::array, 24> light_lut_data; 55 | 56 | Debugger::DebugServer* debug_server = nullptr; 57 | 58 | Settings::Settings* settings = nullptr; 59 | 60 | InterruptListener* os = nullptr; 61 | Memory::PhysicalMemory* mem = nullptr; 62 | 63 | Renderer* renderer = nullptr; 64 | Profiler::Activity* activity = nullptr; 65 | 66 | std::shared_ptr logger; 67 | }; 68 | 69 | } // namespace Pica 70 | -------------------------------------------------------------------------------- /source/arm/processor_interpreter.hpp: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include "processor_default.hpp" 4 | 5 | #include 6 | 7 | namespace Interpreter { 8 | 9 | // TODO: Instead of inhering CPUContext, integrate CPUContext into this class! 10 | struct InterpreterExecutionContext : ExecutionContextWithDefaultMemory, CPUContext { 11 | InterpreterExecutionContext(Processor& parent_, Setup& setup) 12 | : ExecutionContextWithDefaultMemory(parent_, setup.mem), CPUContext(setup.os.get(), &setup) { 13 | 14 | // TODO: Setup virtual memory mappings 15 | } 16 | 17 | ARM::State ToGenericContext() override { 18 | return cpu; 19 | } 20 | 21 | void FromGenericContext(const ARM::State& state) override { 22 | // TODO: Don't use memcpy for copying? 23 | std::memcpy(&cpu, &state, sizeof(state)); 24 | } 25 | 26 | std::optional TranslateVirtualAddress(uint32_t address) { 27 | return ExecutionContextWithDefaultMemory::TranslateVirtualAddress(address); 28 | } 29 | 30 | void SetDebuggingEnabled(bool enabled = true) override { 31 | debugger_attached = enabled; 32 | } 33 | 34 | bool IsDebuggingEnabled() const override { 35 | return debugger_attached; 36 | } 37 | 38 | // Optional reference to coroutine 39 | // TODO: Fix for HostThreadBasedThreadControl 40 | boost::coroutines2::coroutine::push_type* coro = nullptr; 41 | 42 | // Tagged address for LDREX/STREX. 43 | // We perform an implicit CLREX on context switches, so this mainly ensures 44 | // that the STREX following a LDREX uses a matching address. 45 | std::optional monitor_address; 46 | }; 47 | 48 | } // namespace Interpreter 49 | -------------------------------------------------------------------------------- /source/debug/jit.hpp: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include 4 | 5 | #include 6 | 7 | namespace HLE::OS { 8 | using ProcessId = uint32_t; 9 | using ThreadId = uint32_t; 10 | } 11 | 12 | struct JitDebugInfo; 13 | 14 | namespace Debugger { 15 | 16 | class JitId { 17 | uint64_t value; 18 | 19 | JitId(uint64_t value) : value(value) { } 20 | 21 | public: 22 | JitId(HLE::OS::ProcessId pid, HLE::OS::ThreadId tid) : value { (uint64_t { pid } << 32) | tid } { 23 | 24 | } 25 | 26 | static JitId FromRaw(uint64_t value) { 27 | return JitId { value }; 28 | } 29 | 30 | std::pair Decode() const { 31 | HLE::OS::ProcessId pid = value >> 32; 32 | HLE::OS::ThreadId tid = value & 0xffffffff; 33 | return std::make_pair(pid, tid); 34 | } 35 | 36 | struct Hasher { 37 | auto operator()(JitId self) const { 38 | return std::hash{}(self.value); 39 | } 40 | }; 41 | 42 | bool operator==(JitId oth) const { 43 | return value == oth.value; 44 | } 45 | }; 46 | 47 | struct JitService : Service { 48 | std::unordered_map contexts; 49 | 50 | void RegisterContext(HLE::OS::ProcessId pid, HLE::OS::ThreadId tid, JitDebugInfo& jit_context); 51 | void UnregisterContext(HLE::OS::ProcessId pid, HLE::OS::ThreadId tid); 52 | 53 | void doOptions(const Pistache::Rest::Request&, Pistache::Http::ResponseWriter response); 54 | 55 | void doJitBlocks(const Pistache::Rest::Request& request, Pistache::Http::ResponseWriter response); 56 | 57 | void RegisterRoutes(Pistache::Rest::Router& router) override; 58 | }; 59 | 60 | } // namespace Debugger 61 | -------------------------------------------------------------------------------- /source/processes/dlp.cpp: -------------------------------------------------------------------------------- 1 | #include "processes/dlp.hpp" 2 | 3 | #include "fake_process.hpp" 4 | 5 | namespace HLE { 6 | 7 | namespace OS { 8 | 9 | struct FakeDLP { 10 | FakeDLP(FakeThread&); 11 | 12 | static constexpr auto session_limit = 1; 13 | 14 | spdlog::logger& logger; 15 | }; 16 | 17 | 18 | static auto DlpSrvrCommandHandler(FakeThread& thread, FakeDLP& context, const Platform::IPC::CommandHeader& header) { 19 | switch (header.command_id) { 20 | case 0xe: // IsChild 21 | thread.WriteTLS(0x80, IPC::CommandHeader::Make(0, 2, 0).raw); 22 | thread.WriteTLS(0x84, RESULT_OK); 23 | thread.WriteTLS(0x88, 0); // Not a child 24 | break; 25 | 26 | default: 27 | throw std::runtime_error(fmt::format("DownloadPlay: Unknown dlp:SRVR command with header {:#x}", header.raw)); 28 | } 29 | 30 | return ServiceHelper::SendReply; 31 | } 32 | 33 | FakeDLP::FakeDLP(FakeThread& thread) 34 | : logger(*thread.GetLogger()) { 35 | 36 | ServiceHelper service; 37 | service.Append(ServiceUtil::SetupService(thread, "dlp:SRVR", session_limit)); 38 | 39 | auto InvokeCommandHandler = [this](FakeThread& thread, uint32_t /* index of signalled handle */) { 40 | Platform::IPC::CommandHeader header = { thread.ReadTLS(0x80) }; 41 | return DlpSrvrCommandHandler(thread, *this, header); 42 | }; 43 | 44 | service.Run(thread, std::move(InvokeCommandHandler)); 45 | } 46 | 47 | template<> std::shared_ptr CreateFakeProcessViaContext(OS& os, Interpreter::Setup& setup, uint32_t pid, const std::string& name) { 48 | return WrappedFakeProcess::CreateWithContext(os, setup, pid, name); 49 | } 50 | 51 | } // namespace OS 52 | 53 | } // namespace HLE 54 | -------------------------------------------------------------------------------- /source/loader/host_file.cpp: -------------------------------------------------------------------------------- 1 | #include "host_file.hpp" 2 | 3 | #include 4 | #include 5 | 6 | namespace FileSystem { 7 | 8 | class NativeFile final : public HLE::PXI::FS::File { 9 | public: 10 | boost::iostreams::file_descriptor_source source; 11 | 12 | public: 13 | NativeFile(int file_descriptor) : source(file_descriptor, boost::iostreams::never_close_handle) { 14 | } 15 | 16 | HLE::OS::ResultAnd<> Open(HLE::PXI::FS::FileContext&, HLE::PXI::FS::OpenFlags) override { 17 | // Nothing to do 18 | return std::make_tuple(HLE::OS::RESULT_OK); 19 | } 20 | 21 | HLE::OS::ResultAnd GetSize(HLE::PXI::FS::FileContext&) override { 22 | auto begin = boost::iostreams::seek(source, 0, std::ios_base::beg); 23 | auto end = boost::iostreams::seek(source, 0, std::ios_base::end); 24 | return std::make_tuple(HLE::OS::RESULT_OK, static_cast(end - begin)); 25 | } 26 | 27 | HLE::OS::ResultAnd Read(HLE::PXI::FS::FileContext& context, uint64_t offset, uint32_t num_bytes, HLE::PXI::FS::FileBuffer&& dest) override { 28 | boost::iostreams::seek(source, offset, std::ios_base::beg); 29 | 30 | std::vector data; 31 | data.resize(num_bytes); 32 | boost::iostreams::read(source, data.data(), num_bytes); 33 | dest.Write(data.data(), num_bytes); 34 | return std::make_tuple(HLE::OS::RESULT_OK, num_bytes); 35 | } 36 | 37 | void Close() override { 38 | 39 | } 40 | }; 41 | 42 | std::unique_ptr OpenNativeFile(int file_desctiptor) { 43 | return std::unique_ptr(new NativeFile(file_desctiptor)); 44 | } 45 | 46 | } // namespace FileSystem 47 | -------------------------------------------------------------------------------- /source/processes/mcu.hpp: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include "fake_process.hpp" 4 | 5 | namespace HLE { 6 | 7 | namespace OS { 8 | 9 | struct Handle; 10 | 11 | class FakeMCU { 12 | spdlog::logger& logger; 13 | 14 | HandleTable::Entry gpu_event; 15 | HandleTable::Entry hid_event; 16 | 17 | int invocation = 0; 18 | 19 | public: 20 | FakeMCU(FakeThread& thread); 21 | virtual ~FakeMCU() = default; 22 | 23 | void GPUThread(FakeThread& thread); 24 | void HIDThread(FakeThread& thread); 25 | 26 | void GPUCommandHandler(FakeThread& thread, Handle sender, const IPC::CommandHeader& header); 27 | void HIDCommandHandler(FakeThread& thread, Handle sender, const IPC::CommandHeader& header); 28 | 29 | OS::ResultAnd GPUGetLcdPowerState(FakeThread& thread); 30 | OS::ResultAnd<> GPUSetLcdPowerState(FakeThread& thread, uint32_t param1, uint32_t param2); 31 | OS::ResultAnd GPUGetGpuLcdInterfaceState(FakeThread& thread); 32 | OS::ResultAnd<> GPUSetGpuLcdInterfaceState(FakeThread& thread, uint32_t state); 33 | OS::ResultAnd GPUGetMcuFwVerHigh(FakeThread& thread); 34 | OS::ResultAnd GPUGetMcuFwVerLow(FakeThread& thread); 35 | OS::ResultAnd<> GPUSet3dLedState(FakeThread& thread, uint32_t state); 36 | OS::ResultAnd GPUGetMcuGpuEvent(FakeThread& thread); 37 | OS::ResultAnd GPUGetMcuGpuEventReason(FakeThread& thread); 38 | 39 | OS::ResultAnd<> HIDUnknown0x1(FakeThread& thread, uint32_t param); 40 | OS::ResultAnd HIDGetMcuHidEventHandle(FakeThread& thread); 41 | OS::ResultAnd HIDGet3dSliderState(FakeThread& thread); 42 | OS::ResultAnd<> HIDSetAccelerometerState(FakeThread& thread, uint32_t state); 43 | }; 44 | 45 | } // namespace OS 46 | 47 | } // namespace HLE 48 | -------------------------------------------------------------------------------- /.gitlab/build.yml: -------------------------------------------------------------------------------- 1 | image: ubuntu:24.04 2 | 3 | stages: 4 | - build 5 | 6 | variables: 7 | GIT_SUBMODULE_STRATEGY: recursive 8 | CONAN_HOME: ${CI_PROJECT_DIR}/.conan2 9 | 10 | build: 11 | stage: build 12 | tags: 13 | - saas-linux-medium-amd64 14 | cache: 15 | key: conan 16 | paths: 17 | - .conan2 18 | script: 19 | # Development tools 20 | - apt-get update 21 | - apt-get install -y cmake git ninja-build pkgconf python3-pip 22 | - pip install conan --break-system-packages 23 | 24 | # Project dependencies 25 | - apt-get install -y libvulkan-dev libx11-dev libx11-xcb-dev libfontenc-dev libice-dev libxau-dev libxaw7-dev libegl-dev xkb-data libxcomposite-dev libxdamage-dev libxfixes-dev libxi-dev libxinerama-dev libxmuu-dev libxrandr-dev libxrender-dev libxres-dev libxtst-dev libxv-dev libxxf86vm-dev libxcb-glx0-dev libxcb-render-util0-dev libxcb-xkb-dev libxcb-icccm4-dev libxcb-keysyms1-dev libxcb-randr0-dev libxcb-shape0-dev libxcb-xfixes0-dev libxcb-xinerama0-dev libxcb-dri3-dev libxcb-cursor-dev libxcb-dri2-0-dev libxcb-dri3-dev libxcb-present-dev libxcb-ewmh-dev libxcb-res0-dev libxcb-composite0 libxcb-composite0-dev libxcursor-dev libxcursor1 libxkbfile-dev libxkbfile1 libxss-dev libxss1 uuid-dev libxcb-util-dev 26 | - conan profile detect -e 27 | - conan config install --args="-b mikage" https://github.com/mikage-emu/conan-3ds.git && conan setup-3ds 28 | - conan install . --build=missing -c tools.system.package_manager:mode=install 29 | - conan cache clean 30 | 31 | # Setup Conan dependencies and build project 32 | - mkdir build && cd build 33 | - conan install .. -of . 34 | - cmake .. -G Ninja -DCMAKE_INSTALL_PREFIX=${CI_PROJECT_DIR}/install -DCMAKE_PREFIX_PATH=${CI_PROJECT_DIR}/build -DCMAKE_BUILD_TYPE=Release -DCMAKE_COLOR_DIAGNOSTICS=ON 35 | - ninja 36 | -------------------------------------------------------------------------------- /source/settings.cpp: -------------------------------------------------------------------------------- 1 | #include "framework/settings.hpp" 2 | 3 | namespace Settings { 4 | 5 | // std::filesystem doesn't understand "~", so we replace the defaults with 6 | // "/HOME~" for safety. The frontend must replace this substring with the true 7 | // home folder path on startup. 8 | // If a buggy frontend doesn't do this, accessing "/HOME~" will reliably fail 9 | // instead of constructing a relative path. 10 | std::string PathConfigDir::default_val = "/HOME~/.config/mikage"; 11 | std::string PathImmutableDataDir::default_val = "/usr/share/mikage"; 12 | std::string PathDataDir::default_val = "/HOME~/.local/share/mikage"; 13 | std::string PathCacheDir::default_val = "/HOME~/.cache/mikage"; 14 | 15 | CPUEngine CPUEngineTag::default_val = CPUEngine::NARMive; 16 | 17 | } 18 | 19 | namespace Config { 20 | 21 | template<> 22 | bool BooleanOption::default_val = false; 23 | 24 | 25 | template<> 26 | bool BooleanOption::default_val = false; 27 | 28 | template<> 29 | bool BooleanOption::default_val = false; 30 | 31 | 32 | template<> 33 | bool BooleanOption::default_val = false; 34 | 35 | template<> 36 | bool BooleanOption::default_val = false; 37 | 38 | template<> 39 | unsigned IntegralOption::default_val = 0; 40 | 41 | template<> 42 | unsigned IntegralOption::default_val = 0; 43 | 44 | template<> 45 | Settings::Renderer OptionDefault::default_val = Settings::Renderer::Vulkan; 46 | 47 | template<> 48 | Settings::ShaderEngine OptionDefault::default_val = Settings::ShaderEngine::Bytecode; 49 | 50 | template<> 51 | bool BooleanOption::default_val = false; 52 | 53 | } // namespace Config 54 | -------------------------------------------------------------------------------- /source/platform/hid.hpp: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include "ipc.hpp" 4 | 5 | namespace Platform { 6 | 7 | /** 8 | * Manages input peripherals and provides the interface for applications to 9 | * gather information about the current input state. 10 | */ 11 | namespace HID { 12 | 13 | namespace User { 14 | 15 | namespace IPC = Platform::IPC; 16 | 17 | /** 18 | * Returns a shared memory block for input data. The five output events signal 19 | * new data being available in each of the five shared memory regions. 20 | */ 21 | using GetIPCHandles = IPC::IPCCommand<0xa> 22 | ::response::add_handle::add_handle 23 | ::add_handle::add_handle 24 | ::add_handle::add_handle; // TODO: Mark events OneShot 25 | 26 | using EnableGyroscope = IPC::IPCCommand<0x13>::response; 27 | 28 | struct GyroscopeCalibrationData { 29 | uint32_t data[5]; 30 | 31 | static auto IPCSerialize(const GyroscopeCalibrationData& data) { 32 | return std::make_tuple(data.data[0], data.data[1], data.data[2], data.data[3], data.data[4]); 33 | } 34 | 35 | static GyroscopeCalibrationData IPCDeserialize(uint32_t a, uint32_t b, uint32_t c, uint32_t d, uint32_t e) { 36 | return { a, b, c, d, e }; 37 | } 38 | 39 | using ipc_data_size = std::integral_constant; 40 | }; 41 | 42 | // Maps the coefficient used to convert raw gyroscope values to degrees per second 43 | using GetGyroscopeSensitivity = IPC::IPCCommand<0x15>::response::add_uint32; 44 | 45 | using GetGyroscopeCalibrationData = IPC::IPCCommand<0x16>::response::add_serialized; 46 | 47 | } // namespace User 48 | 49 | } // namespace HID 50 | 51 | } // namespace Platform 52 | -------------------------------------------------------------------------------- /source/video_core/externals/nihstro/include/nihstro/preprocessor.h: -------------------------------------------------------------------------------- 1 | // Copyright 2015 Tony Wasserka 2 | // All rights reserved. 3 | // 4 | // Redistribution and use in source and binary forms, with or without 5 | // modification, are permitted provided that the following conditions are met: 6 | // 7 | // * Redistributions of source code must retain the above copyright 8 | // notice, this list of conditions and the following disclaimer. 9 | // * Redistributions in binary form must reproduce the above copyright 10 | // notice, this list of conditions and the following disclaimer in the 11 | // documentation and/or other materials provided with the distribution. 12 | // * Neither the name of the owner nor the names of its contributors may 13 | // be used to endorse or promote products derived from this software 14 | // without specific prior written permission. 15 | // 16 | // THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS 17 | // "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT 18 | // LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR 19 | // A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT 20 | // OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, 21 | // SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT 22 | // LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, 23 | // DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY 24 | // THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT 25 | // (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE 26 | // OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 27 | 28 | #pragma once 29 | 30 | namespace nihstro { 31 | 32 | struct SourceTree; 33 | 34 | SourceTree PreprocessAssemblyFile(const std::string& filename); 35 | 36 | } // namespace 37 | -------------------------------------------------------------------------------- /source/video_core/externals/nihstro/examples/assembler/cube_lighting/data/test.vsh: -------------------------------------------------------------------------------- 1 | // setup constants 2 | .alias myconst c32 as (1.0, 0.0, 0.5, 1.0) 3 | 4 | // setup output map 5 | .alias outpos o0 as position 6 | .alias outcol o1 as color 7 | .alias outtex0 o2.xyzw as texcoord0 // Would like to use .xy instead, but this is not supported by ctrulib currently 8 | .alias outtex1 o3.xyzw as texcoord1 9 | .alias outtex2 o4.xyzw as texcoord2 10 | 11 | // setup uniform map, for use with SHDR_GetUniformRegister 12 | .alias projection c0-c3 13 | .alias modelview c4-c7 14 | 15 | .alias num_lights i1 16 | 17 | .alias light_dir c8 18 | .alias light_diffuse c9 19 | .alias light_ambient c10 20 | .alias light_dir2 c11 21 | .alias light_diffuse2 c12 22 | .alias light_ambient2 c13 23 | 24 | main: 25 | mov r1.xyz, v0.xyz 26 | mov r1.w, myconst.w 27 | 28 | mdvl: // tempreg = mdlvMtx * in.pos 29 | dp4 r0.x, modelview[0], r1 30 | dp4 r0.y, modelview[1], r1 31 | dp4 r0.z, modelview[2], r1 32 | mov r0.w, myconst.w 33 | 34 | proj: // result.pos = projMtx * tempreg 35 | dp4 outpos.x, projection[0], r0 36 | dp4 outpos.y, projection[1], r0 37 | dp4 outpos.z, projection[2], r0 38 | dp4 outpos.w, projection[3], r0 39 | 40 | tex: // result.texcoord = in.texcoord 41 | mov outtex0, v1.xyzw 42 | mov outtex1, myconst.yyyw 43 | mov outtex2, myconst.yyyw 44 | 45 | lighting: // color = sum over all lights(diffuse * clamp(dot(L,N),0) + ambient) 46 | mov r0, myconst.yyyw 47 | 48 | loop num_lights 49 | mov r1.xyz, myconst.yyy 50 | dp3 r1.xyz, light_dir[lcnt].xyz, v2.xyz 51 | max r1.xyz, r1.xyz, myconst.yyy 52 | mul r1.xyz, r1.xyz, light_diffuse[lcnt].xyz 53 | add r1.xyz, r1.xyz, light_ambient[lcnt].xyz 54 | add r0.xyz, r1.xyz, r0.xyz 55 | nop 56 | endloop 57 | min r0.xyz, r0.xyz, myconst.xxx 58 | 59 | mov outcol, r0 60 | 61 | 62 | 63 | nop 64 | end 65 | 66 | endmain: 67 | -------------------------------------------------------------------------------- /source/platform/gpu/zorder.hpp: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include 4 | 5 | namespace Pica { 6 | 7 | struct ZOrderOffset { 8 | static constexpr uint32_t block_width = 8; 9 | static constexpr uint32_t block_height = 8; 10 | 11 | uint32_t texel_within_tile; 12 | uint32_t coarse_x; 13 | uint32_t coarse_y; 14 | }; 15 | 16 | inline ZOrderOffset ZOrderTileOffset(uint32_t x, uint32_t y) { 17 | // Images are split into 8x8 tiles. Each tile is composed of four 4x4 subtiles each 18 | // of which is composed of four 2x2 subtiles each of which is composed of four texels. 19 | // Each structure is embedded into the next-bigger one in a diagonal pattern, e.g. 20 | // texels are laid out in a 2x2 subtile like this: 21 | // 2 3 22 | // 0 1 23 | // 24 | // The full 8x8 tile has the texels arranged like this: 25 | // 26 | // 42 43 46 47 58 59 62 63 27 | // 40 41 44 45 56 57 60 61 28 | // 34 35 38 39 50 51 54 55 29 | // 32 33 36 37 48 49 52 53 30 | // 10 11 14 15 26 27 30 31 31 | // 08 09 12 13 24 25 28 29 32 | // 02 03 06 07 18 19 22 23 33 | // 00 01 04 05 16 17 20 21 34 | 35 | // TODO: More flexible encoding algorithms exist for this! 36 | uint32_t texel_index_within_tile = 0; 37 | for (int block_size_index = 0; block_size_index < 3; ++block_size_index) { 38 | int sub_tile_width = 1 << block_size_index; 39 | int sub_tile_height = 1 << block_size_index; 40 | 41 | int sub_tile_index = (x & sub_tile_width) << block_size_index; 42 | sub_tile_index += 2 * ((y & sub_tile_height) << block_size_index); 43 | texel_index_within_tile += sub_tile_index; 44 | } 45 | 46 | uint32_t coarse_x = (x / ZOrderOffset::block_width) * ZOrderOffset::block_width; 47 | uint32_t coarse_y = (y / ZOrderOffset::block_height) * ZOrderOffset::block_height; 48 | 49 | return { texel_index_within_tile, coarse_x, coarse_y }; 50 | } 51 | 52 | } // namespace Pica 53 | -------------------------------------------------------------------------------- /source/input.hpp: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include 4 | #include 5 | #include 6 | 7 | /** 8 | * Interface for the frontend to set emulator-visible input state. 9 | * 10 | * Get* member functions are reserved for the emulator core. 11 | * Set* member functions should be called from one thread, only. 12 | */ 13 | class InputSource { 14 | struct TouchState { 15 | float x = 0.f; 16 | float y = 0.f; 17 | bool pressed = false; 18 | }; 19 | 20 | struct CirclePadState { 21 | float x = 0.f; 22 | float y = 0.f; 23 | }; 24 | 25 | std::atomic buttons { 0 }; 26 | 27 | std::mutex touch_mutex; 28 | TouchState touch; 29 | CirclePadState circle_pad; 30 | 31 | std::atomic home_button; 32 | 33 | public: 34 | uint16_t GetButtonState() const { 35 | return buttons; 36 | } 37 | 38 | TouchState GetTouchState(); 39 | 40 | CirclePadState GetCirclePadState(); 41 | 42 | bool IsHomeButtonPressed() const { return home_button; }; 43 | 44 | void SetPressedA(bool); 45 | void SetPressedB(bool); 46 | void SetPressedX(bool); 47 | void SetPressedY(bool); 48 | 49 | void SetPressedL(bool); 50 | void SetPressedR(bool); 51 | 52 | void SetPressedSelect(bool); 53 | void SetPressedStart(bool); 54 | 55 | void SetPressedDigiLeft(bool); 56 | void SetPressedDigiRight(bool); 57 | void SetPressedDigiUp(bool); 58 | void SetPressedDigiDown(bool); 59 | 60 | void SetPressedHome(bool); 61 | 62 | /** 63 | * Puts touch pad into "touched" state at the given coordinates 64 | * 65 | * @param x Normalized horizontal coordinate [0..1] 66 | * @param y Normalized vertical coordinate [0..1] 67 | */ 68 | void SetTouch(float x, float y); 69 | 70 | /** 71 | * Puts touch pad into "untouched" state 72 | */ 73 | void EndTouch(); 74 | 75 | void SetCirclePad(float x, float y); 76 | }; 77 | -------------------------------------------------------------------------------- /source/video_core/utils/vulkan_utils/glsl_helper.cpp: -------------------------------------------------------------------------------- 1 | #include "glsl_helper.hpp" 2 | 3 | #include 4 | #include 5 | 6 | #include 7 | 8 | const struct InitGlslang { 9 | InitGlslang() { 10 | glslang::InitializeProcess(); 11 | } 12 | ~InitGlslang() { 13 | glslang::FinalizeProcess(); 14 | } 15 | } manage_glslang; 16 | 17 | std::vector CompileShader(EShLanguage kind, const char* code, const char* entrypoint) { 18 | glslang::TShader shader(kind); 19 | shader.setEntryPoint(entrypoint); 20 | shader.setStrings(&code, 1); 21 | shader.setEnvInput(glslang::EShSourceGlsl, kind, glslang::EShClientVulkan, 100); 22 | shader.setEnvClient(glslang::EShClientVulkan, glslang::EShTargetVulkan_1_2); 23 | shader.setEnvTarget(glslang::EShTargetSpv, glslang::EShTargetSpv_1_0); 24 | 25 | if (!shader.parse(GetDefaultResources(), 100, ECoreProfile, false, false, EShMsgDefault)) { 26 | fmt::print(stderr, "Failed to compile {} shader:\n{}\n", glslang::StageName(kind), code); 27 | throw std::runtime_error(fmt::format( "Failed to compile {} shader: {}\n", 28 | glslang::StageName(kind), shader.getInfoLog())); 29 | } 30 | 31 | glslang::TProgram program; 32 | program.addShader(&shader); 33 | if (!program.link(EShMsgDefault)) { 34 | fmt::print(stderr, "Failed to link {} shader:\n{}\n", glslang::StageName(kind), code); 35 | throw std::runtime_error(fmt::format( "Failed to link {} shader: {}\n", 36 | glslang::StageName(kind), program.getInfoLog())); 37 | } 38 | 39 | glslang::TIntermediate& intermediate = *program.getIntermediate(kind); 40 | std::vector result; 41 | glslang::SpvOptions spv_options; 42 | spv_options.disableOptimizer = false; 43 | glslang::GlslangToSpv(intermediate, result, &spv_options); 44 | return result; 45 | } 46 | -------------------------------------------------------------------------------- /source/processes/i2c.hpp: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include "fake_process.hpp" 4 | 5 | namespace HLE { 6 | 7 | namespace OS { 8 | 9 | class FakeI2C final { 10 | OS& os; 11 | spdlog::logger& logger; 12 | 13 | IPC::StaticBuffer static_buffers[2]; 14 | 15 | public: 16 | FakeI2C(FakeThread& thread); 17 | 18 | void I2CThread(FakeThread& thread, const char* service_name, uint32_t device_mask); 19 | 20 | void CommandHandler(FakeThread& thread, const IPC::CommandHeader& header, uint32_t device_mask); 21 | 22 | OS::ResultAnd<> SetRegisterBits8(FakeThread& thread, uint32_t device_id, uint32_t reg_id, uint32_t reg_data, uint32_t enable_mask); 23 | OS::ResultAnd<> EnableRegisterBits8(FakeThread& thread, uint32_t device_id, uint32_t reg_id, uint32_t enable_mask); 24 | OS::ResultAnd<> DisableRegisterBits8(FakeThread& thread, uint32_t device_id, uint32_t reg_id, uint32_t disable_mask); 25 | OS::ResultAnd<> WriteRegister8(FakeThread& thread, uint32_t device_id, uint32_t reg_id, uint32_t data); 26 | OS::ResultAnd ReadRegister8(FakeThread& thread, uint32_t device_id, uint32_t reg_id); 27 | OS::ResultAnd<> WriteRegisterBuffer8_0xb(FakeThread& thread, uint32_t device_id, uint32_t reg_id, uint32_t size, const IPC::StaticBuffer& data); 28 | OS::ResultAnd ReadRegisterBuffer8(FakeThread& thread, uint32_t device_id, uint32_t reg_id, uint32_t size); 29 | OS::ResultAnd<> WriteRegisterBuffer8_0xe(FakeThread& thread, uint32_t device_id, uint32_t reg_id, uint32_t size, const IPC::StaticBuffer& data); 30 | OS::ResultAnd<> Unknown_0xf(FakeThread& thread, uint32_t param1, uint32_t param2, uint32_t param3); 31 | OS::ResultAnd<> WriteRegisterBuffer_0x11(FakeThread& thread, uint32_t device_id, uint32_t reg_id, uint32_t size, IPC::MappedBuffer input_buffer); 32 | OS::ResultAnd<> ReadRegisterBuffer_0x12(FakeThread& thread, uint32_t device_id, uint32_t reg_id, uint32_t size, IPC::MappedBuffer output_buffer); 33 | }; 34 | 35 | } // namespace OS 36 | 37 | } // namespace HLE 38 | -------------------------------------------------------------------------------- /source/platform/mcu.hpp: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include "ipc.hpp" 4 | 5 | namespace Platform { 6 | 7 | /** 8 | * Low-level interaction with hardware devices. 9 | */ 10 | namespace MCU { 11 | 12 | namespace GPU { 13 | 14 | namespace IPC = Platform::IPC; 15 | 16 | // NOTE: All command signatures below have been verified unless labelled otherwise 17 | 18 | using GetLcdPowerState = IPC::IPCCommand<0x1> 19 | ::response::add_uint32::add_uint32; 20 | using SetLcdPowerState = IPC::IPCCommand<0x2>::add_uint32::add_uint32 21 | ::response; 22 | using GetGpuLcdInterfaceState = IPC::IPCCommand<0x3> 23 | ::response::add_uint32; 24 | using SetGpuLcdInterfaceState = IPC::IPCCommand<0x4>::add_uint32 25 | ::response; 26 | using GetMcuFwVerHigh = IPC::IPCCommand<0x9> 27 | ::response::add_uint32; 28 | using GetMcuFwVerLow = IPC::IPCCommand<0xa> 29 | ::response::add_uint32; 30 | using Set3dLedState = IPC::IPCCommand<0xb>::add_uint32 31 | ::response; 32 | using GetMcuGpuEvent = IPC::IPCCommand<0xd> 33 | ::response::add_handle; 34 | using GetMcuGpuEventReason = IPC::IPCCommand<0xe> 35 | ::response::add_uint32; // Reply not verified 36 | 37 | } // namespace GPU 38 | 39 | 40 | namespace HID { 41 | 42 | // NOTE: All command signatures below have been verified 43 | 44 | using Unknown0x1 = IPC::IPCCommand<0x1>::add_uint32 45 | ::response; 46 | using GetMcuHidEventHandle = IPC::IPCCommand<0xc> 47 | ::response::add_handle; 48 | using Get3dSliderState = IPC::IPCCommand<0x7> 49 | ::response::add_uint32; 50 | using SetAccelerometerState = IPC::IPCCommand<0xf>::add_uint32 51 | ::response; 52 | 53 | } // namespace HID 54 | 55 | } // namespace MCU 56 | 57 | } // namespace Platform 58 | -------------------------------------------------------------------------------- /source/platform/config.hpp: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include "ipc.hpp" 4 | 5 | namespace Platform { 6 | 7 | /** 8 | * Config: Exposes access to various global configuration parameters, most 9 | * of which are stored in this title's NAND savegame. 10 | */ 11 | namespace Config { 12 | 13 | // Lots of these commands are shared between cfg:u, cfg:i, and cfg:s, hence we 14 | // make no effort to separating them into different namespaces 15 | 16 | using GetConfigInfoBlk2 = Platform::IPC::IPCCommand<0x1>::add_uint32::add_uint32::add_buffer_mapping_write 17 | ::response; 18 | 19 | using SecureInfoGetRegion = Platform::IPC::IPCCommand<0x2> 20 | ::response::add_uint32; 21 | 22 | /** 23 | * Checks whether the system is running on a Canada or USA region. 24 | * 25 | * This information is queried from nand:/rw/sys/SecureInfo_A and from the 26 | * CountryInfo savedata block. 1 is returned if both indicate USA/Canada. 27 | */ 28 | using RegionIsCanadaOrUSA = Platform::IPC::IPCCommand<0x4> 29 | ::response::add_uint32; 30 | 31 | using SecureInfoGetRegion2 = Platform::IPC::IPCCommand<0x406> 32 | ::response::add_uint32; 33 | using SecureInfoGetRegion3 = Platform::IPC::IPCCommand<0x816> 34 | ::response::add_uint32; 35 | 36 | using GetSystemModel = Platform::IPC::IPCCommand<0x5> 37 | ::response::add_uint32; 38 | using GetConfigInfoBlk8 = Platform::IPC::IPCCommand<0x401>::add_uint32::add_uint32::add_buffer_mapping_write 39 | ::response::add_buffer_mapping_write; 40 | /** 41 | * Inputs: 42 | * - Block id 43 | * - Size of data to be written 44 | * - Input data 45 | */ 46 | using SetConfigInfoBlk4 = Platform::IPC::IPCCommand<0x402>::add_uint32::add_uint32::add_buffer_mapping_read 47 | ::response::add_buffer_mapping_read; 48 | 49 | } // namespace Config 50 | 51 | } // namespace Platform 52 | -------------------------------------------------------------------------------- /source/video_core/src/video_core/shader_private.hpp: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include 4 | 5 | namespace Pica::VertexShader { 6 | 7 | /** 8 | * Abstract value-type to represent offsets in a shader. 9 | * 10 | * The template parameter is used to distinguish different address spaces, such 11 | * as the space of offsets in the original shader as uploaded through a PICA200 12 | * command list, or the space of offsets in an internal block representation of 13 | * the shader. Distinguishing these address spaces provides a type-safety net 14 | * that ensures code behavior matches developer intent. 15 | */ 16 | template 17 | struct ShaderCodeOffsetBase { 18 | uint32_t value; 19 | 20 | ShaderCodeOffsetBase IncrementedBy(uint32_t instructions) const { 21 | return { value + instructions }; 22 | } 23 | 24 | ShaderCodeOffsetBase GetPrevious() const { 25 | return { value - 1 }; 26 | } 27 | 28 | ShaderCodeOffsetBase GetNext() const { 29 | return { value + 1 }; 30 | } 31 | 32 | bool operator==(ShaderCodeOffsetBase other) const { 33 | return value == other.value; 34 | } 35 | 36 | bool operator!=(ShaderCodeOffsetBase other) const { 37 | return value != other.value; 38 | } 39 | 40 | bool operator<(ShaderCodeOffsetBase other) const { 41 | return value < other.value; 42 | } 43 | 44 | int32_t operator-(ShaderCodeOffsetBase other) const { 45 | return value - other.value; 46 | } 47 | 48 | template 49 | nihstro::Instruction ReadFrom(const Rng& code) const { 50 | return { code[value] }; 51 | } 52 | 53 | constexpr ShaderCodeOffsetBase& operator++() noexcept { 54 | ++value; 55 | return *this; 56 | } 57 | 58 | constexpr ShaderCodeOffsetBase operator++(int) noexcept { 59 | return { value++ + 1 }; 60 | } 61 | 62 | using difference_type = int32_t; 63 | }; 64 | 65 | using ShaderCodeOffset = ShaderCodeOffsetBase; 66 | 67 | } // namespace Pica::VertexShader 68 | -------------------------------------------------------------------------------- /source/video_core/src/video_core/vulkan/pipeline_cache.hpp: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include 4 | #include 5 | 6 | namespace Profiler { 7 | class Activity; 8 | } 9 | 10 | namespace Pica { 11 | 12 | struct Regs; 13 | 14 | namespace Vulkan { 15 | 16 | struct LinkedRenderTargetResource; 17 | 18 | struct PipelineStateHash { 19 | // TODO: vertex_binding_descs, vertex_attribute_descs 20 | 21 | vk::ShaderModule shaders[2]; 22 | 23 | uint64_t vertex_input_mapping; 24 | 25 | uint32_t vertex_input_num_mappings; 26 | 27 | uint8_t flags; 28 | uint8_t triangle_topology; 29 | uint16_t pad; 30 | 31 | uint32_t viewport_data[5]; // TODO: Could compactify this into 3 uint32_ts 32 | 33 | uint32_t blend_config; 34 | 35 | uint32_t blend_constant; 36 | 37 | uint32_t scissor; 38 | 39 | uint32_t depthstencil[2]; 40 | 41 | bool operator==(const PipelineStateHash& other) const { 42 | return (0 == memcmp(this, &other, sizeof(*this))); 43 | } 44 | }; 45 | static_assert(sizeof(PipelineStateHash) == 72); 46 | 47 | struct PipelineStateHashHasher { 48 | size_t operator()(const PipelineStateHash& data) const noexcept; 49 | }; 50 | 51 | class PipelineCache { 52 | vk::Device device; 53 | 54 | std::unordered_map cache; 55 | 56 | public: 57 | PipelineCache(vk::Device device); 58 | ~PipelineCache(); 59 | 60 | vk::Pipeline Lookup(Profiler::Activity&, LinkedRenderTargetResource&, vk::PipelineLayout, vk::ShaderModule vertex_shader, 61 | vk::ShaderModule pixel_shader, vk::PipelineVertexInputStateCreateInfo&, Regs&); 62 | 63 | // Creates an uncached pipeline object for fullscreen effects 64 | vk::UniquePipeline FullscreenEffect(uint32_t fb_width, uint32_t fb_height, vk::RenderPass renderpass, vk::PipelineLayout layout, 65 | vk::ShaderModule vertex_shader, vk::ShaderModule pixel_shader); 66 | }; 67 | 68 | } // namespace Vulkan 69 | 70 | } // namespace Pica 71 | -------------------------------------------------------------------------------- /source/framework/console.hpp: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include 4 | #include 5 | #include 6 | #include 7 | 8 | /** 9 | * Abstract interface for defining console modules, which are handlers for 10 | * a group of commands. For instance, the group of commands starting with "os." 11 | * will all be handled by a single ConsoleModule. 12 | */ 13 | class ConsoleModule { 14 | public: 15 | virtual ~ConsoleModule() = default; 16 | 17 | /** 18 | * Perform the action specified by the given command 19 | * @param command Command to run (excludes the module name prefix) 20 | * @return User-facing reply string. boost::none is considered failure to handle the command. 21 | * @todo Change the parameter to std::string_view type 22 | */ 23 | virtual std::optional HandleCommand(const std::string& command) = 0; 24 | }; 25 | 26 | /** 27 | * Interface to the console. 28 | */ 29 | class Console { 30 | std::map> modules; 31 | 32 | public: 33 | virtual ~Console() = default; 34 | 35 | /** 36 | * Perform the action specified by the given command 37 | * @param command Command to run 38 | * @return User-facing reply string. boost::none is considered failure to handle the command. 39 | */ 40 | std::optional HandleCommand(const std::string& command); 41 | 42 | /** 43 | * Register the given module using the given category name. 44 | */ 45 | void RegisterModule(const std::string& name, std::unique_ptr module); 46 | }; 47 | 48 | /** 49 | * Console implementation that waits for incoming connections on a given port 50 | * and consecutively reads console commands from any clients. 51 | */ 52 | class NetworkConsole : public Console { 53 | std::unique_ptr server; 54 | 55 | public: 56 | NetworkConsole(unsigned port); 57 | ~NetworkConsole(); 58 | 59 | // Blocks until Stop() is called 60 | void Run(); 61 | 62 | // Must be called before object destruction 63 | void Stop(); 64 | }; 65 | -------------------------------------------------------------------------------- /source/processes/ssl.cpp: -------------------------------------------------------------------------------- 1 | #include "ssl.hpp" 2 | 3 | #include 4 | 5 | namespace HLE { 6 | 7 | namespace OS { 8 | 9 | struct FakeSSL { 10 | FakeSSL(FakeThread& thread); 11 | }; 12 | 13 | static auto SSLCCommandHandler(FakeThread& thread, FakeSSL&, std::string_view service_name, const IPC::CommandHeader& header) { 14 | switch (header.command_id) { 15 | using Initialize = IPC::IPCCommand<0x1>::add_process_id::response; 16 | using GenerateRandomBytes = IPC::IPCCommand<0x11>::add_uint32::add_buffer_mapping_write::response; 17 | case Initialize::id: 18 | thread.WriteTLS(0x80, IPC::CommandHeader::Make(0, 1, 0).raw); 19 | thread.WriteTLS(0x84, RESULT_OK); 20 | break; 21 | 22 | case GenerateRandomBytes::id: 23 | // Stubbed. TODO: Forward to ps::GenerateRandomBytes instead 24 | thread.WriteTLS(0x80, IPC::CommandHeader::Make(0, 1, 0).raw); 25 | thread.WriteTLS(0x84, RESULT_OK); 26 | break; 27 | 28 | default: 29 | throw Mikage::Exceptions::NotImplemented("Unknown {} service command with header {:#010x}", service_name, header.raw); 30 | } 31 | 32 | return ServiceHelper::SendReply; 33 | } 34 | 35 | static void SSLCThread(FakeSSL& context, FakeThread& thread) { 36 | ServiceHelper service; 37 | service.Append(ServiceUtil::SetupService(thread, "ssl:C", 1)); 38 | 39 | auto InvokeCommandHandler = [&](FakeThread& thread, uint32_t /*signalled_handle_index*/) { 40 | Platform::IPC::CommandHeader header = { thread.ReadTLS(0x80) }; 41 | return SSLCCommandHandler(thread, context, "ssl:C", header); 42 | }; 43 | 44 | service.Run(thread, std::move(InvokeCommandHandler)); 45 | } 46 | 47 | FakeSSL::FakeSSL(FakeThread& thread) { 48 | thread.name = "SSLCThread"; 49 | 50 | SSLCThread(*this, thread); 51 | } 52 | 53 | template<> std::shared_ptr CreateFakeProcessViaContext(OS& os, Interpreter::Setup& setup, uint32_t pid, const std::string& name) { 54 | return WrappedFakeProcess::CreateWithContext(os, setup, pid, name); 55 | } 56 | 57 | } // namespace OS 58 | 59 | } // namespace HLE 60 | -------------------------------------------------------------------------------- /source/platform/i2c.hpp: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include "ipc.hpp" 4 | 5 | namespace Platform { 6 | 7 | /** 8 | * Interface to access I2C devices. Cf. the I2C registers for details. 9 | */ 10 | namespace I2C { 11 | 12 | // All IPC commands are common between the services in this module, hence they 13 | // are not put in a nested namespace 14 | 15 | namespace IPC = Platform::IPC; 16 | 17 | // TODO: The response headers for these are not verified at all, yet 18 | 19 | using SetRegisterBits8 = IPC::IPCCommand<0x1>::add_uint32::add_uint32::add_uint32::add_uint32 20 | ::response; 21 | using EnableRegisterBits8 = IPC::IPCCommand<0x2>::add_uint32::add_uint32::add_uint32 22 | ::response; 23 | using DisableRegisterBits8 = IPC::IPCCommand<0x3>::add_uint32::add_uint32::add_uint32 24 | ::response; 25 | using WriteRegister8 = IPC::IPCCommand<0x5>::add_uint32::add_uint32::add_uint32 26 | ::response; 27 | using ReadRegister8 = IPC::IPCCommand<0x9>::add_uint32::add_uint32 28 | ::response::add_uint32; 29 | using WriteRegisterBuffer8_0xb = IPC::IPCCommand<0xb>::add_uint32::add_uint32::add_uint32::add_static_buffer 30 | ::response; 31 | using ReadRegisterBuffer8 = IPC::IPCCommand<0xd>::add_uint32::add_uint32::add_uint32 32 | ::response::add_static_buffer; 33 | using WriteRegisterBuffer8_0xe = IPC::IPCCommand<0xe>::add_uint32::add_uint32::add_uint32::add_static_buffer 34 | ::response; 35 | using Unknown_0xf = IPC::IPCCommand<0xf>::add_uint32::add_uint32::add_uint32 36 | ::response; 37 | using WriteRegisterBuffer_0x11 = IPC::IPCCommand<0x11>::add_uint32::add_uint32::add_uint32::add_buffer_mapping_read 38 | ::response;// TODO: unmap buffer 39 | using ReadRegisterBuffer_0x12 = IPC::IPCCommand<0x12>::add_uint32::add_uint32::add_uint32::add_buffer_mapping_write 40 | ::response;// TODO: unmap buffer 41 | 42 | } // namespace I2C 43 | 44 | } // namespace Platform 45 | -------------------------------------------------------------------------------- /source/platform/dsp.hpp: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include "../ipc.hpp" 4 | 5 | namespace Platform { 6 | 7 | namespace CTR { 8 | 9 | namespace DSP { 10 | 11 | /** 12 | * Inputs: 13 | * - Semaphore value (TODOTEST: upper 16 bit ignored?) 14 | */ 15 | using SetSemaphore = HLE::IPC::IPCCommand<0x7>::add_uint32 16 | ::response; 17 | 18 | using WriteProcessPipe = HLE::IPC::IPCCommand<0xd>::add_uint32::add_uint32::add_static_buffer 19 | ::response; 20 | 21 | using ReadPipeIfPossible = HLE::IPC::IPCCommand<0x10>::add_uint32::add_uint32::add_uint32 22 | ::response::add_uint32::add_static_buffer; 23 | 24 | using LoadComponent = HLE::IPC::IPCCommand<0x11>::add_uint32::add_uint32::add_uint32::add_buffer_mapping_read 25 | ::response::add_uint32::add_buffer_mapping_read; 26 | 27 | /** 28 | * Inputs: 29 | * - Virtual base address 30 | * - Number of bytes 31 | * - Process owning the flushed memory 32 | */ 33 | using FlushDataCache = HLE::IPC::IPCCommand<0x13>::add_uint32::add_uint32::add_handle 34 | ::response; 35 | 36 | /** 37 | * Inputs: 38 | * - Virtual base address 39 | * - Number of bytes 40 | * - Process owning the invalidated memory 41 | */ 42 | using InvalidateDataCache = HLE::IPC::IPCCommand<0x14>::add_uint32::add_uint32::add_handle 43 | ::response; 44 | 45 | using RegisterInterruptEvents = HLE::IPC::IPCCommand<0x15>::add_uint32::add_uint32::add_handle 46 | ::response; 47 | 48 | /** 49 | * Outputs: 50 | * - Event for the application to signal whenever it has written data for an audio frame 51 | */ 52 | using GetSemaphoreEventHandle = HLE::IPC::IPCCommand<0x16> 53 | ::response::add_handle; 54 | 55 | using SetSemaphoreMask = HLE::IPC::IPCCommand<0x17>::add_uint32 56 | ::response; 57 | 58 | } // namespace DSP 59 | 60 | } // namespace CTR 61 | 62 | } // namespace Platform 63 | -------------------------------------------------------------------------------- /source/platform/file_formats/bcfnt.hpp: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include 4 | 5 | #include 6 | 7 | #include 8 | #include 9 | 10 | namespace FileFormat::BCFNT { 11 | 12 | struct Header { 13 | BOOST_HANA_DEFINE_STRUCT(Header, 14 | (std::array, magic), // "CFNT" (on filesystem) or "CFNU" (decompressed in NS shared memory) 15 | (std::array, unknown), 16 | (uint16_t, header_size_bytes), 17 | (uint32_t, version), 18 | (uint32_t, file_size_bytes), 19 | (uint32_t, num_blocks) 20 | ); 21 | }; 22 | 23 | // Common structure shared between block headers defined below 24 | struct BlockCommon { 25 | BOOST_HANA_DEFINE_STRUCT(BlockCommon, 26 | (std::array, magic), 27 | (uint32_t, num_bytes) // Number of bytes from block start to the next block; may include data following the headers below 28 | ); 29 | }; 30 | 31 | struct FINF { 32 | static constexpr std::array magic = { 'F', 'I', 'N', 'F' }; 33 | 34 | BOOST_HANA_DEFINE_STRUCT(FINF, 35 | (std::array, unknown1), 36 | (uint32_t, tglp_offset), 37 | (uint32_t, cwdh_offset), 38 | (uint32_t, cmap_offset), 39 | (std::array, unknown2) 40 | ); 41 | }; 42 | static_assert(sizeof(FINF) == 24); 43 | 44 | struct CMAP { 45 | static constexpr std::array magic = { 'C', 'M', 'A', 'P' }; 46 | 47 | BOOST_HANA_DEFINE_STRUCT(CMAP, 48 | (std::array, unknown), 49 | (uint32_t, next_block_offset) 50 | ); 51 | }; 52 | 53 | struct CWDH { 54 | static constexpr std::array magic = { 'C', 'W', 'D', 'H' }; 55 | 56 | BOOST_HANA_DEFINE_STRUCT(CWDH, 57 | (std::array, unknown), 58 | (uint32_t, next_block_offset) 59 | ); 60 | }; 61 | 62 | struct TGLP { 63 | static constexpr std::array magic = { 'T', 'G', 'L', 'P' }; 64 | 65 | BOOST_HANA_DEFINE_STRUCT(TGLP, 66 | (std::array, unknown), 67 | (uint32_t, data_offset) 68 | ); 69 | }; 70 | 71 | } // namespace FileFormat::BCFNT 72 | -------------------------------------------------------------------------------- /source/processes/dsp.hpp: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include "fake_process.hpp" 4 | 5 | namespace HLE { 6 | 7 | namespace OS { 8 | 9 | class FakeDSP final { 10 | OS& os; 11 | spdlog::logger& logger; 12 | 13 | /// Signalled when the DSP interrupt fires (which will be reported either through channel_event or semaphore_event) 14 | Handle interrupt_event_new = HANDLE_INVALID; 15 | 16 | /// Signalled when the DSP is done processing the current command 17 | /// TODO: There should be several slots of these 18 | Handle channel_event = HANDLE_INVALID; 19 | 20 | /// Signalled by a client when they have written to DSP shared memory 21 | Handle semaphore_event; 22 | 23 | uint32_t pipe_address; 24 | 25 | PAddr pipe_info_base = 0; // Address of SubPipeInfo headers 26 | 27 | VAddr static_buffer_addr = 0; 28 | 29 | uint16_t semaphore_mask = 0; 30 | 31 | // TODO: Should check via DSP MMIO instead 32 | bool semaphore_signaled = false; 33 | bool data_signaled = false; 34 | 35 | enum class State { 36 | Running, 37 | Stopped, 38 | Sleeping 39 | } state = State::Stopped; 40 | 41 | public: 42 | FakeDSP(FakeThread& thread); 43 | 44 | void OnIPCRequest(FakeThread& thread, Handle sender, const IPC::CommandHeader& header); 45 | 46 | std::tuple HandleWriteProcessPipe(FakeThread&, uint32_t pipe_index, uint32_t num_bytes, IPC::StaticBuffer); 47 | std::tuple HandleReadPipeIfPossible(FakeThread&, uint32_t pipe_index, uint32_t direction, uint32_t num_bytes); 48 | std::tuple HandleLoadComponent(FakeThread& thread, uint32_t size, uint32_t program_mask, uint32_t data_mask, const IPC::MappedBuffer component_buffer); 49 | 50 | std::tuple HandleFlushDataCache(FakeThread& thread, uint32_t start, uint32_t num_bytes, Handle process); 51 | std::tuple HandleInvalidateDataCache(FakeThread& thread, uint32_t start, uint32_t num_bytes, Handle process); 52 | 53 | std::tuple HandleGetSemaphoreEventHandle(FakeThread& thread); 54 | }; 55 | 56 | } // namespace OS 57 | 58 | } // namespace HLE 59 | -------------------------------------------------------------------------------- /source/video_core/externals/nihstro/CMakeLists.txt: -------------------------------------------------------------------------------- 1 | cmake_minimum_required(VERSION 2.6) 2 | 3 | project(nihstro) 4 | 5 | find_package(Boost COMPONENTS program_options unit_test_framework) 6 | 7 | add_definitions(-std=c++11) 8 | 9 | include_directories(include) 10 | 11 | add_executable(nihstro-disassemble src/disassembler.cpp src/parser_shbin.cpp) 12 | install(TARGETS nihstro-disassemble DESTINATION bin) 13 | 14 | # TODO: Re-enable 15 | # add_subdirectory(examples/inline_assembler/simple) 16 | 17 | # TODO: What if program_options was found but not unit_test_framework? 18 | if(Boost_FOUND) 19 | set(PARSER_SRCS src/parser_assembly.cpp 20 | src/preprocessor.cpp 21 | src/parser_assembly/common.cpp 22 | src/parser_assembly/compare.cpp 23 | src/parser_assembly/declaration.cpp 24 | src/parser_assembly/flowcontrol.cpp 25 | src/parser_assembly/floatop.cpp) 26 | 27 | include_directories(${Boost_INCLUDE_DIRS}) 28 | add_executable(nihstro-assemble src/assembler.cpp ${PARSER_SRCS}) 29 | target_link_libraries(nihstro-assemble ${Boost_PROGRAM_OPTIONS_LIBRARY}) 30 | install(TARGETS nihstro-assemble DESTINATION bin) 31 | 32 | # tests 33 | if(Boost_UNIT_TEST_FRAMEWORK_FOUND) 34 | add_executable(test-parser src/tests/parser.cpp ${PARSER_SRCS}) 35 | target_compile_definitions(test-parser PUBLIC -DBOOST_TEST_DYN_LINK -DBOOST_TEST_MAIN) 36 | target_link_libraries(test-parser ${Boost_UNIT_TEST_FRAMEWORK_LIBRARY}) 37 | enable_testing() 38 | add_test(ParserTests test-parser) 39 | 40 | add_executable(test-source-tree-iterator src/tests/source_tree_iterator.cpp) 41 | target_compile_definitions(test-source-tree-iterator PUBLIC -DBOOST_TEST_DYN_LINK -DBOOST_TEST_MAIN) 42 | target_link_libraries(test-source-tree-iterator ${Boost_UNIT_TEST_FRAMEWORK_LIBRARY}) 43 | add_test(ParserTests test-source-tree-iterator) 44 | else() 45 | message(WARNING "Boost testing framework not found => not building assembler tests") 46 | endif() 47 | else() 48 | message(WARNING "Boost not found => not building assembler") 49 | endif() 50 | -------------------------------------------------------------------------------- /source/video_core/src/video_core/primitive_assembly.cpp: -------------------------------------------------------------------------------- 1 | #include "primitive_assembly.h" 2 | #include "shader.hpp" 3 | 4 | #include 5 | 6 | #include 7 | 8 | namespace Pica { 9 | 10 | template 11 | PrimitiveAssembler::PrimitiveAssembler() { 12 | Configure(Regs::TriangleTopology::List); 13 | } 14 | 15 | template 16 | void PrimitiveAssembler::Configure(Regs::TriangleTopology topology) { 17 | this->topology = topology; 18 | // TODO: Should we indeed reset all state here? Actual hardware probably has an explicit way of doing so! 19 | buffer_index = 0; 20 | strip_ready = false; 21 | } 22 | 23 | template 24 | void PrimitiveAssembler::SubmitVertex(Context& context, VertexType& vtx, TriangleHandler triangle_handler) 25 | { 26 | switch (topology) { 27 | case Regs::TriangleTopology::List: 28 | case Regs::TriangleTopology::ListIndexed: 29 | if (buffer_index < 2) { 30 | buffer[buffer_index++] = vtx; 31 | } else { 32 | buffer_index = 0; 33 | 34 | triangle_handler(context, buffer[0], buffer[1], vtx); 35 | } 36 | break; 37 | 38 | case Regs::TriangleTopology::Strip: 39 | case Regs::TriangleTopology::Fan: 40 | if (strip_ready) 41 | triangle_handler(context, buffer[0], buffer[1], vtx); 42 | 43 | buffer[buffer_index] = vtx; 44 | 45 | if (topology == Regs::TriangleTopology::Strip) { 46 | strip_ready |= (buffer_index == 1); 47 | buffer_index = !buffer_index; 48 | } else if (topology == Regs::TriangleTopology::Fan) { 49 | buffer_index = 1; 50 | strip_ready = true; 51 | } 52 | break; 53 | 54 | default: 55 | LOG_ERROR(HW_GPU, "Unknown triangle topology %x:", (int)topology); 56 | break; 57 | } 58 | } 59 | 60 | // explicitly instantiate use cases 61 | template 62 | struct PrimitiveAssembler; 63 | template 64 | struct PrimitiveAssembler; 65 | 66 | } // namespace 67 | -------------------------------------------------------------------------------- /source/video_core/CMakeLists.txt: -------------------------------------------------------------------------------- 1 | cmake_minimum_required(VERSION 3.13) 2 | 3 | #if(NOT DEFINED PROJECT_NAME) 4 | # set(NOT_SUBPROJET ON) 5 | #endif() 6 | 7 | project(video_core LANGUAGES CXX) 8 | 9 | add_subdirectory(utils/vulkan_utils) 10 | 11 | add_library(video_core SHARED) 12 | 13 | target_sources(video_core PRIVATE 14 | src/core/hw/gpu.cpp 15 | src/video_core/clipper.cpp 16 | src/video_core/command_processor.cpp 17 | src/video_core/primitive_assembly.cpp 18 | src/video_core/rasterizer.cpp 19 | src/video_core/shader.cpp 20 | src/video_core/shader_analysis.cpp 21 | src/video_core/shader_disassembler.cpp 22 | #src/video_core/shader_glsl.cpp 23 | src/video_core/shader_interpreter.cpp 24 | src/video_core/shader_microcode.cpp 25 | src/video_core/debug_utils/debug_utils.cpp 26 | src/video_core/vulkan/pipeline_cache.cpp 27 | src/video_core/vulkan/renderer.cpp 28 | src/video_core/vulkan/resource_manager.cpp 29 | src/video_core/vulkan/shader_gen.cpp) 30 | if (PISTACHE_FOUND) 31 | target_sources(video_core PRIVATE src/debug/gpu.cpp) 32 | endif() 33 | 34 | 35 | target_include_directories(video_core PRIVATE src/support src/ externals/nihstro/include) 36 | target_include_directories(video_core PRIVATE ../) 37 | # target_include_directories(video_core PRIVATE externals/vulkan-hpp/vulkan) TODO: Need to also include Vulkan headers with matching version... For now, I'm just using the headers shipping with my Linux distribution 38 | target_link_libraries(video_core PRIVATE vulkan-util) 39 | target_link_libraries(video_core PRIVATE Boost::boost) 40 | target_link_libraries(video_core PRIVATE range-v3::range-v3) 41 | 42 | target_link_libraries(video_core PRIVATE spdlog::spdlog) 43 | 44 | if (Pistache_FOUND) 45 | target_sources(video_core PRIVATE 46 | src/debug/gpu.cpp) 47 | target_link_libraries(video_core PRIVATE Pistache::Pistache) 48 | endif() 49 | target_link_libraries(video_core PRIVATE Tracy::TracyClient) 50 | target_link_libraries(video_core PRIVATE xxHash::xxhash) 51 | 52 | if (CMAKE_SYSTEM_NAME STREQUAL "Darwin") 53 | # Required for g_vulkan_queue_mutex 54 | target_link_options(video_core PRIVATE "-undefined;dynamic_lookup") 55 | endif() 56 | 57 | # Selectively disable Werror to cover false positives 58 | target_compile_options(video_core PRIVATE -Wno-error=return-type) 59 | -------------------------------------------------------------------------------- /source/processes/fs_hpv.hpp: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include "os_hypervisor_private.hpp" 4 | 5 | #include 6 | 7 | #include 8 | 9 | #include 10 | #include 11 | #include 12 | #include 13 | 14 | namespace HLE { 15 | 16 | namespace OS { 17 | 18 | namespace HPV { 19 | 20 | // TODO: This class could be shared with the actual emulation core 21 | struct Path { 22 | static constexpr uint32_t max_length = 256; // Unverified. TODO: What's the actual maximum? 23 | 24 | using Utf8PathType = std::string; 25 | using BinaryPathType = boost::container::static_vector; 26 | std::variant data; 27 | 28 | Path(Thread&, uint32_t type, uint32_t addr, uint32_t num_bytes); 29 | Path(Thread&, uint32_t type, uint32_t num_bytes, std::function read_next_byte); 30 | 31 | std::string ToString() const; 32 | }; 33 | 34 | struct Archive { 35 | virtual ~Archive() { 36 | } 37 | 38 | virtual std::string Describe() const = 0; 39 | }; 40 | 41 | struct ProcessInfo { 42 | Platform::FS::ProgramInfo program; 43 | Platform::FS::StorageInfo storage; 44 | }; 45 | 46 | struct FSContext : SessionContext { 47 | // Maps each session handle to the process ids registered on that handle 48 | std::unordered_map process_ids; 49 | 50 | // Maps process ids to the ProcessInfo registered to that process via fs:LDR 51 | std::unordered_map process_infos; 52 | 53 | /** 54 | * Maps each archive handle as used in the IPC interface to their 55 | * corresponding Archive object. Note that Archive ownership is shared 56 | * between this and various File instances, since e.g. via 57 | * OpenFileDirectly files may be opened without assigning a public 58 | * archive handle. 59 | */ 60 | std::unordered_map> archives; 61 | }; 62 | 63 | HPV::RefCounted CreateFSUserService(RefCounted port, FSContext& context); 64 | HPV::RefCounted CreateFSLdrService(RefCounted port, FSContext& context); 65 | HPV::RefCounted CreateFSRegService(RefCounted port, FSContext& context); 66 | 67 | } // namespace HPV 68 | 69 | } // namespace HOS 70 | 71 | } // namespace HLE 72 | -------------------------------------------------------------------------------- /source/video_core/utils/vulkan_utils/memory_types.hpp: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include 4 | 5 | namespace spdlog { 6 | class logger; 7 | } 8 | 9 | namespace Pica::Vulkan { 10 | 11 | class MemoryTypeDatabase { 12 | vk::PhysicalDevice physical_device; 13 | 14 | public: 15 | static constexpr std::array known_flags_combinations = {{ 16 | vk::MemoryPropertyFlagBits::eDeviceLocal, 17 | vk::MemoryPropertyFlagBits::eHostVisible | vk::MemoryPropertyFlagBits::eHostCoherent 18 | }}; 19 | 20 | static constexpr bool FlagsAreValid(vk::MemoryPropertyFlags flags) noexcept { 21 | for (auto supported_flags : known_flags_combinations) { 22 | if (flags == supported_flags) { 23 | return true; 24 | } 25 | } 26 | 27 | return false; 28 | } 29 | 30 | public: 31 | MemoryTypeDatabase(spdlog::logger& logger, vk::PhysicalDevice physical_device); 32 | 33 | template 34 | vk::MemoryAllocateInfo GetMemoryAllocateInfo(const vk::MemoryRequirements& requirements) const { 35 | return GetMemoryAllocateInfo(requirements.size, requirements); 36 | } 37 | 38 | template 39 | vk::MemoryAllocateInfo GetMemoryAllocateInfo(vk::DeviceSize size, const vk::MemoryRequirements& requirements) const { 40 | constexpr auto desired_flags = (FlagBits | ...); 41 | 42 | static_assert(FlagsAreValid(desired_flags), "Given combination of flags is not supported"); 43 | 44 | auto properties = physical_device.getMemoryProperties(); 45 | for (uint32_t type_index = 0; type_index < properties.memoryTypeCount; ++type_index) { 46 | if (0 == (requirements.memoryTypeBits & (1 << type_index))) { 47 | continue; 48 | } 49 | 50 | auto& type = properties.memoryTypes[type_index]; 51 | auto& heap = properties.memoryHeaps[type.heapIndex]; 52 | 53 | if ((desired_flags & type.propertyFlags) == desired_flags) { 54 | return { size, type_index }; 55 | } 56 | } 57 | 58 | throw std::runtime_error("Could not find find a memory type with flags " + vk::to_string(desired_flags) + " and memory type bits " + std::to_string(requirements.memoryTypeBits)); 59 | } 60 | }; 61 | 62 | } // namespace Pica::Vulkan 63 | -------------------------------------------------------------------------------- /conanfile.py: -------------------------------------------------------------------------------- 1 | from conan import ConanFile 2 | 3 | class MikageConan(ConanFile): 4 | name = "mikage" 5 | settings = "os", "compiler", "build_type", "arch" 6 | generators = ["CMakeDeps", "CMakeToolchain"] 7 | 8 | requires = [ 9 | #"boost/1.79.0", 10 | "boost/1.84.0", 11 | "spdlog/1.15.3", 12 | "cryptopp/8.9.0", 13 | "sdl/2.30.7", # 2.0.18 fixed swapped X/Y buttons on Switch Pro Controller 14 | "range-v3/0.12.0", 15 | "catch2/2.13.7", 16 | "tracy/0.11.1", 17 | "xxhash/0.8.0", 18 | "fmt/11.2.0", 19 | ] 20 | 21 | options = { 22 | "enable_profiler": [True, False] 23 | } 24 | 25 | default_options = { 26 | "enable_profiler": False 27 | } 28 | 29 | def configure(self): 30 | # TODO: Works around conan-center-index issue 7118 31 | self.options["sdl"].nas = False 32 | self.options["sdl"].alsa = False 33 | self.options["sdl"].shared = True 34 | self.options["pulseaudio"].shared = True 35 | self.options["pulseaudio"].with_alsa = False 36 | #self.options["sdl"].nas = True 37 | 38 | self.options["tracy"].enable = self.options.enable_profiler 39 | self.options["tracy"].fibers = True 40 | 41 | if self.settings.os == "Android": 42 | # With zlib, libxml2 pulls in pkgconf, which Conan can't build for Android 43 | self.options["libxml2"].zlib = False 44 | 45 | def requirements(self): 46 | # Conan does not support building Pistache on Windows and Android 47 | if self.settings.os != "Windows" and self.settings.os != "Android": 48 | self.requires("pistache/0.4.25") 49 | 50 | if self.settings.os == "Linux": 51 | self.requires("libunwind/1.8.0") 52 | 53 | if self.settings.os == "Macos": 54 | self.requires("glslang/1.3.268.0") 55 | self.requires("spirv-tools/1.3.268.0") 56 | self.requires("vulkan-loader/1.3.268.0", force=True) 57 | self.requires("spirv-cross/1.3.268.0", force=True) # for moltenvk 58 | self.requires("moltenvk/1.2.2", force=True) 59 | self.requires("vulkan-headers/1.3.268.0", force=True) # must be a direct dependency to avoid homebrew headers from being pulled in 60 | else: 61 | # More recent version required for GCC 15 build fixes 62 | self.requires("glslang/1.4.313.0") 63 | self.requires("spirv-tools/1.4.313.0") 64 | -------------------------------------------------------------------------------- /source/debug_server.cpp: -------------------------------------------------------------------------------- 1 | #include "debug_server.hpp" 2 | 3 | #include 4 | #include 5 | 6 | #include 7 | #include 8 | #include 9 | 10 | #include 11 | 12 | using namespace Pistache; 13 | 14 | namespace Debugger { 15 | 16 | // Forward declare external services 17 | template<> std::unique_ptr CreateService(); 18 | template<> std::unique_ptr CreateService(); 19 | template<> std::unique_ptr CreateService(); 20 | 21 | class DebugServerImpl { 22 | std::vector> services; 23 | 24 | public: 25 | DebugServerImpl() { 26 | services.push_back(CreateService()); 27 | services.push_back(CreateService()); 28 | services.push_back(CreateService()); 29 | } 30 | 31 | void Run(unsigned port) { 32 | Address addr(Ipv4::any(), static_cast(port)); 33 | Http::Endpoint endpoint(addr); 34 | endpoint.init(Http::Endpoint::options().flags(Tcp::Options::NoDelay | Tcp::Options::ReuseAddr)); 35 | Rest::Router router; 36 | 37 | // Register services 38 | for (auto& service : services) { 39 | service->RegisterRoutes(router); 40 | } 41 | 42 | // Run server 43 | endpoint.setHandler(router.handler()); 44 | endpoint.serve(); 45 | } 46 | 47 | Service& GetService(const std::type_info& service_type) const { 48 | auto it = ranges::find_if(services, 49 | [&](const auto& service) -> bool { 50 | auto& the_service = *service; 51 | return (service_type == typeid(the_service)); 52 | }); 53 | if (it == services.end()) { 54 | throw std::runtime_error("Could not find service" + std::string { service_type.name() }); 55 | } 56 | return **it; 57 | } 58 | }; 59 | 60 | DebugServer::DebugServer() { 61 | impl = std::make_unique(); 62 | } 63 | 64 | DebugServer::~DebugServer() = default; 65 | 66 | void DebugServer::Run(unsigned port) { 67 | impl->Run(port); 68 | } 69 | 70 | Service& DebugServer::GetService(const std::type_info& service_type) const { 71 | return impl->GetService(service_type); 72 | } 73 | 74 | } // namespace Debugger 75 | -------------------------------------------------------------------------------- /source/processes/http.cpp: -------------------------------------------------------------------------------- 1 | #include "http.hpp" 2 | #include "fake_process.hpp" 3 | 4 | namespace HLE { 5 | 6 | namespace OS { 7 | 8 | static auto CommandHandler(FakeHTTP& context, FakeThread& thread, Platform::IPC::CommandHeader header) try { 9 | switch (header.command_id) { 10 | // Initialize 11 | case 0x1: 12 | thread.WriteTLS(0x80, IPC::CommandHeader::Make(0, 1, 0).raw); 13 | thread.WriteTLS(0x84, RESULT_OK); 14 | break; 15 | 16 | using CreateRootCertChain = IPC::IPCCommand<0x2d>::response::add_uint32; 17 | case CreateRootCertChain::id: 18 | thread.WriteTLS(0x80, IPC::CommandHeader::Make(0, 2, 0).raw); 19 | thread.WriteTLS(0x84, RESULT_OK); 20 | thread.WriteTLS(0x88, 0xafafafa0); 21 | break; 22 | 23 | using RootCertChainAddDefaultCert = IPC::IPCCommand<0x30>::add_uint32::add_uint32::response::add_uint32; 24 | case RootCertChainAddDefaultCert::id: 25 | { 26 | thread.WriteTLS(0x80, IPC::CommandHeader::Make(0, 2, 0).raw); 27 | thread.WriteTLS(0x84, RESULT_OK); 28 | thread.WriteTLS(0x88, 0xafafafa1); 29 | break; 30 | } 31 | 32 | using OpenDefaultClientCertContext = IPC::IPCCommand<0x33>::add_uint32::response::add_uint32; 33 | case OpenDefaultClientCertContext::id: 34 | { 35 | thread.WriteTLS(0x80, IPC::CommandHeader::Make(0, 2, 0).raw); 36 | thread.WriteTLS(0x84, RESULT_OK); 37 | thread.WriteTLS(0x88, 0xafafafa2); 38 | break; 39 | } 40 | 41 | default: 42 | throw IPC::IPCError{header.raw, 0xdeadbef0}; 43 | } 44 | 45 | return ServiceHelper::SendReply; 46 | } catch (const IPC::IPCError& err) { 47 | throw std::runtime_error(fmt::format("Unknown HTTP service command with header {:#010x}", err.header)); 48 | } 49 | 50 | static void IPCThread(FakeHTTP& context, FakeThread& thread) { 51 | // TODO: How many parallel sessions should we support? 52 | ServiceHelper service; 53 | service.Append(ServiceUtil::SetupService(thread, "http:C", 4)); 54 | 55 | auto InvokeCommandHandler = [&context](FakeThread& thread, uint32_t index) { 56 | Platform::IPC::CommandHeader header = { thread.ReadTLS(0x80) }; 57 | return CommandHandler(context, thread, header); 58 | }; 59 | 60 | service.Run(thread, std::move(InvokeCommandHandler)); 61 | } 62 | 63 | FakeHTTP::FakeHTTP(FakeThread& thread) { 64 | thread.name = "HTTPThread"; 65 | 66 | IPCThread(*this, thread); 67 | } 68 | 69 | } // namespace OS 70 | 71 | } // namespace HLE 72 | -------------------------------------------------------------------------------- /source/processes/errdisp.cpp: -------------------------------------------------------------------------------- 1 | #include "errdisp.hpp" 2 | #include "os.hpp" 3 | 4 | #include 5 | 6 | namespace HLE { 7 | 8 | namespace OS { 9 | 10 | struct FakeErrorDisp { 11 | void OnIPCRequest(FakeThread&, const IPC::CommandHeader&); 12 | 13 | public: 14 | FakeErrorDisp(FakeThread& thread); 15 | 16 | virtual ~FakeErrorDisp() = default; 17 | }; 18 | 19 | FakeErrorDisp::FakeErrorDisp(FakeThread& thread) { 20 | ServiceHelper service; 21 | auto [result, server_port, client_port] = thread.CallSVC(&OS::SVCCreatePort, "err:f", OS::MAX_SESSIONS); 22 | if (result != RESULT_OK) { 23 | throw Mikage::Exceptions::Invalid("Failed to create err:f port"); 24 | } 25 | 26 | service.Append(server_port); 27 | thread.name = "ErrDispThread"; 28 | auto InvokeCommandHandler = [&](FakeThread& thread, uint32_t /*index*/) { 29 | Platform::IPC::CommandHeader header = { thread.ReadTLS(0x80) }; 30 | OnIPCRequest(thread, header); 31 | return ServiceHelper::SendReply; 32 | }; 33 | service.Run(thread, std::move(InvokeCommandHandler)); 34 | } 35 | 36 | void FakeErrorDisp::OnIPCRequest(FakeThread& thread, const IPC::CommandHeader& header) { 37 | switch (header.command_id) { 38 | case 1: // Throw 39 | { 40 | thread.GetLogger()->info("{} received Throw", ThreadPrinter{thread}); 41 | 42 | // Validate command 43 | if (header.raw != 0x00010800) 44 | thread.GetOS().SVCBreak(thread, OS::BreakReason::Panic); 45 | 46 | auto type = thread.ReadTLS(0x84) & 0xff; 47 | auto error_code = thread.ReadTLS(0x88); 48 | throw std::runtime_error(fmt::format( "ErrDisp exception: type={:#x}, error={:#010x}, pc={:#010x}, process_id={:#x}, title_id={:#010x}{:08x}, app_title_id={:#010x}{:08x}", 49 | type, error_code, thread.ReadTLS(0x8c), thread.ReadTLS(0x90), 50 | thread.ReadTLS(0x94), thread.ReadTLS(0x98), thread.ReadTLS(0x9c), thread.ReadTLS(0xa0))); 51 | } 52 | 53 | default: 54 | throw Mikage::Exceptions::NotImplemented("Unknown err:f IPC request {:#x}", header.raw); 55 | } 56 | } 57 | 58 | template<> std::shared_ptr CreateFakeProcessViaContext(OS& os, Interpreter::Setup& setup, uint32_t pid, const std::string& name) { 59 | return WrappedFakeProcess::CreateWithContext(os, setup, pid, name); 60 | } 61 | 62 | } // namespace OS 63 | 64 | } // namespace HLE 65 | -------------------------------------------------------------------------------- /source/platform/file_formats/dsp1.hpp: -------------------------------------------------------------------------------- 1 | /** 2 | * DSP firmware; uses file extension "cdc" 3 | */ 4 | 5 | #pragma once 6 | 7 | #include 8 | #include 9 | 10 | #include 11 | 12 | #include 13 | 14 | namespace FileFormat { 15 | 16 | struct DSPFirmwareHeader { 17 | struct InitFlags { 18 | BOOST_HANA_DEFINE_STRUCT(InitFlags, 19 | (uint8_t, storage) 20 | ); 21 | 22 | // If set, wait for DSP replies with data == 1 to be sent on each channel 23 | auto wait_for_dsp_reply() const { return BitField::v3::MakeFlagOn<0>(this); } 24 | 25 | // If set, read 3d filter data from config module 26 | auto load_3d_filters() const { return BitField::v3::MakeFlagOn<1>(this); } 27 | }; 28 | 29 | struct SegmentInfo { 30 | BOOST_HANA_DEFINE_STRUCT(SegmentInfo, 31 | (uint32_t, data_offset), // offset in bytes of segment contents from the start of this header 32 | (uint32_t, target_offset), // target offset, 16-bit words 33 | (uint32_t, size_bytes), 34 | (std::array, unknown), 35 | (uint8_t, memory_type), // memory type; 0/1: program memory, 2: data memory 36 | (std::array, hash) // SHA256 hash over segment contents 37 | ); 38 | }; 39 | 40 | BOOST_HANA_DEFINE_STRUCT(DSPFirmwareHeader, 41 | (std::array, signature), // RSA signature over the rest of this header 42 | (std::array, magic), // always "DSP1" 43 | (uint32_t, size_bytes), // size including header 44 | (uint16_t, memory_regions_mask), // mask enabling 32 kB regions in DSP memory 45 | (std::array, unknown), 46 | (uint8_t, memory_type_3d_filters), // memory type for 3D filter segment 47 | (uint8_t, num_segments), // number of entries to use from the segments array 48 | (InitFlags, init_flags), // flags to apply during loading 49 | (uint32_t, start_offset_3d_filters), // counted in 16-bit words. TODO: Is this from the start of DSP memory? 50 | (uint32_t, size_bytes_3d_filters), // size of 3d filters segment 51 | (uint64_t, unknown2), 52 | (std::array, segments) 53 | ); 54 | 55 | struct Tags : expected_size_tag<0x300> {}; 56 | }; 57 | 58 | } // namespace FileFormat 59 | -------------------------------------------------------------------------------- /source/processes/am_hpv.cpp: -------------------------------------------------------------------------------- 1 | #include "am_hpv.hpp" 2 | #include "os_hypervisor_private.hpp" 3 | #include "os.hpp" 4 | 5 | #include 6 | #include 7 | 8 | namespace HLE { 9 | 10 | namespace OS { 11 | 12 | namespace HPV { 13 | 14 | namespace { 15 | 16 | // Covers all am:* services 17 | struct AmService : SessionToPort { 18 | AmService(RefCounted port_, AMContext& context_) : SessionToPort(port_, context_) { 19 | } 20 | 21 | void OnRequest(Hypervisor& hypervisor, Thread& thread, Handle session) override { 22 | using namespace Platform::AM; 23 | 24 | const uint32_t command_header = thread.ReadTLS(0x80); 25 | auto dispatcher = RequestDispatcher<> { thread, *this, command_header }; 26 | 27 | dispatcher.DecodeRequest([&](auto&, uint32_t media_type) { 28 | auto description = fmt::format( "GetProgramList, media_type={}", media_type); 29 | Session::OnRequest(hypervisor, thread, session, description); 30 | }); 31 | 32 | dispatcher.DecodeRequest([&](auto&, uint32_t max_count, uint32_t media_type, IPC::MappedBuffer) { 33 | auto description = fmt::format( "GetProgramList, max_count={:#x}, media_type={}", max_count, media_type); 34 | Session::OnRequest(hypervisor, thread, session, description); 35 | }); 36 | 37 | dispatcher.DecodeRequest([&](auto&, uint32_t media_type, uint32_t max_count, IPC::MappedBuffer title_ids_buffer, IPC::MappedBuffer) { 38 | std::vector title_ids; 39 | for (unsigned idx = 0; idx < max_count; ++idx) { 40 | uint64_t title_id = thread.ReadMemory32(title_ids_buffer.addr + idx * sizeof(uint64_t)) | 41 | (uint64_t { thread.ReadMemory32(title_ids_buffer.addr + idx * sizeof(uint64_t) + 4) } << 32u); 42 | title_ids.push_back(title_id); 43 | } 44 | auto description = fmt::format( "GetProgramInfos, media_type={}, max_count={:#x}, title_ids={{{:#x}}}", 45 | media_type, max_count, fmt::join(title_ids, ", ")); 46 | Session::OnRequest(hypervisor, thread, session, description); 47 | }); 48 | } 49 | }; 50 | 51 | } // anonymous namespace 52 | 53 | HPV::RefCounted CreateAmService(RefCounted port, AMContext& context) { 54 | return HPV::RefCounted(new AmService(port, context)); 55 | } 56 | 57 | } // namespace HPV 58 | 59 | } // namespace HOS 60 | 61 | } // namespace HLE 62 | -------------------------------------------------------------------------------- /source/video_core/src/video_core/renderer.hpp: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include "display.hpp" 4 | 5 | namespace spdlog { 6 | class logger; 7 | } 8 | 9 | namespace EmuDisplay { 10 | struct EmuDisplay; 11 | struct Frame; 12 | } 13 | 14 | namespace Memory { 15 | struct PhysicalMemory; 16 | } 17 | 18 | namespace Pica { 19 | 20 | struct Context; 21 | 22 | namespace VertexShader { 23 | struct OutputVertex; 24 | struct InputVertex; 25 | struct ShaderEngine; 26 | } 27 | 28 | class Renderer { 29 | public: 30 | virtual ~Renderer() = default; 31 | 32 | /** 33 | * @param stride Number of bytes between two consecutive rows in the source image 34 | */ 35 | virtual void ProduceFrame(EmuDisplay::EmuDisplay&, EmuDisplay::Frame&, Memory::PhysicalMemory&, EmuDisplay::DataStreamId, uint32_t addr, uint32_t stride, EmuDisplay::Format) = 0; 36 | 37 | /** 38 | * @param max_triangles Upper bound for the number of triangles that are going to be rendered 39 | */ 40 | virtual void PrepareTriangleBatch(Context&, const VertexShader::ShaderEngine&, uint32_t max_triangles, bool is_indexed) = 0; 41 | 42 | /** 43 | * Adds a pre-assembled, unclipped triangle to the current batch 44 | */ 45 | virtual void AddPreassembledTriangle(Context&, VertexShader::OutputVertex &v0, VertexShader::OutputVertex &v1, VertexShader::OutputVertex &v2) = 0; 46 | 47 | /** 48 | * Adds a single vertex to the current batch for hardware primitive assembly 49 | */ 50 | virtual void AddVertex(Context&, uint32_t num_attributes, VertexShader::InputVertex&) = 0; 51 | 52 | virtual void FinalizeTriangleBatch(Context&, const VertexShader::ShaderEngine&, bool is_indexed) = 0; 53 | 54 | virtual void InvalidateRange(uint32_t /* TODO: PAddr */ start, uint32_t num_bytes) = 0; 55 | virtual void FlushRange(uint32_t /* TODO: PAddr */ start, uint32_t num_bytes) = 0; 56 | 57 | /** 58 | * Performs an image-to-image copy with automatic color conversion and 59 | * linear filtering enabled. FlushRange must be used to make results 60 | * visible in emulated memory. 61 | * 62 | * @param input_format GPU::Regs::FramebufferFormat 63 | * @return true if the Renderer performed a blit, false if a software fallback needs to be executed by the caller 64 | */ 65 | virtual bool BlitImage(Context&, uint32_t /* TODO: PAddr */ input_addr, uint32_t input_width, uint32_t input_height, uint32_t input_stride, 66 | uint32_t input_format, uint32_t output_addr, uint32_t output_width, uint32_t output_height, 67 | uint32_t output_stride, uint32_t output_format, bool flip_y) = 0; 68 | }; 69 | 70 | } // namespace Pica 71 | -------------------------------------------------------------------------------- /source/os_hypervisor.hpp: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include 4 | #include 5 | 6 | struct AudioFrontend; 7 | 8 | namespace Settings { 9 | struct Settings; 10 | } 11 | 12 | namespace HLE { 13 | 14 | namespace OS { 15 | 16 | class Thread; 17 | class Process; 18 | class CodeSet; 19 | struct Handle; 20 | 21 | using ProcessId = uint32_t; 22 | 23 | class Hypervisor; 24 | 25 | namespace HPV { 26 | 27 | class Session; 28 | 29 | // TODO: Small-function optimization! 30 | using IPCCallback = std::function; 31 | 32 | struct State; 33 | 34 | } // namespace HPV 35 | 36 | class Hypervisor { 37 | std::unique_ptr state; 38 | 39 | public: 40 | Hypervisor(Settings::Settings&, AudioFrontend&); 41 | ~Hypervisor(); 42 | 43 | // "thread" refers to the target (i.e. server) thread 44 | void OnIPCRequestFromTo(Thread& from_thread, Thread &to_thread, Handle session_handle); 45 | 46 | // "thread" refers to the source (i.e. server) thread 47 | void OnIPCReplyFromTo(Thread& from_thread, Thread &to_thread, Handle session_handle); 48 | 49 | void OnPortCreated(ProcessId process, std::string_view port_name, Handle port_handle); 50 | 51 | // Registers the session handle as associated to the given named port. 52 | // @note Sessions are associated to *unnamed* ports in ServiceManager::GetServiceHandle via OnSessionNamed 53 | void OnConnectToPort(ProcessId process, std::string_view port_name, Handle session_handle); 54 | 55 | // Registers the session handle as associated to the given port 56 | void OnNewSession(ProcessId process, Handle port_handle, Handle session_handle); 57 | 58 | // Registers a session handle that is not associated with any port 59 | void OnSessionCreated(ProcessId process, Handle session_handle); 60 | 61 | // Will name a port indicated by the handle. Naming will propagate to all other handles to the same session 62 | void SetPortName(ProcessId process, Handle port_handle, std::string_view port_name); 63 | 64 | // Takes ownership of the given Session 65 | void SetSessionObject(ProcessId process, Handle session_handle, HPV::Session*); 66 | 67 | void OnHandleDuplicated(ProcessId source_process, Handle source_handle, ProcessId dest_process, Handle dest_handle); 68 | 69 | void OnHandleClosed(ProcessId, Handle); 70 | 71 | // TODO: Must be wired up internally for shadow services to subscribe to events 72 | void OnEventSignaled(Thread&, ProcessId, Handle event_handle); 73 | 74 | /** 75 | * Associates the object referenced by the given handle in the given thread 76 | * with a human-readable name 77 | */ 78 | void SetObjectTag(Thread&, Handle, std::string name); 79 | }; 80 | 81 | } // namespace OS 82 | 83 | } // namespace HLE 84 | -------------------------------------------------------------------------------- /source/video_core/utils/vulkan_utils/debug_markers.hpp: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include 4 | 5 | #include 6 | 7 | #if VULKAN_HPP_DISPATCH_LOADER_DYNAMIC != 1 8 | #error "May not use this file without dynamic dispatcher" 9 | #endif 10 | 11 | namespace Pica::Vulkan { 12 | 13 | namespace detail { 14 | 15 | template 16 | struct is_unique_handle : std::false_type { }; 17 | template 18 | struct is_unique_handle> : std::true_type { }; 19 | 20 | inline constexpr auto DebugObjectType(vk::Semaphore) { 21 | return vk::DebugReportObjectTypeEXT::eSemaphore; 22 | } 23 | 24 | inline constexpr auto DebugObjectType(vk::CommandBuffer) { 25 | return vk::DebugReportObjectTypeEXT::eCommandBuffer; 26 | } 27 | 28 | inline constexpr auto DebugObjectType(vk::Fence) { 29 | return vk::DebugReportObjectTypeEXT::eFence; 30 | } 31 | 32 | inline constexpr auto DebugObjectType(vk::DeviceMemory) { 33 | return vk::DebugReportObjectTypeEXT::eDeviceMemory; 34 | } 35 | 36 | inline constexpr auto DebugObjectType(vk::Buffer) { 37 | return vk::DebugReportObjectTypeEXT::eBuffer; 38 | } 39 | 40 | inline constexpr auto DebugObjectType(vk::Image) { 41 | return vk::DebugReportObjectTypeEXT::eImage; 42 | } 43 | 44 | inline constexpr auto DebugObjectType(vk::ImageView) { 45 | return vk::DebugReportObjectTypeEXT::eImageView; 46 | } 47 | 48 | } // namespace detail 49 | 50 | template 51 | inline void SetDebugName(vk::Device device, const T& object, const char* name) { 52 | if constexpr (detail::is_unique_handle>::value) { 53 | SetDebugName(device, *object, name); 54 | } else { 55 | if (VULKAN_HPP_DEFAULT_DISPATCHER.vkDebugMarkerSetObjectNameEXT) { 56 | vk::DebugMarkerObjectNameInfoEXT object_info { 57 | detail::DebugObjectType(object), 58 | reinterpret_cast(static_cast(object)), 59 | name 60 | }; 61 | device.debugMarkerSetObjectNameEXT(object_info); 62 | } 63 | } 64 | } 65 | 66 | struct GuardedDebugMarker { 67 | vk::CommandBuffer cmd_buffer; 68 | 69 | GuardedDebugMarker(vk::CommandBuffer cmd_buffer, const char* name, const std::array& color = { }) 70 | : cmd_buffer(cmd_buffer) { 71 | if (HasDebugMarkerExtension()) { 72 | cmd_buffer.debugMarkerBeginEXT({ name, color }); 73 | } 74 | } 75 | 76 | ~GuardedDebugMarker() { 77 | if (HasDebugMarkerExtension()) { 78 | cmd_buffer.debugMarkerEndEXT(); 79 | } 80 | } 81 | 82 | private: 83 | bool HasDebugMarkerExtension() const { 84 | return (VULKAN_HPP_DEFAULT_DISPATCHER.vkCmdDebugMarkerBeginEXT && VULKAN_HPP_DEFAULT_DISPATCHER.vkCmdDebugMarkerEndEXT); 85 | } 86 | }; 87 | 88 | } // namespace Pica::Vulkan 89 | -------------------------------------------------------------------------------- /source/framework/console.cpp: -------------------------------------------------------------------------------- 1 | #include "console.hpp" 2 | #include "utility/simple_tcp.hpp" 3 | 4 | #include 5 | 6 | #include 7 | 8 | #include 9 | #include 10 | 11 | std::optional Console::HandleCommand(const std::string& command) { 12 | const char* syntax_error_reply = "Syntax: . []"; 13 | 14 | auto dot_it = ranges::find(command, '.'); 15 | if (dot_it == command.end()) 16 | return {{syntax_error_reply}}; 17 | 18 | std::string desired_module_name{command.begin(), dot_it}; 19 | 20 | // Find a module matching the prefix 21 | for (auto& module : modules) { 22 | if (module.first != desired_module_name) 23 | continue; 24 | 25 | std::string sub_command{dot_it + 1, command.end()}; 26 | // TODO: command may include a newline character, so this check is pretty much useless currently 27 | if (sub_command.length() == 0) 28 | return {{syntax_error_reply}}; 29 | 30 | return module.second->HandleCommand(sub_command); 31 | } 32 | 33 | return "No module called \"" + desired_module_name + "\" registered"; 34 | } 35 | 36 | void Console::RegisterModule(const std::string& name, std::unique_ptr module) { 37 | if (modules.count(name)) 38 | throw std::runtime_error("Module \"" + name + "\" already registered"); 39 | 40 | modules[name] = std::move(module); 41 | } 42 | 43 | class NetworkConsoleServer : public SimpleTCPServer { 44 | Console& console; 45 | 46 | boost::asio::streambuf streambuf {}; 47 | 48 | std::optional client; 49 | 50 | void OnClientConnected(boost::asio::ip::tcp::socket socket) override { 51 | client = std::move(socket); 52 | SetupLineReceivedHandler(); 53 | } 54 | 55 | void SetupLineReceivedHandler() { 56 | auto handler = [&](boost::system::error_code ec, std::size_t /*bytes_received*/) { 57 | if (!ec) { 58 | std::istream stream(&streambuf); 59 | std::string command; 60 | std::getline(stream, command); 61 | 62 | auto reply = console.HandleCommand(command); 63 | boost::asio::write(*client, boost::asio::buffer(*reply, reply->size())); 64 | } 65 | 66 | SetupLineReceivedHandler(); 67 | }; 68 | boost::asio::async_read_until(*client, streambuf, '\n', handler); 69 | } 70 | 71 | public: 72 | NetworkConsoleServer(Console& console, uint16_t port) : SimpleTCPServer(port), console(console) {} 73 | }; 74 | 75 | NetworkConsole::NetworkConsole(unsigned port) : server(std::make_unique(*this, port)) { 76 | } 77 | 78 | NetworkConsole::~NetworkConsole() = default; 79 | 80 | void NetworkConsole::Run() { 81 | server->RunTCPServer(); 82 | } 83 | 84 | void NetworkConsole::Stop() { 85 | server->StopTCPServer(); 86 | } 87 | -------------------------------------------------------------------------------- /source/platform/file_formats/smdh.hpp: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include 4 | #include 5 | #include 6 | 7 | #include 8 | 9 | #include 10 | #include 11 | 12 | namespace FileFormat { 13 | struct SMDH { 14 | struct SMDHApplicationTitle { 15 | BOOST_HANA_DEFINE_STRUCT(SMDHApplicationTitle, 16 | (std::array, short_description), 17 | (std::array, long_description), 18 | (std::array, publisher_name) 19 | ); 20 | 21 | struct Tags : expected_size_tag<0x200> {}; 22 | }; 23 | 24 | struct SMDHEULAVersion { 25 | BOOST_HANA_DEFINE_STRUCT(SMDHEULAVersion, 26 | (uint8_t, minor), 27 | (uint8_t, major) 28 | ); 29 | 30 | struct Tags : expected_size_tag<0x2> {}; 31 | }; 32 | 33 | enum class SMDHFlags : uint32_t { 34 | Visible = 1 << 0, 35 | Autoboot = 1 << 1, 36 | Allow3D = 1 << 2, 37 | RequireAcceptEULA = 1 << 3, 38 | AutoSaveOnExit = 1 << 4, 39 | UseExtendedBanner = 1 << 5, 40 | RequireGameRating = 1 << 6, 41 | UsesSaveData = 1 << 7, 42 | RecordUsageData = 1 << 8, 43 | DisableSaveBackup = 1 << 10, 44 | New3DSExclusive = 1 << 12, 45 | RestrictedByParentalControls = 1 << 14, 46 | }; 47 | 48 | struct SMDHApplicationSettings { 49 | BOOST_HANA_DEFINE_STRUCT(SMDHApplicationSettings, 50 | (std::array, age_ratings), 51 | (uint32_t, region_flags), 52 | (uint32_t, matchmaker_id), 53 | (uint64_t, matchmaker_bit_id), 54 | (uint32_t, flags), 55 | (SMDHEULAVersion, eula_version), 56 | (uint16_t, reserved), 57 | (uint32_t, optimal_animation_default_frame), 58 | (uint32_t, cec_id) 59 | ); 60 | 61 | struct Tags : expected_size_tag<0x30> {}; 62 | }; 63 | 64 | BOOST_HANA_DEFINE_STRUCT(SMDH, 65 | (std::array, magic), 66 | (uint16_t, version), 67 | (std::array, reserved0), 68 | (std::array, application_titles), 69 | (SMDHApplicationSettings, application_settings), 70 | (std::array, reserved1), 71 | (std::array, small_icon), 72 | (std::array, large_icon) 73 | ); 74 | 75 | struct Tags : expected_size_tag<0x36C0> {}; 76 | }; 77 | 78 | inline SMDH::SMDHFlags operator|(SMDH::SMDHFlags lhs, SMDH::SMDHFlags rhs) { 79 | return static_cast(Meta::to_underlying(lhs) | Meta::to_underlying(rhs)); 80 | } 81 | 82 | } // namespace FileFormat 83 | -------------------------------------------------------------------------------- /source/ui/key_database.cpp: -------------------------------------------------------------------------------- 1 | #include "key_database.hpp" 2 | 3 | #include 4 | 5 | #include 6 | 7 | #include 8 | #include 9 | 10 | #include 11 | 12 | KeyDatabase LoadKeyDatabase(spdlog::logger& logger, const std::filesystem::path& filename) { 13 | KeyDatabase ret; 14 | 15 | std::ifstream file(filename); 16 | if (!file) { 17 | throw Mikage::Exceptions::InvalidUserInput("Failed to load crypto keys: Could not open {}", filename.string()); 18 | } 19 | 20 | std::string line; 21 | while (std::getline(file, line)) { 22 | namespace x3 = boost::spirit::x3; 23 | 24 | auto parse_match = [&](auto& ctx) { 25 | auto& attrs = x3::_attr(ctx); 26 | const uint32_t key_index = at_c<0>(attrs); 27 | const char key_type = at_c<1>(attrs); 28 | const std::vector& key = at_c<2>(attrs); 29 | const auto max_key_index = (key_type == 'C' ? std::tuple_size_v : KeyDatabase::num_aes_slots); 30 | 31 | if (key_index >= max_key_index) { 32 | throw Mikage::Exceptions::InvalidUserInput("Invalid key slot index {:#x} in line \"{}\" of {}", key_index, line, filename.string()); 33 | } 34 | 35 | if (key.size() != std::tuple_size_v) { 36 | throw Mikage::Exceptions::InvalidUserInput("Invalid key in line \"{}\" of {} (should be 32 hex digits)", line, filename.string()); 37 | } 38 | 39 | auto& dest_key = key_type == 'X' ? ret.aes_slots[key_index].x 40 | : key_type == 'Y' ? ret.aes_slots[key_index].y 41 | : key_type == 'N' ? ret.aes_slots[key_index].n 42 | : key_type == 'C' ? ret.common_y[key_index] 43 | : throw Mikage::Exceptions::InvalidUserInput("Invalid key type '{}' in line \"{}\" of {}", key_type, line, filename.string()); 44 | 45 | dest_key = KeyDatabase::KeyType {}; 46 | memcpy(dest_key.value().data(), key.data(), sizeof(dest_key.value())); 47 | }; 48 | 49 | auto pattern_aes = x3::lit("slot0x") >> x3::uint_parser() >> x3::lit("Key") >> x3::alpha >> x3::lit("=") >> x3::repeat(16)[x3::uint_parser()]; 50 | auto pattern_common = x3::lit("common") >> x3::uint_parser() >> x3::attr('C') >> x3::lit("=") >> x3::repeat(16)[x3::uint_parser()]; 51 | if (x3::parse(line.begin(), line.end(), pattern_aes[parse_match])) { 52 | } else if (x3::parse(line.begin(), line.end(), pattern_common[parse_match])) { 53 | } else if (line.empty() || line[0] == '#') { 54 | // Ignore 55 | } else if (line.starts_with("dlp") || line.starts_with("nfc")) { 56 | // Not currently used 57 | } 58 | } 59 | 60 | return ret; 61 | } 62 | -------------------------------------------------------------------------------- /source/gdb_stub.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include "interpreter.h" 4 | 5 | #include 6 | #include 7 | 8 | #include 9 | #include 10 | #include 11 | #include 12 | 13 | class LogManager; 14 | 15 | namespace HLE { 16 | namespace OS { 17 | class OS; 18 | class Thread; 19 | class Process; 20 | 21 | using ProcessId = uint32_t; 22 | using ThreadId = uint32_t; 23 | 24 | } 25 | } 26 | 27 | namespace Interpreter { 28 | 29 | class GDBStubTCPServer; 30 | class GDBStub final : public ProcessorController { 31 | friend class GDBStubTCPServer; 32 | 33 | // We refer to the "OS" more abstractly as env(ironment) here. 34 | // In the future, we may support LLE kernel emulation, and as such 35 | // may only have one thread remaining to debug. To simplify design of 36 | // this stub interface, we might still want to keep around a virtual 37 | // OS layer around at that point, though. 38 | HLE::OS::OS& env; 39 | 40 | std::unique_ptr server; 41 | 42 | std::unordered_map> thread_for_operation; 43 | 44 | std::shared_ptr logger; 45 | 46 | std::vector attached_processes; 47 | 48 | // Information about a process that is waiting for a debugger to attach 49 | std::weak_ptr attach_info; 50 | 51 | /** 52 | * Handle an incoming GDB command 53 | * 54 | * Returns an optional response to send to GDB. 55 | */ 56 | std::optional HandlePacket(const std::string& command); 57 | 58 | void Continue(bool single_step = false); 59 | 60 | /** 61 | * Get CPU Id of the controlled CPU 62 | */ 63 | unsigned GetCPUId(); 64 | 65 | void OnSegfault(uint32_t process_id, uint32_t thread_id) override; 66 | void OnBreakpoint(uint32_t process_id, uint32_t thread_id) override; 67 | void OnReadWatchpoint(uint32_t process_id, uint32_t thread_id, uint32_t data_address) override; 68 | void OnWriteWatchpoint(uint32_t process_id, uint32_t thread_id, uint32_t data_address) override; 69 | void OnProcessLaunched(uint32_t process_id, uint32_t thread_id, uint32_t launched_process_id) override; 70 | 71 | // Environment inspection utility functions 72 | 73 | // Get currently active thread 74 | HLE::OS::Thread& GetCurrentThread() const; 75 | 76 | // Get currently active process 77 | HLE::OS::Process& GetCurrentProcess() const; 78 | 79 | // Get a list of threads running in the environment 80 | std::vector> GetThreadList() const; 81 | 82 | std::shared_ptr GetThreadForOperation(char operation); 83 | 84 | public: 85 | GDBStub(HLE::OS::OS& os, LogManager& log_manager, unsigned port); 86 | ~GDBStub(); 87 | 88 | void OfferAttach(std::weak_ptr attach_info) override; 89 | 90 | void ProcessQueue(); 91 | }; 92 | 93 | } // namespace 94 | -------------------------------------------------------------------------------- /source/loader/gamecard.hpp: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include "framework/settings.hpp" 4 | 5 | #include 6 | #include 7 | #include 8 | #include 9 | #include 10 | 11 | namespace HLE { 12 | namespace PXI { 13 | namespace FS { 14 | class Archive; 15 | class File; 16 | } 17 | } 18 | } 19 | 20 | namespace Loader { 21 | 22 | /// TODO: This is what's called "Content Index" in other places 23 | enum class NCSDPartitionId { 24 | Executable = 0, 25 | Manual = 1, 26 | DownloadPlayChild = 2, 27 | UpdateDataNew3DS = 6, 28 | UpdateData = 7 29 | }; 30 | 31 | /** 32 | * Top-level game card interface. 33 | */ 34 | class GameCard { 35 | public: 36 | /** 37 | * Return a pointer to a File from which the partition's NCCH data can be accessed, or std::nullopt if the partition doesn't exist. 38 | */ 39 | virtual std::optional> GetPartitionFromId(NCSDPartitionId id) = 0; 40 | virtual bool HasPartition(NCSDPartitionId id) = 0; 41 | 42 | virtual ~GameCard(); 43 | }; 44 | 45 | /** 46 | * Utility to wrap a CXI container into a single-partition GameCard. 47 | */ 48 | class GameCardFromCXI final : public GameCard { 49 | std::optional> GetPartitionFromId(NCSDPartitionId id) override; 50 | bool HasPartition(NCSDPartitionId id) override; 51 | 52 | // Filename or file descriptor 53 | std::variant source; 54 | 55 | public: 56 | GameCardFromCXI(std::string_view filename); 57 | GameCardFromCXI(int file_descriptor); 58 | 59 | static bool IsLoadableFile(std::string_view filename); 60 | static bool IsLoadableFile(int file_descriptor); 61 | }; 62 | 63 | class GameCardFromCCI final : public GameCard { 64 | std::optional> GetPartitionFromId(NCSDPartitionId id) override; 65 | bool HasPartition(NCSDPartitionId id) override; 66 | 67 | // Filename or file descriptor 68 | std::variant source; 69 | 70 | public: 71 | GameCardFromCCI(std::string_view filename); 72 | GameCardFromCCI(int file_descriptor); 73 | 74 | static bool IsLoadableFile(std::string_view filename); 75 | static bool IsLoadableFile(int file_descriptor); 76 | }; 77 | 78 | /** 79 | * Utility to wrap a 3DSX file into a single-partition GameCard. 80 | */ 81 | class GameCardFrom3DSX final : public GameCard { 82 | std::optional> GetPartitionFromId(NCSDPartitionId id) override; 83 | bool HasPartition(NCSDPartitionId id) override; 84 | 85 | // Filename or file descriptor 86 | std::variant source; 87 | 88 | std::string data_dir_path; 89 | 90 | public: 91 | GameCardFrom3DSX(std::string_view filename, const std::string& data_dir_path); 92 | GameCardFrom3DSX(int file_descriptor, const std::string& data_dir_path); 93 | 94 | static bool IsLoadableFile(std::string_view filename); 95 | static bool IsLoadableFile(int file_descriptor); 96 | }; 97 | 98 | } // namespace Loader 99 | -------------------------------------------------------------------------------- /source/processes/ro_hpv.cpp: -------------------------------------------------------------------------------- 1 | #include "ro_hpv.hpp" 2 | #include "os_hypervisor_private.hpp" 3 | #include "os.hpp" 4 | 5 | #include 6 | 7 | namespace HLE { 8 | 9 | namespace OS { 10 | 11 | namespace HPV { 12 | 13 | namespace { 14 | 15 | struct RoService : SessionToPort { 16 | RoService(RefCounted port_, ROContext& context_) : SessionToPort(port_, context_) { 17 | } 18 | 19 | void OnRequest(Hypervisor& hypervisor, Thread& thread, Handle session) override { 20 | const uint32_t command_header = thread.ReadTLS(0x80); 21 | auto dispatcher = RequestDispatcher<> { thread, *this, command_header }; 22 | 23 | // TODO: Also add UnloadCRO (command 5) 24 | 25 | using LoadCRO = IPC::IPCCommand<0x4>::add_uint32::add_uint32::add_uint32::add_uint32::add_uint32::add_uint32::add_uint32::add_uint32::add_uint32::add_uint32::add_uint32::add_handle::response::add_uint32; 26 | using LoadCRONew = IPC::IPCCommand<0x9>::add_uint32::add_uint32::add_uint32::add_uint32::add_uint32::add_uint32::add_uint32::add_uint32::add_uint32::add_uint32::add_uint32::add_handle::response::add_uint32; 27 | dispatcher.DecodeRequest([&](auto& response, uint32_t, uint32_t, uint32_t, uint32_t, uint32_t, uint32_t, uint32_t, uint32_t, uint32_t, uint32_t, uint32_t, Handle) { 28 | Session::OnRequest(hypervisor, thread, session, "LoadCRO"); 29 | response.OnResponse([](Hypervisor&, Thread&, Result result, uint32_t) { 30 | if (result != RESULT_OK) { 31 | throw Mikage::Exceptions::Invalid("LoadCRO failed"); 32 | } 33 | }); 34 | }); 35 | dispatcher.DecodeRequest([&](auto& response, uint32_t source_addr, uint32_t, uint32_t, uint32_t, uint32_t, uint32_t, uint32_t, uint32_t, uint32_t, uint32_t, uint32_t, Handle process_handle) { 36 | Session::OnRequest(hypervisor, thread, session, "LoadCRONew"); 37 | 38 | auto source_process = thread.GetProcessHandleTable().FindObject(process_handle); 39 | 40 | auto magic = source_process->ReadMemory32(source_addr + 0x80); 41 | if (magic != 0x304f5243) { 42 | throw Mikage::Exceptions::Invalid("LoadCRONew argument is not a valid CRO file"); 43 | } 44 | 45 | response.OnResponse([](Hypervisor&, Thread&, Result result, uint32_t) { 46 | if (result == 0xd8e12c03) { 47 | throw Mikage::Exceptions::Invalid("LoadCRONew failed (invalid hash?)"); 48 | } 49 | if (result != RESULT_OK) { 50 | throw Mikage::Exceptions::Invalid("LoadCRONew failed"); 51 | } 52 | }); 53 | }); 54 | } 55 | }; 56 | 57 | } // anonymous namespace 58 | 59 | HPV::RefCounted CreateRoService(RefCounted port, ROContext& context) { 60 | return HPV::RefCounted(new RoService(port, context)); 61 | } 62 | 63 | } // namespace HPV 64 | 65 | } // namespace OS 66 | 67 | } // namespace HLE 68 | -------------------------------------------------------------------------------- /source/processes/act.cpp: -------------------------------------------------------------------------------- 1 | #include "act.hpp" 2 | 3 | #include 4 | 5 | namespace HLE { 6 | 7 | namespace OS { 8 | 9 | struct FakeACT { 10 | FakeACT(FakeThread& thread); 11 | }; 12 | 13 | static auto ACTCommandHandler(FakeThread& thread, FakeACT&, std::string_view service_name, const IPC::CommandHeader& header) { 14 | using Initialize = IPC::IPCCommand<0x1>::add_uint32::add_uint32::add_process_id::add_handle::response; 15 | 16 | using GetAccountDataBlock = IPC::IPCCommand<0x6>::add_uint32::add_uint32::add_uint32::add_buffer_mapping_write::response; 17 | 18 | // // TODO: Unknown what reply this sends 19 | // // NOTE: Contrary to what the name (taken from 3dbrew) suggests, the client sends the event 20 | // using GetNZoneBeaconNotFoundEvent = IPC::IPCCommand<0x2f>::add_process_id::add_handle::response; 21 | 22 | // using SetClientVersion = IPC::IPCCommand<0x40>::add_uint32::add_process_id::response; 23 | 24 | switch (header.command_id) { 25 | case Initialize::id: 26 | thread.WriteTLS(0x80, IPC::CommandHeader::Make(0, 1, 0).raw); 27 | thread.WriteTLS(0x84, RESULT_OK); 28 | break; 29 | 30 | case GetAccountDataBlock::id: 31 | thread.WriteTLS(0x80, IPC::CommandHeader::Make(0, 1, 0).raw); 32 | thread.WriteTLS(0x84, RESULT_OK); 33 | break; 34 | 35 | case 0xd: 36 | thread.WriteTLS(0x80, IPC::CommandHeader::Make(0, 10, 0).raw); 37 | thread.WriteTLS(0x84, RESULT_OK); 38 | thread.WriteTLS(0x88, 0); 39 | thread.WriteTLS(0x8c, 0); 40 | thread.WriteTLS(0x90, 0); 41 | thread.WriteTLS(0x94, 0); 42 | thread.WriteTLS(0x98, 0); 43 | thread.WriteTLS(0x9c, 0); 44 | thread.WriteTLS(0xa0, 0); 45 | thread.WriteTLS(0xa4, 0); 46 | thread.WriteTLS(0xa8, 0); 47 | break; 48 | 49 | default: 50 | throw Mikage::Exceptions::NotImplemented("Unknown {} service command with header {:#010x}", service_name, header.raw); 51 | } 52 | 53 | return ServiceHelper::SendReply; 54 | } 55 | 56 | static void ACTUThread(FakeACT& context, FakeThread& thread) { 57 | ServiceHelper service; 58 | service.Append(ServiceUtil::SetupService(thread, "act:u", 2)); 59 | service.Append(ServiceUtil::SetupService(thread, "act:a", 2)); 60 | 61 | auto InvokeCommandHandler = [&](FakeThread& thread, uint32_t /*signalled_handle_index*/) { 62 | Platform::IPC::CommandHeader header = { thread.ReadTLS(0x80) }; 63 | return ACTCommandHandler(thread, context, "act:a/u", header); 64 | }; 65 | 66 | service.Run(thread, std::move(InvokeCommandHandler)); 67 | } 68 | 69 | FakeACT::FakeACT(FakeThread& thread) { 70 | thread.name = "ACUThread"; 71 | 72 | ACTUThread(*this, thread); 73 | } 74 | 75 | template<> std::shared_ptr CreateFakeProcessViaContext(OS& os, Interpreter::Setup& setup, uint32_t pid, const std::string& name) { 76 | return WrappedFakeProcess::CreateWithContext(os, setup, pid, name); 77 | } 78 | 79 | } // namespace OS 80 | 81 | } // namespace HLE 82 | -------------------------------------------------------------------------------- /source/video_core/src/video_core/shader_analysis.hpp: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include "shader_private.hpp" 4 | 5 | #include 6 | 7 | #include 8 | #include 9 | #include 10 | #include 11 | 12 | namespace Pica::VertexShader { 13 | 14 | struct AnalyzedProgram { 15 | enum class SuccessionVia { 16 | BranchToIf, // Branch to if-body 17 | BranchToElse, // Branch to else-body (or past the conditional if else-body is empty) 18 | Fallthrough, // Continue at the next PICA200 instruction 19 | Return, // Return from function to caller 20 | JumpTo, // Jump to another offset 21 | ExitLoop, // Exit from a PICA200 loop 22 | LoopEnd, // Last block in a PICA200 loop 23 | CondJumpTo, // Conditional Jump 24 | }; 25 | 26 | struct PostExecJump { 27 | static PostExecJump PopCallStack() { 28 | PostExecJump ret; 29 | ret.target = CallStackTag { }; 30 | return ret; 31 | } 32 | 33 | static PostExecJump ReturnTo(ShaderCodeOffset target) { 34 | PostExecJump ret; 35 | ret.target = target; 36 | return ret; 37 | } 38 | 39 | bool FromCallStack() const { 40 | return std::holds_alternative(target); 41 | } 42 | 43 | std::optional GetStaticTarget() const { 44 | auto* static_target = std::get_if(&target); 45 | if (!static_target) { 46 | return std::nullopt; 47 | } 48 | return *static_target; 49 | } 50 | 51 | explicit operator bool() const { 52 | return !std::holds_alternative(target); 53 | } 54 | 55 | private: 56 | struct CallStackTag { }; 57 | std::variant target; 58 | }; 59 | 60 | struct BlockInfo { 61 | uint32_t length = 0xffffffff; // 0xffffffff to indicate no known end bound 62 | 63 | std::set predecessors; 64 | 65 | // Block successors (excluding function calls as these are tracked in call_targets) 66 | std::map successors; 67 | 68 | // Optional target to implicitly jump to at the end of the block 69 | PostExecJump post_exec_jump; 70 | 71 | // First instruction that is hit by all code paths following this block 72 | ShaderCodeOffset post_dominator = { 0xffffffff }; 73 | }; 74 | 75 | std::map blocks; 76 | 77 | struct CallInfo { 78 | ShaderCodeOffset target; 79 | ShaderCodeOffset return_site; 80 | }; 81 | 82 | // Map from call instruction offset to call target and return site 83 | std::map call_targets; 84 | 85 | struct Function { 86 | ShaderCodeOffset entry; 87 | ShaderCodeOffset exit; 88 | std::set blocks; 89 | }; 90 | 91 | std::vector functions; 92 | 93 | ShaderCodeOffset entry_point; 94 | // TODO: Add length! 95 | }; 96 | 97 | } // namespace Pica::VertexShader 98 | -------------------------------------------------------------------------------- /source/processes/pxi.hpp: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include "platform/fs.hpp" 4 | #include "fake_process.hpp" 5 | 6 | #include 7 | 8 | namespace FileFormat { 9 | struct ExHeader; 10 | } 11 | 12 | struct KeyDatabase; 13 | 14 | namespace HLE { 15 | 16 | // namespace OS { 17 | 18 | namespace PXI { 19 | namespace FS { 20 | class Archive; 21 | class File; 22 | class FileContext; 23 | } 24 | } 25 | 26 | namespace PXI { 27 | 28 | /** 29 | * TODO: We impose the invariant that the static buffer data describing 30 | * the physical memory chunks for this buffer may not be modified 31 | * throughout the lifetime of instances of this structure. Can we make 32 | * this invariant more explicit? 33 | */ 34 | struct PXIBuffer : IPC::StaticBuffer { 35 | struct ChunkDescriptor { 36 | uint32_t start_addr; 37 | uint32_t size_in_bytes; 38 | }; 39 | 40 | PXIBuffer(const IPC::StaticBuffer& other); 41 | 42 | template 43 | DataType Read(OS::Thread& thread, uint32_t offset) const; 44 | 45 | template 46 | void Write(OS::Thread& thread, uint32_t offset, DataType data) const; 47 | 48 | uint32_t LookupAddress(OS::Thread& thread, uint32_t offset) const; 49 | 50 | // Cached version of chunk descriptors 51 | mutable std::vector chunks; 52 | 53 | // Valid only when chunks is non-empty 54 | mutable uint32_t chunk_size = 0; 55 | }; 56 | 57 | using ArchiveHandle = uint64_t; 58 | using FileHandle = uint64_t; 59 | 60 | using PMProgramHandle = Platform::PXI::PM::ProgramHandle; 61 | 62 | struct Context { 63 | std::unordered_map> archives; 64 | ArchiveHandle next_archive_handle = 0; 65 | 66 | std::unordered_map> files; 67 | FileHandle next_file_handle = 0; 68 | 69 | // Handle to be assigned to the next registered program 70 | PMProgramHandle next_program_handle = { 0x11233211 }; 71 | 72 | // Map from PM internal program handle to program infos for the main title and the update title 73 | std::unordered_map> programs; 74 | 75 | // List of titles installed to the emulated NAND 76 | std::vector nand_titles; 77 | }; 78 | 79 | class FakePXI final { 80 | OS::OS& os; 81 | spdlog::logger& logger; 82 | 83 | public: 84 | FakePXI(OS::FakeThread& thread); 85 | 86 | void FSThread(OS::FakeThread& thread, Context& context, const char* service_name); 87 | void PMThread(OS::FakeThread& thread, Context& context); 88 | void PSThread(OS::FakeThread& thread, Context& context); 89 | void MCThread(OS::FakeThread& thread, Context& context); 90 | void AMThread(OS::FakeThread& thread, Context& context); 91 | }; 92 | 93 | std::optional GetHLEModuleName(uint64_t title_id); 94 | 95 | FileFormat::ExHeader GetExtendedHeader(OS::Thread&, const Platform::FS::ProgramInfo&); 96 | FileFormat::ExHeader GetExtendedHeader(FS::FileContext&, const KeyDatabase&, FS::File& ncch_file); 97 | 98 | } // namespace PXI 99 | 100 | // } // namespace OS 101 | 102 | } // namespace HLE 103 | -------------------------------------------------------------------------------- /source/platform/am.hpp: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include "ipc.hpp" 4 | 5 | namespace Platform { 6 | 7 | /** 8 | * Application Manager: Interface used most prominently by the Home Menu to 9 | * get information about installed titles 10 | */ 11 | namespace AM { 12 | 13 | // All IPC commands in this module have unique command IDs and hence 14 | // are not put in per-service namespaces 15 | 16 | namespace IPC = Platform::IPC; 17 | 18 | /** 19 | * Inputs: 20 | * - Media Type 21 | * 22 | * Outputs: 23 | * - Number of title IDs for this media type 24 | */ 25 | struct GetNumPrograms : IPC::IPCCommand<0x1>::add_uint32 26 | ::response::add_uint32 {}; 27 | 28 | /** 29 | * Inputs: 30 | * - Maximum title count 31 | * - Media Type 32 | * 33 | * Outputs: 34 | * - Number of title IDs actually returned 35 | * - Title IDs 36 | */ 37 | struct GetProgramList : IPC::IPCCommand<0x2>::add_uint32::add_uint32::add_buffer_mapping_write 38 | ::response::add_uint32::add_buffer_mapping_write {}; 39 | 40 | /** 41 | * Inputs: 42 | * - Media Type 43 | * - Number of titles to query ProgramInfos for 44 | * - List of title IDs to query ProgramInfos for 45 | * 46 | * Outputs: 47 | * - ProgramInfos for each input title id 48 | */ 49 | struct GetProgramInfos : IPC::IPCCommand<0x3>::add_uint32::add_uint32::add_buffer_mapping_read::add_buffer_mapping_write 50 | ::response::add_uint32::add_buffer_mapping_read::add_buffer_mapping_write {}; 51 | 52 | /** 53 | * Inputs: 54 | * - Media Type 55 | * - Title ID 56 | * 57 | * Outputs: 58 | * - Number of content infos for this title 59 | */ 60 | struct GetDLCContentInfoCount : IPC::IPCCommand<0x1001>::add_uint32::add_uint64 61 | ::response::add_uint32 {}; 62 | 63 | /** 64 | * Inputs: 65 | * - Number of content infos to read 66 | * - Media Type 67 | * - Title ID 68 | * - Offset (index of first content info to read?) 69 | * - Output buffer 70 | * 71 | * Outputs: 72 | * - Number of content infos read 73 | */ 74 | struct ListDLCContentInfos : IPC::IPCCommand<0x1003>::add_uint32::add_uint32::add_uint64::add_uint32::add_buffer_mapping_write 75 | ::response::add_uint32::add_buffer_mapping_write {}; 76 | 77 | /** 78 | * Inputs: 79 | * - Media Type 80 | * - Number of titles in the input list 81 | * - Title IDs to get title infos for 82 | * - Output buffer for title infos 83 | */ 84 | struct GetDLCTitleInfos : IPC::IPCCommand<0x1005>::add_uint32::add_uint32::add_buffer_mapping_read::add_buffer_mapping_write 85 | ::response::add_buffer_mapping_read::add_buffer_mapping_write {}; 86 | 87 | /** 88 | * Inputs: 89 | * - Number of ticket infos to get 90 | * - Title ID 91 | * - Offset (index of first ticket to get?) 92 | * - Output buffer for ticket infos 93 | * 94 | * Outputs: 95 | * - Number of ticket infos read for this title 96 | */ 97 | struct ListDataTitleTicketInfos : IPC::IPCCommand<0x1007>::add_uint32::add_uint64::add_uint32::add_buffer_mapping_write 98 | ::response::add_uint32::add_buffer_mapping_write {}; 99 | 100 | } // namespace AM 101 | 102 | } // namespace Platform 103 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # Mikage Developer Edition 2 | 3 | ## Build instructions 4 | 5 | CMake and Conan 2 are required to build Mikage. 6 | * `mkdir build` 7 | * `cd build` 8 | * `conan install .. -of . --build=missing` (add `-s build_type=Debug` for Debug builds) 9 | * `cmake .. -G Ninja --toolchain conan_toolchain.cmake -DCMAKE_BUILD_TYPE=Release` 10 | * `ninja && sudo ninja install` 11 | 12 | NOTE: On Linux, Vulkan libraries are also required and will NOT be built by Conan. 13 | 14 | Some dependencies may be provided by system packages instead of building them 15 | via Conan. To enable this behavior, add the following to your Conan profile 16 | (`~/.conan2/profiles/default`): 17 | ```ini 18 | [platform_requires] 19 | boost/1.80.0 20 | cryptopp/8.5.0 21 | sdl/2.0.20 22 | range-v3/0.11.0 23 | catch2/2.13.7 24 | libunwind/1.8.0 25 | ``` 26 | 27 | ### GCC 15 and newer 28 | 29 | Due to a change in default compilation flags in GCC 15, the `conan` step above may fail. If this happens, append the following two lines to your Conan profile (`~/.conan2/profiles/default`) and try again: 30 | ```ini 31 | [conf] 32 | tools.build:cflags=["-std=gnu17"] 33 | ``` 34 | 35 | ## Usage 36 | 37 | For the first launch, three things must be set up: 38 | 1. a complete `aes_keys.txt` must be placed in the `build` folder 39 | 2. a virtual NAND must be bootstrapped from a game update partition 40 | 3. the 3DS initial system setup must be run 41 | 42 | Given any game with an update partition, steps 2 and 3 can be done by running: 43 | ``` 44 | cd build && source conanrun.sh && ./source/mikage game.cci --bootstrap_nand --launch_menu 45 | ``` 46 | NOTE: Currently, this probably requires a game of the EU region. Patching 47 | `SecureInfo_A` by hand after bootstrapping may work, too. 48 | 49 | Once set up, it's sufficient to run `./source/mikage game.cci` or `./source/mikage --launch_menu`. 50 | You'll need to `source conanrun.sh` to set up library directories. 51 | 52 | ## Key bindings 53 | 54 | * Arrow →: A 55 | * Arrow ↓: B 56 | * Arrow ↑: X 57 | * Arrow ←: Y 58 | * WASD keys: Circle pad 59 | * Backspace: HOME (press twice to power down) 60 | * Q key: L 61 | * E key: R 62 | * IJKL keys: D-pad 63 | * Enter key: START 64 | * Shift key: SELECT 65 | 66 | ## Debugging 67 | 68 | Individual processes may be debugged through an embedded GDB remote server over 69 | network port 12345. To enable it, pass `--debug` and `--attach_to_process ` 70 | when launching Mikage. After startup, connect via gdb using `tar ext :12345`, 71 | wait until Mikage prints a message about waiting for the debugger attach, then 72 | type `attach ` in the gdb shell. Note that a gdb build for ARM targets may 73 | be necessary to do this. Debuggers other than gdb may work but are untested. 74 | 75 | Processes running in the emulated kernel can be introspected through a simple 76 | debug console available via telnet on port 12347. 77 | 78 | ## Profiling 79 | 80 | Mikage uses [Tracy](https://github.com/wolfpld/tracy) for profiling. To enable it, 81 | pass `-o enable_profiler=True` to `conan install` and rebuild Mikage. 82 | Get your Tracy server (often available from the system package manager) up and 83 | running and start listening for clients. Then launch Mikage and your server will 84 | start recording. Each emulator frame will be captured containing the Tracy Zones 85 | that have been defined throughout Mikage's code. 86 | -------------------------------------------------------------------------------- /source/framework/exceptions.hpp: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include 4 | #include 5 | 6 | #include 7 | 8 | namespace Mikage { 9 | 10 | namespace Exceptions { 11 | 12 | std::string generate_backtrace(); 13 | 14 | /** 15 | * Exception to throw upon violation internal contracts that are expected to 16 | * hold regardless of invalid emulation or user inputs. 17 | * 18 | * ValidateContract provides a drop-in replacement for assert that throws 19 | * exceptions of this kind. 20 | */ 21 | struct ContractViolated : std::runtime_error { 22 | ContractViolated(std::string_view condition, std::string_view function, std::string_view file, int line) 23 | : std::runtime_error(FormatMessage(condition, function, file, line) + generate_backtrace()) { 24 | } 25 | 26 | static std::string FormatMessage(std::string_view condition, std::string_view function, std::string_view file, int line); 27 | }; 28 | 29 | /** 30 | * Exception to throw when the given situation is valid but handling it is not 31 | * implemented currently. 32 | * 33 | * Examples of this are unhandled (but valid) enum inputs to emulated APIs or 34 | * unhandled corner cases in internal libraries. 35 | * 36 | * This exception may also be used in situations where it's not entirely clear 37 | * whether the behavior may or may not be technically valid. 38 | */ 39 | struct NotImplemented : std::runtime_error { 40 | template 41 | NotImplemented(const char* message, T&&... ts) : std::runtime_error(fmt::format(fmt::runtime(message), std::forward(ts)...) + "\n" + generate_backtrace()) { 42 | 43 | } 44 | }; 45 | 46 | /** 47 | * Exception to throw when the given situation is invalid, such as by violating 48 | * preconditions of an emulated API. 49 | * 50 | * Examples of this are out-of-range parameters and invalid enum values. 51 | * 52 | * When it's not entirely clear whether a situation should be considered 53 | * valid or not, prefer NotImplemented. 54 | */ 55 | struct Invalid : std::runtime_error { 56 | template 57 | Invalid(const char* message, T&&... ts) : std::runtime_error(fmt::format(fmt::runtime(message), std::forward(ts)...) + "\n" + generate_backtrace()) { 58 | 59 | } 60 | }; 61 | 62 | /** 63 | * Exception to throw when the user provides invalid input, for example from 64 | * a command-line argument or a configuration file. 65 | * 66 | * Emulation logic should never use this. 67 | */ 68 | struct InvalidUserInput : std::runtime_error { 69 | template 70 | InvalidUserInput(const char* message, T&&... ts) : std::runtime_error(fmt::format(fmt::runtime(message), std::forward(ts)...) + "\n" + generate_backtrace()) { 71 | 72 | } 73 | }; 74 | 75 | } // namespace Exceptions 76 | 77 | } // namespace Mikage 78 | 79 | // ValidateContract is implemented as a macro so that we can stringify the failed condition 80 | #ifdef NDEBUG 81 | #define ValidateContract(cond) do { if (!(cond)) { \ 82 | throw Mikage::Exceptions::ContractViolated(#cond, __PRETTY_FUNCTION__, __FILE__, __LINE__); \ 83 | } } while (false) 84 | #else 85 | // Use assert() directly in debug mode 86 | #define ValidateContract(cond) do { if (!(cond)) { \ 87 | fputs(Mikage::Exceptions::generate_backtrace().c_str(), stderr); \ 88 | assert((cond)); \ 89 | } } while (false) 90 | #endif 91 | -------------------------------------------------------------------------------- /source/platform/file_formats/formats.cpp: -------------------------------------------------------------------------------- 1 | #define FORMATS_IMPL_EXPLICIT_FORMAT_INSTANTIATIONS_INTENDED 2 | #include 3 | 4 | #include "3dsx.hpp" 5 | #include "bcfnt.hpp" 6 | #include "cia.hpp" 7 | #include "dsp1.hpp" 8 | #include "ncch.hpp" 9 | #include "smdh.hpp" 10 | 11 | namespace FileFormat { 12 | 13 | // 3DSX structures 14 | template struct SerializationInterface; 15 | template struct SerializationInterface; 16 | template struct SerializationInterface; 17 | 18 | // BCFNT structures 19 | template struct SerializationInterface; 20 | template struct SerializationInterface; 21 | template struct SerializationInterface; 22 | template struct SerializationInterface; 23 | template struct SerializationInterface; 24 | template struct SerializationInterface; 25 | 26 | // CIA structures 27 | template struct SerializationInterface; 28 | template struct SerializationInterface>; 29 | template struct SerializationInterface>; 30 | template struct SerializationInterface; 31 | template struct SerializationInterface; 32 | template struct SerializationInterface; 33 | template struct SerializationInterface; 34 | template struct SerializationInterface; 35 | template struct SerializationInterface; 36 | template struct SerializationInterface; 37 | template struct SerializationInterface; 38 | 39 | // DSP structures 40 | template struct SerializationInterface; 41 | 42 | // NCCH structures 43 | template struct SerializationInterface; 44 | template struct SerializationInterface; 45 | template struct SerializationInterface; 46 | 47 | // SMDH structures 48 | template struct SerializationInterface; 49 | 50 | template<> 51 | uint32_t SerializationInterface::Load(std::function reader) { 52 | return ConstructFromReadCallback>{}(reader); 53 | } 54 | 55 | template<> 56 | uint64_t SerializationInterface::Load(std::function reader) { 57 | return ConstructFromReadCallback>{}(reader); 58 | } 59 | 60 | template<> 61 | boost::endian::little_uint32_t SerializationInterface::Load(std::function reader) { 62 | struct Tags : little_endian_tag {}; 63 | return ConstructFromReadCallback>{}(reader); 64 | } 65 | 66 | template<> 67 | boost::endian::big_uint32_t SerializationInterface::Load(std::function reader) { 68 | struct Tags : big_endian_tag {}; 69 | return ConstructFromReadCallback>{}(reader); 70 | } 71 | 72 | template<> 73 | void SerializationInterface::Save(const boost::endian::big_uint32_t& data, std::function writer) { 74 | struct Tags : big_endian_tag {}; 75 | SaveWithWriteCallback>{}(data, writer); 76 | } 77 | 78 | } // namespace FileFormat 79 | -------------------------------------------------------------------------------- /source/processes/news.cpp: -------------------------------------------------------------------------------- 1 | #include "news.hpp" 2 | #include "fake_process.hpp" 3 | 4 | namespace HLE { 5 | 6 | namespace OS { 7 | 8 | static auto NEWSUCommandHandler(FakeNEWS&, FakeThread&, Platform::IPC::CommandHeader header) try { 9 | switch (header.command_id) { 10 | default: 11 | throw IPC::IPCError{header.raw, 0xdeadbeef}; 12 | } 13 | 14 | return ServiceHelper::SendReply; 15 | } catch (const IPC::IPCError& err) { 16 | throw std::runtime_error(fmt::format("Unknown news:u service command with header {:#010x}", err.header)); 17 | } 18 | 19 | static auto NEWSSCommandHandler(FakeNEWS& context, FakeThread& thread, Platform::IPC::CommandHeader header) try { 20 | using GetTotalNotifications = IPC::IPCCommand<0x5>::response::add_uint32; 21 | using GetNewsDBHeader = IPC::IPCCommand<0xa>::add_uint32::add_buffer_mapping_write::response/*::add_buffer_mapping_write*/; 22 | 23 | switch (header.command_id) { 24 | case 1: 25 | thread.WriteTLS(0x80, IPC::CommandHeader::Make(0, 1, 0).raw); 26 | thread.WriteTLS(0x84, RESULT_OK); 27 | break; 28 | 29 | case GetTotalNotifications::id: 30 | thread.WriteTLS(0x80, IPC::CommandHeader::Make(0, 2, 0).raw); 31 | thread.WriteTLS(0x84, RESULT_OK); 32 | thread.WriteTLS(0x88, 0); 33 | break; 34 | 35 | case 0x13: // WriteNewsDBSavedata 36 | thread.WriteTLS(0x80, IPC::CommandHeader::Make(0, 1, 0).raw); 37 | thread.WriteTLS(0x84, RESULT_OK); 38 | break; 39 | 40 | case 0x14: // Unknown, returns at least one uint32_t value in addition to the result 41 | thread.WriteTLS(0x80, IPC::CommandHeader::Make(0, 2, 0).raw); 42 | thread.WriteTLS(0x84, RESULT_OK); 43 | thread.WriteTLS(0x88, 0); 44 | break; 45 | 46 | case GetNewsDBHeader::id: 47 | thread.WriteTLS(0x80, IPC::CommandHeader::Make(0, 2, /*2*/0).raw); 48 | thread.WriteTLS(0x84, RESULT_OK); 49 | thread.WriteTLS(0x88, 0); // number of bytes written to output buffer 50 | break; 51 | 52 | default: 53 | throw IPC::IPCError{header.raw, 0xdeadbeef}; 54 | } 55 | 56 | return ServiceHelper::SendReply; 57 | } catch (const IPC::IPCError& err) { 58 | throw std::runtime_error(fmt::format("Unknown news:s service command with header {:#010x}", err.header)); 59 | } 60 | 61 | template 62 | static void IPCThread(FakeNEWS& context, FakeThread& thread, const char* service_name, uint32_t session_limit, Handler&& handler) { 63 | ServiceHelper service; 64 | service.Append(ServiceUtil::SetupService(thread, service_name, session_limit)); 65 | 66 | auto InvokeCommandHandler = [&context,handler=std::move(handler)](FakeThread& thread, uint32_t) { 67 | Platform::IPC::CommandHeader header = { thread.ReadTLS(0x80) }; 68 | return handler(context, thread, header); 69 | }; 70 | 71 | service.Run(thread, std::move(InvokeCommandHandler)); 72 | } 73 | 74 | FakeNEWS::FakeNEWS(FakeThread& thread) { 75 | thread.name = "news:sThread"; 76 | 77 | { 78 | auto newsu_thread = std::make_shared(thread.GetParentProcess(), [this](FakeThread& thread) { 79 | return IPCThread(*this, thread, "news:u", 1, NEWSUCommandHandler); 80 | }); 81 | newsu_thread->name = "news:uThread"; 82 | thread.GetParentProcess().AttachThread(newsu_thread); 83 | } 84 | 85 | IPCThread(*this, thread, "news:s", 2, NEWSSCommandHandler); 86 | } 87 | 88 | } // namespace OS 89 | 90 | } // namespace HLE 91 | -------------------------------------------------------------------------------- /source/os_types.hpp: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include 4 | #include 5 | 6 | namespace HLE { 7 | 8 | namespace OS { 9 | 10 | // Type of a virtual address 11 | // TODO: Make this a strong typedef 12 | using VAddr = uint32_t; 13 | 14 | // Type of a physical address 15 | // TODO: Make this a strong typedef 16 | using PAddr = uint32_t; 17 | 18 | // TODO: Make this a strong typedef 19 | using ProcessId = uint32_t; 20 | 21 | // TODO: Make this a strong typedef 22 | using ThreadId = uint32_t; 23 | 24 | /** 25 | * Type used to identify objects by a number internally 26 | * This is needed so that the emulated CPU core can pass around object handles 27 | * without actually storing objects in emulated memory. 28 | */ 29 | struct Handle { 30 | uint32_t value; 31 | 32 | bool operator < (const Handle& other) const { 33 | return value < other.value; 34 | } 35 | 36 | bool operator == (const Handle& other) const { 37 | return value == other.value; 38 | } 39 | 40 | bool operator != (const Handle& other) const { 41 | return value != other.value; 42 | } 43 | }; 44 | 45 | const Handle HANDLE_INVALID = { 0 }; 46 | 47 | /** 48 | * Extension to the Handle type that stores additional debug information about 49 | * the handle. DebugHandle compares and hashes equal to the Handle it decays 50 | * to, so when it's used as a key into associative containers, regular Handles 51 | * are sufficient for lookup. 52 | */ 53 | struct DebugHandle : Handle { 54 | enum Source { 55 | Original, // The owning process created the underlying object 56 | Duplicated, // The owning process duplicated an existing handle 57 | FromIPC, // The owning process received the handle from another process via IPC 58 | Raw, // Obtained from emulated application, hence no valid debug information is attached 59 | }; 60 | 61 | struct DebugInfo { 62 | uint64_t created_time_stamp = 0; 63 | 64 | Source source = Raw; 65 | 66 | /// Header of the IPC request due to which the handle was transmitted (only valid if source is FromIPC) 67 | uint32_t ipc_command_header = 0; 68 | 69 | // TODO: For Duplicated, store the original Handle value 70 | } debug_info; 71 | 72 | DebugHandle() = default; 73 | 74 | DebugHandle(uint32_t value, DebugInfo debug_info) : Handle{value}, debug_info(debug_info) {} 75 | 76 | /// Create a DebugHandle from a plain handle, omitting the initialization of any debug information 77 | DebugHandle(const Handle& handle) : Handle(handle) {} 78 | 79 | // bool operator == (const DebugHandle& other) const { 80 | // return value == other.value; 81 | // } 82 | 83 | // bool operator == (const Handle& other) const { 84 | // return value == other.value; 85 | // } 86 | 87 | // bool operator != (const Handle& other) const { 88 | // return value != other.value; 89 | // } 90 | }; 91 | 92 | } // namespace OS 93 | 94 | } // namespace HLE 95 | 96 | namespace std { 97 | template<> 98 | struct hash { 99 | auto operator()(const HLE::OS::Handle& handle) const { 100 | return std::hash{}(handle.value); 101 | } 102 | }; 103 | template<> 104 | struct hash { 105 | auto operator()(const HLE::OS::DebugHandle& handle) const { 106 | return std::hash{}(handle); 107 | } 108 | }; 109 | } // namespace std 110 | -------------------------------------------------------------------------------- /source/processes/fs_common.hpp: -------------------------------------------------------------------------------- 1 | #include 2 | 3 | #include 4 | #include 5 | #include 6 | #include 7 | 8 | namespace HLE { 9 | 10 | namespace OS { 11 | class Thread; 12 | } 13 | namespace IPC { 14 | struct StaticBuffer; 15 | } 16 | namespace PXI { 17 | struct PXIBuffer; 18 | } 19 | 20 | // Produce error codes on invalid API usage instead of throwing exceptions. 21 | // Used for the test suite, but disabled by default since we expect games to 22 | // behave nicely more often than not. 23 | // TODO: Expose this through a Setting instead 24 | inline constexpr bool test_mode = true; 25 | 26 | struct Utf8PathType : std::string { 27 | std::filesystem::path to_std_path() const { 28 | return std::filesystem::path { static_cast(*this) }; 29 | } 30 | }; 31 | struct BinaryPathType : std::vector { }; 32 | 33 | /** 34 | * Helper class for unified path handling. 35 | * 36 | * Non-binary paths are automatically converted to Utf-8, with trailing \0 characters removed. 37 | */ 38 | class CommonPath { 39 | static constexpr uint32_t max_length = 256; // Unverified. TODO: What's the actual maximum? 40 | 41 | std::variant data; 42 | 43 | static CommonPath FromUtf16(std::u16string_view utf16_data); 44 | static CommonPath FromUtf16(std::basic_string_view utf16_data); 45 | 46 | CommonPath(decltype(data) raw) : data(raw) { 47 | } 48 | 49 | template 50 | CommonPath(uint32_t type, uint32_t num_bytes, ReadByte&& read); 51 | 52 | public: 53 | CommonPath(OS::Thread& thread, uint32_t type, uint32_t num_bytes, const IPC::StaticBuffer&); 54 | CommonPath(OS::Thread& thread, uint32_t type, uint32_t num_bytes, const PXI::PXIBuffer&); 55 | 56 | template 57 | auto Visit(Utf8Handler&& utf8_handler, BinaryHandler&& bin_handler) const { 58 | return std::visit( [&](const auto& arg) { 59 | if constexpr (std::is_invocable_v) { 60 | return std::forward(utf8_handler)(arg); 61 | } else { 62 | return std::forward(bin_handler)(arg); 63 | } 64 | }, 65 | data); 66 | } 67 | }; 68 | 69 | /** 70 | * Absolute path verified to be within some sandbox path. 71 | * 72 | * As long as the "path" member isn't modified, it's save to recursively delete 73 | * data from this path without harming the host filesystem 74 | */ 75 | struct ValidatedHostPath { 76 | std::filesystem::path path; 77 | 78 | operator const std::filesystem::path&() const { 79 | return path; 80 | } 81 | 82 | private: 83 | ValidatedHostPath(std::filesystem::path path) : path(std::move(path)) {} 84 | friend class PathValidator; 85 | }; 86 | 87 | class PathValidator { 88 | protected: 89 | struct ErrFileNotFound {}; 90 | struct ErrDirectoryNotFound {}; 91 | struct ErrFileAlreadyExists {}; 92 | struct ErrDirectoryAlreadyExists {}; 93 | 94 | ValidatedHostPath ValidateAndGetSandboxedTreePath(const Utf8PathType&) const; 95 | virtual std::filesystem::path GetSandboxHostRoot() const = 0; 96 | }; 97 | 98 | std::filesystem::path GetRootDataDirectory(Settings::Settings& settings); 99 | 100 | } // namespace HLE 101 | -------------------------------------------------------------------------------- /tools/embed_title/main.cpp: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | 4 | #include 5 | 6 | #include 7 | #include 8 | 9 | #include 10 | 11 | #include 12 | #include 13 | 14 | int main(int argc, char* argv[]) { 15 | std::string in_filename; 16 | std::string target_path; 17 | bool force_overwrite; 18 | 19 | { 20 | namespace bpo = boost::program_options; 21 | 22 | bpo::options_description desc; 23 | desc.add_options() 24 | ("help,h", "Print this help") 25 | ("input,i", bpo::value(&in_filename)->required(), "Path to input NCCH file") 26 | ("base,b", bpo::value(&target_path)->required(), "Path to target NAND tree") 27 | ("force,f", bpo::bool_switch(&force_overwrite)->default_value(false), "Force overwrite if any output path already exists"); 28 | 29 | try { 30 | auto positional_arguments = bpo::positional_options_description{}.add("input", -1); 31 | bpo::variables_map var_map; 32 | bpo::store(bpo::command_line_parser(argc, argv).options(desc).positional(positional_arguments).run(), 33 | var_map); 34 | if (var_map.count("help") ) { 35 | std::cout << desc << std::endl; 36 | return 0; 37 | } 38 | bpo::notify(var_map); 39 | } catch (bpo::error error) { 40 | std::cout << desc << std::endl; 41 | return 1; 42 | } 43 | } 44 | 45 | if (!boost::filesystem::exists(target_path)) { 46 | std::cout << "Target base directory does not exist!" << std::endl; 47 | return 1; 48 | } 49 | 50 | auto [header, exheader] = Meta::invoke([&]() { 51 | std::ifstream file(in_filename, std::ios_base::binary); 52 | file.exceptions(std::ifstream::badbit | std::ifstream::failbit | std::ifstream::eofbit); 53 | 54 | auto strbuf_it = std::istreambuf_iterator(file.rdbuf()); 55 | auto stream = FileFormat::MakeStreamInFromContainer(strbuf_it, decltype(strbuf_it){}); 56 | auto header = FileFormat::Load(stream); 57 | 58 | // NOTE: The given exheader_size only refers to the hashed exheader region rather than the full exheader size 59 | assert(header.exheader_size <= FileFormat::ExHeader::Tags::expected_serialized_size); 60 | 61 | auto exheader = FileFormat::Load(stream); 62 | 63 | return std::make_tuple(header, exheader); 64 | }); 65 | 66 | //exheader.application_title; 67 | std::string title(exheader.application_title.begin(), exheader.application_title.end()); 68 | title.resize(ranges::find(title, '\0') - ranges::begin(title)); 69 | if (title.empty()) 70 | title = "(no_title)"; 71 | uint32_t titleid_high = header.program_id >> 32; 72 | uint32_t titleid_low = static_cast(header.program_id); 73 | 74 | auto out_filename = boost::filesystem::path(target_path); 75 | out_filename += boost::filesystem::path(fmt::format("./{:08x}/{:08x}/content/", titleid_high, titleid_low)); 76 | boost::filesystem::create_directories(out_filename); 77 | out_filename += boost::filesystem::path("./00000000.cxi"); 78 | 79 | boost::filesystem::copy(in_filename, out_filename); 80 | 81 | std::cout << fmt::format("{:08x} {:08x} {} {}", titleid_high, titleid_low, title, out_filename.c_str()) << std::endl; 82 | } 83 | -------------------------------------------------------------------------------- /source/input.cpp: -------------------------------------------------------------------------------- 1 | #include "input.hpp" 2 | 3 | #include 4 | 5 | struct ButtonState { 6 | uint16_t storage; 7 | 8 | using Fields = v2::BitField::Fields; 9 | 10 | auto A() const { return Fields::MakeOn< 0, 1>(this); } 11 | auto B() const { return Fields::MakeOn< 1, 1>(this); } 12 | auto X() const { return Fields::MakeOn<10, 1>(this); } 13 | auto Y() const { return Fields::MakeOn<11, 1>(this); } 14 | auto L() const { return Fields::MakeOn< 9, 1>(this); } 15 | auto R() const { return Fields::MakeOn< 8, 1>(this); } 16 | auto Select() const { return Fields::MakeOn< 2, 1>(this); } 17 | auto Start() const { return Fields::MakeOn< 3, 1>(this); } 18 | auto DigiLeft() const { return Fields::MakeOn< 5, 1>(this); } 19 | auto DigiRight() const { return Fields::MakeOn< 4, 1>(this); } 20 | auto DigiUp() const { return Fields::MakeOn< 6, 1>(this); } 21 | auto DigiDown() const { return Fields::MakeOn< 7, 1>(this); } 22 | }; 23 | 24 | void InputSource::SetPressedA(bool pressed) { 25 | buttons = ButtonState { buttons }.A()(pressed).storage; 26 | } 27 | 28 | void InputSource::SetPressedB(bool pressed) { 29 | buttons = ButtonState { buttons }.B()(pressed).storage; 30 | } 31 | 32 | void InputSource::SetPressedX(bool pressed) { 33 | buttons = ButtonState { buttons }.X()(pressed).storage; 34 | } 35 | 36 | void InputSource::SetPressedY(bool pressed) { 37 | buttons = ButtonState { buttons }.Y()(pressed).storage; 38 | } 39 | 40 | void InputSource::SetPressedL(bool pressed) { 41 | buttons = ButtonState { buttons }.L()(pressed).storage; 42 | } 43 | 44 | void InputSource::SetPressedR(bool pressed) { 45 | buttons = ButtonState { buttons }.R()(pressed).storage; 46 | } 47 | 48 | void InputSource::SetPressedSelect(bool pressed) { 49 | buttons = ButtonState { buttons }.Select()(pressed).storage; 50 | } 51 | 52 | void InputSource::SetPressedStart(bool pressed) { 53 | buttons = ButtonState { buttons }.Start()(pressed).storage; 54 | } 55 | 56 | void InputSource::SetPressedDigiLeft(bool pressed) { 57 | buttons = ButtonState { buttons }.DigiLeft()(pressed).storage; 58 | } 59 | 60 | void InputSource::SetPressedDigiRight(bool pressed) { 61 | buttons = ButtonState { buttons }.DigiRight()(pressed).storage; 62 | } 63 | 64 | void InputSource::SetPressedDigiUp(bool pressed) { 65 | buttons = ButtonState { buttons }.DigiUp()(pressed).storage; 66 | } 67 | 68 | void InputSource::SetPressedDigiDown(bool pressed) { 69 | buttons = ButtonState { buttons }.DigiDown()(pressed).storage; 70 | } 71 | 72 | void InputSource::SetPressedHome(bool pressed) { 73 | home_button = pressed; 74 | } 75 | 76 | auto InputSource::GetTouchState() -> TouchState { 77 | std::lock_guard lock(touch_mutex); 78 | 79 | return touch; 80 | } 81 | 82 | void InputSource::SetTouch(float x, float y) { 83 | std::lock_guard lock(touch_mutex); 84 | 85 | touch.x = x; 86 | touch.y = y; 87 | touch.pressed = true; 88 | } 89 | 90 | void InputSource::EndTouch() { 91 | std::lock_guard lock(touch_mutex); 92 | 93 | touch.pressed = false; 94 | } 95 | 96 | auto InputSource::GetCirclePadState() -> CirclePadState { 97 | std::lock_guard lock(touch_mutex); 98 | 99 | return circle_pad; 100 | } 101 | 102 | void InputSource::SetCirclePad(float x, float y) { 103 | std::lock_guard lock(touch_mutex); 104 | 105 | circle_pad.x = x; 106 | circle_pad.y = y; 107 | } 108 | -------------------------------------------------------------------------------- /source/video_core/externals/nihstro/examples/assembler/cube/source/math.c: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | 4 | #include "math.h" 5 | 6 | void loadIdentity44(float* m) 7 | { 8 | if(!m)return; 9 | 10 | memset(m, 0x00, 16*4); 11 | m[0]=m[5]=m[10]=m[15]=1.0f; 12 | } 13 | 14 | void multMatrix44(float* m1, float* m2, float* m) //4x4 15 | { 16 | int i, j; 17 | for(i=0;i<4;i++)for(j=0;j<4;j++)m[i+j*4]=(m1[0+j*4]*m2[i+0*4])+(m1[1+j*4]*m2[i+1*4])+(m1[2+j*4]*m2[i+2*4])+(m1[3+j*4]*m2[i+3*4]); 18 | 19 | } 20 | 21 | void translateMatrix(float* tm, float x, float y, float z) 22 | { 23 | float rm[16], m[16]; 24 | 25 | loadIdentity44(rm); 26 | rm[3]=x; 27 | rm[7]=y; 28 | rm[11]=z; 29 | 30 | multMatrix44(tm,rm,m); 31 | memcpy(tm,m,16*sizeof(float)); 32 | } 33 | 34 | // 00 01 02 03 35 | // 04 05 06 07 36 | // 08 09 10 11 37 | // 12 13 14 15 38 | 39 | void rotateMatrixX(float* tm, float x, bool r) 40 | { 41 | float rm[16], m[16]; 42 | memset(rm, 0x00, 16*4); 43 | rm[0]=1.0f; 44 | rm[5]=cos(x); 45 | rm[6]=sin(x); 46 | rm[9]=-sin(x); 47 | rm[10]=cos(x); 48 | rm[15]=1.0f; 49 | if(!r)multMatrix44(tm,rm,m); 50 | else multMatrix44(rm,tm,m); 51 | memcpy(tm,m,16*sizeof(float)); 52 | } 53 | 54 | void rotateMatrixY(float* tm, float x, bool r) 55 | { 56 | float rm[16], m[16]; 57 | memset(rm, 0x00, 16*4); 58 | rm[0]=cos(x); 59 | rm[2]=sin(x); 60 | rm[5]=1.0f; 61 | rm[8]=-sin(x); 62 | rm[10]=cos(x); 63 | rm[15]=1.0f; 64 | if(!r)multMatrix44(tm,rm,m); 65 | else multMatrix44(rm,tm,m); 66 | memcpy(tm,m,16*sizeof(float)); 67 | } 68 | 69 | void rotateMatrixZ(float* tm, float x, bool r) 70 | { 71 | float rm[16], m[16]; 72 | memset(rm, 0x00, 16*4); 73 | rm[0]=cos(x); 74 | rm[1]=sin(x); 75 | rm[4]=-sin(x); 76 | rm[5]=cos(x); 77 | rm[10]=1.0f; 78 | rm[15]=1.0f; 79 | if(!r)multMatrix44(tm,rm,m); 80 | else multMatrix44(rm,tm,m); 81 | memcpy(tm,m,16*sizeof(float)); 82 | } 83 | 84 | void scaleMatrix(float* tm, float x, float y, float z) 85 | { 86 | tm[0]*=x; tm[4]*=x; tm[8]*=x; tm[12]*=x; 87 | tm[1]*=y; tm[5]*=y; tm[9]*=y; tm[13]*=y; 88 | tm[2]*=z; tm[6]*=z; tm[10]*=z; tm[14]*=z; 89 | } 90 | 91 | void initProjectionMatrix(float* m, float fovy, float aspect, float near, float far) 92 | { 93 | float top = near*tan(fovy/2); 94 | float right = (top*aspect); 95 | 96 | float mp[4*4]; 97 | 98 | mp[0x0] = near/right; 99 | mp[0x1] = 0.0f; 100 | mp[0x2] = 0.0f; 101 | mp[0x3] = 0.0f; 102 | 103 | mp[0x4] = 0.0f; 104 | mp[0x5] = near/top; 105 | mp[0x6] = 0.0f; 106 | mp[0x7] = 0.0f; 107 | 108 | mp[0x8] = 0.0f; 109 | mp[0x9] = 0.0f; 110 | mp[0xA] = -(far+near)/(far-near); 111 | mp[0xB] = -2.0f*(far*near)/(far-near); 112 | 113 | mp[0xC] = 0.0f; 114 | mp[0xD] = 0.0f; 115 | mp[0xE] = -1.0f; 116 | mp[0xF] = 0.0f; 117 | 118 | float mp2[4*4]; 119 | loadIdentity44(mp2); 120 | mp2[0xA]=0.5; 121 | mp2[0xB]=-0.5; 122 | 123 | multMatrix44(mp2, mp, m); 124 | } 125 | 126 | vect3Df_s getMatrixColumn(float* m, u8 i) 127 | { 128 | if(!m || i>=4)return vect3Df(0,0,0); 129 | return vect3Df(m[0+i*4],m[1+i*4],m[2+i*4]); 130 | } 131 | 132 | vect3Df_s getMatrixRow(float* m, u8 i) 133 | { 134 | if(!m || i>=4)return vect3Df(0,0,0); 135 | return vect3Df(m[i+0*4],m[i+1*4],m[i+2*4]); 136 | } 137 | 138 | vect4Df_s getMatrixColumn4(float* m, u8 i) 139 | { 140 | if(!m || i>=4)return vect4Df(0,0,0,0); 141 | return vect4Df(m[0+i*4],m[1+i*4],m[2+i*4],m[3+i*4]); 142 | } 143 | 144 | vect4Df_s getMatrixRow4(float* m, u8 i) 145 | { 146 | if(!m || i>=4)return vect4Df(0,0,0,0); 147 | return vect4Df(m[i+0*4],m[i+1*4],m[i+2*4],m[i+3*4]); 148 | } 149 | -------------------------------------------------------------------------------- /source/video_core/externals/nihstro/examples/assembler/cube_lighting/source/math.c: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | 4 | #include "math.h" 5 | 6 | void loadIdentity44(float* m) 7 | { 8 | if(!m)return; 9 | 10 | memset(m, 0x00, 16*4); 11 | m[0]=m[5]=m[10]=m[15]=1.0f; 12 | } 13 | 14 | void multMatrix44(float* m1, float* m2, float* m) //4x4 15 | { 16 | int i, j; 17 | for(i=0;i<4;i++)for(j=0;j<4;j++)m[i+j*4]=(m1[0+j*4]*m2[i+0*4])+(m1[1+j*4]*m2[i+1*4])+(m1[2+j*4]*m2[i+2*4])+(m1[3+j*4]*m2[i+3*4]); 18 | 19 | } 20 | 21 | void translateMatrix(float* tm, float x, float y, float z) 22 | { 23 | float rm[16], m[16]; 24 | 25 | loadIdentity44(rm); 26 | rm[3]=x; 27 | rm[7]=y; 28 | rm[11]=z; 29 | 30 | multMatrix44(tm,rm,m); 31 | memcpy(tm,m,16*sizeof(float)); 32 | } 33 | 34 | // 00 01 02 03 35 | // 04 05 06 07 36 | // 08 09 10 11 37 | // 12 13 14 15 38 | 39 | void rotateMatrixX(float* tm, float x, bool r) 40 | { 41 | float rm[16], m[16]; 42 | memset(rm, 0x00, 16*4); 43 | rm[0]=1.0f; 44 | rm[5]=cos(x); 45 | rm[6]=sin(x); 46 | rm[9]=-sin(x); 47 | rm[10]=cos(x); 48 | rm[15]=1.0f; 49 | if(!r)multMatrix44(tm,rm,m); 50 | else multMatrix44(rm,tm,m); 51 | memcpy(tm,m,16*sizeof(float)); 52 | } 53 | 54 | void rotateMatrixY(float* tm, float x, bool r) 55 | { 56 | float rm[16], m[16]; 57 | memset(rm, 0x00, 16*4); 58 | rm[0]=cos(x); 59 | rm[2]=sin(x); 60 | rm[5]=1.0f; 61 | rm[8]=-sin(x); 62 | rm[10]=cos(x); 63 | rm[15]=1.0f; 64 | if(!r)multMatrix44(tm,rm,m); 65 | else multMatrix44(rm,tm,m); 66 | memcpy(tm,m,16*sizeof(float)); 67 | } 68 | 69 | void rotateMatrixZ(float* tm, float x, bool r) 70 | { 71 | float rm[16], m[16]; 72 | memset(rm, 0x00, 16*4); 73 | rm[0]=cos(x); 74 | rm[1]=sin(x); 75 | rm[4]=-sin(x); 76 | rm[5]=cos(x); 77 | rm[10]=1.0f; 78 | rm[15]=1.0f; 79 | if(!r)multMatrix44(tm,rm,m); 80 | else multMatrix44(rm,tm,m); 81 | memcpy(tm,m,16*sizeof(float)); 82 | } 83 | 84 | void scaleMatrix(float* tm, float x, float y, float z) 85 | { 86 | tm[0]*=x; tm[4]*=x; tm[8]*=x; tm[12]*=x; 87 | tm[1]*=y; tm[5]*=y; tm[9]*=y; tm[13]*=y; 88 | tm[2]*=z; tm[6]*=z; tm[10]*=z; tm[14]*=z; 89 | } 90 | 91 | void initProjectionMatrix(float* m, float fovy, float aspect, float near, float far) 92 | { 93 | float top = near*tan(fovy/2); 94 | float right = (top*aspect); 95 | 96 | float mp[4*4]; 97 | 98 | mp[0x0] = near/right; 99 | mp[0x1] = 0.0f; 100 | mp[0x2] = 0.0f; 101 | mp[0x3] = 0.0f; 102 | 103 | mp[0x4] = 0.0f; 104 | mp[0x5] = near/top; 105 | mp[0x6] = 0.0f; 106 | mp[0x7] = 0.0f; 107 | 108 | mp[0x8] = 0.0f; 109 | mp[0x9] = 0.0f; 110 | mp[0xA] = -(far+near)/(far-near); 111 | mp[0xB] = -2.0f*(far*near)/(far-near); 112 | 113 | mp[0xC] = 0.0f; 114 | mp[0xD] = 0.0f; 115 | mp[0xE] = -1.0f; 116 | mp[0xF] = 0.0f; 117 | 118 | float mp2[4*4]; 119 | loadIdentity44(mp2); 120 | mp2[0xA]=0.5; 121 | mp2[0xB]=-0.5; 122 | 123 | multMatrix44(mp2, mp, m); 124 | } 125 | 126 | vect3Df_s getMatrixColumn(float* m, u8 i) 127 | { 128 | if(!m || i>=4)return vect3Df(0,0,0); 129 | return vect3Df(m[0+i*4],m[1+i*4],m[2+i*4]); 130 | } 131 | 132 | vect3Df_s getMatrixRow(float* m, u8 i) 133 | { 134 | if(!m || i>=4)return vect3Df(0,0,0); 135 | return vect3Df(m[i+0*4],m[i+1*4],m[i+2*4]); 136 | } 137 | 138 | vect4Df_s getMatrixColumn4(float* m, u8 i) 139 | { 140 | if(!m || i>=4)return vect4Df(0,0,0,0); 141 | return vect4Df(m[0+i*4],m[1+i*4],m[2+i*4],m[3+i*4]); 142 | } 143 | 144 | vect4Df_s getMatrixRow4(float* m, u8 i) 145 | { 146 | if(!m || i>=4)return vect4Df(0,0,0,0); 147 | return vect4Df(m[i+0*4],m[i+1*4],m[i+2*4],m[i+3*4]); 148 | } 149 | --------------------------------------------------------------------------------