mirror of
https://github.com/wheremyfoodat/Panda3DS.git
synced 2025-04-08 23:25:40 +12:00
Merge remote-tracking branch 'origin/GamingProcessingUnit' into dynapica
This commit is contained in:
commit
02d07f29d7
106 changed files with 23630 additions and 11117 deletions
14
.clang-format
Normal file
14
.clang-format
Normal file
|
@ -0,0 +1,14 @@
|
||||||
|
BasedOnStyle: Google
|
||||||
|
IndentWidth: 4
|
||||||
|
ColumnLimit: 150
|
||||||
|
AccessModifierOffset: -2
|
||||||
|
TabWidth: 4
|
||||||
|
NamespaceIndentation: All
|
||||||
|
UseTab: ForContinuationAndIndentation
|
||||||
|
AllowShortEnumsOnASingleLine: true
|
||||||
|
AllowShortCaseLabelsOnASingleLine: true
|
||||||
|
AllowShortFunctionsOnASingleLine: true
|
||||||
|
AllowShortIfStatementsOnASingleLine: true
|
||||||
|
Cpp11BracedListStyle: true
|
||||||
|
PackConstructorInitializers: BinPack
|
||||||
|
AlignAfterOpenBracket: BlockIndent
|
1
.gitignore
vendored
1
.gitignore
vendored
|
@ -8,6 +8,7 @@
|
||||||
|
|
||||||
#Clion Files
|
#Clion Files
|
||||||
.idea/
|
.idea/
|
||||||
|
cmake-build-*
|
||||||
|
|
||||||
Debug/
|
Debug/
|
||||||
Release/
|
Release/
|
||||||
|
|
|
@ -18,7 +18,6 @@ include_directories(${PROJECT_SOURCE_DIR}/include/kernel)
|
||||||
include_directories (${FMT_INCLUDE_DIR})
|
include_directories (${FMT_INCLUDE_DIR})
|
||||||
include_directories(third_party/boost/)
|
include_directories(third_party/boost/)
|
||||||
include_directories(third_party/elfio/)
|
include_directories(third_party/elfio/)
|
||||||
include_directories(third_party/gl3w/)
|
|
||||||
include_directories(third_party/imgui/)
|
include_directories(third_party/imgui/)
|
||||||
include_directories(third_party/dynarmic/src)
|
include_directories(third_party/dynarmic/src)
|
||||||
include_directories(third_party/cryptopp/)
|
include_directories(third_party/cryptopp/)
|
||||||
|
@ -32,6 +31,7 @@ add_compile_definitions(SDL_MAIN_HANDLED)
|
||||||
set(SDL_STATIC ON CACHE BOOL "" FORCE)
|
set(SDL_STATIC ON CACHE BOOL "" FORCE)
|
||||||
set(SDL_SHARED OFF CACHE BOOL "" FORCE)
|
set(SDL_SHARED OFF CACHE BOOL "" FORCE)
|
||||||
add_subdirectory(third_party/SDL2)
|
add_subdirectory(third_party/SDL2)
|
||||||
|
add_subdirectory(third_party/glad)
|
||||||
include_directories(${SDL2_INCLUDE_DIR})
|
include_directories(${SDL2_INCLUDE_DIR})
|
||||||
|
|
||||||
set(BOOST_ROOT "${CMAKE_SOURCE_DIR}/third_party/boost")
|
set(BOOST_ROOT "${CMAKE_SOURCE_DIR}/third_party/boost")
|
||||||
|
@ -42,7 +42,7 @@ add_compile_definitions(BOOST_NO_CXX98_FUNCTION_BASE) # Forbid Boost from using
|
||||||
add_library(boost INTERFACE)
|
add_library(boost INTERFACE)
|
||||||
target_include_directories(boost SYSTEM INTERFACE ${Boost_INCLUDE_DIR})
|
target_include_directories(boost SYSTEM INTERFACE ${Boost_INCLUDE_DIR})
|
||||||
|
|
||||||
set(CRYPTOPP_BUILD_TESTING OFF)
|
set(CRYPTOPP_BUILD_TESTING OFF)
|
||||||
add_subdirectory(third_party/cryptopp)
|
add_subdirectory(third_party/cryptopp)
|
||||||
|
|
||||||
# Check for x64
|
# Check for x64
|
||||||
|
@ -125,7 +125,9 @@ set(HEADER_FILES include/emulator.hpp include/helpers.hpp include/opengl.hpp inc
|
||||||
include/services/ldr_ro.hpp include/ipc.hpp include/services/act.hpp include/services/nfc.hpp
|
include/services/ldr_ro.hpp include/ipc.hpp include/services/act.hpp include/services/nfc.hpp
|
||||||
include/system_models.hpp include/services/dlp_srvr.hpp include/PICA/dynapica/pica_recs.hpp
|
include/system_models.hpp include/services/dlp_srvr.hpp include/PICA/dynapica/pica_recs.hpp
|
||||||
include/PICA/dynapica/x64_regs.hpp include/PICA/dynapica/vertex_loader_rec.hpp include/PICA/dynapica/shader_rec.hpp
|
include/PICA/dynapica/x64_regs.hpp include/PICA/dynapica/vertex_loader_rec.hpp include/PICA/dynapica/shader_rec.hpp
|
||||||
include/PICA/dynapica/shader_rec_emitter_x64.hpp include/PICA/pica_hash.hpp
|
include/PICA/dynapica/shader_rec_emitter_x64.hpp include/PICA/pica_hash.hpp include/result/result.hpp
|
||||||
|
include/result/result_common.hpp include/result/result_fs.hpp include/result/result_fnd.hpp
|
||||||
|
include/result/result_gsp.hpp include/result/result_kernel.hpp include/result/result_os.hpp
|
||||||
)
|
)
|
||||||
|
|
||||||
set(THIRD_PARTY_SOURCE_FILES third_party/imgui/imgui.cpp
|
set(THIRD_PARTY_SOURCE_FILES third_party/imgui/imgui.cpp
|
||||||
|
@ -133,7 +135,6 @@ set(THIRD_PARTY_SOURCE_FILES third_party/imgui/imgui.cpp
|
||||||
third_party/imgui/imgui_tables.cpp
|
third_party/imgui/imgui_tables.cpp
|
||||||
third_party/imgui/imgui_widgets.cpp
|
third_party/imgui/imgui_widgets.cpp
|
||||||
third_party/imgui/imgui_demo.cpp
|
third_party/imgui/imgui_demo.cpp
|
||||||
third_party/gl3w/gl3w.cpp
|
|
||||||
|
|
||||||
third_party/cityhash/cityhash.cpp
|
third_party/cityhash/cityhash.cpp
|
||||||
third_party/xxhash/xxhash.c
|
third_party/xxhash/xxhash.c
|
||||||
|
@ -154,4 +155,4 @@ ${PICA_SOURCE_FILES} ${RENDERER_GL_SOURCE_FILES} ${THIRD_PARTY_SOURCE_FILES} ${H
|
||||||
# Uncomment to enable LTO
|
# Uncomment to enable LTO
|
||||||
# set_target_properties(Alber PROPERTIES INTERPROCEDURAL_OPTIMIZATION TRUE)
|
# set_target_properties(Alber PROPERTIES INTERPROCEDURAL_OPTIMIZATION TRUE)
|
||||||
|
|
||||||
target_link_libraries(Alber PRIVATE dynarmic SDL2-static)
|
target_link_libraries(Alber PRIVATE dynarmic SDL2-static glad
|
||||||
|
|
Binary file not shown.
Before Width: | Height: | Size: 79 KiB After Width: | Height: | Size: 192 KiB |
Binary file not shown.
Before Width: | Height: | Size: 168 KiB After Width: | Height: | Size: 372 KiB |
Binary file not shown.
Before Width: | Height: | Size: 216 KiB After Width: | Height: | Size: 297 KiB |
|
@ -50,7 +50,7 @@ class GPU {
|
||||||
};
|
};
|
||||||
|
|
||||||
u64 getVertexShaderInputConfig() {
|
u64 getVertexShaderInputConfig() {
|
||||||
return u64(regs[PICAInternalRegs::VertexShaderInputCfgLow]) | (u64(regs[PICAInternalRegs::VertexShaderInputCfgHigh]) << 32);
|
return u64(regs[PICA::InternalRegs::VertexShaderInputCfgLow]) | (u64(regs[PICA::InternalRegs::VertexShaderInputCfgHigh]) << 32);
|
||||||
}
|
}
|
||||||
|
|
||||||
std::array<AttribInfo, maxAttribCount> attributeInfo; // Info for each of the 12 attributes
|
std::array<AttribInfo, maxAttribCount> attributeInfo; // Info for each of the 12 attributes
|
||||||
|
|
|
@ -1,129 +1,237 @@
|
||||||
#pragma once
|
#pragma once
|
||||||
|
#include "helpers.hpp"
|
||||||
|
|
||||||
namespace PICAInternalRegs {
|
namespace PICA {
|
||||||
enum : u32 {
|
namespace InternalRegs {
|
||||||
// Rasterizer registers
|
enum : u32 {
|
||||||
ViewportWidth = 0x41,
|
// Rasterizer registers
|
||||||
ViewportInvw = 0x42,
|
ViewportWidth = 0x41,
|
||||||
ViewportHeight = 0x43,
|
ViewportInvw = 0x42,
|
||||||
ViewportInvh = 0x44,
|
ViewportHeight = 0x43,
|
||||||
|
ViewportInvh = 0x44,
|
||||||
|
|
||||||
DepthScale = 0x4D,
|
DepthScale = 0x4D,
|
||||||
DepthOffset = 0x4E,
|
DepthOffset = 0x4E,
|
||||||
ShaderOutputCount = 0x4F,
|
ShaderOutputCount = 0x4F,
|
||||||
|
ShaderOutmap0 = 0x50,
|
||||||
|
|
||||||
DepthmapEnable = 0x6D,
|
DepthmapEnable = 0x6D,
|
||||||
TexUnitCfg = 0x80,
|
|
||||||
|
|
||||||
// Framebuffer registers
|
// Texture registers
|
||||||
ColourOperation = 0x100,
|
TexUnitCfg = 0x80,
|
||||||
BlendFunc = 0x101,
|
Tex0BorderColor = 0x81,
|
||||||
BlendColour = 0x103,
|
Tex1BorderColor = 0x91,
|
||||||
AlphaTestConfig = 0x104,
|
Tex2BorderColor = 0x99,
|
||||||
DepthAndColorMask = 0x107,
|
TexEnv0Source = 0xC0,
|
||||||
DepthBufferFormat = 0x116,
|
TexEnv1Source = 0xC8,
|
||||||
ColourBufferFormat = 0x117,
|
TexEnv2Source = 0xD0,
|
||||||
DepthBufferLoc = 0x11C,
|
TexEnv3Source = 0xD8,
|
||||||
ColourBufferLoc = 0x11D,
|
TexEnvUpdateBuffer = 0xE0,
|
||||||
FramebufferSize = 0x11E,
|
TexEnv4Source = 0xF0,
|
||||||
|
TexEnv5Source = 0xF8,
|
||||||
|
TexEnvBufferColor = 0xFD,
|
||||||
|
|
||||||
// Geometry pipeline registers
|
// Framebuffer registers
|
||||||
VertexAttribLoc = 0x200,
|
ColourOperation = 0x100,
|
||||||
AttribFormatLow = 0x201,
|
BlendFunc = 0x101,
|
||||||
AttribFormatHigh = 0x202,
|
BlendColour = 0x103,
|
||||||
IndexBufferConfig = 0x227,
|
AlphaTestConfig = 0x104,
|
||||||
VertexCountReg = 0x228,
|
DepthAndColorMask = 0x107,
|
||||||
VertexOffsetReg = 0x22A,
|
DepthBufferFormat = 0x116,
|
||||||
SignalDrawArrays = 0x22E,
|
ColourBufferFormat = 0x117,
|
||||||
SignalDrawElements = 0x22F,
|
DepthBufferLoc = 0x11C,
|
||||||
|
ColourBufferLoc = 0x11D,
|
||||||
|
FramebufferSize = 0x11E,
|
||||||
|
|
||||||
Attrib0Offset = 0x203,
|
// Geometry pipeline registers
|
||||||
Attrib1Offset = 0x206,
|
VertexAttribLoc = 0x200,
|
||||||
Attrib2Offset = 0x209,
|
AttribFormatLow = 0x201,
|
||||||
Attrib3Offset = 0x20C,
|
AttribFormatHigh = 0x202,
|
||||||
Attrib4Offset = 0x20F,
|
IndexBufferConfig = 0x227,
|
||||||
Attrib5Offset = 0x212,
|
VertexCountReg = 0x228,
|
||||||
Attrib6Offset = 0x215,
|
VertexOffsetReg = 0x22A,
|
||||||
Attrib7Offset = 0x218,
|
SignalDrawArrays = 0x22E,
|
||||||
Attrib8Offset = 0x21B,
|
SignalDrawElements = 0x22F,
|
||||||
Attrib9Offset = 0x21E,
|
|
||||||
Attrib10Offset = 0x221,
|
|
||||||
Attrib11Offset = 0x224,
|
|
||||||
|
|
||||||
Attrib0Config2 = 0x205,
|
Attrib0Offset = 0x203,
|
||||||
Attrib1Config2 = 0x208,
|
Attrib1Offset = 0x206,
|
||||||
Attrib2Config2 = 0x20B,
|
Attrib2Offset = 0x209,
|
||||||
Attrib3Config2 = 0x20E,
|
Attrib3Offset = 0x20C,
|
||||||
Attrib4Config2 = 0x211,
|
Attrib4Offset = 0x20F,
|
||||||
Attrib5Config2 = 0x214,
|
Attrib5Offset = 0x212,
|
||||||
Attrib6Config2 = 0x217,
|
Attrib6Offset = 0x215,
|
||||||
Attrib7Config2 = 0x21A,
|
Attrib7Offset = 0x218,
|
||||||
Attrib8Config2 = 0x21D,
|
Attrib8Offset = 0x21B,
|
||||||
Attrib9Config2 = 0x220,
|
Attrib9Offset = 0x21E,
|
||||||
Attrib10Config2 = 0x223,
|
Attrib10Offset = 0x221,
|
||||||
Attrib11Config2 = 0x226,
|
Attrib11Offset = 0x224,
|
||||||
|
|
||||||
AttribInfoStart = Attrib0Offset,
|
Attrib0Config2 = 0x205,
|
||||||
AttribInfoEnd = Attrib11Config2,
|
Attrib1Config2 = 0x208,
|
||||||
|
Attrib2Config2 = 0x20B,
|
||||||
|
Attrib3Config2 = 0x20E,
|
||||||
|
Attrib4Config2 = 0x211,
|
||||||
|
Attrib5Config2 = 0x214,
|
||||||
|
Attrib6Config2 = 0x217,
|
||||||
|
Attrib7Config2 = 0x21A,
|
||||||
|
Attrib8Config2 = 0x21D,
|
||||||
|
Attrib9Config2 = 0x220,
|
||||||
|
Attrib10Config2 = 0x223,
|
||||||
|
Attrib11Config2 = 0x226,
|
||||||
|
|
||||||
// Fixed attribute registers
|
AttribInfoStart = Attrib0Offset,
|
||||||
FixedAttribIndex = 0x232,
|
AttribInfoEnd = Attrib11Config2,
|
||||||
FixedAttribData0 = 0x233,
|
|
||||||
FixedAttribData1 = 0x234,
|
|
||||||
FixedAttribData2 = 0x235,
|
|
||||||
|
|
||||||
// Command processor registers
|
|
||||||
CmdBufSize0 = 0x238,
|
|
||||||
CmdBufSize1 = 0x239,
|
|
||||||
CmdBufAddr0 = 0x23A,
|
|
||||||
CmdBufAddr1 = 0x23B,
|
|
||||||
CmdBufTrigger0 = 0x23C,
|
|
||||||
CmdBufTrigger1 = 0x23D,
|
|
||||||
|
|
||||||
PrimitiveConfig = 0x25E,
|
// Fixed attribute registers
|
||||||
PrimitiveRestart = 0x25F,
|
FixedAttribIndex = 0x232,
|
||||||
|
FixedAttribData0 = 0x233,
|
||||||
|
FixedAttribData1 = 0x234,
|
||||||
|
FixedAttribData2 = 0x235,
|
||||||
|
|
||||||
// Vertex shader registers
|
// Command processor registers
|
||||||
VertexShaderAttrNum = 0x242,
|
CmdBufSize0 = 0x238,
|
||||||
VertexBoolUniform = 0x2B0,
|
CmdBufSize1 = 0x239,
|
||||||
VertexIntUniform0 = 0x2B1,
|
CmdBufAddr0 = 0x23A,
|
||||||
VertexIntUniform1 = 0x2B2,
|
CmdBufAddr1 = 0x23B,
|
||||||
VertexIntUniform2 = 0x2B3,
|
CmdBufTrigger0 = 0x23C,
|
||||||
VertexIntUniform3 = 0x2B4,
|
CmdBufTrigger1 = 0x23D,
|
||||||
|
|
||||||
VertexShaderEntrypoint = 0x2BA,
|
PrimitiveConfig = 0x25E,
|
||||||
VertexShaderTransferEnd = 0x2BF,
|
PrimitiveRestart = 0x25F,
|
||||||
VertexFloatUniformIndex = 0x2C0,
|
|
||||||
VertexFloatUniformData0 = 0x2C1,
|
|
||||||
VertexFloatUniformData1 = 0x2C2,
|
|
||||||
VertexFloatUniformData2 = 0x2C3,
|
|
||||||
VertexFloatUniformData3 = 0x2C4,
|
|
||||||
VertexFloatUniformData4 = 0x2C5,
|
|
||||||
VertexFloatUniformData5 = 0x2C6,
|
|
||||||
VertexFloatUniformData6 = 0x2C7,
|
|
||||||
VertexFloatUniformData7 = 0x2C8,
|
|
||||||
|
|
||||||
VertexShaderInputBufferCfg = 0x2B9,
|
// Vertex shader registers
|
||||||
VertexShaderInputCfgLow = 0x2BB,
|
VertexShaderAttrNum = 0x242,
|
||||||
VertexShaderInputCfgHigh = 0x2BC,
|
VertexBoolUniform = 0x2B0,
|
||||||
|
VertexIntUniform0 = 0x2B1,
|
||||||
|
VertexIntUniform1 = 0x2B2,
|
||||||
|
VertexIntUniform2 = 0x2B3,
|
||||||
|
VertexIntUniform3 = 0x2B4,
|
||||||
|
|
||||||
VertexShaderTransferIndex = 0x2CB,
|
VertexShaderEntrypoint = 0x2BA,
|
||||||
VertexShaderData0 = 0x2CC,
|
VertexShaderTransferEnd = 0x2BF,
|
||||||
VertexShaderData1 = 0x2CD,
|
VertexFloatUniformIndex = 0x2C0,
|
||||||
VertexShaderData2 = 0x2CE,
|
VertexFloatUniformData0 = 0x2C1,
|
||||||
VertexShaderData3 = 0x2CF,
|
VertexFloatUniformData1 = 0x2C2,
|
||||||
VertexShaderData4 = 0x2D0,
|
VertexFloatUniformData2 = 0x2C3,
|
||||||
VertexShaderData5 = 0x2D1,
|
VertexFloatUniformData3 = 0x2C4,
|
||||||
VertexShaderData6 = 0x2D2,
|
VertexFloatUniformData4 = 0x2C5,
|
||||||
VertexShaderData7 = 0x2D3,
|
VertexFloatUniformData5 = 0x2C6,
|
||||||
VertexShaderOpDescriptorIndex = 0x2D5,
|
VertexFloatUniformData6 = 0x2C7,
|
||||||
VertexShaderOpDescriptorData0 = 0x2D6,
|
VertexFloatUniformData7 = 0x2C8,
|
||||||
VertexShaderOpDescriptorData1 = 0x2D7,
|
|
||||||
VertexShaderOpDescriptorData2 = 0x2D8,
|
VertexShaderInputBufferCfg = 0x2B9,
|
||||||
VertexShaderOpDescriptorData3 = 0x2D9,
|
VertexShaderInputCfgLow = 0x2BB,
|
||||||
VertexShaderOpDescriptorData4 = 0x2DA,
|
VertexShaderInputCfgHigh = 0x2BC,
|
||||||
VertexShaderOpDescriptorData5 = 0x2DB,
|
|
||||||
VertexShaderOpDescriptorData6 = 0x2DC,
|
VertexShaderTransferIndex = 0x2CB,
|
||||||
VertexShaderOpDescriptorData7 = 0x2DD,
|
VertexShaderData0 = 0x2CC,
|
||||||
|
VertexShaderData1 = 0x2CD,
|
||||||
|
VertexShaderData2 = 0x2CE,
|
||||||
|
VertexShaderData3 = 0x2CF,
|
||||||
|
VertexShaderData4 = 0x2D0,
|
||||||
|
VertexShaderData5 = 0x2D1,
|
||||||
|
VertexShaderData6 = 0x2D2,
|
||||||
|
VertexShaderData7 = 0x2D3,
|
||||||
|
VertexShaderOpDescriptorIndex = 0x2D5,
|
||||||
|
VertexShaderOpDescriptorData0 = 0x2D6,
|
||||||
|
VertexShaderOpDescriptorData1 = 0x2D7,
|
||||||
|
VertexShaderOpDescriptorData2 = 0x2D8,
|
||||||
|
VertexShaderOpDescriptorData3 = 0x2D9,
|
||||||
|
VertexShaderOpDescriptorData4 = 0x2DA,
|
||||||
|
VertexShaderOpDescriptorData5 = 0x2DB,
|
||||||
|
VertexShaderOpDescriptorData6 = 0x2DC,
|
||||||
|
VertexShaderOpDescriptorData7 = 0x2DD,
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
enum class TextureFmt : u32 {
|
||||||
|
RGBA8 = 0x0,
|
||||||
|
RGB8 = 0x1,
|
||||||
|
RGBA5551 = 0x2,
|
||||||
|
RGB565 = 0x3,
|
||||||
|
RGBA4 = 0x4,
|
||||||
|
IA8 = 0x5,
|
||||||
|
RG8 = 0x6,
|
||||||
|
I8 = 0x7,
|
||||||
|
A8 = 0x8,
|
||||||
|
IA4 = 0x9,
|
||||||
|
I4 = 0xA,
|
||||||
|
A4 = 0xB,
|
||||||
|
ETC1 = 0xC,
|
||||||
|
ETC1A4 = 0xD,
|
||||||
};
|
};
|
||||||
}
|
|
||||||
|
enum class ColorFmt : u32 {
|
||||||
|
RGBA8 = 0x0,
|
||||||
|
RGB8 = 0x1,
|
||||||
|
RGBA5551 = 0x2,
|
||||||
|
RGB565 = 0x3,
|
||||||
|
RGBA4 = 0x4,
|
||||||
|
};
|
||||||
|
|
||||||
|
enum class DepthFmt : u32 {
|
||||||
|
Depth16 = 0,
|
||||||
|
Unknown1 = 1, // Technically selectable, but function is unknown
|
||||||
|
Depth24 = 2,
|
||||||
|
Depth24Stencil8 = 3,
|
||||||
|
};
|
||||||
|
|
||||||
|
// Returns the string representation of a texture format
|
||||||
|
inline constexpr const char* textureFormatToString(TextureFmt fmt) {
|
||||||
|
switch (fmt) {
|
||||||
|
case TextureFmt::RGBA8: return "RGBA8";
|
||||||
|
case TextureFmt::RGB8: return "RGB8";
|
||||||
|
case TextureFmt::RGBA5551: return "RGBA5551";
|
||||||
|
case TextureFmt::RGB565: return "RGB565";
|
||||||
|
case TextureFmt::RGBA4: return "RGBA4";
|
||||||
|
case TextureFmt::IA8: return "IA8";
|
||||||
|
case TextureFmt::RG8: return "RG8";
|
||||||
|
case TextureFmt::I8: return "I8";
|
||||||
|
case TextureFmt::A8: return "A8";
|
||||||
|
case TextureFmt::IA4: return "IA4";
|
||||||
|
case TextureFmt::I4: return "I4";
|
||||||
|
case TextureFmt::A4: return "A4";
|
||||||
|
case TextureFmt::ETC1: return "ETC1";
|
||||||
|
case TextureFmt::ETC1A4: return "ETC1A4";
|
||||||
|
default: return "Unknown";
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
inline constexpr const char* textureFormatToString(ColorFmt fmt) {
|
||||||
|
return textureFormatToString(static_cast<TextureFmt>(fmt));
|
||||||
|
}
|
||||||
|
|
||||||
|
inline constexpr bool hasStencil(DepthFmt format) { return format == PICA::DepthFmt::Depth24Stencil8; }
|
||||||
|
|
||||||
|
// Size occupied by each pixel in bytes
|
||||||
|
|
||||||
|
// All formats are 16BPP except for RGBA8 (32BPP) and BGR8 (24BPP)
|
||||||
|
inline constexpr usize sizePerPixel(TextureFmt format) {
|
||||||
|
switch (format) {
|
||||||
|
case TextureFmt::RGB8: return 3;
|
||||||
|
case TextureFmt::RGBA8: return 4;
|
||||||
|
default: return 2;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
inline constexpr usize sizePerPixel(ColorFmt format) {
|
||||||
|
return sizePerPixel(static_cast<TextureFmt>(format));
|
||||||
|
}
|
||||||
|
|
||||||
|
inline constexpr usize sizePerPixel(DepthFmt format) {
|
||||||
|
switch (format) {
|
||||||
|
case DepthFmt::Depth16: return 2;
|
||||||
|
case DepthFmt::Depth24: return 3;
|
||||||
|
case DepthFmt::Depth24Stencil8: return 4;
|
||||||
|
default: return 1; // Invalid format
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
enum class PrimType : u32 {
|
||||||
|
TriangleList = 0,
|
||||||
|
TriangleStrip = 1,
|
||||||
|
TriangleFan = 2,
|
||||||
|
GeometryPrimitive = 3,
|
||||||
|
};
|
||||||
|
|
||||||
|
} // namespace PICA
|
|
@ -1,5 +1,7 @@
|
||||||
#pragma once
|
#pragma once
|
||||||
|
|
||||||
|
#include <span>
|
||||||
|
|
||||||
#include "dynarmic/interface/A32/a32.h"
|
#include "dynarmic/interface/A32/a32.h"
|
||||||
#include "dynarmic/interface/A32/config.h"
|
#include "dynarmic/interface/A32/config.h"
|
||||||
#include "dynarmic/interface/exclusive_monitor.h"
|
#include "dynarmic/interface/exclusive_monitor.h"
|
||||||
|
@ -132,17 +134,11 @@ public:
|
||||||
return jit->Regs()[index];
|
return jit->Regs()[index];
|
||||||
}
|
}
|
||||||
|
|
||||||
std::array<u32, 16>& regs() {
|
std::span<u32, 16> regs() { return jit->Regs(); }
|
||||||
return jit->Regs();
|
|
||||||
}
|
|
||||||
|
|
||||||
// Get reference to array of FPRs. This array consists of the FPRs as single precision values
|
// Get reference to array of FPRs. This array consists of the FPRs as single precision values
|
||||||
// Hence why its base type is u32
|
// Hence why its base type is u32
|
||||||
// Note: Dynarmic keeps 64 VFP registers as VFPv3 extends the VFP register set to 64 registers.
|
std::span<u32, 32> fprs() { return std::span(jit->ExtRegs()).first<32>(); }
|
||||||
// However the 3DS ARM11 is an ARMv6k processor with VFPv2, so only the first 32 registers are actually used
|
|
||||||
std::array<u32, 64>& fprs() {
|
|
||||||
return jit->ExtRegs();
|
|
||||||
}
|
|
||||||
|
|
||||||
void setCPSR(u32 value) {
|
void setCPSR(u32 value) {
|
||||||
jit->SetCpsr(value);
|
jit->SetCpsr(value);
|
||||||
|
|
|
@ -3,6 +3,7 @@
|
||||||
#include <filesystem>
|
#include <filesystem>
|
||||||
#include <fstream>
|
#include <fstream>
|
||||||
#include <SDL.h>
|
#include <SDL.h>
|
||||||
|
#include <glad/gl.h>
|
||||||
|
|
||||||
#include "cpu.hpp"
|
#include "cpu.hpp"
|
||||||
#include "io_file.hpp"
|
#include "io_file.hpp"
|
||||||
|
@ -35,7 +36,7 @@ class Emulator {
|
||||||
|
|
||||||
public:
|
public:
|
||||||
Emulator() : kernel(cpu, memory, gpu), cpu(memory, kernel), gpu(memory), memory(cpu.getTicksRef()) {
|
Emulator() : kernel(cpu, memory, gpu), cpu(memory, kernel), gpu(memory), memory(cpu.getTicksRef()) {
|
||||||
if (SDL_Init(SDL_INIT_VIDEO) < 0) {
|
if (SDL_Init(SDL_INIT_VIDEO | SDL_INIT_EVENTS) < 0) {
|
||||||
Helpers::panic("Failed to initialize SDL2");
|
Helpers::panic("Failed to initialize SDL2");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -45,8 +46,21 @@ public:
|
||||||
SDL_GL_SetAttribute(SDL_GL_CONTEXT_MAJOR_VERSION, 4);
|
SDL_GL_SetAttribute(SDL_GL_CONTEXT_MAJOR_VERSION, 4);
|
||||||
SDL_GL_SetAttribute(SDL_GL_CONTEXT_MINOR_VERSION, 1);
|
SDL_GL_SetAttribute(SDL_GL_CONTEXT_MINOR_VERSION, 1);
|
||||||
window = SDL_CreateWindow("Alber", SDL_WINDOWPOS_CENTERED, SDL_WINDOWPOS_CENTERED, width, height, SDL_WINDOW_OPENGL);
|
window = SDL_CreateWindow("Alber", SDL_WINDOWPOS_CENTERED, SDL_WINDOWPOS_CENTERED, width, height, SDL_WINDOW_OPENGL);
|
||||||
|
|
||||||
|
if (window == nullptr) {
|
||||||
|
Helpers::panic("Window creation failed: %s", SDL_GetError());
|
||||||
|
}
|
||||||
|
|
||||||
glContext = SDL_GL_CreateContext(window);
|
glContext = SDL_GL_CreateContext(window);
|
||||||
|
|
||||||
|
if (glContext == nullptr) {
|
||||||
|
Helpers::panic("OpenGL context creation failed: %s", SDL_GetError());
|
||||||
|
}
|
||||||
|
|
||||||
|
if(!gladLoadGL(reinterpret_cast<GLADloadfunc>(SDL_GL_GetProcAddress))) {
|
||||||
|
Helpers::panic("OpenGL init failed: %s", SDL_GetError());
|
||||||
|
}
|
||||||
|
|
||||||
reset();
|
reset();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -10,6 +10,9 @@
|
||||||
#include "helpers.hpp"
|
#include "helpers.hpp"
|
||||||
#include "memory.hpp"
|
#include "memory.hpp"
|
||||||
#include "result.hpp"
|
#include "result.hpp"
|
||||||
|
#include "result/result.hpp"
|
||||||
|
|
||||||
|
using Result::HorizonResult;
|
||||||
|
|
||||||
namespace PathType {
|
namespace PathType {
|
||||||
enum : u32 {
|
enum : u32 {
|
||||||
|
@ -97,7 +100,7 @@ struct FileSession {
|
||||||
u32 priority = 0; // TODO: What does this even do
|
u32 priority = 0; // TODO: What does this even do
|
||||||
bool isOpen;
|
bool isOpen;
|
||||||
|
|
||||||
FileSession(ArchiveBase* archive, const FSPath& filePath, const FSPath& archivePath, FILE* fd, bool isOpen = true) :
|
FileSession(ArchiveBase* archive, const FSPath& filePath, const FSPath& archivePath, FILE* fd, bool isOpen = true) :
|
||||||
archive(archive), path(filePath), archivePath(archivePath), fd(fd), isOpen(isOpen), priority(0) {}
|
archive(archive), path(filePath), archivePath(archivePath), fd(fd), isOpen(isOpen), priority(0) {}
|
||||||
|
|
||||||
// For cloning a file session
|
// For cloning a file session
|
||||||
|
@ -128,16 +131,6 @@ struct DirectorySession {
|
||||||
// Otherwise the fd of the opened file is returned (or nullptr if the opened file doesn't require one)
|
// Otherwise the fd of the opened file is returned (or nullptr if the opened file doesn't require one)
|
||||||
using FileDescriptor = std::optional<FILE*>;
|
using FileDescriptor = std::optional<FILE*>;
|
||||||
|
|
||||||
enum class FSResult : u32 {
|
|
||||||
Success = 0,
|
|
||||||
AlreadyExists = 0x82044BE,
|
|
||||||
FileTooLarge = 0x86044D2,
|
|
||||||
FileNotFound = 0xC8804470,
|
|
||||||
NotFoundInvalid = 0xC8A04478, // Also a not found error code used here and there in the FS module.
|
|
||||||
NotFormatted = 0xC8A04554, // Trying to access an archive that needs formatting and has not been formatted
|
|
||||||
UnexpectedFileOrDir = 0xE0C04702
|
|
||||||
};
|
|
||||||
|
|
||||||
class ArchiveBase {
|
class ArchiveBase {
|
||||||
public:
|
public:
|
||||||
struct FormatInfo {
|
struct FormatInfo {
|
||||||
|
@ -149,7 +142,7 @@ public:
|
||||||
|
|
||||||
protected:
|
protected:
|
||||||
using Handle = u32;
|
using Handle = u32;
|
||||||
|
|
||||||
static constexpr FileDescriptor NoFile = nullptr;
|
static constexpr FileDescriptor NoFile = nullptr;
|
||||||
static constexpr FileDescriptor FileError = std::nullopt;
|
static constexpr FileDescriptor FileError = std::nullopt;
|
||||||
Memory& mem;
|
Memory& mem;
|
||||||
|
@ -176,12 +169,12 @@ protected:
|
||||||
// If the path string doesn't begin with / then that means it's accessing outside the FS root, which is invalid & unsafe
|
// If the path string doesn't begin with / then that means it's accessing outside the FS root, which is invalid & unsafe
|
||||||
if (pathString[0] != Char('/')) return false;
|
if (pathString[0] != Char('/')) return false;
|
||||||
|
|
||||||
// Counts how many folders sans the root our file is nested under.
|
// Counts how many folders sans the root our file is nested under.
|
||||||
// If it's < 0 at any point of parsing, then the path is unsafe and tries to crawl outside our file sandbox.
|
// If it's < 0 at any point of parsing, then the path is unsafe and tries to crawl outside our file sandbox.
|
||||||
// If it's 0 then this is the FS root.
|
// If it's 0 then this is the FS root.
|
||||||
// If it's > 0 then we're in a subdirectory of the root.
|
// If it's > 0 then we're in a subdirectory of the root.
|
||||||
int level = 0;
|
int level = 0;
|
||||||
|
|
||||||
// Split the string on / characters and see how many of the substrings are ".."
|
// Split the string on / characters and see how many of the substrings are ".."
|
||||||
size_t pos = 0;
|
size_t pos = 0;
|
||||||
while ((pos = pathString.find(Char('/'))) != String::npos) {
|
while ((pos = pathString.find(Char('/'))) != String::npos) {
|
||||||
|
@ -202,27 +195,27 @@ protected:
|
||||||
public:
|
public:
|
||||||
virtual std::string name() = 0;
|
virtual std::string name() = 0;
|
||||||
virtual u64 getFreeBytes() = 0;
|
virtual u64 getFreeBytes() = 0;
|
||||||
virtual FSResult createFile(const FSPath& path, u64 size) = 0;
|
virtual HorizonResult createFile(const FSPath& path, u64 size) = 0;
|
||||||
virtual FSResult deleteFile(const FSPath& path) = 0;
|
virtual HorizonResult deleteFile(const FSPath& path) = 0;
|
||||||
|
|
||||||
virtual Rust::Result<FormatInfo, FSResult> getFormatInfo(const FSPath& path) {
|
virtual Rust::Result<FormatInfo, HorizonResult> getFormatInfo(const FSPath& path) {
|
||||||
Helpers::panic("Unimplemented GetFormatInfo for %s archive", name().c_str());
|
Helpers::panic("Unimplemented GetFormatInfo for %s archive", name().c_str());
|
||||||
// Return a dummy struct just to avoid the UB of not returning anything, even if we panic
|
// Return a dummy struct just to avoid the UB of not returning anything, even if we panic
|
||||||
return Ok(FormatInfo{ .size = 0, .numOfDirectories = 0, .numOfFiles = 0, .duplicateData = false });
|
return Ok(FormatInfo{ .size = 0, .numOfDirectories = 0, .numOfFiles = 0, .duplicateData = false });
|
||||||
}
|
}
|
||||||
|
|
||||||
virtual FSResult createDirectory(const FSPath& path) {
|
virtual HorizonResult createDirectory(const FSPath& path) {
|
||||||
Helpers::panic("Unimplemented CreateDirectory for %s archive", name().c_str());
|
Helpers::panic("Unimplemented CreateDirectory for %s archive", name().c_str());
|
||||||
return FSResult::AlreadyExists;
|
return Result::FS::AlreadyExists;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Returns nullopt if opening the file failed, otherwise returns a file descriptor to it (nullptr if none is needed)
|
// Returns nullopt if opening the file failed, otherwise returns a file descriptor to it (nullptr if none is needed)
|
||||||
virtual FileDescriptor openFile(const FSPath& path, const FilePerms& perms) = 0;
|
virtual FileDescriptor openFile(const FSPath& path, const FilePerms& perms) = 0;
|
||||||
virtual Rust::Result<ArchiveBase*, FSResult> openArchive(const FSPath& path) = 0;
|
virtual Rust::Result<ArchiveBase*, HorizonResult> openArchive(const FSPath& path) = 0;
|
||||||
|
|
||||||
virtual Rust::Result<DirectorySession, FSResult> openDirectory(const FSPath& path) {
|
virtual Rust::Result<DirectorySession, HorizonResult> openDirectory(const FSPath& path) {
|
||||||
Helpers::panic("Unimplemented OpenDirectory for %s archive", name().c_str());
|
Helpers::panic("Unimplemented OpenDirectory for %s archive", name().c_str());
|
||||||
return Err(FSResult::FileNotFound);
|
return Err(Result::FS::FileNotFoundAlt);
|
||||||
}
|
}
|
||||||
|
|
||||||
virtual void format(const FSPath& path, const FormatInfo& info) {
|
virtual void format(const FSPath& path, const FormatInfo& info) {
|
||||||
|
@ -232,6 +225,6 @@ public:
|
||||||
// Read size bytes from a file starting at offset "offset" into a certain buffer in memory
|
// Read size bytes from a file starting at offset "offset" into a certain buffer in memory
|
||||||
// Returns the number of bytes read, or nullopt if the read failed
|
// Returns the number of bytes read, or nullopt if the read failed
|
||||||
virtual std::optional<u32> readFile(FileSession* file, u64 offset, u32 size, u32 dataPointer) = 0;
|
virtual std::optional<u32> readFile(FileSession* file, u64 offset, u32 size, u32 dataPointer) = 0;
|
||||||
|
|
||||||
ArchiveBase(Memory& mem) : mem(mem) {}
|
ArchiveBase(Memory& mem) : mem(mem) {}
|
||||||
};
|
};
|
|
@ -9,11 +9,11 @@ public:
|
||||||
u64 getFreeBytes() override { Helpers::panic("ExtSaveData::GetFreeBytes unimplemented"); return 0; }
|
u64 getFreeBytes() override { Helpers::panic("ExtSaveData::GetFreeBytes unimplemented"); return 0; }
|
||||||
std::string name() override { return "ExtSaveData::" + backingFolder; }
|
std::string name() override { return "ExtSaveData::" + backingFolder; }
|
||||||
|
|
||||||
FSResult createFile(const FSPath& path, u64 size) override;
|
HorizonResult createFile(const FSPath& path, u64 size) override;
|
||||||
FSResult deleteFile(const FSPath& path) override;
|
HorizonResult deleteFile(const FSPath& path) override;
|
||||||
|
|
||||||
Rust::Result<ArchiveBase*, FSResult> openArchive(const FSPath& path) override;
|
Rust::Result<ArchiveBase*, HorizonResult> openArchive(const FSPath& path) override;
|
||||||
Rust::Result<DirectorySession, FSResult> openDirectory(const FSPath& path) override;
|
Rust::Result<DirectorySession, HorizonResult> openDirectory(const FSPath& path) override;
|
||||||
FileDescriptor openFile(const FSPath& path, const FilePerms& perms) override;
|
FileDescriptor openFile(const FSPath& path, const FilePerms& perms) override;
|
||||||
std::optional<u32> readFile(FileSession* file, u64 offset, u32 size, u32 dataPointer) override;
|
std::optional<u32> readFile(FileSession* file, u64 offset, u32 size, u32 dataPointer) override;
|
||||||
|
|
||||||
|
|
|
@ -8,10 +8,10 @@ public:
|
||||||
u64 getFreeBytes() override { Helpers::panic("NCCH::GetFreeBytes unimplemented"); return 0; }
|
u64 getFreeBytes() override { Helpers::panic("NCCH::GetFreeBytes unimplemented"); return 0; }
|
||||||
std::string name() override { return "NCCH"; }
|
std::string name() override { return "NCCH"; }
|
||||||
|
|
||||||
FSResult createFile(const FSPath& path, u64 size) override;
|
HorizonResult createFile(const FSPath& path, u64 size) override;
|
||||||
FSResult deleteFile(const FSPath& path) override;
|
HorizonResult deleteFile(const FSPath& path) override;
|
||||||
|
|
||||||
Rust::Result<ArchiveBase*, FSResult> openArchive(const FSPath& path) override;
|
Rust::Result<ArchiveBase*, HorizonResult> openArchive(const FSPath& path) override;
|
||||||
FileDescriptor openFile(const FSPath& path, const FilePerms& perms) override;
|
FileDescriptor openFile(const FSPath& path, const FilePerms& perms) override;
|
||||||
std::optional<u32> readFile(FileSession* file, u64 offset, u32 size, u32 dataPointer) override;
|
std::optional<u32> readFile(FileSession* file, u64 offset, u32 size, u32 dataPointer) override;
|
||||||
|
|
||||||
|
|
|
@ -8,17 +8,17 @@ public:
|
||||||
u64 getFreeBytes() override { Helpers::panic("SaveData::GetFreeBytes unimplemented"); return 0; }
|
u64 getFreeBytes() override { Helpers::panic("SaveData::GetFreeBytes unimplemented"); return 0; }
|
||||||
std::string name() override { return "SaveData"; }
|
std::string name() override { return "SaveData"; }
|
||||||
|
|
||||||
FSResult createDirectory(const FSPath& path) override;
|
HorizonResult createDirectory(const FSPath& path) override;
|
||||||
FSResult createFile(const FSPath& path, u64 size) override;
|
HorizonResult createFile(const FSPath& path, u64 size) override;
|
||||||
FSResult deleteFile(const FSPath& path) override;
|
HorizonResult deleteFile(const FSPath& path) override;
|
||||||
|
|
||||||
Rust::Result<ArchiveBase*, FSResult> openArchive(const FSPath& path) override;
|
Rust::Result<ArchiveBase*, HorizonResult> openArchive(const FSPath& path) override;
|
||||||
Rust::Result<DirectorySession, FSResult> openDirectory(const FSPath& path) override;
|
Rust::Result<DirectorySession, HorizonResult> openDirectory(const FSPath& path) override;
|
||||||
FileDescriptor openFile(const FSPath& path, const FilePerms& perms) override;
|
FileDescriptor openFile(const FSPath& path, const FilePerms& perms) override;
|
||||||
std::optional<u32> readFile(FileSession* file, u64 offset, u32 size, u32 dataPointer) override;
|
std::optional<u32> readFile(FileSession* file, u64 offset, u32 size, u32 dataPointer) override;
|
||||||
|
|
||||||
void format(const FSPath& path, const FormatInfo& info) override;
|
void format(const FSPath& path, const FormatInfo& info) override;
|
||||||
Rust::Result<FormatInfo, FSResult> getFormatInfo(const FSPath& path) override;
|
Rust::Result<FormatInfo, HorizonResult> getFormatInfo(const FSPath& path) override;
|
||||||
|
|
||||||
std::filesystem::path getFormatInfoPath() {
|
std::filesystem::path getFormatInfoPath() {
|
||||||
return IOFile::getAppData() / "FormatInfo" / "SaveData.format";
|
return IOFile::getAppData() / "FormatInfo" / "SaveData.format";
|
||||||
|
|
|
@ -1,5 +1,8 @@
|
||||||
#pragma once
|
#pragma once
|
||||||
#include "archive_base.hpp"
|
#include "archive_base.hpp"
|
||||||
|
#include "result/result.hpp"
|
||||||
|
|
||||||
|
using Result::HorizonResult;
|
||||||
|
|
||||||
class SDMCArchive : public ArchiveBase {
|
class SDMCArchive : public ArchiveBase {
|
||||||
public:
|
public:
|
||||||
|
@ -8,10 +11,10 @@ public:
|
||||||
u64 getFreeBytes() override { Helpers::panic("SDMC::GetFreeBytes unimplemented"); return 0; }
|
u64 getFreeBytes() override { Helpers::panic("SDMC::GetFreeBytes unimplemented"); return 0; }
|
||||||
std::string name() override { return "SDMC"; }
|
std::string name() override { return "SDMC"; }
|
||||||
|
|
||||||
FSResult createFile(const FSPath& path, u64 size) override;
|
HorizonResult createFile(const FSPath& path, u64 size) override;
|
||||||
FSResult deleteFile(const FSPath& path) override;
|
HorizonResult deleteFile(const FSPath& path) override;
|
||||||
|
|
||||||
Rust::Result<ArchiveBase*, FSResult> openArchive(const FSPath& path) override;
|
Rust::Result<ArchiveBase*, HorizonResult> openArchive(const FSPath& path) override;
|
||||||
FileDescriptor openFile(const FSPath& path, const FilePerms& perms) override;
|
FileDescriptor openFile(const FSPath& path, const FilePerms& perms) override;
|
||||||
std::optional<u32> readFile(FileSession* file, u64 offset, u32 size, u32 dataPointer) override;
|
std::optional<u32> readFile(FileSession* file, u64 offset, u32 size, u32 dataPointer) override;
|
||||||
};
|
};
|
|
@ -8,10 +8,10 @@ public:
|
||||||
u64 getFreeBytes() override { return 0; }
|
u64 getFreeBytes() override { return 0; }
|
||||||
std::string name() override { return "SelfNCCH"; }
|
std::string name() override { return "SelfNCCH"; }
|
||||||
|
|
||||||
FSResult createFile(const FSPath& path, u64 size) override;
|
HorizonResult createFile(const FSPath& path, u64 size) override;
|
||||||
FSResult deleteFile(const FSPath& path) override;
|
HorizonResult deleteFile(const FSPath& path) override;
|
||||||
|
|
||||||
Rust::Result<ArchiveBase*, FSResult> openArchive(const FSPath& path) override;
|
Rust::Result<ArchiveBase*, HorizonResult> openArchive(const FSPath& path) override;
|
||||||
FileDescriptor openFile(const FSPath& path, const FilePerms& perms) override;
|
FileDescriptor openFile(const FSPath& path, const FilePerms& perms) override;
|
||||||
std::optional<u32> readFile(FileSession* file, u64 offset, u32 size, u32 dataPointer) override;
|
std::optional<u32> readFile(FileSession* file, u64 offset, u32 size, u32 dataPointer) override;
|
||||||
|
|
||||||
|
|
|
@ -1,14 +1,23 @@
|
||||||
#pragma once
|
#pragma once
|
||||||
#include <cstdarg>
|
|
||||||
#include <climits>
|
#include <climits>
|
||||||
|
#include <cstdarg>
|
||||||
|
#include <cstdint>
|
||||||
#include <fstream>
|
#include <fstream>
|
||||||
#include <iostream>
|
#include <iostream>
|
||||||
#include <iterator>
|
#include <iterator>
|
||||||
#include <type_traits>
|
#include <type_traits>
|
||||||
#include <utility>
|
#include <utility>
|
||||||
#include <vector>
|
#include <vector>
|
||||||
|
|
||||||
#include "termcolor.hpp"
|
#include "termcolor.hpp"
|
||||||
|
|
||||||
|
// We have to detect and special-case AppleClang at the moment since its C++20 support is finicky and doesn't quite support std::bit_cast
|
||||||
|
#if defined(__clang__) && defined(__apple_build_version__)
|
||||||
|
#define HELPERS_APPLE_CLANG
|
||||||
|
#else
|
||||||
|
#include <bit>
|
||||||
|
#endif
|
||||||
|
|
||||||
using u8 = std::uint8_t;
|
using u8 = std::uint8_t;
|
||||||
using u16 = std::uint16_t;
|
using u16 = std::uint16_t;
|
||||||
using u32 = std::uint32_t;
|
using u32 = std::uint32_t;
|
||||||
|
@ -22,78 +31,74 @@ using s32 = std::int32_t;
|
||||||
using s64 = std::int64_t;
|
using s64 = std::int64_t;
|
||||||
|
|
||||||
namespace Helpers {
|
namespace Helpers {
|
||||||
[[noreturn]] static void panic(const char* fmt, ...) {
|
[[noreturn]] static void panic(const char* fmt, ...) {
|
||||||
std::va_list args;
|
std::va_list args;
|
||||||
va_start(args, fmt);
|
va_start(args, fmt);
|
||||||
std::cout << termcolor::on_red << "[FATAL] ";
|
std::cout << termcolor::on_red << "[FATAL] ";
|
||||||
std::vprintf (fmt, args);
|
std::vprintf(fmt, args);
|
||||||
std::cout << termcolor::reset << "\n";
|
std::cout << termcolor::reset << "\n";
|
||||||
va_end(args);
|
va_end(args);
|
||||||
|
|
||||||
exit(1);
|
exit(1);
|
||||||
}
|
}
|
||||||
|
|
||||||
static void warn(const char* fmt, ...) {
|
static void warn(const char* fmt, ...) {
|
||||||
std::va_list args;
|
std::va_list args;
|
||||||
va_start(args, fmt);
|
va_start(args, fmt);
|
||||||
std::cout << termcolor::on_red << "[Warning] ";
|
std::cout << termcolor::on_red << "[Warning] ";
|
||||||
std::vprintf (fmt, args);
|
std::vprintf(fmt, args);
|
||||||
std::cout << termcolor::reset << "\n";
|
std::cout << termcolor::reset << "\n";
|
||||||
va_end(args);
|
va_end(args);
|
||||||
}
|
}
|
||||||
|
|
||||||
static std::vector <u8> loadROM(std::string directory) {
|
static std::vector<u8> loadROM(std::string directory) {
|
||||||
std::ifstream file (directory, std::ios::binary);
|
std::ifstream file(directory, std::ios::binary);
|
||||||
if (file.fail())
|
if (file.fail()) panic("Couldn't read %s", directory.c_str());
|
||||||
panic("Couldn't read %s", directory.c_str());
|
|
||||||
|
|
||||||
std::vector<u8> ROM;
|
std::vector<u8> ROM;
|
||||||
|
|
||||||
file.unsetf(std::ios::skipws);
|
file.unsetf(std::ios::skipws);
|
||||||
ROM.insert(ROM.begin(),
|
ROM.insert(ROM.begin(), std::istream_iterator<uint8_t>(file), std::istream_iterator<uint8_t>());
|
||||||
std::istream_iterator<uint8_t>(file),
|
|
||||||
std::istream_iterator<uint8_t>());
|
|
||||||
|
|
||||||
file.close();
|
file.close();
|
||||||
|
|
||||||
printf ("%s loaded successfully\n", directory.c_str());
|
printf("%s loaded successfully\n", directory.c_str());
|
||||||
return ROM;
|
return ROM;
|
||||||
}
|
}
|
||||||
|
|
||||||
static constexpr bool buildingInDebugMode() {
|
static constexpr bool buildingInDebugMode() {
|
||||||
#ifdef NDEBUG
|
#ifdef NDEBUG
|
||||||
return false;
|
return false;
|
||||||
#endif
|
#endif
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
return true;
|
static void debug_printf(const char* fmt, ...) {
|
||||||
}
|
if constexpr (buildingInDebugMode()) {
|
||||||
|
std::va_list args;
|
||||||
static void debug_printf (const char* fmt, ...) {
|
va_start(args, fmt);
|
||||||
if constexpr (buildingInDebugMode()) {
|
std::vprintf(fmt, args);
|
||||||
std::va_list args;
|
va_end(args);
|
||||||
va_start(args, fmt);
|
}
|
||||||
std::vprintf (fmt, args);
|
}
|
||||||
va_end(args);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Sign extend an arbitrary-size value to 32 bits
|
/// Sign extend an arbitrary-size value to 32 bits
|
||||||
static constexpr u32 inline signExtend32 (u32 value, u32 startingSize) {
|
static constexpr u32 inline signExtend32(u32 value, u32 startingSize) {
|
||||||
auto temp = (s32) value;
|
auto temp = (s32)value;
|
||||||
auto bitsToShift = 32 - startingSize;
|
auto bitsToShift = 32 - startingSize;
|
||||||
return (u32) (temp << bitsToShift >> bitsToShift);
|
return (u32)(temp << bitsToShift >> bitsToShift);
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Sign extend an arbitrary-size value to 16 bits
|
/// Sign extend an arbitrary-size value to 16 bits
|
||||||
static constexpr u16 signExtend16 (u16 value, u32 startingSize) {
|
static constexpr u16 signExtend16(u16 value, u32 startingSize) {
|
||||||
auto temp = (s16) value;
|
auto temp = (s16)value;
|
||||||
auto bitsToShift = 16 - startingSize;
|
auto bitsToShift = 16 - startingSize;
|
||||||
return (u16) (temp << bitsToShift >> bitsToShift);
|
return (u16)(temp << bitsToShift >> bitsToShift);
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Create a mask with `count` number of one bits.
|
/// Create a mask with `count` number of one bits.
|
||||||
template<typename T, usize count>
|
template <typename T, usize count>
|
||||||
static constexpr T ones () {
|
static constexpr T ones() {
|
||||||
constexpr usize bitsize = CHAR_BIT * sizeof(T);
|
constexpr usize bitsize = CHAR_BIT * sizeof(T);
|
||||||
static_assert(count <= bitsize, "count larger than bitsize of T");
|
static_assert(count <= bitsize, "count larger than bitsize of T");
|
||||||
|
|
||||||
|
@ -104,74 +109,74 @@ namespace Helpers {
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Extract bits from an integer-type
|
/// Extract bits from an integer-type
|
||||||
template<usize offset, typename T>
|
template <usize offset, typename T>
|
||||||
static constexpr T getBit (T value) {
|
static constexpr T getBit(T value) {
|
||||||
return (value >> offset) & T(1);
|
return (value >> offset) & T(1);
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Extract bits from an integer-type
|
/// Extract bits from an integer-type
|
||||||
template<usize offset, usize bits, typename T>
|
template <usize offset, usize bits, typename T>
|
||||||
static constexpr T getBits (T value) {
|
static constexpr T getBits(T value) {
|
||||||
return (value >> offset) & ones<T, bits>();
|
return (value >> offset) & ones<T, bits>();
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Check if a bit "bit" of value is set
|
/// Check if a bit "bit" of value is set
|
||||||
static constexpr bool isBitSet (u32 value, int bit) {
|
static constexpr bool isBitSet(u32 value, int bit) { return (value >> bit) & 1; }
|
||||||
return (value >> bit) & 1;
|
|
||||||
}
|
|
||||||
|
|
||||||
/// rotate number right
|
/// rotate number right
|
||||||
template <typename T>
|
template <typename T>
|
||||||
static constexpr T rotr (T value, int bits) {
|
static constexpr T rotr(T value, int bits) {
|
||||||
constexpr auto bitWidth = sizeof(T) * 8;
|
constexpr auto bitWidth = sizeof(T) * 8;
|
||||||
bits &= bitWidth - 1;
|
bits &= bitWidth - 1;
|
||||||
return (value >> bits) | (value << (bitWidth - bits));
|
return (value >> bits) | (value << (bitWidth - bits));
|
||||||
}
|
}
|
||||||
|
|
||||||
// rotate number left
|
// rotate number left
|
||||||
template <typename T>
|
template <typename T>
|
||||||
static constexpr T rotl (T value, int bits) {
|
static constexpr T rotl(T value, int bits) {
|
||||||
constexpr auto bitWidth = sizeof(T) * 8;
|
constexpr auto bitWidth = sizeof(T) * 8;
|
||||||
bits &= bitWidth - 1;
|
bits &= bitWidth - 1;
|
||||||
return (value << bits) | (value >> (bitWidth - bits));
|
return (value << bits) | (value >> (bitWidth - bits));
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Used to make the compiler evaluate beeg loops at compile time for the tablegen
|
/// Used to make the compiler evaluate beeg loops at compile time for the tablegen
|
||||||
template <typename T, T Begin, class Func, T ...Is>
|
template <typename T, T Begin, class Func, T... Is>
|
||||||
static constexpr void static_for_impl( Func&& f, std::integer_sequence<T, Is...> ) {
|
static constexpr void static_for_impl(Func&& f, std::integer_sequence<T, Is...>) {
|
||||||
( f( std::integral_constant<T, Begin + Is>{ } ),... );
|
(f(std::integral_constant<T, Begin + Is>{}), ...);
|
||||||
}
|
}
|
||||||
|
|
||||||
template <typename T, T Begin, T End, class Func>
|
template <typename T, T Begin, T End, class Func>
|
||||||
static constexpr void static_for(Func&& f) {
|
static constexpr void static_for(Func&& f) {
|
||||||
static_for_impl<T, Begin>( std::forward<Func>(f), std::make_integer_sequence<T, End - Begin>{ } );
|
static_for_impl<T, Begin>(std::forward<Func>(f), std::make_integer_sequence<T, End - Begin>{});
|
||||||
}
|
}
|
||||||
|
|
||||||
// For values < 0x99
|
// For values < 0x99
|
||||||
static constexpr inline u8 incBCDByte(u8 value) {
|
static constexpr inline u8 incBCDByte(u8 value) { return ((value & 0xf) == 0x9) ? value + 7 : value + 1; }
|
||||||
return ((value & 0xf) == 0x9) ? value + 7 : value + 1;
|
|
||||||
}
|
#ifdef HELPERS_APPLE_CLANG
|
||||||
};
|
template <class To, class From>
|
||||||
|
constexpr To bit_cast(const From& from) noexcept {
|
||||||
|
return *reinterpret_cast<const To*>(&from);
|
||||||
|
}
|
||||||
|
#else
|
||||||
|
template <class To, class From>
|
||||||
|
constexpr To bit_cast(const From& from) noexcept {
|
||||||
|
return std::bit_cast<To, From>(from);
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
}; // namespace Helpers
|
||||||
|
|
||||||
// UDLs for memory size values
|
// UDLs for memory size values
|
||||||
constexpr size_t operator""_KB(unsigned long long int x) {
|
constexpr size_t operator""_KB(unsigned long long int x) { return 1024ULL * x; }
|
||||||
return 1024ULL * x;
|
constexpr size_t operator""_MB(unsigned long long int x) { return 1024_KB * x; }
|
||||||
}
|
constexpr size_t operator""_GB(unsigned long long int x) { return 1024_MB * x; }
|
||||||
|
|
||||||
constexpr size_t operator""_MB(unsigned long long int x) {
|
|
||||||
return 1024_KB * x;
|
|
||||||
}
|
|
||||||
|
|
||||||
constexpr size_t operator""_GB(unsigned long long int x) {
|
|
||||||
return 1024_MB * x;
|
|
||||||
}
|
|
||||||
|
|
||||||
// useful macros
|
// useful macros
|
||||||
// likely/unlikely
|
// likely/unlikely
|
||||||
#ifdef __GNUC__
|
#ifdef __GNUC__
|
||||||
#define likely(x) __builtin_expect((x),1)
|
#define likely(x) __builtin_expect((x), 1)
|
||||||
#define unlikely(x) __builtin_expect((x),0)
|
#define unlikely(x) __builtin_expect((x), 0)
|
||||||
#else
|
#else
|
||||||
#define likely(x) (x)
|
#define likely(x) (x)
|
||||||
#define unlikely(x) (x)
|
#define unlikely(x) (x)
|
||||||
#endif
|
#endif
|
||||||
|
|
|
@ -2,10 +2,12 @@
|
||||||
#include <array>
|
#include <array>
|
||||||
#include <cassert>
|
#include <cassert>
|
||||||
#include <limits>
|
#include <limits>
|
||||||
|
#include <span>
|
||||||
#include <string>
|
#include <string>
|
||||||
#include <vector>
|
#include <vector>
|
||||||
#include "kernel_types.hpp"
|
|
||||||
#include "helpers.hpp"
|
#include "helpers.hpp"
|
||||||
|
#include "kernel_types.hpp"
|
||||||
#include "logger.hpp"
|
#include "logger.hpp"
|
||||||
#include "memory.hpp"
|
#include "memory.hpp"
|
||||||
#include "resource_limits.hpp"
|
#include "resource_limits.hpp"
|
||||||
|
@ -14,7 +16,7 @@
|
||||||
class CPU;
|
class CPU;
|
||||||
|
|
||||||
class Kernel {
|
class Kernel {
|
||||||
std::array<u32, 16>& regs;
|
std::span<u32, 16> regs;
|
||||||
CPU& cpu;
|
CPU& cpu;
|
||||||
Memory& mem;
|
Memory& mem;
|
||||||
|
|
||||||
|
|
|
@ -4,33 +4,7 @@
|
||||||
#include "fs/archive_base.hpp"
|
#include "fs/archive_base.hpp"
|
||||||
#include "handles.hpp"
|
#include "handles.hpp"
|
||||||
#include "helpers.hpp"
|
#include "helpers.hpp"
|
||||||
|
#include "result/result.hpp"
|
||||||
namespace SVCResult {
|
|
||||||
enum : u32 {
|
|
||||||
Success = 0,
|
|
||||||
Failure = 0xFFFFFFFF,
|
|
||||||
ObjectNotFound = 0xD88007FA,
|
|
||||||
|
|
||||||
// Different calls return a different value for these ones
|
|
||||||
InvalidEnumValue = 0xD8E007ED,
|
|
||||||
InvalidEnumValueAlt = 0xD8E093ED,
|
|
||||||
BadHandle = 0xD8E007F7,
|
|
||||||
BadHandleAlt = 0xD9001BF7,
|
|
||||||
|
|
||||||
InvalidCombination = 0xE0E01BEE, // Used for invalid memory permission combinations
|
|
||||||
UnalignedAddr = 0xE0E01BF1,
|
|
||||||
UnalignedSize = 0xE0E01BF2,
|
|
||||||
|
|
||||||
BadThreadPriority = 0xE0E01BFD,
|
|
||||||
PortNameTooLong = 0xE0E0181E,
|
|
||||||
|
|
||||||
// Returned when a thread stops waiting due to timing out
|
|
||||||
Timeout = 0x9401BFE,
|
|
||||||
|
|
||||||
// Returned when a thread releases a mutex it does not own
|
|
||||||
InvalidMutexRelease = 0xD8E0041F
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
enum class KernelObjectType : u8 {
|
enum class KernelObjectType : u8 {
|
||||||
AddressArbiter, Archive, Directory, File, MemoryBlock, Process, ResourceLimit, Session, Dummy,
|
AddressArbiter, Archive, Directory, File, MemoryBlock, Process, ResourceLimit, Session, Dummy,
|
||||||
|
@ -125,7 +99,7 @@ struct Thread {
|
||||||
ThreadStatus status;
|
ThreadStatus status;
|
||||||
Handle handle; // OS handle for this thread
|
Handle handle; // OS handle for this thread
|
||||||
int index; // Index of the thread. 0 for the first thread, 1 for the second, and so on
|
int index; // Index of the thread. 0 for the first thread, 1 for the second, and so on
|
||||||
|
|
||||||
// The waiting address for threads that are waiting on an AddressArbiter
|
// The waiting address for threads that are waiting on an AddressArbiter
|
||||||
u32 waitingAddress;
|
u32 waitingAddress;
|
||||||
|
|
||||||
|
|
|
@ -33,7 +33,10 @@ namespace VirtualAddrs {
|
||||||
|
|
||||||
NormalHeapStart = 0x08000000,
|
NormalHeapStart = 0x08000000,
|
||||||
LinearHeapStartOld = 0x14000000, // If kernel version < 0x22C
|
LinearHeapStartOld = 0x14000000, // If kernel version < 0x22C
|
||||||
|
LinearHeapEndOld = 0x1C000000,
|
||||||
|
|
||||||
LinearHeapStartNew = 0x30000000,
|
LinearHeapStartNew = 0x30000000,
|
||||||
|
LinearHeapEndNew = 0x40000000,
|
||||||
|
|
||||||
// Start of TLS for first thread. Next thread's storage will be at TLSBase + 0x1000, and so on
|
// Start of TLS for first thread. Next thread's storage will be at TLSBase + 0x1000, and so on
|
||||||
TLSBase = 0xFF400000,
|
TLSBase = 0xFF400000,
|
||||||
|
|
|
@ -29,7 +29,20 @@
|
||||||
#include <type_traits>
|
#include <type_traits>
|
||||||
#include <utility>
|
#include <utility>
|
||||||
|
|
||||||
#include "gl3w.h"
|
#include <glad/gl.h>
|
||||||
|
|
||||||
|
// Check if we have C++20. If yes, we can add C++20 std::span support
|
||||||
|
#ifdef _MSVC_LANG // MSVC does not properly define __cplusplus without a compiler flag...
|
||||||
|
#if _MSVC_LANG >= 202002L
|
||||||
|
#define OPENGL_HAVE_CPP20
|
||||||
|
#endif
|
||||||
|
#elif __cplusplus >= 202002L
|
||||||
|
#define OPENGL_HAVE_CPP20
|
||||||
|
#endif // MSVC_LANG
|
||||||
|
|
||||||
|
#ifdef OPENGL_HAVE_CPP20
|
||||||
|
#include <span>
|
||||||
|
#endif
|
||||||
|
|
||||||
// Uncomment the following define if you want GL objects to automatically free themselves when their lifetime ends
|
// Uncomment the following define if you want GL objects to automatically free themselves when their lifetime ends
|
||||||
// #define OPENGL_DESTRUCTORS
|
// #define OPENGL_DESTRUCTORS
|
||||||
|
@ -390,17 +403,30 @@ namespace OpenGL {
|
||||||
void free() { glDeleteBuffers(1, &m_handle); }
|
void free() { glDeleteBuffers(1, &m_handle); }
|
||||||
|
|
||||||
// Reallocates the buffer on every call. Prefer the sub version if possible.
|
// Reallocates the buffer on every call. Prefer the sub version if possible.
|
||||||
template <typename VertType>
|
template <typename VertType>
|
||||||
void bufferVerts(VertType* vertices, int vertCount, GLenum usage = GL_DYNAMIC_DRAW) {
|
void bufferVerts(VertType* vertices, int vertCount, GLenum usage = GL_DYNAMIC_DRAW) {
|
||||||
glBufferData(GL_ARRAY_BUFFER, sizeof(VertType) * vertCount, vertices, usage);
|
glBufferData(GL_ARRAY_BUFFER, sizeof(VertType) * vertCount, vertices, usage);
|
||||||
}
|
}
|
||||||
|
|
||||||
// Only use if you used createFixedSize
|
// Only use if you used createFixedSize
|
||||||
template <typename VertType>
|
template <typename VertType>
|
||||||
void bufferVertsSub(VertType* vertices, int vertCount, GLintptr offset = 0) {
|
void bufferVertsSub(VertType* vertices, int vertCount, GLintptr offset = 0) {
|
||||||
glBufferSubData(GL_ARRAY_BUFFER, offset, sizeof(VertType) * vertCount, vertices);
|
glBufferSubData(GL_ARRAY_BUFFER, offset, sizeof(VertType) * vertCount, vertices);
|
||||||
}
|
}
|
||||||
};
|
|
||||||
|
// If C++20 is available, add overloads that take std::span instead of raw pointers
|
||||||
|
#ifdef OPENGL_HAVE_CPP20
|
||||||
|
template <typename VertType>
|
||||||
|
void bufferVerts(std::span<const VertType> vertices, GLenum usage = GL_DYNAMIC_DRAW) {
|
||||||
|
glBufferData(GL_ARRAY_BUFFER, sizeof(VertType) * vertices.size(), vertices.data(), usage);
|
||||||
|
}
|
||||||
|
|
||||||
|
template <typename VertType>
|
||||||
|
void bufferVertsSub(std::span<const VertType> vertices, GLintptr offset = 0) {
|
||||||
|
glBufferSubData(GL_ARRAY_BUFFER, offset, sizeof(VertType) * vertices.size(), vertices.data());
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
};
|
||||||
|
|
||||||
enum DepthFunc {
|
enum DepthFunc {
|
||||||
Never = GL_NEVER, // Depth test never passes
|
Never = GL_NEVER, // Depth test never passes
|
||||||
|
@ -498,8 +524,6 @@ namespace OpenGL {
|
||||||
static GLint getProgram() { return get<GLint>(GL_CURRENT_PROGRAM); }
|
static GLint getProgram() { return get<GLint>(GL_CURRENT_PROGRAM); }
|
||||||
static bool scissorEnabled() { return isEnabled(GL_SCISSOR_TEST); }
|
static bool scissorEnabled() { return isEnabled(GL_SCISSOR_TEST); }
|
||||||
|
|
||||||
static bool versionSupported(int major, int minor) { return gl3wIsSupported(major, minor); }
|
|
||||||
|
|
||||||
[[nodiscard]] static GLint uniformLocation(GLuint program, const char* name) {
|
[[nodiscard]] static GLint uniformLocation(GLuint program, const char* name) {
|
||||||
return glGetUniformLocation(program, name);
|
return glGetUniformLocation(program, name);
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,10 +1,14 @@
|
||||||
#pragma once
|
#pragma once
|
||||||
#include <array>
|
#include <array>
|
||||||
|
#include <span>
|
||||||
|
|
||||||
|
#include "PICA/float_types.hpp"
|
||||||
#include "helpers.hpp"
|
#include "helpers.hpp"
|
||||||
#include "logger.hpp"
|
#include "logger.hpp"
|
||||||
#include "opengl.hpp"
|
#include "opengl.hpp"
|
||||||
#include "surface_cache.hpp"
|
#include "surface_cache.hpp"
|
||||||
#include "textures.hpp"
|
#include "textures.hpp"
|
||||||
|
#include "PICA/regs.hpp"
|
||||||
|
|
||||||
// More circular dependencies!
|
// More circular dependencies!
|
||||||
class GPU;
|
class GPU;
|
||||||
|
@ -12,7 +16,11 @@ class GPU;
|
||||||
struct Vertex {
|
struct Vertex {
|
||||||
OpenGL::vec4 position;
|
OpenGL::vec4 position;
|
||||||
OpenGL::vec4 colour;
|
OpenGL::vec4 colour;
|
||||||
OpenGL::vec2 UVs;
|
OpenGL::vec2 texcoord0;
|
||||||
|
OpenGL::vec2 texcoord1;
|
||||||
|
Floats::f24 texcoord0_w;
|
||||||
|
u32 padding; // pad so that texcoord2 is 64-bit aligned
|
||||||
|
OpenGL::vec2 texcoord2;
|
||||||
};
|
};
|
||||||
|
|
||||||
class Renderer {
|
class Renderer {
|
||||||
|
@ -24,7 +32,16 @@ class Renderer {
|
||||||
OpenGL::VertexBuffer vbo;
|
OpenGL::VertexBuffer vbo;
|
||||||
GLint alphaControlLoc = -1;
|
GLint alphaControlLoc = -1;
|
||||||
GLint texUnitConfigLoc = -1;
|
GLint texUnitConfigLoc = -1;
|
||||||
|
|
||||||
|
// TEV configuration uniform locations
|
||||||
|
GLint textureEnvSourceLoc = -1;
|
||||||
|
GLint textureEnvOperandLoc = -1;
|
||||||
|
GLint textureEnvCombinerLoc = -1;
|
||||||
|
GLint textureEnvColorLoc = -1;
|
||||||
|
GLint textureEnvScaleLoc = -1;
|
||||||
|
GLint textureEnvUpdateBufferLoc = -1;
|
||||||
|
GLint textureEnvBufferColorLoc = -1;
|
||||||
|
|
||||||
// Depth configuration uniform locations
|
// Depth configuration uniform locations
|
||||||
GLint depthOffsetLoc = -1;
|
GLint depthOffsetLoc = -1;
|
||||||
GLint depthScaleLoc = -1;
|
GLint depthScaleLoc = -1;
|
||||||
|
@ -41,55 +58,56 @@ class Renderer {
|
||||||
SurfaceCache<ColourBuffer, 10> colourBufferCache;
|
SurfaceCache<ColourBuffer, 10> colourBufferCache;
|
||||||
SurfaceCache<Texture, 256> textureCache;
|
SurfaceCache<Texture, 256> textureCache;
|
||||||
|
|
||||||
OpenGL::uvec2 fbSize; // The size of the framebuffer (ie both the colour and depth buffer)'
|
OpenGL::uvec2 fbSize; // The size of the framebuffer (ie both the colour and depth buffer)'
|
||||||
|
|
||||||
|
u32 colourBufferLoc; // Location in 3DS VRAM for the colour buffer
|
||||||
|
PICA::ColorFmt colourBufferFormat; // Format of the colours stored in the colour buffer
|
||||||
|
|
||||||
u32 colourBufferLoc; // Location in 3DS VRAM for the colour buffer
|
|
||||||
ColourBuffer::Formats colourBufferFormat; // Format of the colours stored in the colour buffer
|
|
||||||
|
|
||||||
// Same for the depth/stencil buffer
|
// Same for the depth/stencil buffer
|
||||||
u32 depthBufferLoc;
|
u32 depthBufferLoc;
|
||||||
DepthBuffer::Formats depthBufferFormat;
|
PICA::DepthFmt depthBufferFormat;
|
||||||
|
|
||||||
// Dummy VAO/VBO for blitting the final output
|
// Dummy VAO/VBO for blitting the final output
|
||||||
OpenGL::VertexArray dummyVAO;
|
OpenGL::VertexArray dummyVAO;
|
||||||
OpenGL::VertexBuffer dummyVBO;
|
OpenGL::VertexBuffer dummyVBO;
|
||||||
|
|
||||||
static constexpr u32 regNum = 0x300; // Number of internal PICA registers
|
static constexpr u32 regNum = 0x300; // Number of internal PICA registers
|
||||||
const std::array<u32, regNum>& regs;
|
const std::array<u32, regNum>& regs;
|
||||||
|
|
||||||
|
OpenGL::Texture screenTexture;
|
||||||
|
OpenGL::Framebuffer screenFramebuffer;
|
||||||
|
|
||||||
OpenGL::Framebuffer getColourFBO();
|
OpenGL::Framebuffer getColourFBO();
|
||||||
OpenGL::Texture getTexture(Texture& tex);
|
OpenGL::Texture getTexture(Texture& tex);
|
||||||
|
|
||||||
MAKE_LOG_FUNCTION(log, rendererLogger)
|
MAKE_LOG_FUNCTION(log, rendererLogger)
|
||||||
void setupBlending();
|
void setupBlending();
|
||||||
void bindDepthBuffer();
|
void bindDepthBuffer();
|
||||||
|
void setupTextureEnvState();
|
||||||
|
void bindTexturesToSlots();
|
||||||
|
|
||||||
public:
|
public:
|
||||||
Renderer(GPU& gpu, const std::array<u32, regNum>& internalRegs) : gpu(gpu), regs(internalRegs) {}
|
Renderer(GPU& gpu, const std::array<u32, regNum>& internalRegs) : gpu(gpu), regs(internalRegs) {}
|
||||||
|
|
||||||
void reset();
|
void reset();
|
||||||
void display(); // Display the 3DS screen contents to the window
|
void display(); // Display the 3DS screen contents to the window
|
||||||
void initGraphicsContext(); // Initialize graphics context
|
void initGraphicsContext(); // Initialize graphics context
|
||||||
void getGraphicsContext(); // Set up graphics context for rendering
|
void getGraphicsContext(); // Set up graphics context for rendering
|
||||||
void clearBuffer(u32 startAddress, u32 endAddress, u32 value, u32 control); // Clear a GPU buffer in VRAM
|
void clearBuffer(u32 startAddress, u32 endAddress, u32 value, u32 control); // Clear a GPU buffer in VRAM
|
||||||
void displayTransfer(u32 inputAddr, u32 outputAddr, u32 inputSize, u32 outputSize, u32 flags); // Perform display transfer
|
void displayTransfer(u32 inputAddr, u32 outputAddr, u32 inputSize, u32 outputSize, u32 flags); // Perform display transfer
|
||||||
void drawVertices(OpenGL::Primitives primType, Vertex* vertices, u32 count); // Draw the given vertices
|
void drawVertices(PICA::PrimType primType, std::span<const Vertex> vertices); // Draw the given vertices
|
||||||
|
|
||||||
void setFBSize(u32 width, u32 height) {
|
void setFBSize(u32 width, u32 height) {
|
||||||
fbSize.x() = width;
|
fbSize.x() = width;
|
||||||
fbSize.y() = height;
|
fbSize.y() = height;
|
||||||
}
|
}
|
||||||
|
|
||||||
void setColourFormat(ColourBuffer::Formats format) { colourBufferFormat = format; }
|
void setColourFormat(PICA::ColorFmt format) { colourBufferFormat = format; }
|
||||||
void setColourFormat(u32 format) { colourBufferFormat = static_cast<ColourBuffer::Formats>(format); }
|
void setDepthFormat(PICA::DepthFmt format) {
|
||||||
|
if (format == PICA::DepthFmt::Unknown1) {
|
||||||
void setDepthFormat(DepthBuffer::Formats format) { depthBufferFormat = format; }
|
|
||||||
void setDepthFormat(u32 format) {
|
|
||||||
if (format == 1) {
|
|
||||||
Helpers::panic("[PICA] Undocumented depth-stencil mode!");
|
Helpers::panic("[PICA] Undocumented depth-stencil mode!");
|
||||||
}
|
}
|
||||||
|
depthBufferFormat = format;
|
||||||
depthBufferFormat = static_cast<DepthBuffer::Formats>(format);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void setColourBufferLoc(u32 loc) { colourBufferLoc = loc; }
|
void setColourBufferLoc(u32 loc) { colourBufferLoc = loc; }
|
||||||
|
|
|
@ -13,6 +13,7 @@
|
||||||
// Are concerned. We could overload the == operator, but that implies full equality
|
// Are concerned. We could overload the == operator, but that implies full equality
|
||||||
// Including equality of the allocated OpenGL resources, which we don't want
|
// Including equality of the allocated OpenGL resources, which we don't want
|
||||||
// - A "valid" member that tells us whether the function is still valid or not
|
// - A "valid" member that tells us whether the function is still valid or not
|
||||||
|
// - A "location" member which tells us which location in 3DS memory this surface occupies
|
||||||
template <typename SurfaceType, size_t capacity>
|
template <typename SurfaceType, size_t capacity>
|
||||||
class SurfaceCache {
|
class SurfaceCache {
|
||||||
// Vanilla std::optional can't hold actual references
|
// Vanilla std::optional can't hold actual references
|
||||||
|
@ -40,6 +41,15 @@ public:
|
||||||
return std::nullopt;
|
return std::nullopt;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
OptionalRef findFromAddress(u32 address) {
|
||||||
|
for (auto& e : buffer) {
|
||||||
|
if (e.location == address && e.valid)
|
||||||
|
return e;
|
||||||
|
}
|
||||||
|
|
||||||
|
return std::nullopt;
|
||||||
|
}
|
||||||
|
|
||||||
// Adds a surface object to the cache and returns it
|
// Adds a surface object to the cache and returns it
|
||||||
SurfaceType& add(const SurfaceType& surface) {
|
SurfaceType& add(const SurfaceType& surface) {
|
||||||
if (size >= capacity) {
|
if (size >= capacity) {
|
||||||
|
|
|
@ -1,4 +1,5 @@
|
||||||
#pragma once
|
#pragma once
|
||||||
|
#include "PICA/regs.hpp"
|
||||||
#include "boost/icl/interval.hpp"
|
#include "boost/icl/interval.hpp"
|
||||||
#include "helpers.hpp"
|
#include "helpers.hpp"
|
||||||
#include "opengl.hpp"
|
#include "opengl.hpp"
|
||||||
|
@ -7,18 +8,8 @@ template <typename T>
|
||||||
using Interval = boost::icl::right_open_interval<T>;
|
using Interval = boost::icl::right_open_interval<T>;
|
||||||
|
|
||||||
struct ColourBuffer {
|
struct ColourBuffer {
|
||||||
enum class Formats : u32 {
|
|
||||||
RGBA8 = 0,
|
|
||||||
BGR8 = 1,
|
|
||||||
RGB5A1 = 2,
|
|
||||||
RGB565 = 3,
|
|
||||||
RGBA4 = 4,
|
|
||||||
|
|
||||||
Trash1 = 5, Trash2 = 6, Trash3 = 7 // Technically selectable, but their function is unknown
|
|
||||||
};
|
|
||||||
|
|
||||||
u32 location;
|
u32 location;
|
||||||
Formats format;
|
PICA::ColorFmt format;
|
||||||
OpenGL::uvec2 size;
|
OpenGL::uvec2 size;
|
||||||
bool valid;
|
bool valid;
|
||||||
|
|
||||||
|
@ -30,7 +21,7 @@ struct ColourBuffer {
|
||||||
|
|
||||||
ColourBuffer() : valid(false) {}
|
ColourBuffer() : valid(false) {}
|
||||||
|
|
||||||
ColourBuffer(u32 loc, Formats format, u32 x, u32 y, bool valid = true)
|
ColourBuffer(u32 loc, PICA::ColorFmt format, u32 x, u32 y, bool valid = true)
|
||||||
: location(loc), format(format), size({x, y}), valid(valid) {
|
: location(loc), format(format), size({x, y}), valid(valid) {
|
||||||
|
|
||||||
u64 endLoc = (u64)loc + sizeInBytes();
|
u64 endLoc = (u64)loc + sizeInBytes();
|
||||||
|
@ -78,31 +69,14 @@ struct ColourBuffer {
|
||||||
size.x() == other.size.x() && size.y() == other.size.y();
|
size.x() == other.size.x() && size.y() == other.size.y();
|
||||||
}
|
}
|
||||||
|
|
||||||
// Size occupied by each pixel in bytes
|
|
||||||
// All formats are 16BPP except for RGBA8 (32BPP) and BGR8 (24BPP)
|
|
||||||
size_t sizePerPixel() {
|
|
||||||
switch (format) {
|
|
||||||
case Formats::BGR8: return 3;
|
|
||||||
case Formats::RGBA8: return 4;
|
|
||||||
default: return 2;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
size_t sizeInBytes() {
|
size_t sizeInBytes() {
|
||||||
return (size_t)size.x() * (size_t)size.y() * sizePerPixel();
|
return (size_t)size.x() * (size_t)size.y() * PICA::sizePerPixel(format);
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
struct DepthBuffer {
|
struct DepthBuffer {
|
||||||
enum class Formats : u32 {
|
|
||||||
Depth16 = 0,
|
|
||||||
Garbage = 1,
|
|
||||||
Depth24 = 2,
|
|
||||||
Depth24Stencil8 = 3
|
|
||||||
};
|
|
||||||
|
|
||||||
u32 location;
|
u32 location;
|
||||||
Formats format;
|
PICA::DepthFmt format;
|
||||||
OpenGL::uvec2 size; // Implicitly set to the size of the framebuffer
|
OpenGL::uvec2 size; // Implicitly set to the size of the framebuffer
|
||||||
bool valid;
|
bool valid;
|
||||||
|
|
||||||
|
@ -113,7 +87,7 @@ struct DepthBuffer {
|
||||||
|
|
||||||
DepthBuffer() : valid(false) {}
|
DepthBuffer() : valid(false) {}
|
||||||
|
|
||||||
DepthBuffer(u32 loc, Formats format, u32 x, u32 y, bool valid = true) :
|
DepthBuffer(u32 loc, PICA::DepthFmt format, u32 x, u32 y, bool valid = true) :
|
||||||
location(loc), format(format), size({x, y}), valid(valid) {
|
location(loc), format(format), size({x, y}), valid(valid) {
|
||||||
|
|
||||||
u64 endLoc = (u64)loc + sizeInBytes();
|
u64 endLoc = (u64)loc + sizeInBytes();
|
||||||
|
@ -121,10 +95,6 @@ struct DepthBuffer {
|
||||||
range = Interval<u32>(loc, (u32)endLoc);
|
range = Interval<u32>(loc, (u32)endLoc);
|
||||||
}
|
}
|
||||||
|
|
||||||
bool hasStencil() {
|
|
||||||
return format == Formats::Depth24Stencil8;
|
|
||||||
}
|
|
||||||
|
|
||||||
void allocate() {
|
void allocate() {
|
||||||
// Create texture for the FBO, setting up filters and the like
|
// Create texture for the FBO, setting up filters and the like
|
||||||
// Reading back the current texture is slow, but allocate calls should be few and far between.
|
// Reading back the current texture is slow, but allocate calls should be few and far between.
|
||||||
|
@ -167,18 +137,7 @@ struct DepthBuffer {
|
||||||
size.x() == other.size.x() && size.y() == other.size.y();
|
size.x() == other.size.x() && size.y() == other.size.y();
|
||||||
}
|
}
|
||||||
|
|
||||||
// Size occupied by each pixel in bytes
|
|
||||||
size_t sizePerPixel() {
|
|
||||||
switch (format) {
|
|
||||||
case Formats::Depth16: return 2;
|
|
||||||
case Formats::Depth24: return 3;
|
|
||||||
case Formats::Depth24Stencil8: return 4;
|
|
||||||
|
|
||||||
default: return 1; // Invalid format
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
size_t sizeInBytes() {
|
size_t sizeInBytes() {
|
||||||
return (size_t)size.x() * (size_t)size.y() * sizePerPixel();
|
return (size_t)size.x() * (size_t)size.y() * PICA::sizePerPixel(format);
|
||||||
}
|
}
|
||||||
};
|
};
|
|
@ -1,6 +1,7 @@
|
||||||
#pragma once
|
#pragma once
|
||||||
#include <array>
|
#include <array>
|
||||||
#include <string>
|
#include <string>
|
||||||
|
#include "PICA/regs.hpp"
|
||||||
#include "boost/icl/interval.hpp"
|
#include "boost/icl/interval.hpp"
|
||||||
#include "helpers.hpp"
|
#include "helpers.hpp"
|
||||||
#include "opengl.hpp"
|
#include "opengl.hpp"
|
||||||
|
@ -9,28 +10,9 @@ template <typename T>
|
||||||
using Interval = boost::icl::right_open_interval<T>;
|
using Interval = boost::icl::right_open_interval<T>;
|
||||||
|
|
||||||
struct Texture {
|
struct Texture {
|
||||||
enum class Formats : u32 {
|
|
||||||
RGBA8 = 0,
|
|
||||||
RGB8 = 1,
|
|
||||||
RGBA5551 = 2,
|
|
||||||
RGB565 = 3,
|
|
||||||
RGBA4 = 4,
|
|
||||||
IA8 = 5,
|
|
||||||
RG8 = 6,
|
|
||||||
I8 = 7,
|
|
||||||
A8 = 8,
|
|
||||||
IA4 = 9,
|
|
||||||
I4 = 10,
|
|
||||||
A4 = 11,
|
|
||||||
ETC1 = 12,
|
|
||||||
ETC1A4 = 13,
|
|
||||||
|
|
||||||
Trash1 = 14, Trash2 = 15 // TODO: What are these?
|
|
||||||
};
|
|
||||||
|
|
||||||
u32 location;
|
u32 location;
|
||||||
u32 config; // Magnification/minification filter, wrapping configs, etc
|
u32 config; // Magnification/minification filter, wrapping configs, etc
|
||||||
Formats format;
|
PICA::TextureFmt format;
|
||||||
OpenGL::uvec2 size;
|
OpenGL::uvec2 size;
|
||||||
bool valid;
|
bool valid;
|
||||||
|
|
||||||
|
@ -41,7 +23,7 @@ struct Texture {
|
||||||
|
|
||||||
Texture() : valid(false) {}
|
Texture() : valid(false) {}
|
||||||
|
|
||||||
Texture(u32 loc, Formats format, u32 x, u32 y, u32 config, bool valid = true)
|
Texture(u32 loc, PICA::TextureFmt format, u32 x, u32 y, u32 config, bool valid = true)
|
||||||
: location(loc), format(format), size({x, y}), config(config), valid(valid) {
|
: location(loc), format(format), size({x, y}), config(config), valid(valid) {
|
||||||
|
|
||||||
u64 endLoc = (u64)loc + sizeInBytes();
|
u64 endLoc = (u64)loc + sizeInBytes();
|
||||||
|
@ -62,7 +44,7 @@ struct Texture {
|
||||||
void free();
|
void free();
|
||||||
u64 sizeInBytes();
|
u64 sizeInBytes();
|
||||||
|
|
||||||
u32 decodeTexel(u32 u, u32 v, Formats fmt, const void* data);
|
u32 decodeTexel(u32 u, u32 v, PICA::TextureFmt fmt, const void* data);
|
||||||
|
|
||||||
// Get the morton interleave offset of a texel based on its U and V values
|
// Get the morton interleave offset of a texel based on its U and V values
|
||||||
static u32 mortonInterleave(u32 u, u32 v);
|
static u32 mortonInterleave(u32 u, u32 v);
|
||||||
|
@ -70,12 +52,9 @@ struct Texture {
|
||||||
static u32 getSwizzledOffset(u32 u, u32 v, u32 width, u32 bytesPerPixel);
|
static u32 getSwizzledOffset(u32 u, u32 v, u32 width, u32 bytesPerPixel);
|
||||||
static u32 getSwizzledOffset_4bpp(u32 u, u32 v, u32 width);
|
static u32 getSwizzledOffset_4bpp(u32 u, u32 v, u32 width);
|
||||||
|
|
||||||
// Returns the string representation of a texture format
|
|
||||||
static std::string textureFormatToString(Formats fmt);
|
|
||||||
|
|
||||||
// Returns the format of this texture as a string
|
// Returns the format of this texture as a string
|
||||||
std::string formatToString() {
|
std::string formatToString() {
|
||||||
return textureFormatToString(format);
|
return PICA::textureFormatToString(format);
|
||||||
}
|
}
|
||||||
|
|
||||||
// Returns the texel at coordinates (u, v) of an ETC1(A4) texture
|
// Returns the texel at coordinates (u, v) of an ETC1(A4) texture
|
||||||
|
|
8
include/result/result.hpp
Normal file
8
include/result/result.hpp
Normal file
|
@ -0,0 +1,8 @@
|
||||||
|
#pragma once
|
||||||
|
|
||||||
|
#include "result_common.hpp"
|
||||||
|
#include "result_kernel.hpp"
|
||||||
|
#include "result_os.hpp"
|
||||||
|
#include "result_fnd.hpp"
|
||||||
|
#include "result_fs.hpp"
|
||||||
|
#include "result_gsp.hpp"
|
206
include/result/result_common.hpp
Normal file
206
include/result/result_common.hpp
Normal file
|
@ -0,0 +1,206 @@
|
||||||
|
#pragma once
|
||||||
|
|
||||||
|
#include <climits>
|
||||||
|
#include <cstdint>
|
||||||
|
#include <type_traits>
|
||||||
|
|
||||||
|
namespace Result {
|
||||||
|
enum class HorizonResultLevel : uint32_t {
|
||||||
|
Success = 0,
|
||||||
|
Info = 1,
|
||||||
|
Status = 25,
|
||||||
|
Temporary = 26,
|
||||||
|
Permanent = 27,
|
||||||
|
Usage = 28,
|
||||||
|
Reinitialize = 29,
|
||||||
|
Reset = 30,
|
||||||
|
Fatal = 31,
|
||||||
|
};
|
||||||
|
|
||||||
|
enum class HorizonResultSummary : uint32_t {
|
||||||
|
Success = 0,
|
||||||
|
NothingHappened = 1,
|
||||||
|
WouldBlock = 2,
|
||||||
|
OutOfResource = 3,
|
||||||
|
NotFound = 4,
|
||||||
|
InvalidState = 5,
|
||||||
|
NotSupported = 6,
|
||||||
|
InvalidArgument = 7,
|
||||||
|
WrongArgument = 8,
|
||||||
|
Canceled = 9,
|
||||||
|
StatusChanged = 10,
|
||||||
|
Internal = 11,
|
||||||
|
};
|
||||||
|
|
||||||
|
enum class HorizonResultModule : uint32_t {
|
||||||
|
Common = 0,
|
||||||
|
Kernel = 1,
|
||||||
|
Util = 2,
|
||||||
|
FileServer = 3,
|
||||||
|
LoaderServer = 4,
|
||||||
|
TCB = 5,
|
||||||
|
OS = 6,
|
||||||
|
DBG = 7,
|
||||||
|
DMNT = 8,
|
||||||
|
PDN = 9 ,
|
||||||
|
GSP = 10,
|
||||||
|
I2C = 11,
|
||||||
|
GPIO = 12,
|
||||||
|
DD = 13,
|
||||||
|
CODEC = 14,
|
||||||
|
SPI = 15,
|
||||||
|
PXI = 16,
|
||||||
|
FS = 17,
|
||||||
|
DI = 18,
|
||||||
|
HID = 19,
|
||||||
|
CAM = 20,
|
||||||
|
PI = 21,
|
||||||
|
PM = 22,
|
||||||
|
PM_LOW = 23,
|
||||||
|
FSI = 24,
|
||||||
|
SRV = 25,
|
||||||
|
NDM = 26,
|
||||||
|
NWM = 27,
|
||||||
|
SOC = 28,
|
||||||
|
LDR = 29,
|
||||||
|
ACC = 30,
|
||||||
|
RomFS = 31,
|
||||||
|
AM = 32,
|
||||||
|
HIO = 33,
|
||||||
|
Updater = 34,
|
||||||
|
MIC = 35,
|
||||||
|
FND = 36,
|
||||||
|
MP = 37,
|
||||||
|
MPWL = 38,
|
||||||
|
AC = 39,
|
||||||
|
HTTP = 40,
|
||||||
|
DSP = 41,
|
||||||
|
SND = 42,
|
||||||
|
DLP = 43,
|
||||||
|
HIO_LOW = 44,
|
||||||
|
CSND = 45,
|
||||||
|
SSL = 46,
|
||||||
|
AM_LOW = 47,
|
||||||
|
NEX = 48,
|
||||||
|
Friends = 49,
|
||||||
|
RDT = 50,
|
||||||
|
Applet = 51,
|
||||||
|
NIM = 52,
|
||||||
|
PTM = 53,
|
||||||
|
MIDI = 54,
|
||||||
|
MC = 55,
|
||||||
|
SWC = 56,
|
||||||
|
FatFS = 57,
|
||||||
|
NGC = 58,
|
||||||
|
CARD = 59,
|
||||||
|
CARDNOR = 60,
|
||||||
|
SDMC = 61,
|
||||||
|
BOSS = 62,
|
||||||
|
DBM = 63,
|
||||||
|
Config = 64,
|
||||||
|
PS = 65,
|
||||||
|
CEC = 66,
|
||||||
|
IR = 67,
|
||||||
|
UDS = 68,
|
||||||
|
PL = 69,
|
||||||
|
CUP = 70,
|
||||||
|
Gyroscope = 71,
|
||||||
|
MCU = 72,
|
||||||
|
NS = 73,
|
||||||
|
News = 74,
|
||||||
|
RO = 75,
|
||||||
|
GD = 76,
|
||||||
|
CardSPI = 77,
|
||||||
|
EC = 78,
|
||||||
|
WebBrowser = 79,
|
||||||
|
Test = 80,
|
||||||
|
ENC = 81,
|
||||||
|
PIA = 82,
|
||||||
|
ACT = 83,
|
||||||
|
VCTL = 84,
|
||||||
|
OLV = 85,
|
||||||
|
NEIA = 86,
|
||||||
|
NPNS = 87,
|
||||||
|
AVD = 90,
|
||||||
|
L2B = 91,
|
||||||
|
MVD = 92,
|
||||||
|
NFC = 93,
|
||||||
|
UART = 94,
|
||||||
|
SPM = 95,
|
||||||
|
QTM = 96,
|
||||||
|
NFP = 97,
|
||||||
|
};
|
||||||
|
|
||||||
|
class HorizonResult {
|
||||||
|
private:
|
||||||
|
static const uint32_t DescriptionBits = 10;
|
||||||
|
static const uint32_t ModuleBits = 8;
|
||||||
|
static const uint32_t ReservedBits = 3;
|
||||||
|
static const uint32_t SummaryBits = 6;
|
||||||
|
static const uint32_t LevelBits = 5;
|
||||||
|
|
||||||
|
static const uint32_t DescriptionOffset = 0;
|
||||||
|
static const uint32_t ModuleOffset = DescriptionOffset + DescriptionBits;
|
||||||
|
static const uint32_t SummaryOffset = ModuleOffset + ModuleBits + ReservedBits;
|
||||||
|
static const uint32_t LevelOffset = SummaryOffset + SummaryBits;
|
||||||
|
|
||||||
|
static_assert(DescriptionBits + ModuleBits + SummaryBits + LevelBits + ReservedBits == sizeof(uint32_t) * CHAR_BIT, "Invalid Result size");
|
||||||
|
|
||||||
|
uint32_t m_value;
|
||||||
|
|
||||||
|
constexpr inline uint32_t getBitsValue(int offset, int amount) {
|
||||||
|
return (m_value >> offset) & ~(~0 << amount);
|
||||||
|
}
|
||||||
|
|
||||||
|
static constexpr inline uint32_t makeValue(uint32_t description, uint32_t module, uint32_t summary, uint32_t level) {
|
||||||
|
return (description << DescriptionOffset) | (module << ModuleOffset) | (summary << SummaryOffset) | (level << LevelOffset);
|
||||||
|
}
|
||||||
|
|
||||||
|
public:
|
||||||
|
constexpr HorizonResult() {}
|
||||||
|
constexpr HorizonResult(uint32_t value) : m_value(value) {}
|
||||||
|
constexpr HorizonResult(uint32_t description, HorizonResultModule module, HorizonResultSummary summary, HorizonResultLevel level) : m_value(makeValue(description, static_cast<uint32_t>(module), static_cast<uint32_t>(summary), static_cast<uint32_t>(level))) {}
|
||||||
|
constexpr operator uint32_t() const { return m_value; }
|
||||||
|
|
||||||
|
constexpr inline uint32_t getDescription() {
|
||||||
|
return getBitsValue(DescriptionOffset, DescriptionBits);
|
||||||
|
}
|
||||||
|
|
||||||
|
constexpr inline HorizonResultModule getModule() {
|
||||||
|
return static_cast<HorizonResultModule>(getBitsValue(ModuleOffset, ModuleBits));
|
||||||
|
}
|
||||||
|
|
||||||
|
constexpr inline HorizonResultSummary getSummary() {
|
||||||
|
return static_cast<HorizonResultSummary>(getBitsValue(SummaryOffset, SummaryBits));
|
||||||
|
}
|
||||||
|
|
||||||
|
constexpr inline HorizonResultLevel getLevel() {
|
||||||
|
return static_cast<HorizonResultLevel>(getBitsValue(LevelOffset, LevelBits));
|
||||||
|
}
|
||||||
|
|
||||||
|
constexpr inline uint32_t getRawValue() {
|
||||||
|
return m_value;
|
||||||
|
}
|
||||||
|
|
||||||
|
constexpr inline bool isSuccess() {
|
||||||
|
return m_value == 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
constexpr inline bool isFailure() {
|
||||||
|
return m_value != 0;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
static_assert(std::is_trivially_destructible<HorizonResult>::value, "std::is_trivially_destructible<HorizonResult>::value");
|
||||||
|
|
||||||
|
#define DEFINE_HORIZON_RESULT_MODULE(ns, value) \
|
||||||
|
namespace ns::Detail {\
|
||||||
|
static constexpr HorizonResultModule ModuleId = HorizonResultModule::value; \
|
||||||
|
}
|
||||||
|
|
||||||
|
#define DEFINE_HORIZON_RESULT(name, description, summary, level) \
|
||||||
|
static constexpr HorizonResult name(description, Detail::ModuleId, HorizonResultSummary::summary, HorizonResultLevel::level);
|
||||||
|
|
||||||
|
static constexpr HorizonResult Success(0);
|
||||||
|
static constexpr HorizonResult FailurePlaceholder(UINT32_MAX);
|
||||||
|
};
|
8
include/result/result_fnd.hpp
Normal file
8
include/result/result_fnd.hpp
Normal file
|
@ -0,0 +1,8 @@
|
||||||
|
#pragma once
|
||||||
|
#include "result_common.hpp"
|
||||||
|
|
||||||
|
DEFINE_HORIZON_RESULT_MODULE(Result::FND, FND);
|
||||||
|
|
||||||
|
namespace Result::FND {
|
||||||
|
DEFINE_HORIZON_RESULT(InvalidEnumValue, 1005, InvalidArgument, Permanent);
|
||||||
|
};
|
18
include/result/result_fs.hpp
Normal file
18
include/result/result_fs.hpp
Normal file
|
@ -0,0 +1,18 @@
|
||||||
|
#pragma once
|
||||||
|
#include "result_common.hpp"
|
||||||
|
|
||||||
|
DEFINE_HORIZON_RESULT_MODULE(Result::FS, FS);
|
||||||
|
|
||||||
|
namespace Result::FS {
|
||||||
|
// TODO: Verify this
|
||||||
|
DEFINE_HORIZON_RESULT(FileNotFound, 100, NotFound, Status);
|
||||||
|
// TODO: Verify this
|
||||||
|
DEFINE_HORIZON_RESULT(FileNotFoundAlt, 112, NotFound, Status);
|
||||||
|
// Also a not found error code used here and there in the FS module.
|
||||||
|
DEFINE_HORIZON_RESULT(NotFoundInvalid, 120, InvalidState, Status);
|
||||||
|
DEFINE_HORIZON_RESULT(AlreadyExists, 190, NothingHappened, Info);
|
||||||
|
DEFINE_HORIZON_RESULT(FileTooLarge, 210, OutOfResource, Info);
|
||||||
|
// Trying to access an archive that needs formatting and has not been formatted
|
||||||
|
DEFINE_HORIZON_RESULT(NotFormatted, 340, InvalidState, Status);
|
||||||
|
DEFINE_HORIZON_RESULT(UnexpectedFileOrDir, 770, NotSupported, Usage);
|
||||||
|
};
|
8
include/result/result_gsp.hpp
Normal file
8
include/result/result_gsp.hpp
Normal file
|
@ -0,0 +1,8 @@
|
||||||
|
#pragma once
|
||||||
|
#include "result_common.hpp"
|
||||||
|
|
||||||
|
DEFINE_HORIZON_RESULT_MODULE(Result::GSP, GSP);
|
||||||
|
|
||||||
|
namespace Result::GSP {
|
||||||
|
DEFINE_HORIZON_RESULT(SuccessRegisterIRQ, 519, Success, Success);
|
||||||
|
};
|
15
include/result/result_kernel.hpp
Normal file
15
include/result/result_kernel.hpp
Normal file
|
@ -0,0 +1,15 @@
|
||||||
|
#pragma once
|
||||||
|
#include "result_common.hpp"
|
||||||
|
|
||||||
|
DEFINE_HORIZON_RESULT_MODULE(Result::Kernel, Kernel);
|
||||||
|
|
||||||
|
namespace Result::Kernel {
|
||||||
|
// Returned when a thread releases a mutex it does not own
|
||||||
|
DEFINE_HORIZON_RESULT(InvalidMutexRelease, 31, InvalidArgument, Permanent);
|
||||||
|
DEFINE_HORIZON_RESULT(NotFound, 1018, NotFound, Permanent);
|
||||||
|
DEFINE_HORIZON_RESULT(InvalidEnumValue, 1005, InvalidArgument, Permanent);
|
||||||
|
DEFINE_HORIZON_RESULT(InvalidHandle, 1015, InvalidArgument, Permanent);
|
||||||
|
|
||||||
|
|
||||||
|
static_assert(InvalidHandle == 0xD8E007F7, "conversion broken");
|
||||||
|
};
|
14
include/result/result_os.hpp
Normal file
14
include/result/result_os.hpp
Normal file
|
@ -0,0 +1,14 @@
|
||||||
|
#pragma once
|
||||||
|
#include "result_common.hpp"
|
||||||
|
|
||||||
|
DEFINE_HORIZON_RESULT_MODULE(Result::OS, OS);
|
||||||
|
|
||||||
|
namespace Result::OS {
|
||||||
|
DEFINE_HORIZON_RESULT(PortNameTooLong, 30, InvalidArgument, Usage);
|
||||||
|
DEFINE_HORIZON_RESULT(InvalidHandle, 1015, WrongArgument, Permanent);
|
||||||
|
DEFINE_HORIZON_RESULT(InvalidCombination, 1006, InvalidArgument, Usage);
|
||||||
|
DEFINE_HORIZON_RESULT(MisalignedAddress, 1009, InvalidArgument, Usage);
|
||||||
|
DEFINE_HORIZON_RESULT(MisalignedSize, 1010, InvalidArgument, Usage);
|
||||||
|
DEFINE_HORIZON_RESULT(OutOfRange, 1021, InvalidArgument, Usage);
|
||||||
|
DEFINE_HORIZON_RESULT(Timeout, 1022, StatusChanged, Info);
|
||||||
|
};
|
|
@ -3,6 +3,7 @@
|
||||||
#include "kernel_types.hpp"
|
#include "kernel_types.hpp"
|
||||||
#include "logger.hpp"
|
#include "logger.hpp"
|
||||||
#include "memory.hpp"
|
#include "memory.hpp"
|
||||||
|
#include "result/result.hpp"
|
||||||
|
|
||||||
class ACService {
|
class ACService {
|
||||||
Handle handle = KernelHandles::AC;
|
Handle handle = KernelHandles::AC;
|
||||||
|
|
|
@ -3,6 +3,7 @@
|
||||||
#include "kernel_types.hpp"
|
#include "kernel_types.hpp"
|
||||||
#include "logger.hpp"
|
#include "logger.hpp"
|
||||||
#include "memory.hpp"
|
#include "memory.hpp"
|
||||||
|
#include "result/result.hpp"
|
||||||
|
|
||||||
class ACTService {
|
class ACTService {
|
||||||
Handle handle = KernelHandles::ACT;
|
Handle handle = KernelHandles::ACT;
|
||||||
|
|
|
@ -3,6 +3,7 @@
|
||||||
#include "kernel_types.hpp"
|
#include "kernel_types.hpp"
|
||||||
#include "logger.hpp"
|
#include "logger.hpp"
|
||||||
#include "memory.hpp"
|
#include "memory.hpp"
|
||||||
|
#include "result/result.hpp"
|
||||||
|
|
||||||
class AMService {
|
class AMService {
|
||||||
Handle handle = KernelHandles::AM;
|
Handle handle = KernelHandles::AM;
|
||||||
|
|
|
@ -4,6 +4,7 @@
|
||||||
#include "kernel_types.hpp"
|
#include "kernel_types.hpp"
|
||||||
#include "logger.hpp"
|
#include "logger.hpp"
|
||||||
#include "memory.hpp"
|
#include "memory.hpp"
|
||||||
|
#include "result/result.hpp"
|
||||||
|
|
||||||
// Yay, more circular dependencies
|
// Yay, more circular dependencies
|
||||||
class Kernel;
|
class Kernel;
|
||||||
|
|
|
@ -3,6 +3,7 @@
|
||||||
#include "kernel_types.hpp"
|
#include "kernel_types.hpp"
|
||||||
#include "logger.hpp"
|
#include "logger.hpp"
|
||||||
#include "memory.hpp"
|
#include "memory.hpp"
|
||||||
|
#include "result/result.hpp"
|
||||||
|
|
||||||
class BOSSService {
|
class BOSSService {
|
||||||
Handle handle = KernelHandles::BOSS;
|
Handle handle = KernelHandles::BOSS;
|
||||||
|
|
|
@ -3,6 +3,7 @@
|
||||||
#include "kernel_types.hpp"
|
#include "kernel_types.hpp"
|
||||||
#include "logger.hpp"
|
#include "logger.hpp"
|
||||||
#include "memory.hpp"
|
#include "memory.hpp"
|
||||||
|
#include "result/result.hpp"
|
||||||
|
|
||||||
class CAMService {
|
class CAMService {
|
||||||
Handle handle = KernelHandles::CAM;
|
Handle handle = KernelHandles::CAM;
|
||||||
|
|
|
@ -4,6 +4,7 @@
|
||||||
#include "kernel_types.hpp"
|
#include "kernel_types.hpp"
|
||||||
#include "logger.hpp"
|
#include "logger.hpp"
|
||||||
#include "memory.hpp"
|
#include "memory.hpp"
|
||||||
|
#include "result/result.hpp"
|
||||||
|
|
||||||
class Kernel;
|
class Kernel;
|
||||||
|
|
||||||
|
|
|
@ -4,6 +4,7 @@
|
||||||
#include "logger.hpp"
|
#include "logger.hpp"
|
||||||
#include "memory.hpp"
|
#include "memory.hpp"
|
||||||
#include "region_codes.hpp"
|
#include "region_codes.hpp"
|
||||||
|
#include "result/result.hpp"
|
||||||
|
|
||||||
class CFGService {
|
class CFGService {
|
||||||
Handle handle = KernelHandles::CFG;
|
Handle handle = KernelHandles::CFG;
|
||||||
|
|
|
@ -3,6 +3,7 @@
|
||||||
#include "kernel_types.hpp"
|
#include "kernel_types.hpp"
|
||||||
#include "logger.hpp"
|
#include "logger.hpp"
|
||||||
#include "memory.hpp"
|
#include "memory.hpp"
|
||||||
|
#include "result/result.hpp"
|
||||||
|
|
||||||
// Please forgive me for how everything in this file is named
|
// Please forgive me for how everything in this file is named
|
||||||
// "dlp:SRVR" is not a nice name to work with
|
// "dlp:SRVR" is not a nice name to work with
|
||||||
|
|
|
@ -4,6 +4,7 @@
|
||||||
#include "helpers.hpp"
|
#include "helpers.hpp"
|
||||||
#include "logger.hpp"
|
#include "logger.hpp"
|
||||||
#include "memory.hpp"
|
#include "memory.hpp"
|
||||||
|
#include "result/result.hpp"
|
||||||
|
|
||||||
namespace DSPPipeType {
|
namespace DSPPipeType {
|
||||||
enum : u32 {
|
enum : u32 {
|
||||||
|
|
|
@ -4,6 +4,7 @@
|
||||||
#include "kernel_types.hpp"
|
#include "kernel_types.hpp"
|
||||||
#include "logger.hpp"
|
#include "logger.hpp"
|
||||||
#include "memory.hpp"
|
#include "memory.hpp"
|
||||||
|
#include "result/result.hpp"
|
||||||
|
|
||||||
// It's important to keep this struct to 16 bytes as we use its sizeof in the service functions in frd.cpp
|
// It's important to keep this struct to 16 bytes as we use its sizeof in the service functions in frd.cpp
|
||||||
struct FriendKey {
|
struct FriendKey {
|
||||||
|
|
|
@ -8,6 +8,7 @@
|
||||||
#include "kernel_types.hpp"
|
#include "kernel_types.hpp"
|
||||||
#include "logger.hpp"
|
#include "logger.hpp"
|
||||||
#include "memory.hpp"
|
#include "memory.hpp"
|
||||||
|
#include "result/result.hpp"
|
||||||
|
|
||||||
// Yay, more circular dependencies
|
// Yay, more circular dependencies
|
||||||
class Kernel;
|
class Kernel;
|
||||||
|
@ -29,8 +30,8 @@ class FSService {
|
||||||
ExtSaveDataArchive sharedExtSaveData_nand;
|
ExtSaveDataArchive sharedExtSaveData_nand;
|
||||||
|
|
||||||
ArchiveBase* getArchiveFromID(u32 id, const FSPath& archivePath);
|
ArchiveBase* getArchiveFromID(u32 id, const FSPath& archivePath);
|
||||||
Rust::Result<Handle, FSResult> openArchiveHandle(u32 archiveID, const FSPath& path);
|
Rust::Result<Handle, HorizonResult> openArchiveHandle(u32 archiveID, const FSPath& path);
|
||||||
Rust::Result<Handle, FSResult> openDirectoryHandle(ArchiveBase* archive, const FSPath& path);
|
Rust::Result<Handle, HorizonResult> openDirectoryHandle(ArchiveBase* archive, const FSPath& path);
|
||||||
std::optional<Handle> openFileHandle(ArchiveBase* archive, const FSPath& path, const FSPath& archivePath, const FilePerms& perms);
|
std::optional<Handle> openFileHandle(ArchiveBase* archive, const FSPath& path, const FSPath& archivePath, const FilePerms& perms);
|
||||||
FSPath readPath(u32 type, u32 pointer, u32 size);
|
FSPath readPath(u32 type, u32 pointer, u32 size);
|
||||||
|
|
||||||
|
@ -62,7 +63,7 @@ public:
|
||||||
sharedExtSaveData_nand(mem, "../SharedFiles/NAND", true), extSaveData_sdmc(mem, "SDMC"),
|
sharedExtSaveData_nand(mem, "../SharedFiles/NAND", true), extSaveData_sdmc(mem, "SDMC"),
|
||||||
sdmc(mem), selfNcch(mem), ncch(mem), kernel(kernel)
|
sdmc(mem), selfNcch(mem), ncch(mem), kernel(kernel)
|
||||||
{}
|
{}
|
||||||
|
|
||||||
void reset();
|
void reset();
|
||||||
void handleSyncRequest(u32 messagePointer);
|
void handleSyncRequest(u32 messagePointer);
|
||||||
// Creates directories for NAND, ExtSaveData, etc if they don't already exist. Should be executed after loading a new ROM.
|
// Creates directories for NAND, ExtSaveData, etc if they don't already exist. Should be executed after loading a new ROM.
|
||||||
|
|
|
@ -6,6 +6,7 @@
|
||||||
#include "kernel_types.hpp"
|
#include "kernel_types.hpp"
|
||||||
#include "logger.hpp"
|
#include "logger.hpp"
|
||||||
#include "memory.hpp"
|
#include "memory.hpp"
|
||||||
|
#include "result/result.hpp"
|
||||||
|
|
||||||
enum class GPUInterrupt : u8 {
|
enum class GPUInterrupt : u8 {
|
||||||
PSC0 = 0, // Memory fill completed
|
PSC0 = 0, // Memory fill completed
|
||||||
|
|
|
@ -3,6 +3,7 @@
|
||||||
#include "kernel_types.hpp"
|
#include "kernel_types.hpp"
|
||||||
#include "logger.hpp"
|
#include "logger.hpp"
|
||||||
#include "memory.hpp"
|
#include "memory.hpp"
|
||||||
|
#include "result/result.hpp"
|
||||||
|
|
||||||
class LCDService {
|
class LCDService {
|
||||||
Handle handle = KernelHandles::LCD;
|
Handle handle = KernelHandles::LCD;
|
||||||
|
|
|
@ -5,6 +5,7 @@
|
||||||
#include "kernel_types.hpp"
|
#include "kernel_types.hpp"
|
||||||
#include "logger.hpp"
|
#include "logger.hpp"
|
||||||
#include "memory.hpp"
|
#include "memory.hpp"
|
||||||
|
#include "result/result.hpp"
|
||||||
|
|
||||||
namespace HID::Keys {
|
namespace HID::Keys {
|
||||||
enum : u32 {
|
enum : u32 {
|
||||||
|
|
|
@ -3,6 +3,7 @@
|
||||||
#include "kernel_types.hpp"
|
#include "kernel_types.hpp"
|
||||||
#include "logger.hpp"
|
#include "logger.hpp"
|
||||||
#include "memory.hpp"
|
#include "memory.hpp"
|
||||||
|
#include "result/result.hpp"
|
||||||
|
|
||||||
class LDRService {
|
class LDRService {
|
||||||
Handle handle = KernelHandles::LDR_RO;
|
Handle handle = KernelHandles::LDR_RO;
|
||||||
|
|
|
@ -3,6 +3,7 @@
|
||||||
#include "kernel_types.hpp"
|
#include "kernel_types.hpp"
|
||||||
#include "logger.hpp"
|
#include "logger.hpp"
|
||||||
#include "memory.hpp"
|
#include "memory.hpp"
|
||||||
|
#include "result/result.hpp"
|
||||||
|
|
||||||
class MICService {
|
class MICService {
|
||||||
Handle handle = KernelHandles::MIC;
|
Handle handle = KernelHandles::MIC;
|
||||||
|
|
|
@ -3,6 +3,7 @@
|
||||||
#include "kernel_types.hpp"
|
#include "kernel_types.hpp"
|
||||||
#include "logger.hpp"
|
#include "logger.hpp"
|
||||||
#include "memory.hpp"
|
#include "memory.hpp"
|
||||||
|
#include "result/result.hpp"
|
||||||
|
|
||||||
class NDMService {
|
class NDMService {
|
||||||
Handle handle = KernelHandles::NDM;
|
Handle handle = KernelHandles::NDM;
|
||||||
|
|
|
@ -3,6 +3,7 @@
|
||||||
#include "kernel_types.hpp"
|
#include "kernel_types.hpp"
|
||||||
#include "logger.hpp"
|
#include "logger.hpp"
|
||||||
#include "memory.hpp"
|
#include "memory.hpp"
|
||||||
|
#include "result/result.hpp"
|
||||||
|
|
||||||
// You know the drill
|
// You know the drill
|
||||||
class Kernel;
|
class Kernel;
|
||||||
|
|
|
@ -3,6 +3,7 @@
|
||||||
#include "kernel_types.hpp"
|
#include "kernel_types.hpp"
|
||||||
#include "logger.hpp"
|
#include "logger.hpp"
|
||||||
#include "memory.hpp"
|
#include "memory.hpp"
|
||||||
|
#include "result/result.hpp"
|
||||||
|
|
||||||
class NIMService {
|
class NIMService {
|
||||||
Handle handle = KernelHandles::NIM;
|
Handle handle = KernelHandles::NIM;
|
||||||
|
|
|
@ -3,6 +3,7 @@
|
||||||
#include "kernel_types.hpp"
|
#include "kernel_types.hpp"
|
||||||
#include "logger.hpp"
|
#include "logger.hpp"
|
||||||
#include "memory.hpp"
|
#include "memory.hpp"
|
||||||
|
#include "result/result.hpp"
|
||||||
|
|
||||||
class PTMService {
|
class PTMService {
|
||||||
Handle handle = KernelHandles::PTM;
|
Handle handle = KernelHandles::PTM;
|
||||||
|
|
|
@ -1,6 +1,8 @@
|
||||||
#pragma once
|
#pragma once
|
||||||
#include <array>
|
#include <array>
|
||||||
#include <optional>
|
#include <optional>
|
||||||
|
#include <span>
|
||||||
|
|
||||||
#include "kernel_types.hpp"
|
#include "kernel_types.hpp"
|
||||||
#include "logger.hpp"
|
#include "logger.hpp"
|
||||||
#include "memory.hpp"
|
#include "memory.hpp"
|
||||||
|
@ -14,16 +16,16 @@
|
||||||
#include "services/cfg.hpp"
|
#include "services/cfg.hpp"
|
||||||
#include "services/dlp_srvr.hpp"
|
#include "services/dlp_srvr.hpp"
|
||||||
#include "services/dsp.hpp"
|
#include "services/dsp.hpp"
|
||||||
#include "services/hid.hpp"
|
|
||||||
#include "services/frd.hpp"
|
#include "services/frd.hpp"
|
||||||
#include "services/fs.hpp"
|
#include "services/fs.hpp"
|
||||||
#include "services/gsp_gpu.hpp"
|
#include "services/gsp_gpu.hpp"
|
||||||
#include "services/gsp_lcd.hpp"
|
#include "services/gsp_lcd.hpp"
|
||||||
|
#include "services/hid.hpp"
|
||||||
#include "services/ldr_ro.hpp"
|
#include "services/ldr_ro.hpp"
|
||||||
#include "services/mic.hpp"
|
#include "services/mic.hpp"
|
||||||
|
#include "services/ndm.hpp"
|
||||||
#include "services/nfc.hpp"
|
#include "services/nfc.hpp"
|
||||||
#include "services/nim.hpp"
|
#include "services/nim.hpp"
|
||||||
#include "services/ndm.hpp"
|
|
||||||
#include "services/ptm.hpp"
|
#include "services/ptm.hpp"
|
||||||
#include "services/y2r.hpp"
|
#include "services/y2r.hpp"
|
||||||
|
|
||||||
|
@ -31,7 +33,7 @@
|
||||||
class Kernel;
|
class Kernel;
|
||||||
|
|
||||||
class ServiceManager {
|
class ServiceManager {
|
||||||
std::array<u32, 16>& regs;
|
std::span<u32, 16> regs;
|
||||||
Memory& mem;
|
Memory& mem;
|
||||||
Kernel& kernel;
|
Kernel& kernel;
|
||||||
|
|
||||||
|
@ -69,8 +71,8 @@ class ServiceManager {
|
||||||
void registerClient(u32 messagePointer);
|
void registerClient(u32 messagePointer);
|
||||||
void subscribe(u32 messagePointer);
|
void subscribe(u32 messagePointer);
|
||||||
|
|
||||||
public:
|
public:
|
||||||
ServiceManager(std::array<u32, 16>& regs, Memory& mem, GPU& gpu, u32& currentPID, Kernel& kernel);
|
ServiceManager(std::span<u32, 16> regs, Memory& mem, GPU& gpu, u32& currentPID, Kernel& kernel);
|
||||||
void reset();
|
void reset();
|
||||||
void initializeFS() { fs.initializeFilesystem(); }
|
void initializeFS() { fs.initializeFilesystem(); }
|
||||||
void handleSyncRequest(u32 messagePointer);
|
void handleSyncRequest(u32 messagePointer);
|
||||||
|
|
|
@ -3,6 +3,11 @@
|
||||||
|
|
||||||
Panda3DS is an HLE, red-panda-themed Nintendo 3DS emulator written in C++ which started out as a fun project out of curiosity, but evolved into something that can sort of play games!
|
Panda3DS is an HLE, red-panda-themed Nintendo 3DS emulator written in C++ which started out as a fun project out of curiosity, but evolved into something that can sort of play games!
|
||||||
|
|
||||||
|
# Discussion
|
||||||
|
Join our Discord server by pressing on the banner below!
|
||||||
|
|
||||||
|
[](https://discord.gg/ZYbugsEmsw)
|
||||||
|
|
||||||
  
|
  
|
||||||
|
|
||||||
# Compatibility
|
# Compatibility
|
||||||
|
|
|
@ -1,10 +1,51 @@
|
||||||
#include "PICA/gpu.hpp"
|
#include "PICA/gpu.hpp"
|
||||||
|
|
||||||
|
#include <array>
|
||||||
|
#include <cstdio>
|
||||||
|
#include <cstddef>
|
||||||
|
|
||||||
#include "PICA/float_types.hpp"
|
#include "PICA/float_types.hpp"
|
||||||
#include "PICA/regs.hpp"
|
#include "PICA/regs.hpp"
|
||||||
#include <cstdio>
|
|
||||||
|
|
||||||
using namespace Floats;
|
using namespace Floats;
|
||||||
|
|
||||||
|
// A representation of the output vertex as it comes out of the vertex shader, with padding and all
|
||||||
|
struct OutputVertex {
|
||||||
|
using vec2f = OpenGL::Vector<f24, 2>;
|
||||||
|
using vec3f = OpenGL::Vector<f24, 3>;
|
||||||
|
using vec4f = OpenGL::Vector<f24, 4>;
|
||||||
|
|
||||||
|
union {
|
||||||
|
struct {
|
||||||
|
vec4f positions; // Vertex position
|
||||||
|
vec4f quaternion; // Quaternion specifying the normal/tangent frame (for fragment lighting)
|
||||||
|
vec4f colour; // Vertex color
|
||||||
|
vec2f texcoord0; // Texcoords for texture unit 0 (Only U and V, W is stored separately for 3D textures!)
|
||||||
|
vec2f texcoord1; // Texcoords for TU 1
|
||||||
|
f24 texcoord0_w; // W component for texcoord 0 if using a 3D texture
|
||||||
|
u32 padding; // Unused
|
||||||
|
|
||||||
|
vec3f view; // View vector (for fragment lighting)
|
||||||
|
u32 padding2; // Unused
|
||||||
|
vec2f texcoord2; // Texcoords for TU 2
|
||||||
|
} s;
|
||||||
|
|
||||||
|
// The software, non-accelerated vertex loader writes here and then reads specific components from the above struct
|
||||||
|
f24 raw[0x20];
|
||||||
|
};
|
||||||
|
OutputVertex() {}
|
||||||
|
};
|
||||||
|
#define ASSERT_POS(member, pos) static_assert(offsetof(OutputVertex, s.member) == pos * sizeof(f24), "OutputVertex struct is broken!");
|
||||||
|
|
||||||
|
ASSERT_POS(positions, 0)
|
||||||
|
ASSERT_POS(quaternion, 4)
|
||||||
|
ASSERT_POS(colour, 8)
|
||||||
|
ASSERT_POS(texcoord0, 12)
|
||||||
|
ASSERT_POS(texcoord1, 14)
|
||||||
|
ASSERT_POS(texcoord0_w, 16)
|
||||||
|
ASSERT_POS(view, 18)
|
||||||
|
ASSERT_POS(texcoord2, 22)
|
||||||
|
|
||||||
GPU::GPU(Memory& mem) : mem(mem), renderer(*this, regs) {
|
GPU::GPU(Memory& mem) : mem(mem), renderer(*this, regs) {
|
||||||
vram = new u8[vramSize];
|
vram = new u8[vramSize];
|
||||||
mem.setVRAM(vram); // Give the bus a pointer to our VRAM
|
mem.setVRAM(vram); // Give the bus a pointer to our VRAM
|
||||||
|
@ -51,36 +92,37 @@ void GPU::drawArrays(bool indexed) {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
Vertex* vertices = new Vertex[Renderer::vertexBufferSize];
|
static std::array<Vertex, Renderer::vertexBufferSize> vertices;
|
||||||
|
|
||||||
template <bool indexed, bool useShaderJIT>
|
template <bool indexed, bool useShaderJIT>
|
||||||
void GPU::drawArrays() {
|
void GPU::drawArrays() {
|
||||||
// Base address for vertex attributes
|
// Base address for vertex attributes
|
||||||
// The vertex base is always on a quadword boundary because the PICA does weird alignment shit any time possible
|
// The vertex base is always on a quadword boundary because the PICA does weird alignment shit any time possible
|
||||||
const u32 vertexBase = ((regs[PICAInternalRegs::VertexAttribLoc] >> 1) & 0xfffffff) * 16;
|
const u32 vertexBase = ((regs[PICA::InternalRegs::VertexAttribLoc] >> 1) & 0xfffffff) * 16;
|
||||||
const u32 vertexCount = regs[PICAInternalRegs::VertexCountReg]; // Total # of vertices to transfer
|
const u32 vertexCount = regs[PICA::InternalRegs::VertexCountReg]; // Total # of vertices to transfer
|
||||||
|
|
||||||
// Configures the type of primitive and the number of vertex shader outputs
|
// Configures the type of primitive and the number of vertex shader outputs
|
||||||
const u32 primConfig = regs[PICAInternalRegs::PrimitiveConfig];
|
const u32 primConfig = regs[PICA::InternalRegs::PrimitiveConfig];
|
||||||
const u32 primType = Helpers::getBits<8, 2>(primConfig);
|
const PICA::PrimType primType = static_cast<PICA::PrimType>(Helpers::getBits<8, 2>(primConfig));
|
||||||
if (primType != 0 && primType != 1 && primType != 3) Helpers::panic("[PICA] Tried to draw unimplemented shape %d\n", primType);
|
if (primType == PICA::PrimType::TriangleFan) Helpers::panic("[PICA] Tried to draw unimplemented shape %d\n", primType);
|
||||||
if (vertexCount > Renderer::vertexBufferSize) Helpers::panic("[PICA] vertexCount > vertexBufferSize");
|
if (vertexCount > Renderer::vertexBufferSize) Helpers::panic("[PICA] vertexCount > vertexBufferSize");
|
||||||
|
|
||||||
if ((primType == 0 && vertexCount % 3) || (primType == 1 && vertexCount < 3)) {
|
if ((primType == PICA::PrimType::TriangleList && vertexCount % 3) ||
|
||||||
|
(primType == PICA::PrimType::TriangleStrip && vertexCount < 3)) {
|
||||||
Helpers::panic("Invalid vertex count for primitive. Type: %d, vert count: %d\n", primType, vertexCount);
|
Helpers::panic("Invalid vertex count for primitive. Type: %d, vert count: %d\n", primType, vertexCount);
|
||||||
}
|
}
|
||||||
|
|
||||||
// Get the configuration for the index buffer, used only for indexed drawing
|
// Get the configuration for the index buffer, used only for indexed drawing
|
||||||
u32 indexBufferConfig = regs[PICAInternalRegs::IndexBufferConfig];
|
u32 indexBufferConfig = regs[PICA::InternalRegs::IndexBufferConfig];
|
||||||
u32 indexBufferPointer = vertexBase + (indexBufferConfig & 0xfffffff);
|
u32 indexBufferPointer = vertexBase + (indexBufferConfig & 0xfffffff);
|
||||||
bool shortIndex = Helpers::getBit<31>(indexBufferConfig); // Indicates whether vert indices are 16-bit or 8-bit
|
bool shortIndex = Helpers::getBit<31>(indexBufferConfig); // Indicates whether vert indices are 16-bit or 8-bit
|
||||||
|
|
||||||
// Stuff the global attribute config registers in one u64 to make attr parsing easier
|
// Stuff the global attribute config registers in one u64 to make attr parsing easier
|
||||||
// TODO: Cache this when the vertex attribute format registers are written to
|
// TODO: Cache this when the vertex attribute format registers are written to
|
||||||
u64 vertexCfg = u64(regs[PICAInternalRegs::AttribFormatLow]) | (u64(regs[PICAInternalRegs::AttribFormatHigh]) << 32);
|
u64 vertexCfg = u64(regs[PICA::InternalRegs::AttribFormatLow]) | (u64(regs[PICA::InternalRegs::AttribFormatHigh]) << 32);
|
||||||
|
|
||||||
if constexpr (!indexed) {
|
if constexpr (!indexed) {
|
||||||
u32 offset = regs[PICAInternalRegs::VertexOffsetReg];
|
u32 offset = regs[PICA::InternalRegs::VertexOffsetReg];
|
||||||
log("PICA::DrawArrays(vertex count = %d, vertexOffset = %d)\n", vertexCount, offset);
|
log("PICA::DrawArrays(vertex count = %d, vertexOffset = %d)\n", vertexCount, offset);
|
||||||
} else {
|
} else {
|
||||||
log("PICA::DrawElements(vertex count = %d, index buffer config = %08X)\n", vertexCount, indexBufferConfig);
|
log("PICA::DrawElements(vertex count = %d, index buffer config = %08X)\n", vertexCount, indexBufferConfig);
|
||||||
|
@ -91,14 +133,14 @@ void GPU::drawArrays() {
|
||||||
}
|
}
|
||||||
|
|
||||||
// Total number of input attributes to shader. Differs between GS and VS. Currently stubbed to the VS one, as we don't have geometry shaders.
|
// Total number of input attributes to shader. Differs between GS and VS. Currently stubbed to the VS one, as we don't have geometry shaders.
|
||||||
const u32 inputAttrCount = (regs[PICAInternalRegs::VertexShaderInputBufferCfg] & 0xf) + 1;
|
const u32 inputAttrCount = (regs[PICA::InternalRegs::VertexShaderInputBufferCfg] & 0xf) + 1;
|
||||||
const u64 inputAttrCfg = getVertexShaderInputConfig();
|
const u64 inputAttrCfg = getVertexShaderInputConfig();
|
||||||
|
|
||||||
for (u32 i = 0; i < vertexCount; i++) {
|
for (u32 i = 0; i < vertexCount; i++) {
|
||||||
u32 vertexIndex; // Index of the vertex in the VBO
|
u32 vertexIndex; // Index of the vertex in the VBO
|
||||||
|
|
||||||
if constexpr (!indexed) {
|
if constexpr (!indexed) {
|
||||||
vertexIndex = i + regs[PICAInternalRegs::VertexOffsetReg];
|
vertexIndex = i + regs[PICA::InternalRegs::VertexOffsetReg];
|
||||||
} else {
|
} else {
|
||||||
if (shortIndex) {
|
if (shortIndex) {
|
||||||
auto ptr = getPointerPhys<u16>(indexBufferPointer);
|
auto ptr = getPointerPhys<u16>(indexBufferPointer);
|
||||||
|
@ -204,32 +246,42 @@ void GPU::drawArrays() {
|
||||||
std::memcpy(&shaderUnit.vs.inputs[mapping], ¤tAttributes[j], sizeof(vec4f));
|
std::memcpy(&shaderUnit.vs.inputs[mapping], ¤tAttributes[j], sizeof(vec4f));
|
||||||
}
|
}
|
||||||
|
|
||||||
if constexpr (useShaderJIT) {
|
if constexpr (useShaderJIT) {
|
||||||
shaderJIT.run(shaderUnit.vs);
|
shaderJIT.run(shaderUnit.vs);
|
||||||
} else {
|
} else {
|
||||||
shaderUnit.vs.run();
|
shaderUnit.vs.run();
|
||||||
}
|
}
|
||||||
|
|
||||||
std::memcpy(&vertices[i].position, &shaderUnit.vs.outputs[0], sizeof(vec4f));
|
OutputVertex out;
|
||||||
std::memcpy(&vertices[i].colour, &shaderUnit.vs.outputs[1], sizeof(vec4f));
|
// Map shader outputs to fixed function properties
|
||||||
std::memcpy(&vertices[i].UVs, &shaderUnit.vs.outputs[2], 2 * sizeof(f24));
|
const u32 totalShaderOutputs = regs[PICA::InternalRegs::ShaderOutputCount] & 7;
|
||||||
|
for (int i = 0; i < totalShaderOutputs; i++) {
|
||||||
|
const u32 config = regs[PICA::InternalRegs::ShaderOutmap0 + i];
|
||||||
|
|
||||||
|
for (int j = 0; j < 4; j++) { // pls unroll
|
||||||
|
const u32 mapping = (config >> (j * 8)) & 0x1F;
|
||||||
|
out.raw[mapping] = shaderUnit.vs.outputs[i][j];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
std::memcpy(&vertices[i].position, &out.s.positions, sizeof(vec4f));
|
||||||
|
std::memcpy(&vertices[i].colour, &out.s.colour, sizeof(vec4f));
|
||||||
|
std::memcpy(&vertices[i].texcoord0, &out.s.texcoord0, 2 * sizeof(f24));
|
||||||
|
std::memcpy(&vertices[i].texcoord1, &out.s.texcoord1, 2 * sizeof(f24));
|
||||||
|
std::memcpy(&vertices[i].texcoord0_w, &out.s.texcoord0_w, sizeof(f24));
|
||||||
|
std::memcpy(&vertices[i].texcoord2, &out.s.texcoord2, 2 * sizeof(f24));
|
||||||
|
|
||||||
//printf("(x, y, z, w) = (%f, %f, %f, %f)\n", (double)vertices[i].position.x(), (double)vertices[i].position.y(), (double)vertices[i].position.z(), (double)vertices[i].position.w());
|
//printf("(x, y, z, w) = (%f, %f, %f, %f)\n", (double)vertices[i].position.x(), (double)vertices[i].position.y(), (double)vertices[i].position.z(), (double)vertices[i].position.w());
|
||||||
//printf("(r, g, b, a) = (%f, %f, %f, %f)\n", (double)vertices[i].colour.r(), (double)vertices[i].colour.g(), (double)vertices[i].colour.b(), (double)vertices[i].colour.a());
|
//printf("(r, g, b, a) = (%f, %f, %f, %f)\n", (double)vertices[i].colour.r(), (double)vertices[i].colour.g(), (double)vertices[i].colour.b(), (double)vertices[i].colour.a());
|
||||||
//printf("(u, v ) = (%f, %f)\n", vertices[i].UVs.u(), vertices[i].UVs.v());
|
//printf("(u, v ) = (%f, %f)\n", vertices[i].UVs.u(), vertices[i].UVs.v());
|
||||||
}
|
}
|
||||||
|
|
||||||
// The fourth type is meant to be "Geometry primitive". TODO: Find out what that is
|
renderer.drawVertices(primType, std::span(vertices).first(vertexCount));
|
||||||
static constexpr std::array<OpenGL::Primitives, 4> primTypes = {
|
|
||||||
OpenGL::Triangle, OpenGL::TriangleStrip, OpenGL::TriangleFan, OpenGL::Triangle
|
|
||||||
};
|
|
||||||
const auto shape = primTypes[primType];
|
|
||||||
renderer.drawVertices(shape, vertices, vertexCount);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
Vertex GPU::getImmediateModeVertex() {
|
Vertex GPU::getImmediateModeVertex() {
|
||||||
Vertex v;
|
Vertex v;
|
||||||
const int totalAttrCount = (regs[PICAInternalRegs::VertexShaderAttrNum] & 0xf) + 1;
|
const int totalAttrCount = (regs[PICA::InternalRegs::VertexShaderAttrNum] & 0xf) + 1;
|
||||||
|
|
||||||
// Copy immediate mode attributes to vertex shader unit
|
// Copy immediate mode attributes to vertex shader unit
|
||||||
for (int i = 0; i < totalAttrCount; i++) {
|
for (int i = 0; i < totalAttrCount; i++) {
|
||||||
|
@ -240,11 +292,11 @@ Vertex GPU::getImmediateModeVertex() {
|
||||||
shaderUnit.vs.run();
|
shaderUnit.vs.run();
|
||||||
std::memcpy(&v.position, &shaderUnit.vs.outputs[0], sizeof(vec4f));
|
std::memcpy(&v.position, &shaderUnit.vs.outputs[0], sizeof(vec4f));
|
||||||
std::memcpy(&v.colour, &shaderUnit.vs.outputs[1], sizeof(vec4f));
|
std::memcpy(&v.colour, &shaderUnit.vs.outputs[1], sizeof(vec4f));
|
||||||
std::memcpy(&v.UVs, &shaderUnit.vs.outputs[2], 2 * sizeof(f24));
|
std::memcpy(&v.texcoord0, &shaderUnit.vs.outputs[2], 2 * sizeof(f24));
|
||||||
|
|
||||||
printf("(x, y, z, w) = (%f, %f, %f, %f)\n", (double)v.position.x(), (double)v.position.y(), (double)v.position.z(), (double)v.position.w());
|
printf("(x, y, z, w) = (%f, %f, %f, %f)\n", (double)v.position.x(), (double)v.position.y(), (double)v.position.z(), (double)v.position.w());
|
||||||
printf("(r, g, b, a) = (%f, %f, %f, %f)\n", (double)v.colour.r(), (double)v.colour.g(), (double)v.colour.b(), (double)v.colour.a());
|
printf("(r, g, b, a) = (%f, %f, %f, %f)\n", (double)v.colour.r(), (double)v.colour.g(), (double)v.colour.b(), (double)v.colour.a());
|
||||||
printf("(u, v ) = (%f, %f)\n", v.UVs.u(), v.UVs.v());
|
printf("(u, v ) = (%f, %f)\n", v.texcoord0.u(), v.texcoord0.v());
|
||||||
|
|
||||||
return v;
|
return v;
|
||||||
}
|
}
|
||||||
|
|
|
@ -33,7 +33,7 @@ u32 GPU::readInternalReg(u32 index) {
|
||||||
}
|
}
|
||||||
|
|
||||||
void GPU::writeInternalReg(u32 index, u32 value, u32 mask) {
|
void GPU::writeInternalReg(u32 index, u32 value, u32 mask) {
|
||||||
using namespace PICAInternalRegs;
|
using namespace PICA::InternalRegs;
|
||||||
|
|
||||||
if (index > regNum) {
|
if (index > regNum) {
|
||||||
Helpers::panic("Tried to write to invalid GPU register. Index: %X, value: %08X\n", index, value);
|
Helpers::panic("Tried to write to invalid GPU register. Index: %X, value: %08X\n", index, value);
|
||||||
|
@ -68,7 +68,7 @@ void GPU::writeInternalReg(u32 index, u32 value, u32 mask) {
|
||||||
|
|
||||||
case ColourBufferFormat: {
|
case ColourBufferFormat: {
|
||||||
u32 format = getBits<16, 3>(value);
|
u32 format = getBits<16, 3>(value);
|
||||||
renderer.setColourFormat(format);
|
renderer.setColourFormat(static_cast<PICA::ColorFmt>(format));
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -79,8 +79,8 @@ void GPU::writeInternalReg(u32 index, u32 value, u32 mask) {
|
||||||
}
|
}
|
||||||
|
|
||||||
case DepthBufferFormat: {
|
case DepthBufferFormat: {
|
||||||
u32 fmt = value & 0x3;
|
u32 format = value & 0x3;
|
||||||
renderer.setDepthFormat(fmt);
|
renderer.setDepthFormat(static_cast<PICA::DepthFmt>(format));
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -137,7 +137,7 @@ void GPU::writeInternalReg(u32 index, u32 value, u32 mask) {
|
||||||
if (fixedAttribIndex < 12) [[likely]] {
|
if (fixedAttribIndex < 12) [[likely]] {
|
||||||
shaderUnit.vs.fixedAttributes[fixedAttribIndex++] = attr;
|
shaderUnit.vs.fixedAttributes[fixedAttribIndex++] = attr;
|
||||||
} else if (fixedAttribIndex == 15) { // Otherwise if it's 15, we're submitting an immediate mode vertex
|
} else if (fixedAttribIndex == 15) { // Otherwise if it's 15, we're submitting an immediate mode vertex
|
||||||
const uint totalAttrCount = (regs[PICAInternalRegs::VertexShaderAttrNum] & 0xf) + 1;
|
const uint totalAttrCount = (regs[PICA::InternalRegs::VertexShaderAttrNum] & 0xf) + 1;
|
||||||
if (totalAttrCount <= immediateModeAttrIndex) {
|
if (totalAttrCount <= immediateModeAttrIndex) {
|
||||||
printf("Broken state in the immediate mode vertex submission pipeline. Failing silently\n");
|
printf("Broken state in the immediate mode vertex submission pipeline. Failing silently\n");
|
||||||
immediateModeAttrIndex = 0;
|
immediateModeAttrIndex = 0;
|
||||||
|
@ -151,13 +151,13 @@ void GPU::writeInternalReg(u32 index, u32 value, u32 mask) {
|
||||||
immediateModeVertices[immediateModeVertIndex++] = v;
|
immediateModeVertices[immediateModeVertIndex++] = v;
|
||||||
|
|
||||||
// Get primitive type
|
// Get primitive type
|
||||||
const u32 primConfig = regs[PICAInternalRegs::PrimitiveConfig];
|
const u32 primConfig = regs[PICA::InternalRegs::PrimitiveConfig];
|
||||||
const u32 primType = getBits<8, 2>(primConfig);
|
const u32 primType = getBits<8, 2>(primConfig);
|
||||||
|
|
||||||
// If we've reached 3 verts, issue a draw call
|
// If we've reached 3 verts, issue a draw call
|
||||||
// Handle rendering depending on the primitive type
|
// Handle rendering depending on the primitive type
|
||||||
if (immediateModeVertIndex == 3) {
|
if (immediateModeVertIndex == 3) {
|
||||||
renderer.drawVertices(OpenGL::Triangle, &immediateModeVertices[0], 3);
|
renderer.drawVertices(PICA::PrimType::TriangleList, immediateModeVertices);
|
||||||
|
|
||||||
switch (primType) {
|
switch (primType) {
|
||||||
// Triangle or geometry primitive. Draw a triangle and discard all vertices
|
// Triangle or geometry primitive. Draw a triangle and discard all vertices
|
||||||
|
|
|
@ -3,7 +3,7 @@
|
||||||
|
|
||||||
namespace fs = std::filesystem;
|
namespace fs = std::filesystem;
|
||||||
|
|
||||||
FSResult ExtSaveDataArchive::createFile(const FSPath& path, u64 size) {
|
HorizonResult ExtSaveDataArchive::createFile(const FSPath& path, u64 size) {
|
||||||
if (size == 0)
|
if (size == 0)
|
||||||
Helpers::panic("ExtSaveData file does not support size == 0");
|
Helpers::panic("ExtSaveData file does not support size == 0");
|
||||||
|
|
||||||
|
@ -15,22 +15,22 @@ FSResult ExtSaveDataArchive::createFile(const FSPath& path, u64 size) {
|
||||||
p += fs::path(path.utf16_string).make_preferred();
|
p += fs::path(path.utf16_string).make_preferred();
|
||||||
|
|
||||||
if (fs::exists(p))
|
if (fs::exists(p))
|
||||||
return FSResult::AlreadyExists;
|
return Result::FS::AlreadyExists;
|
||||||
|
|
||||||
// Create a file of size "size" by creating an empty one, seeking to size - 1 and just writing a 0 there
|
// Create a file of size "size" by creating an empty one, seeking to size - 1 and just writing a 0 there
|
||||||
IOFile file(p.string().c_str(), "wb");
|
IOFile file(p.string().c_str(), "wb");
|
||||||
if (file.seek(size - 1, SEEK_SET) && file.writeBytes("", 1).second == 1) {
|
if (file.seek(size - 1, SEEK_SET) && file.writeBytes("", 1).second == 1) {
|
||||||
return FSResult::Success;
|
return Result::Success;
|
||||||
}
|
}
|
||||||
|
|
||||||
return FSResult::FileTooLarge;
|
return Result::FS::FileTooLarge;
|
||||||
}
|
}
|
||||||
|
|
||||||
Helpers::panic("ExtSaveDataArchive::OpenFile: Failed");
|
Helpers::panic("ExtSaveDataArchive::OpenFile: Failed");
|
||||||
return FSResult::Success;
|
return Result::Success;
|
||||||
}
|
}
|
||||||
|
|
||||||
FSResult ExtSaveDataArchive::deleteFile(const FSPath& path) {
|
HorizonResult ExtSaveDataArchive::deleteFile(const FSPath& path) {
|
||||||
if (path.type == PathType::UTF16) {
|
if (path.type == PathType::UTF16) {
|
||||||
if (!isPathSafe<PathType::UTF16>(path))
|
if (!isPathSafe<PathType::UTF16>(path))
|
||||||
Helpers::panic("Unsafe path in ExtSaveData::DeleteFile");
|
Helpers::panic("Unsafe path in ExtSaveData::DeleteFile");
|
||||||
|
@ -43,7 +43,7 @@ FSResult ExtSaveDataArchive::deleteFile(const FSPath& path) {
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!fs::is_regular_file(p)) {
|
if (!fs::is_regular_file(p)) {
|
||||||
return FSResult::FileNotFound;
|
return Result::FS::FileNotFoundAlt;
|
||||||
}
|
}
|
||||||
|
|
||||||
std::error_code ec;
|
std::error_code ec;
|
||||||
|
@ -55,11 +55,11 @@ FSResult ExtSaveDataArchive::deleteFile(const FSPath& path) {
|
||||||
Helpers::warn("ExtSaveData::DeleteFile: fs::remove failed\n");
|
Helpers::warn("ExtSaveData::DeleteFile: fs::remove failed\n");
|
||||||
}
|
}
|
||||||
|
|
||||||
return FSResult::Success;
|
return Result::Success;
|
||||||
}
|
}
|
||||||
|
|
||||||
Helpers::panic("ExtSaveDataArchive::DeleteFile: Unknown path type");
|
Helpers::panic("ExtSaveDataArchive::DeleteFile: Unknown path type");
|
||||||
return FSResult::Success;
|
return Result::Success;
|
||||||
}
|
}
|
||||||
|
|
||||||
FileDescriptor ExtSaveDataArchive::openFile(const FSPath& path, const FilePerms& perms) {
|
FileDescriptor ExtSaveDataArchive::openFile(const FSPath& path, const FilePerms& perms) {
|
||||||
|
@ -95,7 +95,7 @@ std::string ExtSaveDataArchive::getExtSaveDataPathFromBinary(const FSPath& path)
|
||||||
return backingFolder + std::to_string(saveLow) + std::to_string(saveHigh);
|
return backingFolder + std::to_string(saveLow) + std::to_string(saveHigh);
|
||||||
}
|
}
|
||||||
|
|
||||||
Rust::Result<ArchiveBase*, FSResult> ExtSaveDataArchive::openArchive(const FSPath& path) {
|
Rust::Result<ArchiveBase*, HorizonResult> ExtSaveDataArchive::openArchive(const FSPath& path) {
|
||||||
if (path.type != PathType::Binary || path.binary.size() != 12) {
|
if (path.type != PathType::Binary || path.binary.size() != 12) {
|
||||||
Helpers::panic("ExtSaveData accessed with an invalid path in OpenArchive");
|
Helpers::panic("ExtSaveData accessed with an invalid path in OpenArchive");
|
||||||
}
|
}
|
||||||
|
@ -105,13 +105,13 @@ Rust::Result<ArchiveBase*, FSResult> ExtSaveDataArchive::openArchive(const FSPat
|
||||||
// fs::path formatInfopath = IOFile::getAppData() / "FormatInfo" / (getExtSaveDataPathFromBinary(path) + ".format");
|
// fs::path formatInfopath = IOFile::getAppData() / "FormatInfo" / (getExtSaveDataPathFromBinary(path) + ".format");
|
||||||
// Format info not found so the archive is not formatted
|
// Format info not found so the archive is not formatted
|
||||||
// if (!fs::is_regular_file(formatInfopath)) {
|
// if (!fs::is_regular_file(formatInfopath)) {
|
||||||
// return isShared ? Err(FSResult::NotFormatted) : Err(FSResult::NotFoundInvalid);
|
// return isShared ? Err(Result::FS::NotFormatted) : Err(Result::FS::NotFoundInvalid);
|
||||||
//}
|
//}
|
||||||
|
|
||||||
return Ok((ArchiveBase*)this);
|
return Ok((ArchiveBase*)this);
|
||||||
}
|
}
|
||||||
|
|
||||||
Rust::Result<DirectorySession, FSResult> ExtSaveDataArchive::openDirectory(const FSPath& path) {
|
Rust::Result<DirectorySession, HorizonResult> ExtSaveDataArchive::openDirectory(const FSPath& path) {
|
||||||
if (path.type == PathType::UTF16) {
|
if (path.type == PathType::UTF16) {
|
||||||
if (!isPathSafe<PathType::UTF16>(path))
|
if (!isPathSafe<PathType::UTF16>(path))
|
||||||
Helpers::panic("Unsafe path in ExtSaveData::OpenDirectory");
|
Helpers::panic("Unsafe path in ExtSaveData::OpenDirectory");
|
||||||
|
@ -121,18 +121,18 @@ Rust::Result<DirectorySession, FSResult> ExtSaveDataArchive::openDirectory(const
|
||||||
|
|
||||||
if (fs::is_regular_file(p)) {
|
if (fs::is_regular_file(p)) {
|
||||||
printf("ExtSaveData: OpenArchive used with a file path");
|
printf("ExtSaveData: OpenArchive used with a file path");
|
||||||
return Err(FSResult::UnexpectedFileOrDir);
|
return Err(Result::FS::UnexpectedFileOrDir);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (fs::is_directory(p)) {
|
if (fs::is_directory(p)) {
|
||||||
return Ok(DirectorySession(this, p));
|
return Ok(DirectorySession(this, p));
|
||||||
} else {
|
} else {
|
||||||
return Err(FSResult::FileNotFound);
|
return Err(Result::FS::FileNotFoundAlt);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
Helpers::panic("ExtSaveDataArchive::OpenDirectory: Unimplemented path type");
|
Helpers::panic("ExtSaveDataArchive::OpenDirectory: Unimplemented path type");
|
||||||
return Err(FSResult::Success);
|
return Err(Result::Success);
|
||||||
}
|
}
|
||||||
|
|
||||||
std::optional<u32> ExtSaveDataArchive::readFile(FileSession* file, u64 offset, u32 size, u32 dataPointer) {
|
std::optional<u32> ExtSaveDataArchive::readFile(FileSession* file, u64 offset, u32 size, u32 dataPointer) {
|
||||||
|
|
|
@ -21,14 +21,14 @@ namespace MediaType {
|
||||||
};
|
};
|
||||||
};
|
};
|
||||||
|
|
||||||
FSResult NCCHArchive::createFile(const FSPath& path, u64 size) {
|
HorizonResult NCCHArchive::createFile(const FSPath& path, u64 size) {
|
||||||
Helpers::panic("[NCCH] CreateFile not yet supported");
|
Helpers::panic("[NCCH] CreateFile not yet supported");
|
||||||
return FSResult::Success;
|
return Result::Success;
|
||||||
}
|
}
|
||||||
|
|
||||||
FSResult NCCHArchive::deleteFile(const FSPath& path) {
|
HorizonResult NCCHArchive::deleteFile(const FSPath& path) {
|
||||||
Helpers::panic("[NCCH] Unimplemented DeleteFile");
|
Helpers::panic("[NCCH] Unimplemented DeleteFile");
|
||||||
return FSResult::Success;
|
return Result::Success;
|
||||||
}
|
}
|
||||||
|
|
||||||
FileDescriptor NCCHArchive::openFile(const FSPath& path, const FilePerms& perms) {
|
FileDescriptor NCCHArchive::openFile(const FSPath& path, const FilePerms& perms) {
|
||||||
|
@ -48,7 +48,7 @@ FileDescriptor NCCHArchive::openFile(const FSPath& path, const FilePerms& perms)
|
||||||
return NoFile;
|
return NoFile;
|
||||||
}
|
}
|
||||||
|
|
||||||
Rust::Result<ArchiveBase*, FSResult> NCCHArchive::openArchive(const FSPath& path) {
|
Rust::Result<ArchiveBase*, HorizonResult> NCCHArchive::openArchive(const FSPath& path) {
|
||||||
if (path.type != PathType::Binary || path.binary.size() != 16) {
|
if (path.type != PathType::Binary || path.binary.size() != 16) {
|
||||||
Helpers::panic("NCCHArchive::OpenArchive: Invalid path");
|
Helpers::panic("NCCHArchive::OpenArchive: Invalid path");
|
||||||
}
|
}
|
||||||
|
|
|
@ -4,7 +4,7 @@
|
||||||
|
|
||||||
namespace fs = std::filesystem;
|
namespace fs = std::filesystem;
|
||||||
|
|
||||||
FSResult SaveDataArchive::createFile(const FSPath& path, u64 size) {
|
HorizonResult SaveDataArchive::createFile(const FSPath& path, u64 size) {
|
||||||
if (path.type == PathType::UTF16) {
|
if (path.type == PathType::UTF16) {
|
||||||
if (!isPathSafe<PathType::UTF16>(path))
|
if (!isPathSafe<PathType::UTF16>(path))
|
||||||
Helpers::panic("Unsafe path in SaveData::CreateFile");
|
Helpers::panic("Unsafe path in SaveData::CreateFile");
|
||||||
|
@ -13,28 +13,28 @@ FSResult SaveDataArchive::createFile(const FSPath& path, u64 size) {
|
||||||
p += fs::path(path.utf16_string).make_preferred();
|
p += fs::path(path.utf16_string).make_preferred();
|
||||||
|
|
||||||
if (fs::exists(p))
|
if (fs::exists(p))
|
||||||
return FSResult::AlreadyExists;
|
return Result::FS::AlreadyExists;
|
||||||
|
|
||||||
IOFile file(p.string().c_str(), "wb");
|
IOFile file(p.string().c_str(), "wb");
|
||||||
|
|
||||||
// If the size is 0, leave the file empty and return success
|
// If the size is 0, leave the file empty and return success
|
||||||
if (size == 0) {
|
if (size == 0) {
|
||||||
return FSResult::Success;
|
return Result::Success;
|
||||||
}
|
}
|
||||||
|
|
||||||
// If it is not empty, seek to size - 1 and write a 0 to create a file of size "size"
|
// If it is not empty, seek to size - 1 and write a 0 to create a file of size "size"
|
||||||
else if (file.seek(size - 1, SEEK_SET) && file.writeBytes("", 1).second == 1) {
|
else if (file.seek(size - 1, SEEK_SET) && file.writeBytes("", 1).second == 1) {
|
||||||
return FSResult::Success;
|
return Result::Success;
|
||||||
}
|
}
|
||||||
|
|
||||||
return FSResult::FileTooLarge;
|
return Result::FS::FileTooLarge;
|
||||||
}
|
}
|
||||||
|
|
||||||
Helpers::panic("SaveDataArchive::OpenFile: Failed");
|
Helpers::panic("SaveDataArchive::OpenFile: Failed");
|
||||||
return FSResult::Success;
|
return Result::Success;
|
||||||
}
|
}
|
||||||
|
|
||||||
FSResult SaveDataArchive::createDirectory(const FSPath& path) {
|
HorizonResult SaveDataArchive::createDirectory(const FSPath& path) {
|
||||||
if (path.type == PathType::UTF16) {
|
if (path.type == PathType::UTF16) {
|
||||||
if (!isPathSafe<PathType::UTF16>(path))
|
if (!isPathSafe<PathType::UTF16>(path))
|
||||||
Helpers::panic("Unsafe path in SaveData::OpenFile");
|
Helpers::panic("Unsafe path in SaveData::OpenFile");
|
||||||
|
@ -43,19 +43,19 @@ FSResult SaveDataArchive::createDirectory(const FSPath& path) {
|
||||||
p += fs::path(path.utf16_string).make_preferred();
|
p += fs::path(path.utf16_string).make_preferred();
|
||||||
|
|
||||||
if (fs::is_directory(p))
|
if (fs::is_directory(p))
|
||||||
return FSResult::AlreadyExists;
|
return Result::FS::AlreadyExists;
|
||||||
if (fs::is_regular_file(p)) {
|
if (fs::is_regular_file(p)) {
|
||||||
Helpers::panic("File path passed to SaveData::CreateDirectory");
|
Helpers::panic("File path passed to SaveData::CreateDirectory");
|
||||||
}
|
}
|
||||||
|
|
||||||
bool success = fs::create_directory(p);
|
bool success = fs::create_directory(p);
|
||||||
return success ? FSResult::Success : FSResult::UnexpectedFileOrDir;
|
return success ? Result::Success : Result::FS::UnexpectedFileOrDir;
|
||||||
} else {
|
} else {
|
||||||
Helpers::panic("Unimplemented SaveData::CreateDirectory");
|
Helpers::panic("Unimplemented SaveData::CreateDirectory");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
FSResult SaveDataArchive::deleteFile(const FSPath& path) {
|
HorizonResult SaveDataArchive::deleteFile(const FSPath& path) {
|
||||||
if (path.type == PathType::UTF16) {
|
if (path.type == PathType::UTF16) {
|
||||||
if (!isPathSafe<PathType::UTF16>(path))
|
if (!isPathSafe<PathType::UTF16>(path))
|
||||||
Helpers::panic("Unsafe path in SaveData::DeleteFile");
|
Helpers::panic("Unsafe path in SaveData::DeleteFile");
|
||||||
|
@ -68,7 +68,7 @@ FSResult SaveDataArchive::deleteFile(const FSPath& path) {
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!fs::is_regular_file(p)) {
|
if (!fs::is_regular_file(p)) {
|
||||||
return FSResult::FileNotFound;
|
return Result::FS::FileNotFoundAlt;
|
||||||
}
|
}
|
||||||
|
|
||||||
std::error_code ec;
|
std::error_code ec;
|
||||||
|
@ -80,11 +80,11 @@ FSResult SaveDataArchive::deleteFile(const FSPath& path) {
|
||||||
Helpers::warn("SaveData::DeleteFile: fs::remove failed\n");
|
Helpers::warn("SaveData::DeleteFile: fs::remove failed\n");
|
||||||
}
|
}
|
||||||
|
|
||||||
return FSResult::Success;
|
return Result::Success;
|
||||||
}
|
}
|
||||||
|
|
||||||
Helpers::panic("SaveDataArchive::DeleteFile: Unknown path type");
|
Helpers::panic("SaveDataArchive::DeleteFile: Unknown path type");
|
||||||
return FSResult::Success;
|
return Result::Success;
|
||||||
}
|
}
|
||||||
|
|
||||||
FileDescriptor SaveDataArchive::openFile(const FSPath& path, const FilePerms& perms) {
|
FileDescriptor SaveDataArchive::openFile(const FSPath& path, const FilePerms& perms) {
|
||||||
|
@ -121,7 +121,7 @@ FileDescriptor SaveDataArchive::openFile(const FSPath& path, const FilePerms& pe
|
||||||
return FileError;
|
return FileError;
|
||||||
}
|
}
|
||||||
|
|
||||||
Rust::Result<DirectorySession, FSResult> SaveDataArchive::openDirectory(const FSPath& path) {
|
Rust::Result<DirectorySession, HorizonResult> SaveDataArchive::openDirectory(const FSPath& path) {
|
||||||
if (path.type == PathType::UTF16) {
|
if (path.type == PathType::UTF16) {
|
||||||
if (!isPathSafe<PathType::UTF16>(path))
|
if (!isPathSafe<PathType::UTF16>(path))
|
||||||
Helpers::panic("Unsafe path in SaveData::OpenDirectory");
|
Helpers::panic("Unsafe path in SaveData::OpenDirectory");
|
||||||
|
@ -131,34 +131,34 @@ Rust::Result<DirectorySession, FSResult> SaveDataArchive::openDirectory(const FS
|
||||||
|
|
||||||
if (fs::is_regular_file(p)) {
|
if (fs::is_regular_file(p)) {
|
||||||
printf("SaveData: OpenDirectory used with a file path");
|
printf("SaveData: OpenDirectory used with a file path");
|
||||||
return Err(FSResult::UnexpectedFileOrDir);
|
return Err(Result::FS::UnexpectedFileOrDir);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (fs::is_directory(p)) {
|
if (fs::is_directory(p)) {
|
||||||
return Ok(DirectorySession(this, p));
|
return Ok(DirectorySession(this, p));
|
||||||
} else {
|
} else {
|
||||||
return Err(FSResult::FileNotFound);
|
return Err(Result::FS::FileNotFoundAlt);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
Helpers::panic("SaveDataArchive::OpenDirectory: Unimplemented path type");
|
Helpers::panic("SaveDataArchive::OpenDirectory: Unimplemented path type");
|
||||||
return Err(FSResult::Success);
|
return Err(Result::Success);
|
||||||
}
|
}
|
||||||
|
|
||||||
Rust::Result<ArchiveBase::FormatInfo, FSResult> SaveDataArchive::getFormatInfo(const FSPath& path) {
|
Rust::Result<ArchiveBase::FormatInfo, HorizonResult> SaveDataArchive::getFormatInfo(const FSPath& path) {
|
||||||
const fs::path formatInfoPath = getFormatInfoPath();
|
const fs::path formatInfoPath = getFormatInfoPath();
|
||||||
IOFile file(formatInfoPath, "rb");
|
IOFile file(formatInfoPath, "rb");
|
||||||
|
|
||||||
// If the file failed to open somehow, we return that the archive is not formatted
|
// If the file failed to open somehow, we return that the archive is not formatted
|
||||||
if (!file.isOpen()) {
|
if (!file.isOpen()) {
|
||||||
return Err(FSResult::NotFormatted);
|
return Err(Result::FS::NotFormatted);
|
||||||
}
|
}
|
||||||
|
|
||||||
FormatInfo ret;
|
FormatInfo ret;
|
||||||
auto [success, bytesRead] = file.readBytes(&ret, sizeof(FormatInfo));
|
auto [success, bytesRead] = file.readBytes(&ret, sizeof(FormatInfo));
|
||||||
if (!success || bytesRead != sizeof(FormatInfo)) {
|
if (!success || bytesRead != sizeof(FormatInfo)) {
|
||||||
Helpers::warn("SaveData::GetFormatInfo: Format file exists but was not properly read into the FormatInfo struct");
|
Helpers::warn("SaveData::GetFormatInfo: Format file exists but was not properly read into the FormatInfo struct");
|
||||||
return Err(FSResult::NotFormatted);
|
return Err(Result::FS::NotFormatted);
|
||||||
}
|
}
|
||||||
|
|
||||||
return Ok(ret);
|
return Ok(ret);
|
||||||
|
@ -168,7 +168,7 @@ void SaveDataArchive::format(const FSPath& path, const ArchiveBase::FormatInfo&
|
||||||
const fs::path saveDataPath = IOFile::getAppData() / "SaveData";
|
const fs::path saveDataPath = IOFile::getAppData() / "SaveData";
|
||||||
const fs::path formatInfoPath = getFormatInfoPath();
|
const fs::path formatInfoPath = getFormatInfoPath();
|
||||||
|
|
||||||
// Delete all contents by deleting the directory then recreating it
|
// Delete all contents by deleting the directory then recreating it
|
||||||
fs::remove_all(saveDataPath);
|
fs::remove_all(saveDataPath);
|
||||||
fs::create_directories(saveDataPath);
|
fs::create_directories(saveDataPath);
|
||||||
|
|
||||||
|
@ -177,16 +177,16 @@ void SaveDataArchive::format(const FSPath& path, const ArchiveBase::FormatInfo&
|
||||||
file.writeBytes(&info, sizeof(info));
|
file.writeBytes(&info, sizeof(info));
|
||||||
}
|
}
|
||||||
|
|
||||||
Rust::Result<ArchiveBase*, FSResult> SaveDataArchive::openArchive(const FSPath& path) {
|
Rust::Result<ArchiveBase*, HorizonResult> SaveDataArchive::openArchive(const FSPath& path) {
|
||||||
if (path.type != PathType::Empty) {
|
if (path.type != PathType::Empty) {
|
||||||
Helpers::panic("Unimplemented path type for SaveData archive: %d\n", path.type);
|
Helpers::panic("Unimplemented path type for SaveData archive: %d\n", path.type);
|
||||||
return Err(FSResult::NotFoundInvalid);
|
return Err(Result::FS::NotFoundInvalid);
|
||||||
}
|
}
|
||||||
|
|
||||||
const fs::path formatInfoPath = getFormatInfoPath();
|
const fs::path formatInfoPath = getFormatInfoPath();
|
||||||
// Format info not found so the archive is not formatted
|
// Format info not found so the archive is not formatted
|
||||||
if (!fs::is_regular_file(formatInfoPath)) {
|
if (!fs::is_regular_file(formatInfoPath)) {
|
||||||
return Err(FSResult::NotFormatted);
|
return Err(Result::FS::NotFormatted);
|
||||||
}
|
}
|
||||||
|
|
||||||
return Ok((ArchiveBase*)this);
|
return Ok((ArchiveBase*)this);
|
||||||
|
|
|
@ -1,14 +1,14 @@
|
||||||
#include "fs/archive_sdmc.hpp"
|
#include "fs/archive_sdmc.hpp"
|
||||||
#include <memory>
|
#include <memory>
|
||||||
|
|
||||||
FSResult SDMCArchive::createFile(const FSPath& path, u64 size) {
|
HorizonResult SDMCArchive::createFile(const FSPath& path, u64 size) {
|
||||||
Helpers::panic("[SDMC] CreateFile not yet supported");
|
Helpers::panic("[SDMC] CreateFile not yet supported");
|
||||||
return FSResult::Success;
|
return Result::Success;
|
||||||
}
|
}
|
||||||
|
|
||||||
FSResult SDMCArchive::deleteFile(const FSPath& path) {
|
HorizonResult SDMCArchive::deleteFile(const FSPath& path) {
|
||||||
Helpers::panic("[SDMC] Unimplemented DeleteFile");
|
Helpers::panic("[SDMC] Unimplemented DeleteFile");
|
||||||
return FSResult::Success;
|
return Result::Success;
|
||||||
}
|
}
|
||||||
|
|
||||||
FileDescriptor SDMCArchive::openFile(const FSPath& path, const FilePerms& perms) {
|
FileDescriptor SDMCArchive::openFile(const FSPath& path, const FilePerms& perms) {
|
||||||
|
@ -16,9 +16,9 @@ FileDescriptor SDMCArchive::openFile(const FSPath& path, const FilePerms& perms)
|
||||||
return FileError;
|
return FileError;
|
||||||
}
|
}
|
||||||
|
|
||||||
Rust::Result<ArchiveBase*, FSResult> SDMCArchive::openArchive(const FSPath& path) {
|
Rust::Result<ArchiveBase*, HorizonResult> SDMCArchive::openArchive(const FSPath& path) {
|
||||||
printf("SDMCArchive::OpenArchive: Failed\n");
|
printf("SDMCArchive::OpenArchive: Failed\n");
|
||||||
return Err(FSResult::NotFormatted);
|
return Err(Result::FS::NotFormatted);
|
||||||
}
|
}
|
||||||
|
|
||||||
std::optional<u32> SDMCArchive::readFile(FileSession* file, u64 offset, u32 size, u32 dataPointer) {
|
std::optional<u32> SDMCArchive::readFile(FileSession* file, u64 offset, u32 size, u32 dataPointer) {
|
||||||
|
|
|
@ -9,14 +9,14 @@ namespace PathType {
|
||||||
};
|
};
|
||||||
};
|
};
|
||||||
|
|
||||||
FSResult SelfNCCHArchive::createFile(const FSPath& path, u64 size) {
|
HorizonResult SelfNCCHArchive::createFile(const FSPath& path, u64 size) {
|
||||||
Helpers::panic("[SelfNCCH] CreateFile not yet supported");
|
Helpers::panic("[SelfNCCH] CreateFile not yet supported");
|
||||||
return FSResult::Success;
|
return Result::Success;
|
||||||
}
|
}
|
||||||
|
|
||||||
FSResult SelfNCCHArchive::deleteFile(const FSPath& path) {
|
HorizonResult SelfNCCHArchive::deleteFile(const FSPath& path) {
|
||||||
Helpers::panic("[SelfNCCH] Unimplemented DeleteFile");
|
Helpers::panic("[SelfNCCH] Unimplemented DeleteFile");
|
||||||
return FSResult::Success;
|
return Result::Success;
|
||||||
}
|
}
|
||||||
|
|
||||||
FileDescriptor SelfNCCHArchive::openFile(const FSPath& path, const FilePerms& perms) {
|
FileDescriptor SelfNCCHArchive::openFile(const FSPath& path, const FilePerms& perms) {
|
||||||
|
@ -40,10 +40,10 @@ FileDescriptor SelfNCCHArchive::openFile(const FSPath& path, const FilePerms& pe
|
||||||
return NoFile; // No file descriptor needed for RomFS
|
return NoFile; // No file descriptor needed for RomFS
|
||||||
}
|
}
|
||||||
|
|
||||||
Rust::Result<ArchiveBase*, FSResult> SelfNCCHArchive::openArchive(const FSPath& path) {
|
Rust::Result<ArchiveBase*, HorizonResult> SelfNCCHArchive::openArchive(const FSPath& path) {
|
||||||
if (path.type != PathType::Empty) {
|
if (path.type != PathType::Empty) {
|
||||||
Helpers::panic("Invalid path type for SelfNCCH archive: %d\n", path.type);
|
Helpers::panic("Invalid path type for SelfNCCH archive: %d\n", path.type);
|
||||||
return Err(FSResult::NotFoundInvalid);
|
return Err(Result::FS::NotFoundInvalid);
|
||||||
}
|
}
|
||||||
|
|
||||||
return Ok((ArchiveBase*)this);
|
return Ok((ArchiveBase*)this);
|
||||||
|
|
|
@ -26,7 +26,7 @@ Handle Kernel::makeArbiter() {
|
||||||
// Result CreateAddressArbiter(Handle* arbiter)
|
// Result CreateAddressArbiter(Handle* arbiter)
|
||||||
void Kernel::createAddressArbiter() {
|
void Kernel::createAddressArbiter() {
|
||||||
logSVC("CreateAddressArbiter\n");
|
logSVC("CreateAddressArbiter\n");
|
||||||
regs[0] = SVCResult::Success;
|
regs[0] = Result::Success;
|
||||||
regs[1] = makeArbiter();
|
regs[1] = makeArbiter();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -43,7 +43,7 @@ void Kernel::arbitrateAddress() {
|
||||||
|
|
||||||
const auto arbiter = getObject(handle, KernelObjectType::AddressArbiter);
|
const auto arbiter = getObject(handle, KernelObjectType::AddressArbiter);
|
||||||
if (arbiter == nullptr) [[unlikely]] {
|
if (arbiter == nullptr) [[unlikely]] {
|
||||||
regs[0] = SVCResult::BadHandle;
|
regs[0] = Result::Kernel::InvalidHandle;
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -52,16 +52,16 @@ void Kernel::arbitrateAddress() {
|
||||||
}
|
}
|
||||||
|
|
||||||
if (type > 4) [[unlikely]] {
|
if (type > 4) [[unlikely]] {
|
||||||
regs[0] = SVCResult::InvalidEnumValueAlt;
|
regs[0] = Result::FND::InvalidEnumValue;
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
// This needs to put the error code in r0 before we change threads
|
// This needs to put the error code in r0 before we change threads
|
||||||
regs[0] = SVCResult::Success;
|
regs[0] = Result::Success;
|
||||||
|
|
||||||
switch (static_cast<ArbitrationType>(type)) {
|
switch (static_cast<ArbitrationType>(type)) {
|
||||||
// Puts this thread to sleep if word < value until another thread arbitrates the address using SIGNAL
|
// Puts this thread to sleep if word < value until another thread arbitrates the address using SIGNAL
|
||||||
case ArbitrationType::WaitIfLess: {
|
case ArbitrationType::WaitIfLess: {
|
||||||
s32 word = static_cast<s32>(mem.read32(address)); // Yes this is meant to be signed
|
s32 word = static_cast<s32>(mem.read32(address)); // Yes this is meant to be signed
|
||||||
if (word < value) {
|
if (word < value) {
|
||||||
sleepThreadOnArbiter(address);
|
sleepThreadOnArbiter(address);
|
||||||
}
|
}
|
||||||
|
@ -71,7 +71,7 @@ void Kernel::arbitrateAddress() {
|
||||||
// Puts this thread to sleep if word < value until another thread arbitrates the address using SIGNAL
|
// Puts this thread to sleep if word < value until another thread arbitrates the address using SIGNAL
|
||||||
// If the thread is put to sleep, the arbiter address is decremented
|
// If the thread is put to sleep, the arbiter address is decremented
|
||||||
case ArbitrationType::DecrementAndWaitIfLess: {
|
case ArbitrationType::DecrementAndWaitIfLess: {
|
||||||
s32 word = static_cast<s32>(mem.read32(address)); // Yes this is meant to be signed
|
s32 word = static_cast<s32>(mem.read32(address)); // Yes this is meant to be signed
|
||||||
if (word < value) {
|
if (word < value) {
|
||||||
mem.write32(address, word - 1);
|
mem.write32(address, word - 1);
|
||||||
sleepThreadOnArbiter(address);
|
sleepThreadOnArbiter(address);
|
||||||
|
|
|
@ -7,12 +7,6 @@ namespace DirectoryOps {
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
namespace Result {
|
|
||||||
enum : u32 {
|
|
||||||
Success = 0
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
void Kernel::handleDirectoryOperation(u32 messagePointer, Handle directory) {
|
void Kernel::handleDirectoryOperation(u32 messagePointer, Handle directory) {
|
||||||
const u32 cmd = mem.read32(messagePointer);
|
const u32 cmd = mem.read32(messagePointer);
|
||||||
switch (cmd) {
|
switch (cmd) {
|
||||||
|
|
|
@ -38,7 +38,7 @@ bool Kernel::signalEvent(Handle handle) {
|
||||||
// One-shot events get cleared once they are acquired by some thread and only wake up 1 thread at a time
|
// One-shot events get cleared once they are acquired by some thread and only wake up 1 thread at a time
|
||||||
if (event->resetType == ResetType::OneShot) {
|
if (event->resetType == ResetType::OneShot) {
|
||||||
int index = wakeupOneThread(event->waitlist, handle); // Wake up one thread with the highest priority
|
int index = wakeupOneThread(event->waitlist, handle); // Wake up one thread with the highest priority
|
||||||
event->waitlist ^= (1ull << index); // Remove thread from waitlist
|
event->waitlist ^= (1ull << index); // Remove thread from waitlist
|
||||||
event->fired = false;
|
event->fired = false;
|
||||||
} else {
|
} else {
|
||||||
wakeupAllThreads(event->waitlist, handle);
|
wakeupAllThreads(event->waitlist, handle);
|
||||||
|
@ -64,11 +64,11 @@ void Kernel::svcCreateEvent() {
|
||||||
|
|
||||||
logSVC("CreateEvent(handle pointer = %08X, resetType = %s)\n", outPointer, resetTypeToString(resetType));
|
logSVC("CreateEvent(handle pointer = %08X, resetType = %s)\n", outPointer, resetTypeToString(resetType));
|
||||||
|
|
||||||
regs[0] = SVCResult::Success;
|
regs[0] = Result::Success;
|
||||||
regs[1] = makeEvent(static_cast<ResetType>(resetType));
|
regs[1] = makeEvent(static_cast<ResetType>(resetType));
|
||||||
}
|
}
|
||||||
|
|
||||||
// Result ClearEvent(Handle event)
|
// Result ClearEvent(Handle event)
|
||||||
void Kernel::svcClearEvent() {
|
void Kernel::svcClearEvent() {
|
||||||
const Handle handle = regs[0];
|
const Handle handle = regs[0];
|
||||||
const auto event = getObject(handle, KernelObjectType::Event);
|
const auto event = getObject(handle, KernelObjectType::Event);
|
||||||
|
@ -76,15 +76,15 @@ void Kernel::svcClearEvent() {
|
||||||
|
|
||||||
if (event == nullptr) [[unlikely]] {
|
if (event == nullptr) [[unlikely]] {
|
||||||
Helpers::panic("Tried to clear non-existent event (handle = %X)", handle);
|
Helpers::panic("Tried to clear non-existent event (handle = %X)", handle);
|
||||||
regs[0] = SVCResult::BadHandle;
|
regs[0] = Result::Kernel::InvalidHandle;
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
event->getData<Event>()->fired = false;
|
event->getData<Event>()->fired = false;
|
||||||
regs[0] = SVCResult::Success;
|
regs[0] = Result::Success;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Result SignalEvent(Handle event)
|
// Result SignalEvent(Handle event)
|
||||||
void Kernel::svcSignalEvent() {
|
void Kernel::svcSignalEvent() {
|
||||||
const Handle handle = regs[0];
|
const Handle handle = regs[0];
|
||||||
logSVC("SignalEvent(event handle = %X)\n", handle);
|
logSVC("SignalEvent(event handle = %X)\n", handle);
|
||||||
|
@ -92,15 +92,15 @@ void Kernel::svcSignalEvent() {
|
||||||
|
|
||||||
if (object == nullptr) {
|
if (object == nullptr) {
|
||||||
Helpers::panic("Signalled non-existent event: %X\n", handle);
|
Helpers::panic("Signalled non-existent event: %X\n", handle);
|
||||||
regs[0] = SVCResult::BadHandle;
|
regs[0] = Result::Kernel::InvalidHandle;
|
||||||
} else {
|
} else {
|
||||||
// We must signalEvent after setting r0, otherwise the r0 of the new thread will ne corrupted
|
// We must signalEvent after setting r0, otherwise the r0 of the new thread will ne corrupted
|
||||||
regs[0] = SVCResult::Success;
|
regs[0] = Result::Success;
|
||||||
signalEvent(handle);
|
signalEvent(handle);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Result WaitSynchronization1(Handle handle, s64 timeout_nanoseconds)
|
// Result WaitSynchronization1(Handle handle, s64 timeout_nanoseconds)
|
||||||
void Kernel::waitSynchronization1() {
|
void Kernel::waitSynchronization1() {
|
||||||
const Handle handle = regs[0];
|
const Handle handle = regs[0];
|
||||||
const s64 ns = s64(u64(regs[1]) | (u64(regs[2]) << 32));
|
const s64 ns = s64(u64(regs[1]) | (u64(regs[2]) << 32));
|
||||||
|
@ -110,7 +110,7 @@ void Kernel::waitSynchronization1() {
|
||||||
|
|
||||||
if (object == nullptr) [[unlikely]] {
|
if (object == nullptr) [[unlikely]] {
|
||||||
Helpers::panic("WaitSynchronization1: Bad event handle %X\n", handle);
|
Helpers::panic("WaitSynchronization1: Bad event handle %X\n", handle);
|
||||||
regs[0] = SVCResult::BadHandle;
|
regs[0] = Result::Kernel::InvalidHandle;
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -120,16 +120,16 @@ void Kernel::waitSynchronization1() {
|
||||||
|
|
||||||
if (!shouldWaitOnObject(object)) {
|
if (!shouldWaitOnObject(object)) {
|
||||||
acquireSyncObject(object, threads[currentThreadIndex]); // Acquire the object since it's ready
|
acquireSyncObject(object, threads[currentThreadIndex]); // Acquire the object since it's ready
|
||||||
regs[0] = SVCResult::Success;
|
regs[0] = Result::Success;
|
||||||
rescheduleThreads();
|
rescheduleThreads();
|
||||||
} else {
|
} else {
|
||||||
// Timeout is 0, don't bother waiting, instantly timeout
|
// Timeout is 0, don't bother waiting, instantly timeout
|
||||||
if (ns == 0) {
|
if (ns == 0) {
|
||||||
regs[0] = SVCResult::Timeout;
|
regs[0] = Result::OS::Timeout;
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
regs[0] = SVCResult::Timeout; // This will be overwritten with success if we don't timeout
|
regs[0] = Result::OS::Timeout; // This will be overwritten with success if we don't timeout
|
||||||
|
|
||||||
auto& t = threads[currentThreadIndex];
|
auto& t = threads[currentThreadIndex];
|
||||||
t.waitList.resize(1);
|
t.waitList.resize(1);
|
||||||
|
@ -180,7 +180,7 @@ void Kernel::waitSynchronizationN() {
|
||||||
// Panic if one of the objects is not even an object
|
// Panic if one of the objects is not even an object
|
||||||
if (object == nullptr) [[unlikely]] {
|
if (object == nullptr) [[unlikely]] {
|
||||||
Helpers::panic("WaitSynchronizationN: Bad object handle %X\n", handle);
|
Helpers::panic("WaitSynchronizationN: Bad object handle %X\n", handle);
|
||||||
regs[0] = SVCResult::BadHandle;
|
regs[0] = Result::Kernel::InvalidHandle;
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -206,16 +206,16 @@ void Kernel::waitSynchronizationN() {
|
||||||
|
|
||||||
// We only need to wait on one object. Easy...?!
|
// We only need to wait on one object. Easy...?!
|
||||||
if (!waitAll) {
|
if (!waitAll) {
|
||||||
// If there's ready objects, acquire the first one and return
|
// If there's ready objects, acquire the first one and return
|
||||||
if (oneObjectReady) {
|
if (oneObjectReady) {
|
||||||
regs[0] = SVCResult::Success;
|
regs[0] = Result::Success;
|
||||||
regs[1] = firstReadyObjectIndex; // Return index of the acquired object
|
regs[1] = firstReadyObjectIndex; // Return index of the acquired object
|
||||||
acquireSyncObject(waitObjects[firstReadyObjectIndex].second, t); // Acquire object
|
acquireSyncObject(waitObjects[firstReadyObjectIndex].second, t); // Acquire object
|
||||||
rescheduleThreads();
|
rescheduleThreads();
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
regs[0] = SVCResult::Timeout; // This will be overwritten with success if we don't timeout
|
regs[0] = Result::OS::Timeout; // This will be overwritten with success if we don't timeout
|
||||||
// If the thread wakes up without timeout, this will be adjusted to the index of the handle that woke us up
|
// If the thread wakes up without timeout, this will be adjusted to the index of the handle that woke us up
|
||||||
regs[1] = 0xFFFFFFFF;
|
regs[1] = 0xFFFFFFFF;
|
||||||
t.waitList.resize(handleCount);
|
t.waitList.resize(handleCount);
|
||||||
|
@ -223,7 +223,7 @@ void Kernel::waitSynchronizationN() {
|
||||||
t.outPointer = outPointer;
|
t.outPointer = outPointer;
|
||||||
t.waitingNanoseconds = ns;
|
t.waitingNanoseconds = ns;
|
||||||
t.sleepTick = cpu.getTicks();
|
t.sleepTick = cpu.getTicks();
|
||||||
|
|
||||||
for (s32 i = 0; i < handleCount; i++) {
|
for (s32 i = 0; i < handleCount; i++) {
|
||||||
t.waitList[i] = waitObjects[i].first; // Add object to this thread's waitlist
|
t.waitList[i] = waitObjects[i].first; // Add object to this thread's waitlist
|
||||||
waitObjects[i].second->getWaitlist() |= (1ull << currentThreadIndex); // And add the thread to the object's waitlist
|
waitObjects[i].second->getWaitlist() |= (1ull << currentThreadIndex); // And add the thread to the object's waitlist
|
||||||
|
|
|
@ -14,12 +14,6 @@ namespace FileOps {
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
namespace Result {
|
|
||||||
enum : u32 {
|
|
||||||
Success = 0
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
void Kernel::handleFileOperation(u32 messagePointer, Handle file) {
|
void Kernel::handleFileOperation(u32 messagePointer, Handle file) {
|
||||||
const u32 cmd = mem.read32(messagePointer);
|
const u32 cmd = mem.read32(messagePointer);
|
||||||
|
@ -90,7 +84,7 @@ void Kernel::readFile(u32 messagePointer, Handle fileHandle) {
|
||||||
if (!file->isOpen) {
|
if (!file->isOpen) {
|
||||||
Helpers::panic("Tried to read closed file");
|
Helpers::panic("Tried to read closed file");
|
||||||
}
|
}
|
||||||
|
|
||||||
// Handle files with their own file descriptors by just fread'ing the data
|
// Handle files with their own file descriptors by just fread'ing the data
|
||||||
if (file->fd) {
|
if (file->fd) {
|
||||||
std::unique_ptr<u8[]> data(new u8[size]);
|
std::unique_ptr<u8[]> data(new u8[size]);
|
||||||
|
|
|
@ -149,7 +149,7 @@ u32 Kernel::getTLSPointer() {
|
||||||
// Result CloseHandle(Handle handle)
|
// Result CloseHandle(Handle handle)
|
||||||
void Kernel::svcCloseHandle() {
|
void Kernel::svcCloseHandle() {
|
||||||
logSVC("CloseHandle(handle = %d) (Unimplemented)\n", regs[0]);
|
logSVC("CloseHandle(handle = %d) (Unimplemented)\n", regs[0]);
|
||||||
regs[0] = SVCResult::Success;
|
regs[0] = Result::Success;
|
||||||
}
|
}
|
||||||
|
|
||||||
// u64 GetSystemTick()
|
// u64 GetSystemTick()
|
||||||
|
@ -169,7 +169,7 @@ void Kernel::outputDebugString() {
|
||||||
|
|
||||||
std::string message = mem.readString(pointer, size);
|
std::string message = mem.readString(pointer, size);
|
||||||
logDebugString("[OutputDebugString] %s\n", message.c_str());
|
logDebugString("[OutputDebugString] %s\n", message.c_str());
|
||||||
regs[0] = SVCResult::Success;
|
regs[0] = Result::Success;
|
||||||
}
|
}
|
||||||
|
|
||||||
void Kernel::getProcessID() {
|
void Kernel::getProcessID() {
|
||||||
|
@ -178,11 +178,11 @@ void Kernel::getProcessID() {
|
||||||
logSVC("GetProcessID(process: %s)\n", getProcessName(pid).c_str());
|
logSVC("GetProcessID(process: %s)\n", getProcessName(pid).c_str());
|
||||||
|
|
||||||
if (process == nullptr) [[unlikely]] {
|
if (process == nullptr) [[unlikely]] {
|
||||||
regs[0] = SVCResult::BadHandle;
|
regs[0] = Result::Kernel::InvalidHandle;
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
regs[0] = SVCResult::Success;
|
regs[0] = Result::Success;
|
||||||
regs[1] = process->getData<Process>()->id;
|
regs[1] = process->getData<Process>()->id;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -194,7 +194,7 @@ void Kernel::getProcessInfo() {
|
||||||
logSVC("GetProcessInfo(process: %s, type = %d)\n", getProcessName(pid).c_str(), type);
|
logSVC("GetProcessInfo(process: %s, type = %d)\n", getProcessName(pid).c_str(), type);
|
||||||
|
|
||||||
if (process == nullptr) [[unlikely]] {
|
if (process == nullptr) [[unlikely]] {
|
||||||
regs[0] = SVCResult::BadHandle;
|
regs[0] = Result::Kernel::InvalidHandle;
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -215,7 +215,7 @@ void Kernel::getProcessInfo() {
|
||||||
Helpers::panic("GetProcessInfo: unimplemented type %d", type);
|
Helpers::panic("GetProcessInfo: unimplemented type %d", type);
|
||||||
}
|
}
|
||||||
|
|
||||||
regs[0] = SVCResult::Success;
|
regs[0] = Result::Success;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Result DuplicateHandle(Handle* out, Handle original)
|
// Result DuplicateHandle(Handle* out, Handle original)
|
||||||
|
@ -224,7 +224,7 @@ void Kernel::duplicateHandle() {
|
||||||
logSVC("DuplicateHandle(handle = %X)\n", original);
|
logSVC("DuplicateHandle(handle = %X)\n", original);
|
||||||
|
|
||||||
if (original == KernelHandles::CurrentThread) {
|
if (original == KernelHandles::CurrentThread) {
|
||||||
regs[0] = SVCResult::Success;
|
regs[0] = Result::Success;
|
||||||
Handle ret = makeObject(KernelObjectType::Thread);
|
Handle ret = makeObject(KernelObjectType::Thread);
|
||||||
objects[ret].data = &threads[currentThreadIndex];
|
objects[ret].data = &threads[currentThreadIndex];
|
||||||
|
|
||||||
|
|
|
@ -36,7 +36,7 @@ static constexpr bool isAligned(u32 value) {
|
||||||
return (value & 0xFFF) == 0;
|
return (value & 0xFFF) == 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Result ControlMemory(u32* outaddr, u32 addr0, u32 addr1, u32 size,
|
// Result ControlMemory(u32* outaddr, u32 addr0, u32 addr1, u32 size,
|
||||||
// MemoryOperation operation, MemoryPermission permissions)
|
// MemoryOperation operation, MemoryPermission permissions)
|
||||||
// This has a weird ABI documented here https://www.3dbrew.org/wiki/Kernel_ABI
|
// This has a weird ABI documented here https://www.3dbrew.org/wiki/Kernel_ABI
|
||||||
// TODO: Does this need to write to outaddr?
|
// TODO: Does this need to write to outaddr?
|
||||||
|
@ -64,7 +64,7 @@ void Kernel::controlMemory() {
|
||||||
if (!isAligned(addr0) || !isAligned(addr1) || !isAligned(size)) {
|
if (!isAligned(addr0) || !isAligned(addr1) || !isAligned(size)) {
|
||||||
Helpers::panic("ControlMemory: Unaligned parameters\nAddr0: %08X\nAddr1: %08X\nSize: %08X", addr0, addr1, size);
|
Helpers::panic("ControlMemory: Unaligned parameters\nAddr0: %08X\nAddr1: %08X\nSize: %08X", addr0, addr1, size);
|
||||||
}
|
}
|
||||||
|
|
||||||
logSVC("ControlMemory(addr0 = %08X, addr1 = %08X, size = %08X, operation = %X (%c%c%c)%s\n",
|
logSVC("ControlMemory(addr0 = %08X, addr1 = %08X, size = %08X, operation = %X (%c%c%c)%s\n",
|
||||||
addr0, addr1, size, operation, r ? 'r' : '-', w ? 'w' : '-', x ? 'x' : '-', linear ? ", linear" : ""
|
addr0, addr1, size, operation, r ? 'r' : '-', w ? 'w' : '-', x ? 'x' : '-', linear ? ", linear" : ""
|
||||||
);
|
);
|
||||||
|
@ -90,7 +90,7 @@ void Kernel::controlMemory() {
|
||||||
default: Helpers::panic("ControlMemory: unknown operation %X\n", operation);
|
default: Helpers::panic("ControlMemory: unknown operation %X\n", operation);
|
||||||
}
|
}
|
||||||
|
|
||||||
regs[0] = SVCResult::Success;
|
regs[0] = Result::Success;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Result QueryMemory(MemoryInfo* memInfo, PageInfo* pageInfo, u32 addr)
|
// Result QueryMemory(MemoryInfo* memInfo, PageInfo* pageInfo, u32 addr)
|
||||||
|
@ -102,7 +102,7 @@ void Kernel::queryMemory() {
|
||||||
logSVC("QueryMemory(mem info pointer = %08X, page info pointer = %08X, addr = %08X)\n", memInfo, pageInfo, addr);
|
logSVC("QueryMemory(mem info pointer = %08X, page info pointer = %08X, addr = %08X)\n", memInfo, pageInfo, addr);
|
||||||
|
|
||||||
const auto info = mem.queryMemory(addr);
|
const auto info = mem.queryMemory(addr);
|
||||||
regs[0] = SVCResult::Success;
|
regs[0] = Result::Success;
|
||||||
regs[1] = info.baseAddr;
|
regs[1] = info.baseAddr;
|
||||||
regs[2] = info.size;
|
regs[2] = info.size;
|
||||||
regs[3] = info.perms;
|
regs[3] = info.perms;
|
||||||
|
@ -110,7 +110,7 @@ void Kernel::queryMemory() {
|
||||||
regs[5] = 0; // page flags
|
regs[5] = 0; // page flags
|
||||||
}
|
}
|
||||||
|
|
||||||
// Result MapMemoryBlock(Handle memblock, u32 addr, MemoryPermission myPermissions, MemoryPermission otherPermission)
|
// Result MapMemoryBlock(Handle memblock, u32 addr, MemoryPermission myPermissions, MemoryPermission otherPermission)
|
||||||
void Kernel::mapMemoryBlock() {
|
void Kernel::mapMemoryBlock() {
|
||||||
const Handle block = regs[0];
|
const Handle block = regs[0];
|
||||||
u32 addr = regs[1];
|
u32 addr = regs[1];
|
||||||
|
@ -146,7 +146,7 @@ void Kernel::mapMemoryBlock() {
|
||||||
Helpers::panic("MapMemoryBlock where the handle does not refer to a known piece of kernel shared mem");
|
Helpers::panic("MapMemoryBlock where the handle does not refer to a known piece of kernel shared mem");
|
||||||
}
|
}
|
||||||
|
|
||||||
regs[0] = SVCResult::Success;
|
regs[0] = Result::Success;
|
||||||
}
|
}
|
||||||
|
|
||||||
Handle Kernel::makeMemoryBlock(u32 addr, u32 size, u32 myPermission, u32 otherPermission) {
|
Handle Kernel::makeMemoryBlock(u32 addr, u32 size, u32 myPermission, u32 otherPermission) {
|
||||||
|
@ -180,13 +180,13 @@ void Kernel::createMemoryBlock() {
|
||||||
|
|
||||||
// Throw error if the size of the shared memory block is not aligned to page boundary
|
// Throw error if the size of the shared memory block is not aligned to page boundary
|
||||||
if (!isAligned(size)) {
|
if (!isAligned(size)) {
|
||||||
regs[0] = SVCResult::UnalignedSize;
|
regs[0] = Result::OS::MisalignedSize;
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Throw error if one of the permissions is not valid
|
// Throw error if one of the permissions is not valid
|
||||||
if (!isPermValid(myPermission) || !isPermValid(otherPermission)) {
|
if (!isPermValid(myPermission) || !isPermValid(otherPermission)) {
|
||||||
regs[0] = SVCResult::InvalidCombination;
|
regs[0] = Result::OS::InvalidCombination;
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -199,6 +199,6 @@ void Kernel::createMemoryBlock() {
|
||||||
if (myPermission == MemoryPermissions::DontCare) myPermission = MemoryPermissions::ReadWrite;
|
if (myPermission == MemoryPermissions::DontCare) myPermission = MemoryPermissions::ReadWrite;
|
||||||
if (otherPermission == MemoryPermissions::DontCare) otherPermission = MemoryPermissions::ReadWrite;
|
if (otherPermission == MemoryPermissions::DontCare) otherPermission = MemoryPermissions::ReadWrite;
|
||||||
|
|
||||||
regs[0] = SVCResult::Success;
|
regs[0] = Result::Success;
|
||||||
regs[1] = makeMemoryBlock(addr, size, myPermission, otherPermission);
|
regs[1] = makeMemoryBlock(addr, size, myPermission, otherPermission);
|
||||||
}
|
}
|
|
@ -43,7 +43,7 @@ void Kernel::connectToPort() {
|
||||||
|
|
||||||
if (port.size() > Port::maxNameLen) {
|
if (port.size() > Port::maxNameLen) {
|
||||||
Helpers::panic("ConnectToPort: Port name too long\n");
|
Helpers::panic("ConnectToPort: Port name too long\n");
|
||||||
regs[0] = SVCResult::PortNameTooLong;
|
regs[0] = Result::OS::PortNameTooLong;
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -51,7 +51,7 @@ void Kernel::connectToPort() {
|
||||||
std::optional<Handle> optionalHandle = getPortHandle(port.c_str());
|
std::optional<Handle> optionalHandle = getPortHandle(port.c_str());
|
||||||
if (!optionalHandle.has_value()) [[unlikely]] {
|
if (!optionalHandle.has_value()) [[unlikely]] {
|
||||||
Helpers::panic("ConnectToPort: Port doesn't exist\n");
|
Helpers::panic("ConnectToPort: Port doesn't exist\n");
|
||||||
regs[0] = SVCResult::ObjectNotFound;
|
regs[0] = Result::Kernel::NotFound;
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -65,7 +65,7 @@ void Kernel::connectToPort() {
|
||||||
// TODO: Actually create session
|
// TODO: Actually create session
|
||||||
Handle sessionHandle = makeSession(portHandle);
|
Handle sessionHandle = makeSession(portHandle);
|
||||||
|
|
||||||
regs[0] = SVCResult::Success;
|
regs[0] = Result::Success;
|
||||||
regs[1] = sessionHandle;
|
regs[1] = sessionHandle;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -80,7 +80,7 @@ void Kernel::sendSyncRequest() {
|
||||||
if (KernelHandles::isServiceHandle(handle)) {
|
if (KernelHandles::isServiceHandle(handle)) {
|
||||||
// The service call might cause a reschedule and change threads. Hence, set r0 before executing the service call
|
// The service call might cause a reschedule and change threads. Hence, set r0 before executing the service call
|
||||||
// Because if the service call goes first, we might corrupt the new thread's r0!!
|
// Because if the service call goes first, we might corrupt the new thread's r0!!
|
||||||
regs[0] = SVCResult::Success;
|
regs[0] = Result::Success;
|
||||||
serviceManager.sendCommandToService(messagePointer, handle);
|
serviceManager.sendCommandToService(messagePointer, handle);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
@ -88,7 +88,7 @@ void Kernel::sendSyncRequest() {
|
||||||
// Check if our sync request is targetting a file instead of a service
|
// Check if our sync request is targetting a file instead of a service
|
||||||
bool isFileOperation = getObject(handle, KernelObjectType::File) != nullptr;
|
bool isFileOperation = getObject(handle, KernelObjectType::File) != nullptr;
|
||||||
if (isFileOperation) {
|
if (isFileOperation) {
|
||||||
regs[0] = SVCResult::Success; // r0 goes first here too
|
regs[0] = Result::Success; // r0 goes first here too
|
||||||
handleFileOperation(messagePointer, handle);
|
handleFileOperation(messagePointer, handle);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
@ -96,7 +96,7 @@ void Kernel::sendSyncRequest() {
|
||||||
// Check if our sync request is targetting a directory instead of a service
|
// Check if our sync request is targetting a directory instead of a service
|
||||||
bool isDirectoryOperation = getObject(handle, KernelObjectType::Directory) != nullptr;
|
bool isDirectoryOperation = getObject(handle, KernelObjectType::Directory) != nullptr;
|
||||||
if (isDirectoryOperation) {
|
if (isDirectoryOperation) {
|
||||||
regs[0] = SVCResult::Success; // r0 goes first here too
|
regs[0] = Result::Success; // r0 goes first here too
|
||||||
handleDirectoryOperation(messagePointer, handle);
|
handleDirectoryOperation(messagePointer, handle);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
@ -105,7 +105,7 @@ void Kernel::sendSyncRequest() {
|
||||||
const auto session = getObject(handle, KernelObjectType::Session);
|
const auto session = getObject(handle, KernelObjectType::Session);
|
||||||
if (session == nullptr) [[unlikely]] {
|
if (session == nullptr) [[unlikely]] {
|
||||||
Helpers::panic("SendSyncRequest: Invalid handle");
|
Helpers::panic("SendSyncRequest: Invalid handle");
|
||||||
regs[0] = SVCResult::BadHandle;
|
regs[0] = Result::Kernel::InvalidHandle;
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -113,10 +113,10 @@ void Kernel::sendSyncRequest() {
|
||||||
const Handle portHandle = sessionData->portHandle;
|
const Handle portHandle = sessionData->portHandle;
|
||||||
|
|
||||||
if (portHandle == srvHandle) { // Special-case SendSyncRequest targetting the "srv: port"
|
if (portHandle == srvHandle) { // Special-case SendSyncRequest targetting the "srv: port"
|
||||||
regs[0] = SVCResult::Success;
|
regs[0] = Result::Success;
|
||||||
serviceManager.handleSyncRequest(messagePointer);
|
serviceManager.handleSyncRequest(messagePointer);
|
||||||
} else if (portHandle == errorPortHandle) { // Special-case "err:f" for juicy logs too
|
} else if (portHandle == errorPortHandle) { // Special-case "err:f" for juicy logs too
|
||||||
regs[0] = SVCResult::Success;
|
regs[0] = Result::Success;
|
||||||
handleErrorSyncRequest(messagePointer);
|
handleErrorSyncRequest(messagePointer);
|
||||||
} else {
|
} else {
|
||||||
const auto portData = objects[portHandle].getData<Port>();
|
const auto portData = objects[portHandle].getData<Port>();
|
||||||
|
|
|
@ -10,13 +10,13 @@ void Kernel::getResourceLimit() {
|
||||||
logSVC("GetResourceLimit (handle pointer = %08X, process: %s)\n", handlePointer, getProcessName(pid).c_str());
|
logSVC("GetResourceLimit (handle pointer = %08X, process: %s)\n", handlePointer, getProcessName(pid).c_str());
|
||||||
|
|
||||||
if (process == nullptr) [[unlikely]] {
|
if (process == nullptr) [[unlikely]] {
|
||||||
regs[0] = SVCResult::BadHandle;
|
regs[0] = Result::Kernel::InvalidHandle;
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
const auto processData = static_cast<Process*>(process->data);
|
const auto processData = static_cast<Process*>(process->data);
|
||||||
|
|
||||||
regs[0] = SVCResult::Success;
|
regs[0] = Result::Success;
|
||||||
regs[1] = processData->limits.handle;
|
regs[1] = processData->limits.handle;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -29,7 +29,7 @@ void Kernel::getResourceLimitLimitValues() {
|
||||||
|
|
||||||
const KernelObject* limit = getObject(resourceLimit, KernelObjectType::ResourceLimit);
|
const KernelObject* limit = getObject(resourceLimit, KernelObjectType::ResourceLimit);
|
||||||
if (limit == nullptr) [[unlikely]] {
|
if (limit == nullptr) [[unlikely]] {
|
||||||
regs[0] = SVCResult::BadHandle;
|
regs[0] = Result::Kernel::InvalidHandle;
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -46,7 +46,7 @@ void Kernel::getResourceLimitLimitValues() {
|
||||||
count--;
|
count--;
|
||||||
}
|
}
|
||||||
|
|
||||||
regs[0] = SVCResult::Success;
|
regs[0] = Result::Success;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Result GetResourceLimitCurrentValues(s64* values, Handle resourceLimit, LimitableResource* names, s32 nameCount)
|
// Result GetResourceLimitCurrentValues(s64* values, Handle resourceLimit, LimitableResource* names, s32 nameCount)
|
||||||
|
@ -59,7 +59,7 @@ void Kernel::getResourceLimitCurrentValues() {
|
||||||
|
|
||||||
const KernelObject* limit = getObject(resourceLimit, KernelObjectType::ResourceLimit);
|
const KernelObject* limit = getObject(resourceLimit, KernelObjectType::ResourceLimit);
|
||||||
if (limit == nullptr) [[unlikely]] {
|
if (limit == nullptr) [[unlikely]] {
|
||||||
regs[0] = SVCResult::BadHandle;
|
regs[0] = Result::Kernel::InvalidHandle;
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -75,7 +75,7 @@ void Kernel::getResourceLimitCurrentValues() {
|
||||||
count--;
|
count--;
|
||||||
}
|
}
|
||||||
|
|
||||||
regs[0] = SVCResult::Success;
|
regs[0] = Result::Success;
|
||||||
}
|
}
|
||||||
|
|
||||||
s32 Kernel::getCurrentResourceValue(const KernelObject* limit, u32 resourceName) {
|
s32 Kernel::getCurrentResourceValue(const KernelObject* limit, u32 resourceName) {
|
||||||
|
|
|
@ -1,8 +1,11 @@
|
||||||
|
#include <bit>
|
||||||
#include <cassert>
|
#include <cassert>
|
||||||
#include <cstring>
|
#include <cstring>
|
||||||
#include "kernel.hpp"
|
|
||||||
#include "arm_defs.hpp"
|
#include "arm_defs.hpp"
|
||||||
// This header needs to be included because I did stupid forward decl hack so the kernel and CPU can both access each other
|
#include "kernel.hpp"
|
||||||
|
// This header needs to be included because I did stupid forward decl hack so the kernel and CPU can both access each
|
||||||
|
// other
|
||||||
#include "cpu.hpp"
|
#include "cpu.hpp"
|
||||||
#include "resource_limits.hpp"
|
#include "resource_limits.hpp"
|
||||||
|
|
||||||
|
@ -20,17 +23,17 @@ void Kernel::switchThread(int newThreadIndex) {
|
||||||
}
|
}
|
||||||
|
|
||||||
// Backup context
|
// Backup context
|
||||||
std::memcpy(&oldThread.gprs[0], &cpu.regs()[0], 16 * sizeof(u32)); // Backup the 16 GPRs
|
std::memcpy(oldThread.gprs.data(), cpu.regs().data(), cpu.regs().size_bytes()); // Backup the 16 GPRs
|
||||||
std::memcpy(&oldThread.fprs[0], &cpu.fprs()[0], 32 * sizeof(u32)); // Backup the 32 FPRs
|
std::memcpy(oldThread.fprs.data(), cpu.fprs().data(), cpu.fprs().size_bytes()); // Backup the 32 FPRs
|
||||||
oldThread.cpsr = cpu.getCPSR(); // Backup CPSR
|
oldThread.cpsr = cpu.getCPSR(); // Backup CPSR
|
||||||
oldThread.fpscr = cpu.getFPSCR(); // Backup FPSCR
|
oldThread.fpscr = cpu.getFPSCR(); // Backup FPSCR
|
||||||
|
|
||||||
// Load new context
|
// Load new context
|
||||||
std::memcpy(&cpu.regs()[0], &newThread.gprs[0], 16 * sizeof(u32)); // Load 16 GPRs
|
std::memcpy(cpu.regs().data(), newThread.gprs.data(), cpu.regs().size_bytes()); // Load 16 GPRs
|
||||||
std::memcpy(&cpu.fprs()[0], &newThread.fprs[0], 32 * sizeof(u32)); // Load 32 FPRs
|
std::memcpy(cpu.fprs().data(), newThread.fprs.data(), cpu.fprs().size_bytes()); // Load 32 FPRs
|
||||||
cpu.setCPSR(newThread.cpsr); // Load CPSR
|
cpu.setCPSR(newThread.cpsr); // Load CPSR
|
||||||
cpu.setFPSCR(newThread.fpscr); // Load FPSCR
|
cpu.setFPSCR(newThread.fpscr); // Load FPSCR
|
||||||
cpu.setTLSBase(newThread.tlsBase); // Load CP15 thread-local-storage pointer register
|
cpu.setTLSBase(newThread.tlsBase); // Load CP15 thread-local-storage pointer register
|
||||||
|
|
||||||
currentThreadIndex = newThreadIndex;
|
currentThreadIndex = newThreadIndex;
|
||||||
}
|
}
|
||||||
|
@ -47,7 +50,7 @@ void Kernel::sortThreads() {
|
||||||
bool Kernel::canThreadRun(const Thread& t) {
|
bool Kernel::canThreadRun(const Thread& t) {
|
||||||
if (t.status == ThreadStatus::Ready) {
|
if (t.status == ThreadStatus::Ready) {
|
||||||
return true;
|
return true;
|
||||||
} else if (t.status == ThreadStatus::WaitSleep || t.status == ThreadStatus::WaitSync1
|
} else if (t.status == ThreadStatus::WaitSleep || t.status == ThreadStatus::WaitSync1
|
||||||
|| t.status == ThreadStatus::WaitSyncAny || t.status == ThreadStatus::WaitSyncAll) {
|
|| t.status == ThreadStatus::WaitSyncAny || t.status == ThreadStatus::WaitSyncAll) {
|
||||||
const u64 elapsedTicks = cpu.getTicks() - t.sleepTick;
|
const u64 elapsedTicks = cpu.getTicks() - t.sleepTick;
|
||||||
|
|
||||||
|
@ -81,7 +84,7 @@ std::optional<int> Kernel::getNextThread() {
|
||||||
|
|
||||||
void Kernel::switchToNextThread() {
|
void Kernel::switchToNextThread() {
|
||||||
std::optional<int> newThreadIndex = getNextThread();
|
std::optional<int> newThreadIndex = getNextThread();
|
||||||
|
|
||||||
if (!newThreadIndex.has_value()) {
|
if (!newThreadIndex.has_value()) {
|
||||||
log("Kernel tried to switch to the next thread but none found. Switching to random thread\n");
|
log("Kernel tried to switch to the next thread but none found. Switching to random thread\n");
|
||||||
assert(aliveThreadCount != 0);
|
assert(aliveThreadCount != 0);
|
||||||
|
@ -101,7 +104,7 @@ void Kernel::switchToNextThread() {
|
||||||
// See if there;s a higher priority, ready thread and switch to that
|
// See if there;s a higher priority, ready thread and switch to that
|
||||||
void Kernel::rescheduleThreads() {
|
void Kernel::rescheduleThreads() {
|
||||||
std::optional<int> newThreadIndex = getNextThread();
|
std::optional<int> newThreadIndex = getNextThread();
|
||||||
|
|
||||||
if (newThreadIndex.has_value() && newThreadIndex.value() != currentThreadIndex) {
|
if (newThreadIndex.has_value() && newThreadIndex.value() != currentThreadIndex) {
|
||||||
threads[currentThreadIndex].status = ThreadStatus::Ready;
|
threads[currentThreadIndex].status = ThreadStatus::Ready;
|
||||||
switchThread(newThreadIndex.value());
|
switchThread(newThreadIndex.value());
|
||||||
|
@ -273,12 +276,12 @@ int Kernel::wakeupOneThread(u64 waitlist, Handle handle) {
|
||||||
switch (t.status) {
|
switch (t.status) {
|
||||||
case ThreadStatus::WaitSync1:
|
case ThreadStatus::WaitSync1:
|
||||||
t.status = ThreadStatus::Ready;
|
t.status = ThreadStatus::Ready;
|
||||||
t.gprs[0] = SVCResult::Success; // The thread did not timeout, so write success to r0
|
t.gprs[0] = Result::Success; // The thread did not timeout, so write success to r0
|
||||||
break;
|
break;
|
||||||
|
|
||||||
case ThreadStatus::WaitSyncAny:
|
case ThreadStatus::WaitSyncAny:
|
||||||
t.status = ThreadStatus::Ready;
|
t.status = ThreadStatus::Ready;
|
||||||
t.gprs[0] = SVCResult::Success; // The thread did not timeout, so write success to r0
|
t.gprs[0] = Result::Success; // The thread did not timeout, so write success to r0
|
||||||
|
|
||||||
// Get the index of the event in the object's waitlist, write it to r1
|
// Get the index of the event in the object's waitlist, write it to r1
|
||||||
for (size_t i = 0; i < t.waitList.size(); i++) {
|
for (size_t i = 0; i < t.waitList.size(); i++) {
|
||||||
|
@ -308,12 +311,12 @@ void Kernel::wakeupAllThreads(u64 waitlist, Handle handle) {
|
||||||
switch (t.status) {
|
switch (t.status) {
|
||||||
case ThreadStatus::WaitSync1:
|
case ThreadStatus::WaitSync1:
|
||||||
t.status = ThreadStatus::Ready;
|
t.status = ThreadStatus::Ready;
|
||||||
t.gprs[0] = SVCResult::Success; // The thread did not timeout, so write success to r0
|
t.gprs[0] = Result::Success; // The thread did not timeout, so write success to r0
|
||||||
break;
|
break;
|
||||||
|
|
||||||
case ThreadStatus::WaitSyncAny:
|
case ThreadStatus::WaitSyncAny:
|
||||||
t.status = ThreadStatus::Ready;
|
t.status = ThreadStatus::Ready;
|
||||||
t.gprs[0] = SVCResult::Success; // The thread did not timeout, so write success to r0
|
t.gprs[0] = Result::Success; // The thread did not timeout, so write success to r0
|
||||||
|
|
||||||
// Get the index of the event in the object's waitlist, write it to r1
|
// Get the index of the event in the object's waitlist, write it to r1
|
||||||
for (size_t i = 0; i < t.waitList.size(); i++) {
|
for (size_t i = 0; i < t.waitList.size(); i++) {
|
||||||
|
@ -352,7 +355,7 @@ void Kernel::sleepThread(s64 ns) {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Result CreateThread(s32 priority, ThreadFunc entrypoint, u32 arg, u32 stacktop, s32 threadPriority, s32 processorID)
|
// Result CreateThread(s32 priority, ThreadFunc entrypoint, u32 arg, u32 stacktop, s32 threadPriority, s32 processorID)
|
||||||
void Kernel::createThread() {
|
void Kernel::createThread() {
|
||||||
u32 priority = regs[0];
|
u32 priority = regs[0];
|
||||||
u32 entrypoint = regs[1];
|
u32 entrypoint = regs[1];
|
||||||
|
@ -365,11 +368,11 @@ void Kernel::createThread() {
|
||||||
|
|
||||||
if (priority > 0x3F) [[unlikely]] {
|
if (priority > 0x3F) [[unlikely]] {
|
||||||
Helpers::panic("Created thread with bad priority value %X", priority);
|
Helpers::panic("Created thread with bad priority value %X", priority);
|
||||||
regs[0] = SVCResult::BadThreadPriority;
|
regs[0] = Result::OS::OutOfRange;
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
regs[0] = SVCResult::Success;
|
regs[0] = Result::Success;
|
||||||
regs[1] = makeThread(entrypoint, initialSP, priority, id, arg, ThreadStatus::Ready);
|
regs[1] = makeThread(entrypoint, initialSP, priority, id, arg, ThreadStatus::Ready);
|
||||||
rescheduleThreads();
|
rescheduleThreads();
|
||||||
}
|
}
|
||||||
|
@ -379,7 +382,7 @@ void Kernel::svcSleepThread() {
|
||||||
const s64 ns = s64(u64(regs[0]) | (u64(regs[1]) << 32));
|
const s64 ns = s64(u64(regs[0]) | (u64(regs[1]) << 32));
|
||||||
//logSVC("SleepThread(ns = %lld)\n", ns);
|
//logSVC("SleepThread(ns = %lld)\n", ns);
|
||||||
|
|
||||||
regs[0] = SVCResult::Success;
|
regs[0] = Result::Success;
|
||||||
sleepThread(ns);
|
sleepThread(ns);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -388,18 +391,18 @@ void Kernel::getThreadID() {
|
||||||
logSVC("GetThreadID(handle = %X)\n", handle);
|
logSVC("GetThreadID(handle = %X)\n", handle);
|
||||||
|
|
||||||
if (handle == KernelHandles::CurrentThread) {
|
if (handle == KernelHandles::CurrentThread) {
|
||||||
regs[0] = SVCResult::Success;
|
regs[0] = Result::Success;
|
||||||
regs[1] = currentThreadIndex;
|
regs[1] = currentThreadIndex;
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
const auto thread = getObject(handle, KernelObjectType::Thread);
|
const auto thread = getObject(handle, KernelObjectType::Thread);
|
||||||
if (thread == nullptr) [[unlikely]] {
|
if (thread == nullptr) [[unlikely]] {
|
||||||
regs[0] = SVCResult::BadHandle;
|
regs[0] = Result::Kernel::InvalidHandle;
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
regs[0] = SVCResult::Success;
|
regs[0] = Result::Success;
|
||||||
regs[1] = thread->getData<Thread>()->index;
|
regs[1] = thread->getData<Thread>()->index;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -408,14 +411,14 @@ void Kernel::getThreadPriority() {
|
||||||
logSVC("GetThreadPriority (handle = %X)\n", handle);
|
logSVC("GetThreadPriority (handle = %X)\n", handle);
|
||||||
|
|
||||||
if (handle == KernelHandles::CurrentThread) {
|
if (handle == KernelHandles::CurrentThread) {
|
||||||
regs[0] = SVCResult::Success;
|
regs[0] = Result::Success;
|
||||||
regs[1] = threads[currentThreadIndex].priority;
|
regs[1] = threads[currentThreadIndex].priority;
|
||||||
} else {
|
} else {
|
||||||
auto object = getObject(handle, KernelObjectType::Thread);
|
auto object = getObject(handle, KernelObjectType::Thread);
|
||||||
if (object == nullptr) [[unlikely]] {
|
if (object == nullptr) [[unlikely]] {
|
||||||
regs[0] = SVCResult::BadHandle;
|
regs[0] = Result::Kernel::InvalidHandle;
|
||||||
} else {
|
} else {
|
||||||
regs[0] = SVCResult::Success;
|
regs[0] = Result::Success;
|
||||||
regs[1] = object->getData<Thread>()->priority;
|
regs[1] = object->getData<Thread>()->priority;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -427,20 +430,20 @@ void Kernel::setThreadPriority() {
|
||||||
logSVC("SetThreadPriority (handle = %X, priority = %X)\n", handle, priority);
|
logSVC("SetThreadPriority (handle = %X, priority = %X)\n", handle, priority);
|
||||||
|
|
||||||
if (priority > 0x3F) {
|
if (priority > 0x3F) {
|
||||||
regs[0] = SVCResult::BadThreadPriority;
|
regs[0] = Result::OS::OutOfRange;
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (handle == KernelHandles::CurrentThread) {
|
if (handle == KernelHandles::CurrentThread) {
|
||||||
regs[0] = SVCResult::Success;
|
regs[0] = Result::Success;
|
||||||
threads[currentThreadIndex].priority = priority;
|
threads[currentThreadIndex].priority = priority;
|
||||||
} else {
|
} else {
|
||||||
auto object = getObject(handle, KernelObjectType::Thread);
|
auto object = getObject(handle, KernelObjectType::Thread);
|
||||||
if (object == nullptr) [[unlikely]] {
|
if (object == nullptr) [[unlikely]] {
|
||||||
regs[0] = SVCResult::BadHandle;
|
regs[0] = Result::Kernel::InvalidHandle;
|
||||||
return;
|
return;
|
||||||
} else {
|
} else {
|
||||||
regs[0] = SVCResult::Success;
|
regs[0] = Result::Success;
|
||||||
object->getData<Thread>()->priority = priority;
|
object->getData<Thread>()->priority = priority;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -476,7 +479,7 @@ void Kernel::svcCreateMutex() {
|
||||||
bool locked = regs[1] != 0;
|
bool locked = regs[1] != 0;
|
||||||
logSVC("CreateMutex (locked = %s)\n", locked ? "yes" : "no");
|
logSVC("CreateMutex (locked = %s)\n", locked ? "yes" : "no");
|
||||||
|
|
||||||
regs[0] = SVCResult::Success;
|
regs[0] = Result::Success;
|
||||||
regs[1] = makeMutex(locked);
|
regs[1] = makeMutex(locked);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -487,18 +490,18 @@ void Kernel::svcReleaseMutex() {
|
||||||
const auto object = getObject(handle, KernelObjectType::Mutex);
|
const auto object = getObject(handle, KernelObjectType::Mutex);
|
||||||
if (object == nullptr) [[unlikely]] {
|
if (object == nullptr) [[unlikely]] {
|
||||||
Helpers::panic("Tried to release non-existent mutex");
|
Helpers::panic("Tried to release non-existent mutex");
|
||||||
regs[0] = SVCResult::BadHandle;
|
regs[0] = Result::Kernel::InvalidHandle;
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
Mutex* moo = object->getData<Mutex>();
|
Mutex* moo = object->getData<Mutex>();
|
||||||
// A thread can't release a mutex it does not own
|
// A thread can't release a mutex it does not own
|
||||||
if (!moo->locked || moo->ownerThread != currentThreadIndex) {
|
if (!moo->locked || moo->ownerThread != currentThreadIndex) {
|
||||||
regs[0] = SVCResult::InvalidMutexRelease;
|
regs[0] = Result::Kernel::InvalidMutexRelease;
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
regs[0] = SVCResult::Success;
|
regs[0] = Result::Success;
|
||||||
releaseMutex(moo);
|
releaseMutex(moo);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -513,7 +516,7 @@ void Kernel::svcCreateSemaphore() {
|
||||||
if (initialCount < 0 || maxCount < 0)
|
if (initialCount < 0 || maxCount < 0)
|
||||||
Helpers::panic("CreateSemaphore: Negative count value");
|
Helpers::panic("CreateSemaphore: Negative count value");
|
||||||
|
|
||||||
regs[0] = SVCResult::Success;
|
regs[0] = Result::Success;
|
||||||
regs[1] = makeSemaphore(initialCount, maxCount);
|
regs[1] = makeSemaphore(initialCount, maxCount);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -525,7 +528,7 @@ void Kernel::svcReleaseSemaphore() {
|
||||||
const auto object = getObject(handle, KernelObjectType::Semaphore);
|
const auto object = getObject(handle, KernelObjectType::Semaphore);
|
||||||
if (object == nullptr) [[unlikely]] {
|
if (object == nullptr) [[unlikely]] {
|
||||||
Helpers::panic("Tried to release non-existent semaphore");
|
Helpers::panic("Tried to release non-existent semaphore");
|
||||||
regs[0] = SVCResult::BadHandle;
|
regs[0] = Result::Kernel::InvalidHandle;
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -537,7 +540,7 @@ void Kernel::svcReleaseSemaphore() {
|
||||||
Helpers::panic("ReleaseSemaphore: Release count too high");
|
Helpers::panic("ReleaseSemaphore: Release count too high");
|
||||||
|
|
||||||
// Write success and old available count to r0 and r1 respectively
|
// Write success and old available count to r0 and r1 respectively
|
||||||
regs[0] = SVCResult::Success;
|
regs[0] = Result::Success;
|
||||||
regs[1] = s->availableCount;
|
regs[1] = s->availableCount;
|
||||||
// Bump available count
|
// Bump available count
|
||||||
s->availableCount += releaseCount;
|
s->availableCount += releaseCount;
|
||||||
|
|
|
@ -11,45 +11,221 @@ using namespace Helpers;
|
||||||
const char* vertexShader = R"(
|
const char* vertexShader = R"(
|
||||||
#version 410 core
|
#version 410 core
|
||||||
|
|
||||||
layout (location = 0) in vec4 coords;
|
layout (location = 0) in vec4 a_coords;
|
||||||
layout (location = 1) in vec4 vertexColour;
|
layout (location = 1) in vec4 a_vertexColour;
|
||||||
layout (location = 2) in vec2 inUVs_texture0;
|
layout (location = 2) in vec2 a_texcoord0;
|
||||||
|
layout (location = 3) in vec2 a_texcoord1;
|
||||||
|
layout (location = 4) in float a_texcoord0_w;
|
||||||
|
layout (location = 5) in vec2 a_texcoord2;
|
||||||
|
|
||||||
out vec4 colour;
|
out vec4 v_colour;
|
||||||
out vec2 tex0_UVs;
|
out vec3 v_texcoord0;
|
||||||
|
out vec2 v_texcoord1;
|
||||||
|
out vec2 v_texcoord2;
|
||||||
|
flat out vec4 v_textureEnvColor[6];
|
||||||
|
flat out vec4 v_textureEnvBufferColor;
|
||||||
|
|
||||||
|
// TEV uniforms
|
||||||
|
uniform uint u_textureEnvColor[6];
|
||||||
|
uniform uint u_textureEnvBufferColor;
|
||||||
|
|
||||||
|
vec4 abgr8888ToVec4(uint abgr) {
|
||||||
|
const float scale = 1.0 / 255.0;
|
||||||
|
|
||||||
|
return vec4(
|
||||||
|
scale * float(abgr & 0xffu),
|
||||||
|
scale * float((abgr >> 8) & 0xffu),
|
||||||
|
scale * float((abgr >> 16) & 0xffu),
|
||||||
|
scale * float(abgr >> 24)
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
void main() {
|
void main() {
|
||||||
gl_Position = coords;
|
gl_Position = a_coords;
|
||||||
colour = vertexColour;
|
v_colour = a_vertexColour;
|
||||||
|
|
||||||
// Flip y axis of UVs because OpenGL uses an inverted y for texture sampling compared to the PICA
|
// Flip y axis of UVs because OpenGL uses an inverted y for texture sampling compared to the PICA
|
||||||
tex0_UVs = vec2(inUVs_texture0.x, 1.0 - inUVs_texture0.y);
|
v_texcoord0 = vec3(a_texcoord0.x, 1.0 - a_texcoord0.y, a_texcoord0_w);
|
||||||
|
v_texcoord1 = vec2(a_texcoord1.x, 1.0 - a_texcoord1.y);
|
||||||
|
v_texcoord2 = vec2(a_texcoord2.x, 1.0 - a_texcoord2.y);
|
||||||
|
|
||||||
|
for (int i = 0; i < 6; i++) {
|
||||||
|
v_textureEnvColor[i] = abgr8888ToVec4(u_textureEnvColor[i]);
|
||||||
|
}
|
||||||
|
|
||||||
|
v_textureEnvBufferColor = abgr8888ToVec4(u_textureEnvBufferColor);
|
||||||
}
|
}
|
||||||
)";
|
)";
|
||||||
|
|
||||||
const char* fragmentShader = R"(
|
const char* fragmentShader = R"(
|
||||||
#version 410 core
|
#version 410 core
|
||||||
|
|
||||||
in vec4 colour;
|
in vec4 v_colour;
|
||||||
in vec2 tex0_UVs;
|
in vec3 v_texcoord0;
|
||||||
|
in vec2 v_texcoord1;
|
||||||
|
in vec2 v_texcoord2;
|
||||||
|
flat in vec4 v_textureEnvColor[6];
|
||||||
|
flat in vec4 v_textureEnvBufferColor;
|
||||||
|
|
||||||
out vec4 fragColour;
|
out vec4 fragColour;
|
||||||
|
|
||||||
uniform uint u_alphaControl;
|
uniform uint u_alphaControl;
|
||||||
uniform uint u_textureConfig;
|
uniform uint u_textureConfig;
|
||||||
|
|
||||||
|
// TEV uniforms
|
||||||
|
uniform uint u_textureEnvSource[6];
|
||||||
|
uniform uint u_textureEnvOperand[6];
|
||||||
|
uniform uint u_textureEnvCombiner[6];
|
||||||
|
uniform uint u_textureEnvScale[6];
|
||||||
|
uniform uint u_textureEnvUpdateBuffer;
|
||||||
|
|
||||||
// Depth control uniforms
|
// Depth control uniforms
|
||||||
uniform float u_depthScale;
|
uniform float u_depthScale;
|
||||||
uniform float u_depthOffset;
|
uniform float u_depthOffset;
|
||||||
uniform bool u_depthmapEnable;
|
uniform bool u_depthmapEnable;
|
||||||
|
|
||||||
uniform sampler2D u_tex0;
|
uniform sampler2D u_tex0;
|
||||||
|
uniform sampler2D u_tex1;
|
||||||
|
uniform sampler2D u_tex2;
|
||||||
|
|
||||||
|
vec4 tevSources[16];
|
||||||
|
vec4 tevNextPreviousBuffer;
|
||||||
|
bool tevUnimplementedSourceFlag = false;
|
||||||
|
|
||||||
|
// OpenGL ES 1.1 reference pages for TEVs (this is what the PICA200 implements):
|
||||||
|
// https://registry.khronos.org/OpenGL-Refpages/es1.1/xhtml/glTexEnv.xml
|
||||||
|
|
||||||
|
vec4 tevFetchSource(uint src_id) {
|
||||||
|
if (src_id >= 6u && src_id < 13u) {
|
||||||
|
tevUnimplementedSourceFlag = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
return tevSources[src_id];
|
||||||
|
}
|
||||||
|
|
||||||
|
vec4 tevGetColorAndAlphaSource(int tev_id, int src_id) {
|
||||||
|
vec4 result;
|
||||||
|
|
||||||
|
vec4 colorSource = tevFetchSource((u_textureEnvSource[tev_id] >> (src_id * 4)) & 15u);
|
||||||
|
vec4 alphaSource = tevFetchSource((u_textureEnvSource[tev_id] >> (src_id * 4 + 16)) & 15u);
|
||||||
|
|
||||||
|
uint colorOperand = (u_textureEnvOperand[tev_id] >> (src_id * 4)) & 15u;
|
||||||
|
uint alphaOperand = (u_textureEnvOperand[tev_id] >> (12 + src_id * 4)) & 7u;
|
||||||
|
|
||||||
|
// TODO: figure out what the undocumented values do
|
||||||
|
switch (colorOperand) {
|
||||||
|
case 0u: result.rgb = colorSource.rgb; break; // Source color
|
||||||
|
case 1u: result.rgb = 1.0 - colorSource.rgb; break; // One minus source color
|
||||||
|
case 2u: result.rgb = vec3(colorSource.a); break; // Source alpha
|
||||||
|
case 3u: result.rgb = vec3(1.0 - colorSource.a); break; // One minus source alpha
|
||||||
|
case 4u: result.rgb = vec3(colorSource.r); break; // Source red
|
||||||
|
case 5u: result.rgb = vec3(1.0 - colorSource.r); break; // One minus source red
|
||||||
|
case 8u: result.rgb = vec3(colorSource.g); break; // Source green
|
||||||
|
case 9u: result.rgb = vec3(1.0 - colorSource.g); break; // One minus source green
|
||||||
|
case 12u: result.rgb = vec3(colorSource.b); break; // Source blue
|
||||||
|
case 13u: result.rgb = vec3(1.0 - colorSource.b); break; // One minus source blue
|
||||||
|
default: break;
|
||||||
|
}
|
||||||
|
|
||||||
|
// TODO: figure out what the undocumented values do
|
||||||
|
switch (alphaOperand) {
|
||||||
|
case 0u: result.a = alphaSource.a; break; // Source alpha
|
||||||
|
case 1u: result.a = 1.0 - alphaSource.a; break; // One minus source alpha
|
||||||
|
case 2u: result.a = alphaSource.r; break; // Source red
|
||||||
|
case 3u: result.a = 1.0 - alphaSource.r; break; // One minus source red
|
||||||
|
case 4u: result.a = alphaSource.g; break; // Source green
|
||||||
|
case 5u: result.a = 1.0 - alphaSource.g; break; // One minus source green
|
||||||
|
case 6u: result.a = alphaSource.b; break; // Source blue
|
||||||
|
case 7u: result.a = 1.0 - alphaSource.b; break; // One minus source blue
|
||||||
|
default: break;
|
||||||
|
}
|
||||||
|
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
vec4 tevCalculateCombiner(int tev_id) {
|
||||||
|
vec4 source0 = tevGetColorAndAlphaSource(tev_id, 0);
|
||||||
|
vec4 source1 = tevGetColorAndAlphaSource(tev_id, 1);
|
||||||
|
vec4 source2 = tevGetColorAndAlphaSource(tev_id, 2);
|
||||||
|
|
||||||
|
uint colorCombine = u_textureEnvCombiner[tev_id] & 15u;
|
||||||
|
uint alphaCombine = (u_textureEnvCombiner[tev_id] >> 16) & 15u;
|
||||||
|
|
||||||
|
vec4 result = vec4(1.0);
|
||||||
|
|
||||||
|
// TODO: figure out what the undocumented values do
|
||||||
|
switch (colorCombine) {
|
||||||
|
case 0u: result.rgb = source0.rgb; break; // Replace
|
||||||
|
case 1u: result.rgb = source0.rgb * source1.rgb; break; // Modulate
|
||||||
|
case 2u: result.rgb = min(vec3(1.0), source0.rgb + source1.rgb); break; // Add
|
||||||
|
case 3u: result.rgb = clamp(source0.rgb + source1.rgb - 0.5, 0.0, 1.0); break; // Add signed
|
||||||
|
case 4u: result.rgb = mix(source1.rgb, source0.rgb, source2.rgb); break; // Interpolate
|
||||||
|
case 5u: result.rgb = max(source0.rgb - source1.rgb, 0.0); break; // Subtract
|
||||||
|
case 6u: result.rgb = vec3(4.0 * dot(source0.rgb - 0.5 , source1.rgb - 0.5)); break; // Dot3 RGB
|
||||||
|
case 7u: result = vec4(4.0 * dot(source0.rgb - 0.5 , source1.rgb - 0.5)); break; // Dot3 RGBA
|
||||||
|
case 8u: result.rgb = min(source0.rgb * source1.rgb + source2.rgb, 1.0); break; // Multiply then add
|
||||||
|
case 9u: result.rgb = min((source0.rgb + source1.rgb) * source2.rgb, 1.0); break; // Add then multiply
|
||||||
|
default: break;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (colorCombine != 7u) { // The color combiner also writes the alpha channel in the "Dot3 RGBA" mode.
|
||||||
|
// TODO: figure out what the undocumented values do
|
||||||
|
// TODO: test if the alpha combiner supports all the same modes as the color combiner.
|
||||||
|
switch (alphaCombine) {
|
||||||
|
case 0u: result.a = source0.a; break; // Replace
|
||||||
|
case 1u: result.a = source0.a * source1.a; break; // Modulate
|
||||||
|
case 2u: result.a = min(1.0, source0.a + source1.a); break; // Add
|
||||||
|
case 3u: result.a = clamp(source0.a + source1.a - 0.5, 0.0, 1.0); break; // Add signed
|
||||||
|
case 4u: result.a = mix(source1.a, source0.a, source2.a); break; // Interpolate
|
||||||
|
case 5u: result.a = max(0.0, source0.a - source1.a); break; // Subtract
|
||||||
|
case 8u: result.a = min(1.0, source0.a * source1.a + source2.a); break; // Multiply then add
|
||||||
|
case 9u: result.a = min(1.0, (source0.a + source1.a) * source2.a); break; // Add then multiply
|
||||||
|
default: break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
result.rgb *= float(1 << (u_textureEnvScale[tev_id] & 3u));
|
||||||
|
result.a *= float(1 << ((u_textureEnvScale[tev_id] >> 16) & 3u));
|
||||||
|
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
void main() {
|
void main() {
|
||||||
if ((u_textureConfig & 1u) != 0) { // Render texture 0 if enabled
|
vec2 tex2UV = (u_textureConfig & (1u << 13)) != 0u ? v_texcoord1 : v_texcoord2;
|
||||||
fragColour = texture(u_tex0, tex0_UVs);
|
|
||||||
} else {
|
// TODO: what do invalid sources and disabled textures read as?
|
||||||
fragColour = colour;
|
// And what does the "previous combiner" source read initially?
|
||||||
|
tevSources[0] = v_colour; // Primary/vertex color
|
||||||
|
tevSources[1] = vec4(vec3(0.5), 1.0); // Fragment primary color
|
||||||
|
tevSources[2] = vec4(vec3(0.5), 1.0); // Fragment secondary color
|
||||||
|
if ((u_textureConfig & 1u) != 0u) tevSources[3] = texture(u_tex0, v_texcoord0.xy);
|
||||||
|
if ((u_textureConfig & 2u) != 0u) tevSources[4] = texture(u_tex1, v_texcoord1);
|
||||||
|
if ((u_textureConfig & 4u) != 0u) tevSources[5] = texture(u_tex2, tex2UV);
|
||||||
|
tevSources[13] = vec4(0.0); // Previous buffer
|
||||||
|
tevSources[15] = vec4(0.0); // Previous combiner
|
||||||
|
|
||||||
|
tevNextPreviousBuffer = v_textureEnvBufferColor;
|
||||||
|
|
||||||
|
for (int i = 0; i < 6; i++) {
|
||||||
|
tevSources[14] = v_textureEnvColor[i]; // Constant color
|
||||||
|
tevSources[15] = tevCalculateCombiner(i);
|
||||||
|
tevSources[13] = tevNextPreviousBuffer;
|
||||||
|
|
||||||
|
if (i < 4) {
|
||||||
|
if ((u_textureEnvUpdateBuffer & (0x100u << i)) != 0u) {
|
||||||
|
tevNextPreviousBuffer.rgb = tevSources[15].rgb;
|
||||||
|
}
|
||||||
|
|
||||||
|
if ((u_textureEnvUpdateBuffer & (0x1000u << i)) != 0u) {
|
||||||
|
tevNextPreviousBuffer.a = tevSources[15].a;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fragColour = tevSources[15];
|
||||||
|
|
||||||
|
if (tevUnimplementedSourceFlag) {
|
||||||
|
// fragColour = vec4(1.0, 0.0, 1.0, 1.0);
|
||||||
}
|
}
|
||||||
|
|
||||||
// Get original depth value by converting from [near, far] = [0, 1] to [-1, 1]
|
// Get original depth value by converting from [near, far] = [0, 1] to [-1, 1]
|
||||||
|
@ -119,10 +295,10 @@ const char* displayVertexShader = R"(
|
||||||
vec2(1.0, 0.0), // Bottom-right
|
vec2(1.0, 0.0), // Bottom-right
|
||||||
vec2(0.0, 1.0), // Top-left
|
vec2(0.0, 1.0), // Top-left
|
||||||
vec2(0.0, 0.0) // Bottom-left
|
vec2(0.0, 0.0) // Bottom-left
|
||||||
);
|
);
|
||||||
|
|
||||||
gl_Position = positions[gl_VertexID];
|
gl_Position = positions[gl_VertexID];
|
||||||
UV = texcoords[gl_VertexID];
|
UV = texcoords[gl_VertexID];
|
||||||
}
|
}
|
||||||
)";
|
)";
|
||||||
|
|
||||||
|
@ -144,10 +320,10 @@ void Renderer::reset() {
|
||||||
|
|
||||||
// Init the colour/depth buffer settings to some random defaults on reset
|
// Init the colour/depth buffer settings to some random defaults on reset
|
||||||
colourBufferLoc = 0;
|
colourBufferLoc = 0;
|
||||||
colourBufferFormat = ColourBuffer::Formats::RGBA8;
|
colourBufferFormat = PICA::ColorFmt::RGBA8;
|
||||||
|
|
||||||
depthBufferLoc = 0;
|
depthBufferLoc = 0;
|
||||||
depthBufferFormat = DepthBuffer::Formats::Depth16;
|
depthBufferFormat = PICA::DepthFmt::Depth16;
|
||||||
|
|
||||||
if (triangleProgram.exists()) {
|
if (triangleProgram.exists()) {
|
||||||
const auto oldProgram = OpenGL::getProgram();
|
const auto oldProgram = OpenGL::getProgram();
|
||||||
|
@ -179,16 +355,28 @@ void Renderer::initGraphicsContext() {
|
||||||
alphaControlLoc = OpenGL::uniformLocation(triangleProgram, "u_alphaControl");
|
alphaControlLoc = OpenGL::uniformLocation(triangleProgram, "u_alphaControl");
|
||||||
texUnitConfigLoc = OpenGL::uniformLocation(triangleProgram, "u_textureConfig");
|
texUnitConfigLoc = OpenGL::uniformLocation(triangleProgram, "u_textureConfig");
|
||||||
|
|
||||||
|
textureEnvSourceLoc = OpenGL::uniformLocation(triangleProgram, "u_textureEnvSource");
|
||||||
|
textureEnvOperandLoc = OpenGL::uniformLocation(triangleProgram, "u_textureEnvOperand");
|
||||||
|
textureEnvCombinerLoc = OpenGL::uniformLocation(triangleProgram, "u_textureEnvCombiner");
|
||||||
|
textureEnvColorLoc = OpenGL::uniformLocation(triangleProgram, "u_textureEnvColor");
|
||||||
|
textureEnvScaleLoc = OpenGL::uniformLocation(triangleProgram, "u_textureEnvScale");
|
||||||
|
textureEnvUpdateBufferLoc = OpenGL::uniformLocation(triangleProgram, "u_textureEnvUpdateBuffer");
|
||||||
|
textureEnvBufferColorLoc = OpenGL::uniformLocation(triangleProgram, "u_textureEnvBufferColor");
|
||||||
|
|
||||||
depthScaleLoc = OpenGL::uniformLocation(triangleProgram, "u_depthScale");
|
depthScaleLoc = OpenGL::uniformLocation(triangleProgram, "u_depthScale");
|
||||||
depthOffsetLoc = OpenGL::uniformLocation(triangleProgram, "u_depthOffset");
|
depthOffsetLoc = OpenGL::uniformLocation(triangleProgram, "u_depthOffset");
|
||||||
depthmapEnableLoc = OpenGL::uniformLocation(triangleProgram, "u_depthmapEnable");
|
depthmapEnableLoc = OpenGL::uniformLocation(triangleProgram, "u_depthmapEnable");
|
||||||
glUniform1i(OpenGL::uniformLocation(triangleProgram, "u_tex0"), 0); // Init sampler object
|
|
||||||
|
// Init sampler objects
|
||||||
|
glUniform1i(OpenGL::uniformLocation(triangleProgram, "u_tex0"), 0);
|
||||||
|
glUniform1i(OpenGL::uniformLocation(triangleProgram, "u_tex1"), 1);
|
||||||
|
glUniform1i(OpenGL::uniformLocation(triangleProgram, "u_tex2"), 2);
|
||||||
|
|
||||||
OpenGL::Shader vertDisplay(displayVertexShader, OpenGL::Vertex);
|
OpenGL::Shader vertDisplay(displayVertexShader, OpenGL::Vertex);
|
||||||
OpenGL::Shader fragDisplay(displayFragmentShader, OpenGL::Fragment);
|
OpenGL::Shader fragDisplay(displayFragmentShader, OpenGL::Fragment);
|
||||||
displayProgram.create({ vertDisplay, fragDisplay });
|
displayProgram.create({ vertDisplay, fragDisplay });
|
||||||
displayProgram.use();
|
|
||||||
|
|
||||||
|
displayProgram.use();
|
||||||
glUniform1i(OpenGL::uniformLocation(displayProgram, "u_texture"), 0); // Init sampler object
|
glUniform1i(OpenGL::uniformLocation(displayProgram, "u_texture"), 0); // Init sampler object
|
||||||
|
|
||||||
vbo.createFixedSize(sizeof(Vertex) * vertexBufferSize, GL_STREAM_DRAW);
|
vbo.createFixedSize(sizeof(Vertex) * vertexBufferSize, GL_STREAM_DRAW);
|
||||||
|
@ -202,27 +390,53 @@ void Renderer::initGraphicsContext() {
|
||||||
// Colour attribute
|
// Colour attribute
|
||||||
vao.setAttributeFloat<float>(1, 4, sizeof(Vertex), offsetof(Vertex, colour));
|
vao.setAttributeFloat<float>(1, 4, sizeof(Vertex), offsetof(Vertex, colour));
|
||||||
vao.enableAttribute(1);
|
vao.enableAttribute(1);
|
||||||
// UV attribute
|
// UV 0 attribute
|
||||||
vao.setAttributeFloat<float>(2, 2, sizeof(Vertex), offsetof(Vertex, UVs));
|
vao.setAttributeFloat<float>(2, 2, sizeof(Vertex), offsetof(Vertex, texcoord0));
|
||||||
vao.enableAttribute(2);
|
vao.enableAttribute(2);
|
||||||
|
// UV 1 attribute
|
||||||
|
vao.setAttributeFloat<float>(3, 2, sizeof(Vertex), offsetof(Vertex, texcoord1));
|
||||||
|
vao.enableAttribute(3);
|
||||||
|
// UV 0 W-component attribute
|
||||||
|
vao.setAttributeFloat<float>(4, 1, sizeof(Vertex), offsetof(Vertex, texcoord0_w));
|
||||||
|
vao.enableAttribute(4);
|
||||||
|
// UV 2 attribute
|
||||||
|
vao.setAttributeFloat<float>(5, 2, sizeof(Vertex), offsetof(Vertex, texcoord2));
|
||||||
|
vao.enableAttribute(5);
|
||||||
|
|
||||||
dummyVBO.create();
|
dummyVBO.create();
|
||||||
dummyVAO.create();
|
dummyVAO.create();
|
||||||
|
|
||||||
|
// Create texture and framebuffer for the 3DS screen
|
||||||
|
const u32 screenTextureWidth = 2 * 400; // Top screen is 400 pixels wide, bottom is 320
|
||||||
|
const u32 screenTextureHeight = 2 * 240; // Both screens are 240 pixels tall
|
||||||
|
|
||||||
|
auto prevTexture = OpenGL::getTex2D();
|
||||||
|
screenTexture.create(screenTextureWidth, screenTextureHeight, GL_RGBA8);
|
||||||
|
screenTexture.bind();
|
||||||
|
screenTexture.setMinFilter(OpenGL::Linear);
|
||||||
|
screenTexture.setMagFilter(OpenGL::Linear);
|
||||||
|
glBindTexture(GL_TEXTURE_2D, prevTexture);
|
||||||
|
|
||||||
|
screenFramebuffer.createWithDrawTexture(screenTexture);
|
||||||
|
screenFramebuffer.bind(OpenGL::DrawAndReadFramebuffer);
|
||||||
|
|
||||||
|
if (glCheckFramebufferStatus(GL_FRAMEBUFFER) != GL_FRAMEBUFFER_COMPLETE)
|
||||||
|
Helpers::panic("Incomplete framebuffer");
|
||||||
|
|
||||||
|
// TODO: This should not clear the framebuffer contents. It should load them from VRAM.
|
||||||
|
GLint oldViewport[4];
|
||||||
|
glGetIntegerv(GL_VIEWPORT, oldViewport);
|
||||||
|
OpenGL::setViewport(screenTextureWidth, screenTextureHeight);
|
||||||
|
OpenGL::setClearColor(0.0, 0.0, 0.0, 1.0);
|
||||||
|
OpenGL::clearColor();
|
||||||
|
OpenGL::setViewport(oldViewport[0], oldViewport[1], oldViewport[2], oldViewport[3]);
|
||||||
|
|
||||||
reset();
|
reset();
|
||||||
}
|
}
|
||||||
|
|
||||||
void Renderer::getGraphicsContext() {
|
|
||||||
OpenGL::disableScissor();
|
|
||||||
OpenGL::setViewport(400, 240);
|
|
||||||
|
|
||||||
vbo.bind();
|
|
||||||
vao.bind();
|
|
||||||
triangleProgram.use();
|
|
||||||
}
|
|
||||||
|
|
||||||
// Set up the OpenGL blending context to match the emulated PICA
|
// Set up the OpenGL blending context to match the emulated PICA
|
||||||
void Renderer::setupBlending() {
|
void Renderer::setupBlending() {
|
||||||
const bool blendingEnabled = (regs[PICAInternalRegs::ColourOperation] & (1 << 8)) != 0;
|
const bool blendingEnabled = (regs[PICA::InternalRegs::ColourOperation] & (1 << 8)) != 0;
|
||||||
|
|
||||||
// Map of PICA blending equations to OpenGL blending equations. The unused blending equations are equivalent to equation 0 (add)
|
// Map of PICA blending equations to OpenGL blending equations. The unused blending equations are equivalent to equation 0 (add)
|
||||||
static constexpr std::array<GLenum, 8> blendingEquations = {
|
static constexpr std::array<GLenum, 8> blendingEquations = {
|
||||||
|
@ -242,7 +456,7 @@ void Renderer::setupBlending() {
|
||||||
OpenGL::enableBlend();
|
OpenGL::enableBlend();
|
||||||
|
|
||||||
// Get blending equations
|
// Get blending equations
|
||||||
const u32 blendControl = regs[PICAInternalRegs::BlendFunc];
|
const u32 blendControl = regs[PICA::InternalRegs::BlendFunc];
|
||||||
const u32 rgbEquation = blendControl & 0x7;
|
const u32 rgbEquation = blendControl & 0x7;
|
||||||
const u32 alphaEquation = getBits<8, 3>(blendControl);
|
const u32 alphaEquation = getBits<8, 3>(blendControl);
|
||||||
|
|
||||||
|
@ -252,7 +466,7 @@ void Renderer::setupBlending() {
|
||||||
const u32 alphaSourceFunc = getBits<24, 4>(blendControl);
|
const u32 alphaSourceFunc = getBits<24, 4>(blendControl);
|
||||||
const u32 alphaDestFunc = getBits<28, 4>(blendControl);
|
const u32 alphaDestFunc = getBits<28, 4>(blendControl);
|
||||||
|
|
||||||
const u32 constantColor = regs[PICAInternalRegs::BlendColour];
|
const u32 constantColor = regs[PICA::InternalRegs::BlendColour];
|
||||||
const u32 r = constantColor & 0xff;
|
const u32 r = constantColor & 0xff;
|
||||||
const u32 g = getBits<8, 8>(constantColor);
|
const u32 g = getBits<8, 8>(constantColor);
|
||||||
const u32 b = getBits<16, 8>(constantColor);
|
const u32 b = getBits<16, 8>(constantColor);
|
||||||
|
@ -265,9 +479,93 @@ void Renderer::setupBlending() {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void Renderer::drawVertices(OpenGL::Primitives primType, Vertex* vertices, u32 count) {
|
void Renderer::setupTextureEnvState() {
|
||||||
|
// TODO: Only update uniforms when the TEV config changed. Use an UBO potentially.
|
||||||
|
|
||||||
|
static constexpr std::array<u32, 6> ioBases = {
|
||||||
|
PICA::InternalRegs::TexEnv0Source, PICA::InternalRegs::TexEnv1Source,
|
||||||
|
PICA::InternalRegs::TexEnv2Source, PICA::InternalRegs::TexEnv3Source,
|
||||||
|
PICA::InternalRegs::TexEnv4Source, PICA::InternalRegs::TexEnv5Source
|
||||||
|
};
|
||||||
|
|
||||||
|
u32 textureEnvSourceRegs[6];
|
||||||
|
u32 textureEnvOperandRegs[6];
|
||||||
|
u32 textureEnvCombinerRegs[6];
|
||||||
|
u32 textureEnvColourRegs[6];
|
||||||
|
u32 textureEnvScaleRegs[6];
|
||||||
|
|
||||||
|
for (int i = 0; i < 6; i++) {
|
||||||
|
const u32 ioBase = ioBases[i];
|
||||||
|
|
||||||
|
textureEnvSourceRegs[i] = regs[ioBase];
|
||||||
|
textureEnvOperandRegs[i] = regs[ioBase + 1];
|
||||||
|
textureEnvCombinerRegs[i] = regs[ioBase + 2];
|
||||||
|
textureEnvColourRegs[i] = regs[ioBase + 3];
|
||||||
|
textureEnvScaleRegs[i] = regs[ioBase + 4];
|
||||||
|
}
|
||||||
|
|
||||||
|
glUniform1uiv(textureEnvSourceLoc, 6, textureEnvSourceRegs);
|
||||||
|
glUniform1uiv(textureEnvOperandLoc, 6, textureEnvOperandRegs);
|
||||||
|
glUniform1uiv(textureEnvCombinerLoc, 6, textureEnvCombinerRegs);
|
||||||
|
glUniform1uiv(textureEnvColorLoc, 6, textureEnvColourRegs);
|
||||||
|
glUniform1uiv(textureEnvScaleLoc, 6, textureEnvScaleRegs);
|
||||||
|
glUniform1ui(textureEnvUpdateBufferLoc, regs[PICA::InternalRegs::TexEnvUpdateBuffer]);
|
||||||
|
glUniform1ui(textureEnvBufferColorLoc, regs[PICA::InternalRegs::TexEnvBufferColor]);
|
||||||
|
}
|
||||||
|
|
||||||
|
void Renderer::bindTexturesToSlots() {
|
||||||
|
static constexpr std::array<u32, 3> ioBases = {
|
||||||
|
PICA::InternalRegs::Tex0BorderColor, PICA::InternalRegs::Tex1BorderColor, PICA::InternalRegs::Tex2BorderColor
|
||||||
|
};
|
||||||
|
|
||||||
|
for (int i = 0; i < 3; i++) {
|
||||||
|
if ((regs[PICA::InternalRegs::TexUnitCfg] & (1 << i)) == 0) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
const size_t ioBase = ioBases[i];
|
||||||
|
|
||||||
|
const u32 dim = regs[ioBase + 1];
|
||||||
|
const u32 config = regs[ioBase + 2];
|
||||||
|
const u32 height = dim & 0x7ff;
|
||||||
|
const u32 width = getBits<16, 11>(dim);
|
||||||
|
const u32 addr = (regs[ioBase + 4] & 0x0FFFFFFF) << 3;
|
||||||
|
u32 format = regs[ioBase + (i == 0 ? 13 : 5)] & 0xF;
|
||||||
|
|
||||||
|
glActiveTexture(GL_TEXTURE0 + i);
|
||||||
|
Texture targetTex(addr, static_cast<PICA::TextureFmt>(format), width, height, config);
|
||||||
|
OpenGL::Texture tex = getTexture(targetTex);
|
||||||
|
tex.bind();
|
||||||
|
}
|
||||||
|
|
||||||
|
glActiveTexture(GL_TEXTURE0);
|
||||||
|
|
||||||
|
// Update the texture unit configuration uniform if it changed
|
||||||
|
const u32 texUnitConfig = regs[PICA::InternalRegs::TexUnitCfg];
|
||||||
|
if (oldTexUnitConfig != texUnitConfig) {
|
||||||
|
oldTexUnitConfig = texUnitConfig;
|
||||||
|
glUniform1ui(texUnitConfigLoc, texUnitConfig);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void Renderer::drawVertices(PICA::PrimType primType, std::span<const Vertex> vertices) {
|
||||||
|
// The fourth type is meant to be "Geometry primitive". TODO: Find out what that is
|
||||||
|
static constexpr std::array<OpenGL::Primitives, 4> primTypes = {
|
||||||
|
OpenGL::Triangle, OpenGL::TriangleStrip, OpenGL::TriangleFan, OpenGL::Triangle
|
||||||
|
};
|
||||||
|
const auto primitiveTopology = primTypes[static_cast<usize>(primType)];
|
||||||
|
|
||||||
|
// TODO: We should implement a GL state tracker that tracks settings like scissor, blending, bound program, etc
|
||||||
|
// This way if we attempt to eg do multiple glEnable(GL_BLEND) calls in a row, it will say "Oh blending is already enabled"
|
||||||
|
// And not actually perform the very expensive driver call for it
|
||||||
|
OpenGL::disableScissor();
|
||||||
|
|
||||||
|
vbo.bind();
|
||||||
|
vao.bind();
|
||||||
|
triangleProgram.use();
|
||||||
|
|
||||||
// Adjust alpha test if necessary
|
// Adjust alpha test if necessary
|
||||||
const u32 alphaControl = regs[PICAInternalRegs::AlphaTestConfig];
|
const u32 alphaControl = regs[PICA::InternalRegs::AlphaTestConfig];
|
||||||
if (alphaControl != oldAlphaControl) {
|
if (alphaControl != oldAlphaControl) {
|
||||||
oldAlphaControl = alphaControl;
|
oldAlphaControl = alphaControl;
|
||||||
glUniform1ui(alphaControlLoc, alphaControl);
|
glUniform1ui(alphaControlLoc, alphaControl);
|
||||||
|
@ -277,7 +575,7 @@ void Renderer::drawVertices(OpenGL::Primitives primType, Vertex* vertices, u32 c
|
||||||
OpenGL::Framebuffer poop = getColourFBO();
|
OpenGL::Framebuffer poop = getColourFBO();
|
||||||
poop.bind(OpenGL::DrawAndReadFramebuffer);
|
poop.bind(OpenGL::DrawAndReadFramebuffer);
|
||||||
|
|
||||||
const u32 depthControl = regs[PICAInternalRegs::DepthAndColorMask];
|
const u32 depthControl = regs[PICA::InternalRegs::DepthAndColorMask];
|
||||||
const bool depthEnable = depthControl & 1;
|
const bool depthEnable = depthControl & 1;
|
||||||
const bool depthWriteEnable = getBit<12>(depthControl);
|
const bool depthWriteEnable = getBit<12>(depthControl);
|
||||||
const int depthFunc = getBits<4, 3>(depthControl);
|
const int depthFunc = getBits<4, 3>(depthControl);
|
||||||
|
@ -288,9 +586,9 @@ void Renderer::drawVertices(OpenGL::Primitives primType, Vertex* vertices, u32 c
|
||||||
GL_NEVER, GL_ALWAYS, GL_EQUAL, GL_NOTEQUAL, GL_LESS, GL_LEQUAL, GL_GREATER, GL_GEQUAL
|
GL_NEVER, GL_ALWAYS, GL_EQUAL, GL_NOTEQUAL, GL_LESS, GL_LEQUAL, GL_GREATER, GL_GEQUAL
|
||||||
};
|
};
|
||||||
|
|
||||||
const float depthScale = f24::fromRaw(regs[PICAInternalRegs::DepthScale] & 0xffffff).toFloat32();
|
const float depthScale = f24::fromRaw(regs[PICA::InternalRegs::DepthScale] & 0xffffff).toFloat32();
|
||||||
const float depthOffset = f24::fromRaw(regs[PICAInternalRegs::DepthOffset] & 0xffffff).toFloat32();
|
const float depthOffset = f24::fromRaw(regs[PICA::InternalRegs::DepthOffset] & 0xffffff).toFloat32();
|
||||||
const bool depthMapEnable = regs[PICAInternalRegs::DepthmapEnable] & 1;
|
const bool depthMapEnable = regs[PICA::InternalRegs::DepthmapEnable] & 1;
|
||||||
|
|
||||||
// Update depth uniforms
|
// Update depth uniforms
|
||||||
if (oldDepthScale != depthScale) {
|
if (oldDepthScale != depthScale) {
|
||||||
|
@ -308,30 +606,12 @@ void Renderer::drawVertices(OpenGL::Primitives primType, Vertex* vertices, u32 c
|
||||||
glUniform1i(depthmapEnableLoc, depthMapEnable);
|
glUniform1i(depthmapEnableLoc, depthMapEnable);
|
||||||
}
|
}
|
||||||
|
|
||||||
// Hack for rendering texture 1
|
setupTextureEnvState();
|
||||||
if (regs[0x80] & 1) {
|
bindTexturesToSlots();
|
||||||
const u32 dim = regs[0x82];
|
|
||||||
const u32 config = regs[0x83];
|
|
||||||
const u32 height = dim & 0x7ff;
|
|
||||||
const u32 width = getBits<16, 11>(dim);
|
|
||||||
const u32 addr = (regs[0x85] & 0x0FFFFFFF) << 3;
|
|
||||||
const u32 format = regs[0x8E] & 0xF;
|
|
||||||
|
|
||||||
Texture targetTex(addr, static_cast<Texture::Formats>(format), width, height, config);
|
|
||||||
OpenGL::Texture tex = getTexture(targetTex);
|
|
||||||
tex.bind();
|
|
||||||
}
|
|
||||||
|
|
||||||
// Update the texture unit configuration uniform if it changed
|
|
||||||
const u32 texUnitConfig = regs[PICAInternalRegs::TexUnitCfg];
|
|
||||||
if (oldTexUnitConfig != texUnitConfig) {
|
|
||||||
oldTexUnitConfig = texUnitConfig;
|
|
||||||
glUniform1ui(texUnitConfigLoc, texUnitConfig);
|
|
||||||
}
|
|
||||||
|
|
||||||
// TODO: Actually use this
|
// TODO: Actually use this
|
||||||
float viewportWidth = f24::fromRaw(regs[PICAInternalRegs::ViewportWidth] & 0xffffff).toFloat32() * 2.0;
|
float viewportWidth = f24::fromRaw(regs[PICA::InternalRegs::ViewportWidth] & 0xffffff).toFloat32() * 2.0;
|
||||||
float viewportHeight = f24::fromRaw(regs[PICAInternalRegs::ViewportHeight] & 0xffffff).toFloat32() * 2.0;
|
float viewportHeight = f24::fromRaw(regs[PICA::InternalRegs::ViewportHeight] & 0xffffff).toFloat32() * 2.0;
|
||||||
OpenGL::setViewport(viewportWidth, viewportHeight);
|
OpenGL::setViewport(viewportWidth, viewportHeight);
|
||||||
|
|
||||||
// Note: The code below must execute after we've bound the colour buffer & its framebuffer
|
// Note: The code below must execute after we've bound the colour buffer & its framebuffer
|
||||||
|
@ -352,8 +632,8 @@ void Renderer::drawVertices(OpenGL::Primitives primType, Vertex* vertices, u32 c
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
vbo.bufferVertsSub(vertices, count);
|
vbo.bufferVertsSub(vertices);
|
||||||
OpenGL::draw(primType, count);
|
OpenGL::draw(primitiveTopology, vertices.size());
|
||||||
}
|
}
|
||||||
|
|
||||||
constexpr u32 topScreenBuffer = 0x1f000000;
|
constexpr u32 topScreenBuffer = 0x1f000000;
|
||||||
|
@ -361,20 +641,11 @@ constexpr u32 bottomScreenBuffer = 0x1f05dc00;
|
||||||
|
|
||||||
// Quick hack to display top screen for now
|
// Quick hack to display top screen for now
|
||||||
void Renderer::display() {
|
void Renderer::display() {
|
||||||
OpenGL::disableBlend();
|
|
||||||
OpenGL::disableDepth();
|
|
||||||
OpenGL::disableScissor();
|
OpenGL::disableScissor();
|
||||||
|
|
||||||
OpenGL::bindScreenFramebuffer();
|
glBindFramebuffer(GL_DRAW_FRAMEBUFFER, 0);
|
||||||
colourBufferCache[0].texture.bind();
|
screenFramebuffer.bind(OpenGL::ReadFramebuffer);
|
||||||
|
glBlitFramebuffer(0, 0, 400, 480, 0, 0, 400, 480, GL_COLOR_BUFFER_BIT, GL_LINEAR);
|
||||||
displayProgram.use();
|
|
||||||
|
|
||||||
dummyVAO.bind();
|
|
||||||
OpenGL::setClearColor(0.0, 0.0, 1.0, 1.0); // Clear screen colour
|
|
||||||
OpenGL::clearColor();
|
|
||||||
OpenGL::setViewport(0, 240, 400, 240); // Actually draw our 3DS screen
|
|
||||||
OpenGL::draw(OpenGL::TriangleStrip, 4);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void Renderer::clearBuffer(u32 startAddress, u32 endAddress, u32 value, u32 control) {
|
void Renderer::clearBuffer(u32 startAddress, u32 endAddress, u32 value, u32 control) {
|
||||||
|
@ -424,8 +695,8 @@ void Renderer::bindDepthBuffer() {
|
||||||
tex = depthBufferCache.add(sampleBuffer).texture.m_handle;
|
tex = depthBufferCache.add(sampleBuffer).texture.m_handle;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (DepthBuffer::Formats::Depth24Stencil8 != depthBufferFormat) Helpers::panic("TODO: Should we remove stencil attachment?");
|
if (PICA::DepthFmt::Depth24Stencil8 != depthBufferFormat) Helpers::panic("TODO: Should we remove stencil attachment?");
|
||||||
auto attachment = depthBufferFormat == DepthBuffer::Formats::Depth24Stencil8 ? GL_DEPTH_STENCIL_ATTACHMENT : GL_DEPTH_ATTACHMENT;
|
auto attachment = depthBufferFormat == PICA::DepthFmt::Depth24Stencil8 ? GL_DEPTH_STENCIL_ATTACHMENT : GL_DEPTH_ATTACHMENT;
|
||||||
glFramebufferTexture2D(GL_FRAMEBUFFER, attachment, GL_TEXTURE_2D, tex, 0);
|
glFramebufferTexture2D(GL_FRAMEBUFFER, attachment, GL_TEXTURE_2D, tex, 0);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -450,4 +721,28 @@ void Renderer::displayTransfer(u32 inputAddr, u32 outputAddr, u32 inputSize, u32
|
||||||
|
|
||||||
const u32 outputWidth = outputSize & 0xffff;
|
const u32 outputWidth = outputSize & 0xffff;
|
||||||
const u32 outputGap = outputSize >> 16;
|
const u32 outputGap = outputSize >> 16;
|
||||||
|
|
||||||
|
auto framebuffer = colourBufferCache.findFromAddress(inputAddr);
|
||||||
|
// If there's a framebuffer at this address, use it. Otherwise go back to our old hack and display framebuffer 0
|
||||||
|
// Displays are hard I really don't want to try implementing them because getting a fast solution is terrible
|
||||||
|
OpenGL::Texture& tex = framebuffer.has_value() ? framebuffer.value().get().texture : colourBufferCache[0].texture;
|
||||||
|
|
||||||
|
tex.bind();
|
||||||
|
screenFramebuffer.bind(OpenGL::DrawFramebuffer);
|
||||||
|
|
||||||
|
OpenGL::disableBlend();
|
||||||
|
OpenGL::disableDepth();
|
||||||
|
OpenGL::disableScissor();
|
||||||
|
displayProgram.use();
|
||||||
|
|
||||||
|
// Hack: Detect whether we are writing to the top or bottom screen by checking output gap and drawing to the proper part of the output texture
|
||||||
|
// We consider output gap == 320 to mean bottom, and anything else to mean top
|
||||||
|
if (outputGap == 320) {
|
||||||
|
OpenGL::setViewport(40, 0, 320, 240); // Bottom screen viewport
|
||||||
|
} else {
|
||||||
|
OpenGL::setViewport(0, 240, 400, 240); // Top screen viewport
|
||||||
|
}
|
||||||
|
|
||||||
|
dummyVAO.bind();
|
||||||
|
OpenGL::draw(OpenGL::TriangleStrip, 4); // Actually draw our 3DS screen
|
||||||
}
|
}
|
|
@ -43,34 +43,34 @@ u64 Texture::sizeInBytes() {
|
||||||
u64 pixelCount = u64(size.x()) * u64(size.y());
|
u64 pixelCount = u64(size.x()) * u64(size.y());
|
||||||
|
|
||||||
switch (format) {
|
switch (format) {
|
||||||
case Formats::RGBA8: // 4 bytes per pixel
|
case PICA::TextureFmt::RGBA8: // 4 bytes per pixel
|
||||||
return pixelCount * 4;
|
return pixelCount * 4;
|
||||||
|
|
||||||
case Formats::RGB8: // 3 bytes per pixel
|
case PICA::TextureFmt::RGB8: // 3 bytes per pixel
|
||||||
return pixelCount * 3;
|
return pixelCount * 3;
|
||||||
|
|
||||||
case Formats::RGBA5551: // 2 bytes per pixel
|
case PICA::TextureFmt::RGBA5551: // 2 bytes per pixel
|
||||||
case Formats::RGB565:
|
case PICA::TextureFmt::RGB565:
|
||||||
case Formats::RGBA4:
|
case PICA::TextureFmt::RGBA4:
|
||||||
case Formats::RG8:
|
case PICA::TextureFmt::RG8:
|
||||||
case Formats::IA8:
|
case PICA::TextureFmt::IA8:
|
||||||
return pixelCount * 2;
|
return pixelCount * 2;
|
||||||
|
|
||||||
case Formats::A8: // 1 byte per pixel
|
case PICA::TextureFmt::A8: // 1 byte per pixel
|
||||||
case Formats::I8:
|
case PICA::TextureFmt::I8:
|
||||||
case Formats::IA4:
|
case PICA::TextureFmt::IA4:
|
||||||
return pixelCount;
|
return pixelCount;
|
||||||
|
|
||||||
case Formats::I4: // 4 bits per pixel
|
case PICA::TextureFmt::I4: // 4 bits per pixel
|
||||||
case Formats::A4:
|
case PICA::TextureFmt::A4:
|
||||||
return pixelCount / 2;
|
return pixelCount / 2;
|
||||||
|
|
||||||
case Formats::ETC1: // Compressed formats
|
case PICA::TextureFmt::ETC1: // Compressed formats
|
||||||
case Formats::ETC1A4: {
|
case PICA::TextureFmt::ETC1A4: {
|
||||||
// Number of 4x4 tiles
|
// Number of 4x4 tiles
|
||||||
const u64 tileCount = pixelCount / 16;
|
const u64 tileCount = pixelCount / 16;
|
||||||
// Tiles are 8 bytes each on ETC1 and 16 bytes each on ETC1A4
|
// Tiles are 8 bytes each on ETC1 and 16 bytes each on ETC1A4
|
||||||
const u64 tileSize = format == Formats::ETC1 ? 8 : 16;
|
const u64 tileSize = format == PICA::TextureFmt::ETC1 ? 8 : 16;
|
||||||
return tileCount * tileSize;
|
return tileCount * tileSize;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -111,9 +111,9 @@ u32 Texture::getSwizzledOffset_4bpp(u32 u, u32 v, u32 width) {
|
||||||
// Get the texel at position (u, v)
|
// Get the texel at position (u, v)
|
||||||
// fmt: format of the texture
|
// fmt: format of the texture
|
||||||
// data: texture data of the texture
|
// data: texture data of the texture
|
||||||
u32 Texture::decodeTexel(u32 u, u32 v, Texture::Formats fmt, const void* data) {
|
u32 Texture::decodeTexel(u32 u, u32 v, PICA::TextureFmt fmt, const void* data) {
|
||||||
switch (fmt) {
|
switch (fmt) {
|
||||||
case Formats::RGBA4: {
|
case PICA::TextureFmt::RGBA4: {
|
||||||
u32 offset = getSwizzledOffset(u, v, size.u(), 2);
|
u32 offset = getSwizzledOffset(u, v, size.u(), 2);
|
||||||
auto ptr = static_cast<const u8*>(data);
|
auto ptr = static_cast<const u8*>(data);
|
||||||
u16 texel = u16(ptr[offset]) | (u16(ptr[offset + 1]) << 8);
|
u16 texel = u16(ptr[offset]) | (u16(ptr[offset + 1]) << 8);
|
||||||
|
@ -126,7 +126,7 @@ u32 Texture::decodeTexel(u32 u, u32 v, Texture::Formats fmt, const void* data) {
|
||||||
return (alpha << 24) | (b << 16) | (g << 8) | r;
|
return (alpha << 24) | (b << 16) | (g << 8) | r;
|
||||||
}
|
}
|
||||||
|
|
||||||
case Formats::RGBA5551: {
|
case PICA::TextureFmt::RGBA5551: {
|
||||||
u32 offset = getSwizzledOffset(u, v, size.u(), 2);
|
u32 offset = getSwizzledOffset(u, v, size.u(), 2);
|
||||||
auto ptr = static_cast<const u8*>(data);
|
auto ptr = static_cast<const u8*>(data);
|
||||||
u16 texel = u16(ptr[offset]) | (u16(ptr[offset + 1]) << 8);
|
u16 texel = u16(ptr[offset]) | (u16(ptr[offset + 1]) << 8);
|
||||||
|
@ -139,7 +139,7 @@ u32 Texture::decodeTexel(u32 u, u32 v, Texture::Formats fmt, const void* data) {
|
||||||
return (alpha << 24) | (b << 16) | (g << 8) | r;
|
return (alpha << 24) | (b << 16) | (g << 8) | r;
|
||||||
}
|
}
|
||||||
|
|
||||||
case Formats::RGB565: {
|
case PICA::TextureFmt::RGB565: {
|
||||||
u32 offset = getSwizzledOffset(u, v, size.u(), 2);
|
u32 offset = getSwizzledOffset(u, v, size.u(), 2);
|
||||||
auto ptr = static_cast<const u8*>(data);
|
auto ptr = static_cast<const u8*>(data);
|
||||||
u16 texel = u16(ptr[offset]) | (u16(ptr[offset + 1]) << 8);
|
u16 texel = u16(ptr[offset]) | (u16(ptr[offset + 1]) << 8);
|
||||||
|
@ -151,7 +151,7 @@ u32 Texture::decodeTexel(u32 u, u32 v, Texture::Formats fmt, const void* data) {
|
||||||
return (0xff << 24) | (b << 16) | (g << 8) | r;
|
return (0xff << 24) | (b << 16) | (g << 8) | r;
|
||||||
}
|
}
|
||||||
|
|
||||||
case Formats::RG8: {
|
case PICA::TextureFmt::RG8: {
|
||||||
u32 offset = getSwizzledOffset(u, v, size.u(), 2);
|
u32 offset = getSwizzledOffset(u, v, size.u(), 2);
|
||||||
auto ptr = static_cast<const u8*>(data);
|
auto ptr = static_cast<const u8*>(data);
|
||||||
|
|
||||||
|
@ -162,7 +162,7 @@ u32 Texture::decodeTexel(u32 u, u32 v, Texture::Formats fmt, const void* data) {
|
||||||
return (0xff << 24) | (b << 16) | (g << 8) | r;
|
return (0xff << 24) | (b << 16) | (g << 8) | r;
|
||||||
}
|
}
|
||||||
|
|
||||||
case Formats::RGB8: {
|
case PICA::TextureFmt::RGB8: {
|
||||||
u32 offset = getSwizzledOffset(u, v, size.u(), 3);
|
u32 offset = getSwizzledOffset(u, v, size.u(), 3);
|
||||||
auto ptr = static_cast<const u8*>(data);
|
auto ptr = static_cast<const u8*>(data);
|
||||||
|
|
||||||
|
@ -173,7 +173,7 @@ u32 Texture::decodeTexel(u32 u, u32 v, Texture::Formats fmt, const void* data) {
|
||||||
return (0xff << 24) | (b << 16) | (g << 8) | r;
|
return (0xff << 24) | (b << 16) | (g << 8) | r;
|
||||||
}
|
}
|
||||||
|
|
||||||
case Formats::RGBA8: {
|
case PICA::TextureFmt::RGBA8: {
|
||||||
u32 offset = getSwizzledOffset(u, v, size.u(), 4);
|
u32 offset = getSwizzledOffset(u, v, size.u(), 4);
|
||||||
auto ptr = static_cast<const u8*>(data);
|
auto ptr = static_cast<const u8*>(data);
|
||||||
|
|
||||||
|
@ -185,7 +185,7 @@ u32 Texture::decodeTexel(u32 u, u32 v, Texture::Formats fmt, const void* data) {
|
||||||
return (alpha << 24) | (b << 16) | (g << 8) | r;
|
return (alpha << 24) | (b << 16) | (g << 8) | r;
|
||||||
}
|
}
|
||||||
|
|
||||||
case Formats::IA4: {
|
case PICA::TextureFmt::IA4: {
|
||||||
u32 offset = getSwizzledOffset(u, v, size.u(), 1);
|
u32 offset = getSwizzledOffset(u, v, size.u(), 1);
|
||||||
auto ptr = static_cast<const u8*>(data);
|
auto ptr = static_cast<const u8*>(data);
|
||||||
|
|
||||||
|
@ -197,7 +197,7 @@ u32 Texture::decodeTexel(u32 u, u32 v, Texture::Formats fmt, const void* data) {
|
||||||
return (alpha << 24) | (intensity << 16) | (intensity << 8) | intensity;
|
return (alpha << 24) | (intensity << 16) | (intensity << 8) | intensity;
|
||||||
}
|
}
|
||||||
|
|
||||||
case Formats::A4: {
|
case PICA::TextureFmt::A4: {
|
||||||
u32 offset = getSwizzledOffset_4bpp(u, v, size.u());
|
u32 offset = getSwizzledOffset_4bpp(u, v, size.u());
|
||||||
auto ptr = static_cast<const u8*>(data);
|
auto ptr = static_cast<const u8*>(data);
|
||||||
|
|
||||||
|
@ -209,7 +209,7 @@ u32 Texture::decodeTexel(u32 u, u32 v, Texture::Formats fmt, const void* data) {
|
||||||
return (alpha << 24) | (0 << 16) | (0 << 8) | 0;
|
return (alpha << 24) | (0 << 16) | (0 << 8) | 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
case Formats::A8: {
|
case PICA::TextureFmt::A8: {
|
||||||
u32 offset = getSwizzledOffset(u, v, size.u(), 1);
|
u32 offset = getSwizzledOffset(u, v, size.u(), 1);
|
||||||
auto ptr = static_cast<const u8*>(data);
|
auto ptr = static_cast<const u8*>(data);
|
||||||
const u8 alpha = ptr[offset];
|
const u8 alpha = ptr[offset];
|
||||||
|
@ -218,7 +218,7 @@ u32 Texture::decodeTexel(u32 u, u32 v, Texture::Formats fmt, const void* data) {
|
||||||
return (alpha << 24) | (0 << 16) | (0 << 8) | 0;
|
return (alpha << 24) | (0 << 16) | (0 << 8) | 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
case Formats::I4: {
|
case PICA::TextureFmt::I4: {
|
||||||
u32 offset = getSwizzledOffset_4bpp(u, v, size.u());
|
u32 offset = getSwizzledOffset_4bpp(u, v, size.u());
|
||||||
auto ptr = static_cast<const u8*>(data);
|
auto ptr = static_cast<const u8*>(data);
|
||||||
|
|
||||||
|
@ -230,7 +230,7 @@ u32 Texture::decodeTexel(u32 u, u32 v, Texture::Formats fmt, const void* data) {
|
||||||
return (0xff << 24) | (intensity << 16) | (intensity << 8) | intensity;
|
return (0xff << 24) | (intensity << 16) | (intensity << 8) | intensity;
|
||||||
}
|
}
|
||||||
|
|
||||||
case Formats::I8: {
|
case PICA::TextureFmt::I8: {
|
||||||
u32 offset = getSwizzledOffset(u, v, size.u(), 1);
|
u32 offset = getSwizzledOffset(u, v, size.u(), 1);
|
||||||
auto ptr = static_cast<const u8*>(data);
|
auto ptr = static_cast<const u8*>(data);
|
||||||
const u8 intensity = ptr[offset];
|
const u8 intensity = ptr[offset];
|
||||||
|
@ -239,7 +239,7 @@ u32 Texture::decodeTexel(u32 u, u32 v, Texture::Formats fmt, const void* data) {
|
||||||
return (0xff << 24) | (intensity << 16) | (intensity << 8) | intensity;
|
return (0xff << 24) | (intensity << 16) | (intensity << 8) | intensity;
|
||||||
}
|
}
|
||||||
|
|
||||||
case Formats::IA8: {
|
case PICA::TextureFmt::IA8: {
|
||||||
u32 offset = getSwizzledOffset(u, v, size.u(), 2);
|
u32 offset = getSwizzledOffset(u, v, size.u(), 2);
|
||||||
auto ptr = static_cast<const u8*>(data);
|
auto ptr = static_cast<const u8*>(data);
|
||||||
|
|
||||||
|
@ -249,8 +249,8 @@ u32 Texture::decodeTexel(u32 u, u32 v, Texture::Formats fmt, const void* data) {
|
||||||
return (alpha << 24) | (intensity << 16) | (intensity << 8) | intensity;
|
return (alpha << 24) | (intensity << 16) | (intensity << 8) | intensity;
|
||||||
}
|
}
|
||||||
|
|
||||||
case Formats::ETC1: return getTexelETC(false, u, v, size.u(), data);
|
case PICA::TextureFmt::ETC1: return getTexelETC(false, u, v, size.u(), data);
|
||||||
case Formats::ETC1A4: return getTexelETC(true, u, v, size.u(), data);
|
case PICA::TextureFmt::ETC1A4: return getTexelETC(true, u, v, size.u(), data);
|
||||||
|
|
||||||
default:
|
default:
|
||||||
Helpers::panic("[Texture::DecodeTexel] Unimplemented format = %d", static_cast<int>(fmt));
|
Helpers::panic("[Texture::DecodeTexel] Unimplemented format = %d", static_cast<int>(fmt));
|
||||||
|
@ -271,24 +271,4 @@ void Texture::decodeTexture(const void* data) {
|
||||||
|
|
||||||
texture.bind();
|
texture.bind();
|
||||||
glTexSubImage2D(GL_TEXTURE_2D, 0, 0, 0, size.u(), size.v(), GL_RGBA, GL_UNSIGNED_BYTE, decoded.data());
|
glTexSubImage2D(GL_TEXTURE_2D, 0, 0, 0, size.u(), size.v(), GL_RGBA, GL_UNSIGNED_BYTE, decoded.data());
|
||||||
}
|
|
||||||
|
|
||||||
std::string Texture::textureFormatToString(Texture::Formats fmt) {
|
|
||||||
switch (fmt) {
|
|
||||||
case Formats::A4: return "A4";
|
|
||||||
case Formats::A8: return "A8";
|
|
||||||
case Formats::ETC1: return "ETC1";
|
|
||||||
case Formats::ETC1A4: return "ETC1A4";
|
|
||||||
case Formats::I4: return "I4";
|
|
||||||
case Formats::I8: return "I8";
|
|
||||||
case Formats::IA4: return "IA4";
|
|
||||||
case Formats::IA8: return "IA8";
|
|
||||||
case Formats::RG8: return "RG8";
|
|
||||||
case Formats::RGB565: return "RGB565";
|
|
||||||
case Formats::RGB8: return "RGB8";
|
|
||||||
case Formats::RGBA4: return "RGBA4";
|
|
||||||
case Formats::RGBA5551: return "RGBA5551";
|
|
||||||
case Formats::RGBA8: return "RGBA8";
|
|
||||||
default: return "Unknown";
|
|
||||||
}
|
|
||||||
}
|
}
|
|
@ -7,12 +7,6 @@ namespace ACCommands {
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
namespace Result {
|
|
||||||
enum : u32 {
|
|
||||||
Success = 0,
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
void ACService::reset() {}
|
void ACService::reset() {}
|
||||||
|
|
||||||
void ACService::handleSyncRequest(u32 messagePointer) {
|
void ACService::handleSyncRequest(u32 messagePointer) {
|
||||||
|
|
|
@ -7,12 +7,6 @@ namespace ACTCommands {
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
namespace Result {
|
|
||||||
enum : u32 {
|
|
||||||
Success = 0,
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
void ACTService::reset() {}
|
void ACTService::reset() {}
|
||||||
|
|
||||||
void ACTService::handleSyncRequest(u32 messagePointer) {
|
void ACTService::handleSyncRequest(u32 messagePointer) {
|
||||||
|
|
|
@ -8,12 +8,6 @@ namespace AMCommands {
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
namespace Result {
|
|
||||||
enum : u32 {
|
|
||||||
Success = 0,
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
void AMService::reset() {}
|
void AMService::reset() {}
|
||||||
|
|
||||||
void AMService::handleSyncRequest(u32 messagePointer) {
|
void AMService::handleSyncRequest(u32 messagePointer) {
|
||||||
|
|
|
@ -25,12 +25,6 @@ namespace APTCommands {
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
namespace Result {
|
|
||||||
enum : u32 {
|
|
||||||
Success = 0,
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
// https://www.3dbrew.org/wiki/NS_and_APT_Services#Command
|
// https://www.3dbrew.org/wiki/NS_and_APT_Services#Command
|
||||||
namespace APTTransitions {
|
namespace APTTransitions {
|
||||||
enum : u32 {
|
enum : u32 {
|
||||||
|
@ -154,7 +148,7 @@ void APTService::inquireNotification(u32 messagePointer) {
|
||||||
|
|
||||||
mem.write32(messagePointer, IPC::responseHeader(0xB, 2, 0));
|
mem.write32(messagePointer, IPC::responseHeader(0xB, 2, 0));
|
||||||
mem.write32(messagePointer + 4, Result::Success);
|
mem.write32(messagePointer + 4, Result::Success);
|
||||||
mem.write32(messagePointer + 8, static_cast<u32>(NotificationType::None));
|
mem.write32(messagePointer + 8, static_cast<u32>(NotificationType::None));
|
||||||
}
|
}
|
||||||
|
|
||||||
void APTService::getLockHandle(u32 messagePointer) {
|
void APTService::getLockHandle(u32 messagePointer) {
|
||||||
|
|
|
@ -14,12 +14,6 @@ namespace BOSSCommands {
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
namespace Result {
|
|
||||||
enum : u32 {
|
|
||||||
Success = 0,
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
void BOSSService::reset() {
|
void BOSSService::reset() {
|
||||||
optoutFlag = 0;
|
optoutFlag = 0;
|
||||||
}
|
}
|
||||||
|
|
|
@ -8,12 +8,6 @@ namespace CAMCommands {
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
namespace Result {
|
|
||||||
enum : u32 {
|
|
||||||
Success = 0,
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
void CAMService::reset() {}
|
void CAMService::reset() {}
|
||||||
|
|
||||||
void CAMService::handleSyncRequest(u32 messagePointer) {
|
void CAMService::handleSyncRequest(u32 messagePointer) {
|
||||||
|
|
|
@ -8,12 +8,6 @@ namespace CECDCommands {
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
namespace Result {
|
|
||||||
enum : u32 {
|
|
||||||
Success = 0,
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
void CECDService::reset() {
|
void CECDService::reset() {
|
||||||
infoEvent = std::nullopt;
|
infoEvent = std::nullopt;
|
||||||
}
|
}
|
||||||
|
|
|
@ -16,12 +16,6 @@ namespace CFGCommands {
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
namespace Result {
|
|
||||||
enum : u32 {
|
|
||||||
Success = 0,
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
void CFGService::reset() {}
|
void CFGService::reset() {}
|
||||||
|
|
||||||
void CFGService::handleSyncRequest(u32 messagePointer) {
|
void CFGService::handleSyncRequest(u32 messagePointer) {
|
||||||
|
@ -79,7 +73,7 @@ void CFGService::getConfigInfoBlk2(u32 messagePointer) {
|
||||||
};
|
};
|
||||||
|
|
||||||
for (int i = 0; i < 8; i++) {
|
for (int i = 0; i < 8; i++) {
|
||||||
mem.write32(output + i * 4, std::bit_cast<u32, float>(STEREO_CAMERA_SETTINGS[i]));
|
mem.write32(output + i * 4, Helpers::bit_cast<u32, float>(STEREO_CAMERA_SETTINGS[i]));
|
||||||
}
|
}
|
||||||
} else if (size == 0x1C && blockID == 0xA0000) { // Username
|
} else if (size == 0x1C && blockID == 0xA0000) { // Username
|
||||||
writeStringU16(output, u"Pander");
|
writeStringU16(output, u"Pander");
|
||||||
|
@ -89,7 +83,7 @@ void CFGService::getConfigInfoBlk2(u32 messagePointer) {
|
||||||
} else if (size == 4 && blockID == 0xD0000) { // Agreed EULA version (first 2 bytes) and latest EULA version (next 2 bytes)
|
} else if (size == 4 && blockID == 0xD0000) { // Agreed EULA version (first 2 bytes) and latest EULA version (next 2 bytes)
|
||||||
log("Read EULA info\n");
|
log("Read EULA info\n");
|
||||||
mem.write16(output, 0x0202); // Agreed EULA version = 2.2 (Random number. TODO: Check)
|
mem.write16(output, 0x0202); // Agreed EULA version = 2.2 (Random number. TODO: Check)
|
||||||
mem.write16(output + 2, 0x0202); // Latest EULA version = 2.2
|
mem.write16(output + 2, 0x0202); // Latest EULA version = 2.2
|
||||||
} else if (size == 0x800 && blockID == 0xB0001) { // UTF-16 name for our country in every language at 0x80 byte intervals
|
} else if (size == 0x800 && blockID == 0xB0001) { // UTF-16 name for our country in every language at 0x80 byte intervals
|
||||||
constexpr size_t languageCount = 16;
|
constexpr size_t languageCount = 16;
|
||||||
constexpr size_t nameSize = 0x80; // Max size of each name in bytes
|
constexpr size_t nameSize = 0x80; // Max size of each name in bytes
|
||||||
|
@ -105,7 +99,7 @@ void CFGService::getConfigInfoBlk2(u32 messagePointer) {
|
||||||
std::u16string name = u"Pandington"; // Note: This + the null terminator needs to fit in 0x80 bytes
|
std::u16string name = u"Pandington"; // Note: This + the null terminator needs to fit in 0x80 bytes
|
||||||
|
|
||||||
for (int i = 0; i < languageCount; i++) {
|
for (int i = 0; i < languageCount; i++) {
|
||||||
u32 pointer = output + i * nameSize;
|
u32 pointer = output + i * nameSize;
|
||||||
writeStringU16(pointer, name);
|
writeStringU16(pointer, name);
|
||||||
}
|
}
|
||||||
} else if (size == 4 && blockID == 0xB0003) { // Coordinates (latidude and longtitude) as s16
|
} else if (size == 4 && blockID == 0xB0003) { // Coordinates (latidude and longtitude) as s16
|
||||||
|
|
|
@ -7,12 +7,6 @@ namespace DlpSrvrCommands {
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
namespace Result {
|
|
||||||
enum : u32 {
|
|
||||||
Success = 0,
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
void DlpSrvrService::reset() {}
|
void DlpSrvrService::reset() {}
|
||||||
|
|
||||||
void DlpSrvrService::handleSyncRequest(u32 messagePointer) {
|
void DlpSrvrService::handleSyncRequest(u32 messagePointer) {
|
||||||
|
|
|
@ -25,7 +25,6 @@ namespace DSPCommands {
|
||||||
|
|
||||||
namespace Result {
|
namespace Result {
|
||||||
enum : u32 {
|
enum : u32 {
|
||||||
Success = 0,
|
|
||||||
HeadphonesNotInserted = 0,
|
HeadphonesNotInserted = 0,
|
||||||
HeadphonesInserted = 1
|
HeadphonesInserted = 1
|
||||||
};
|
};
|
||||||
|
@ -215,7 +214,7 @@ void DSPService::registerInterruptEvents(u32 messagePointer) {
|
||||||
const u32 channel = mem.read32(messagePointer + 8);
|
const u32 channel = mem.read32(messagePointer + 8);
|
||||||
const u32 eventHandle = mem.read32(messagePointer + 16);
|
const u32 eventHandle = mem.read32(messagePointer + 16);
|
||||||
log("DSP::RegisterInterruptEvents (interrupt = %d, channel = %d, event = %d)\n", interrupt, channel, eventHandle);
|
log("DSP::RegisterInterruptEvents (interrupt = %d, channel = %d, event = %d)\n", interrupt, channel, eventHandle);
|
||||||
|
|
||||||
// The event handle being 0 means we're removing an event
|
// The event handle being 0 means we're removing an event
|
||||||
if (eventHandle == 0) {
|
if (eventHandle == 0) {
|
||||||
DSPEvent& e = getEventRef(interrupt, channel); // Get event
|
DSPEvent& e = getEventRef(interrupt, channel); // Get event
|
||||||
|
|
|
@ -17,12 +17,6 @@ namespace FRDCommands {
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
namespace Result {
|
|
||||||
enum : u32 {
|
|
||||||
Success = 0,
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
void FRDService::reset() {}
|
void FRDService::reset() {}
|
||||||
|
|
||||||
void FRDService::handleSyncRequest(u32 messagePointer) {
|
void FRDService::handleSyncRequest(u32 messagePointer) {
|
||||||
|
@ -91,7 +85,7 @@ void FRDService::getMyProfile(u32 messagePointer) {
|
||||||
mem.write32(messagePointer + 4, Result::Success);
|
mem.write32(messagePointer + 4, Result::Success);
|
||||||
|
|
||||||
// TODO: Should maybe make these user-configurable. Not super important though
|
// TODO: Should maybe make these user-configurable. Not super important though
|
||||||
mem.write8(messagePointer + 8, static_cast<u8>(Regions::USA)); // Region
|
mem.write8(messagePointer + 8, static_cast<u8>(Regions::USA)); // Region
|
||||||
mem.write8(messagePointer + 9, static_cast<u8>(CountryCodes::US)); // Country
|
mem.write8(messagePointer + 9, static_cast<u8>(CountryCodes::US)); // Country
|
||||||
mem.write8(messagePointer + 10, 2); // Area (this should be Washington)
|
mem.write8(messagePointer + 10, 2); // Area (this should be Washington)
|
||||||
mem.write8(messagePointer + 11, static_cast<u8>(LanguageCodes::English)); // Language
|
mem.write8(messagePointer + 11, static_cast<u8>(LanguageCodes::English)); // Language
|
||||||
|
|
|
@ -32,14 +32,6 @@ namespace FSCommands {
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
namespace ResultCode {
|
|
||||||
enum : u32 {
|
|
||||||
Success = 0,
|
|
||||||
FileNotFound = 0xC8804464, // TODO: Verify this
|
|
||||||
Failure = 0xFFFFFFFF,
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
void FSService::reset() {
|
void FSService::reset() {
|
||||||
priority = 0;
|
priority = 0;
|
||||||
}
|
}
|
||||||
|
@ -96,15 +88,15 @@ std::optional<Handle> FSService::openFileHandle(ArchiveBase* archive, const FSPa
|
||||||
|
|
||||||
auto& file = kernel.getObjects()[handle];
|
auto& file = kernel.getObjects()[handle];
|
||||||
file.data = new FileSession(archive, path, archivePath, opened.value());
|
file.data = new FileSession(archive, path, archivePath, opened.value());
|
||||||
|
|
||||||
return handle;
|
return handle;
|
||||||
} else {
|
} else {
|
||||||
return std::nullopt;
|
return std::nullopt;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
Rust::Result<Handle, FSResult> FSService::openDirectoryHandle(ArchiveBase* archive, const FSPath& path) {
|
Rust::Result<Handle, Result::HorizonResult> FSService::openDirectoryHandle(ArchiveBase* archive, const FSPath& path) {
|
||||||
Rust::Result<DirectorySession, FSResult> opened = archive->openDirectory(path);
|
Rust::Result<DirectorySession, Result::HorizonResult> opened = archive->openDirectory(path);
|
||||||
if (opened.isOk()) { // If opened doesn't have a value, we failed to open the directory
|
if (opened.isOk()) { // If opened doesn't have a value, we failed to open the directory
|
||||||
auto handle = kernel.makeObject(KernelObjectType::Directory);
|
auto handle = kernel.makeObject(KernelObjectType::Directory);
|
||||||
auto& object = kernel.getObjects()[handle];
|
auto& object = kernel.getObjects()[handle];
|
||||||
|
@ -116,15 +108,15 @@ Rust::Result<Handle, FSResult> FSService::openDirectoryHandle(ArchiveBase* archi
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
Rust::Result<Handle, FSResult> FSService::openArchiveHandle(u32 archiveID, const FSPath& path) {
|
Rust::Result<Handle, Result::HorizonResult> FSService::openArchiveHandle(u32 archiveID, const FSPath& path) {
|
||||||
ArchiveBase* archive = getArchiveFromID(archiveID, path);
|
ArchiveBase* archive = getArchiveFromID(archiveID, path);
|
||||||
|
|
||||||
if (archive == nullptr) [[unlikely]] {
|
if (archive == nullptr) [[unlikely]] {
|
||||||
Helpers::panic("OpenArchive: Tried to open unknown archive %d.", archiveID);
|
Helpers::panic("OpenArchive: Tried to open unknown archive %d.", archiveID);
|
||||||
return Err(FSResult::NotFormatted);
|
return Err(Result::FS::NotFormatted);
|
||||||
}
|
}
|
||||||
|
|
||||||
Rust::Result<ArchiveBase*, FSResult> res = archive->openArchive(path);
|
Rust::Result<ArchiveBase*, Result::HorizonResult> res = archive->openArchive(path);
|
||||||
if (res.isOk()) {
|
if (res.isOk()) {
|
||||||
auto handle = kernel.makeObject(KernelObjectType::Archive);
|
auto handle = kernel.makeObject(KernelObjectType::Archive);
|
||||||
auto& archiveObject = kernel.getObjects()[handle];
|
auto& archiveObject = kernel.getObjects()[handle];
|
||||||
|
@ -175,7 +167,7 @@ void FSService::handleSyncRequest(u32 messagePointer) {
|
||||||
void FSService::initialize(u32 messagePointer) {
|
void FSService::initialize(u32 messagePointer) {
|
||||||
log("FS::Initialize\n");
|
log("FS::Initialize\n");
|
||||||
mem.write32(messagePointer, IPC::responseHeader(0x801, 1, 0));
|
mem.write32(messagePointer, IPC::responseHeader(0x801, 1, 0));
|
||||||
mem.write32(messagePointer + 4, ResultCode::Success);
|
mem.write32(messagePointer + 4, Result::Success);
|
||||||
}
|
}
|
||||||
|
|
||||||
// TODO: Figure out how this is different from Initialize
|
// TODO: Figure out how this is different from Initialize
|
||||||
|
@ -184,7 +176,7 @@ void FSService::initializeWithSdkVersion(u32 messagePointer) {
|
||||||
log("FS::InitializeWithSDKVersion(version = %d)\n", version);
|
log("FS::InitializeWithSDKVersion(version = %d)\n", version);
|
||||||
|
|
||||||
mem.write32(messagePointer, IPC::responseHeader(0x861, 1, 0));
|
mem.write32(messagePointer, IPC::responseHeader(0x861, 1, 0));
|
||||||
mem.write32(messagePointer + 4, ResultCode::Success);
|
mem.write32(messagePointer + 4, Result::Success);
|
||||||
}
|
}
|
||||||
|
|
||||||
void FSService::closeArchive(u32 messagePointer) {
|
void FSService::closeArchive(u32 messagePointer) {
|
||||||
|
@ -196,10 +188,10 @@ void FSService::closeArchive(u32 messagePointer) {
|
||||||
|
|
||||||
if (object == nullptr) {
|
if (object == nullptr) {
|
||||||
log("FSService::CloseArchive: Tried to close invalid archive %X\n", handle);
|
log("FSService::CloseArchive: Tried to close invalid archive %X\n", handle);
|
||||||
mem.write32(messagePointer + 4, ResultCode::Failure);
|
mem.write32(messagePointer + 4, Result::FailurePlaceholder);
|
||||||
} else {
|
} else {
|
||||||
object->getData<ArchiveSession>()->isOpen = false;
|
object->getData<ArchiveSession>()->isOpen = false;
|
||||||
mem.write32(messagePointer + 4, ResultCode::Success);
|
mem.write32(messagePointer + 4, Result::Success);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -211,15 +203,15 @@ void FSService::openArchive(u32 messagePointer) {
|
||||||
|
|
||||||
auto archivePath = readPath(archivePathType, archivePathPointer, archivePathSize);
|
auto archivePath = readPath(archivePathType, archivePathPointer, archivePathSize);
|
||||||
log("FS::OpenArchive(archive ID = %d, archive path type = %d)\n", archiveID, archivePathType);
|
log("FS::OpenArchive(archive ID = %d, archive path type = %d)\n", archiveID, archivePathType);
|
||||||
|
|
||||||
Rust::Result<Handle, FSResult> res = openArchiveHandle(archiveID, archivePath);
|
Rust::Result<Handle, Result::HorizonResult> res = openArchiveHandle(archiveID, archivePath);
|
||||||
mem.write32(messagePointer, IPC::responseHeader(0x80C, 3, 0));
|
mem.write32(messagePointer, IPC::responseHeader(0x80C, 3, 0));
|
||||||
if (res.isOk()) {
|
if (res.isOk()) {
|
||||||
mem.write32(messagePointer + 4, ResultCode::Success);
|
mem.write32(messagePointer + 4, Result::Success);
|
||||||
mem.write64(messagePointer + 8, res.unwrap());
|
mem.write64(messagePointer + 8, res.unwrap());
|
||||||
} else {
|
} else {
|
||||||
log("FS::OpenArchive: Failed to open archive with id = %d. Error %08X\n", archiveID, (u32)res.unwrapErr());
|
log("FS::OpenArchive: Failed to open archive with id = %d. Error %08X\n", archiveID, (u32)res.unwrapErr());
|
||||||
mem.write32(messagePointer + 4, static_cast<u32>(res.unwrapErr()));
|
mem.write32(messagePointer + 4, res.unwrapErr());
|
||||||
mem.write64(messagePointer + 8, 0);
|
mem.write64(messagePointer + 8, 0);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -237,7 +229,7 @@ void FSService::openFile(u32 messagePointer) {
|
||||||
auto archiveObject = kernel.getObject(archiveHandle, KernelObjectType::Archive);
|
auto archiveObject = kernel.getObject(archiveHandle, KernelObjectType::Archive);
|
||||||
if (archiveObject == nullptr) [[unlikely]] {
|
if (archiveObject == nullptr) [[unlikely]] {
|
||||||
log("FS::OpenFile: Invalid archive handle %d\n", archiveHandle);
|
log("FS::OpenFile: Invalid archive handle %d\n", archiveHandle);
|
||||||
mem.write32(messagePointer + 4, ResultCode::Failure);
|
mem.write32(messagePointer + 4, Result::FailurePlaceholder);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -251,9 +243,9 @@ void FSService::openFile(u32 messagePointer) {
|
||||||
mem.write32(messagePointer, IPC::responseHeader(0x802, 1, 2));
|
mem.write32(messagePointer, IPC::responseHeader(0x802, 1, 2));
|
||||||
if (!handle.has_value()) {
|
if (!handle.has_value()) {
|
||||||
printf("OpenFile failed\n");
|
printf("OpenFile failed\n");
|
||||||
mem.write32(messagePointer + 4, ResultCode::FileNotFound);
|
mem.write32(messagePointer + 4, Result::FS::FileNotFound);
|
||||||
} else {
|
} else {
|
||||||
mem.write32(messagePointer + 4, ResultCode::Success);
|
mem.write32(messagePointer + 4, Result::Success);
|
||||||
mem.write32(messagePointer + 8, 0x10); // "Move handle descriptor"
|
mem.write32(messagePointer + 8, 0x10); // "Move handle descriptor"
|
||||||
mem.write32(messagePointer + 12, handle.value());
|
mem.write32(messagePointer + 12, handle.value());
|
||||||
}
|
}
|
||||||
|
@ -270,13 +262,13 @@ void FSService::createDirectory(u32 messagePointer) {
|
||||||
KernelObject* archiveObject = kernel.getObject(archiveHandle, KernelObjectType::Archive);
|
KernelObject* archiveObject = kernel.getObject(archiveHandle, KernelObjectType::Archive);
|
||||||
if (archiveObject == nullptr) [[unlikely]] {
|
if (archiveObject == nullptr) [[unlikely]] {
|
||||||
log("FS::CreateDirectory: Invalid archive handle %d\n", archiveHandle);
|
log("FS::CreateDirectory: Invalid archive handle %d\n", archiveHandle);
|
||||||
mem.write32(messagePointer + 4, ResultCode::Failure);
|
mem.write32(messagePointer + 4, Result::FailurePlaceholder);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
ArchiveBase* archive = archiveObject->getData<ArchiveSession>()->archive;
|
ArchiveBase* archive = archiveObject->getData<ArchiveSession>()->archive;
|
||||||
const auto dirPath = readPath(pathType, pathPointer, pathSize);
|
const auto dirPath = readPath(pathType, pathPointer, pathSize);
|
||||||
const FSResult res = archive->createDirectory(dirPath);
|
const Result::HorizonResult res = archive->createDirectory(dirPath);
|
||||||
|
|
||||||
mem.write32(messagePointer, IPC::responseHeader(0x809, 1, 0));
|
mem.write32(messagePointer, IPC::responseHeader(0x809, 1, 0));
|
||||||
mem.write32(messagePointer + 4, static_cast<u32>(res));
|
mem.write32(messagePointer + 4, static_cast<u32>(res));
|
||||||
|
@ -292,7 +284,7 @@ void FSService::openDirectory(u32 messagePointer) {
|
||||||
KernelObject* archiveObject = kernel.getObject(archiveHandle, KernelObjectType::Archive);
|
KernelObject* archiveObject = kernel.getObject(archiveHandle, KernelObjectType::Archive);
|
||||||
if (archiveObject == nullptr) [[unlikely]] {
|
if (archiveObject == nullptr) [[unlikely]] {
|
||||||
log("FS::OpenDirectory: Invalid archive handle %d\n", archiveHandle);
|
log("FS::OpenDirectory: Invalid archive handle %d\n", archiveHandle);
|
||||||
mem.write32(messagePointer + 4, ResultCode::Failure);
|
mem.write32(messagePointer + 4, Result::FailurePlaceholder);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -302,7 +294,7 @@ void FSService::openDirectory(u32 messagePointer) {
|
||||||
|
|
||||||
mem.write32(messagePointer, IPC::responseHeader(0x80B, 1, 2));
|
mem.write32(messagePointer, IPC::responseHeader(0x80B, 1, 2));
|
||||||
if (dir.isOk()) {
|
if (dir.isOk()) {
|
||||||
mem.write32(messagePointer + 4, ResultCode::Success);
|
mem.write32(messagePointer + 4, Result::Success);
|
||||||
mem.write32(messagePointer + 12, dir.unwrap());
|
mem.write32(messagePointer + 12, dir.unwrap());
|
||||||
} else {
|
} else {
|
||||||
printf("FS::OpenDirectory failed\n");
|
printf("FS::OpenDirectory failed\n");
|
||||||
|
@ -324,14 +316,14 @@ void FSService::openFileDirectly(u32 messagePointer) {
|
||||||
|
|
||||||
auto archivePath = readPath(archivePathType, archivePathPointer, archivePathSize);
|
auto archivePath = readPath(archivePathType, archivePathPointer, archivePathSize);
|
||||||
ArchiveBase* archive = getArchiveFromID(archiveID, archivePath);
|
ArchiveBase* archive = getArchiveFromID(archiveID, archivePath);
|
||||||
|
|
||||||
if (archive == nullptr) [[unlikely]] {
|
if (archive == nullptr) [[unlikely]] {
|
||||||
Helpers::panic("OpenFileDirectly: Tried to open unknown archive %d.", archiveID);
|
Helpers::panic("OpenFileDirectly: Tried to open unknown archive %d.", archiveID);
|
||||||
}
|
}
|
||||||
auto filePath = readPath(filePathType, filePathPointer, filePathSize);
|
auto filePath = readPath(filePathType, filePathPointer, filePathSize);
|
||||||
const FilePerms perms(openFlags);
|
const FilePerms perms(openFlags);
|
||||||
|
|
||||||
Rust::Result<ArchiveBase*, FSResult> res = archive->openArchive(archivePath);
|
Rust::Result<ArchiveBase*, Result::HorizonResult> res = archive->openArchive(archivePath);
|
||||||
if (res.isErr()) [[unlikely]] {
|
if (res.isErr()) [[unlikely]] {
|
||||||
Helpers::panic("OpenFileDirectly: Failed to open archive with given path");
|
Helpers::panic("OpenFileDirectly: Failed to open archive with given path");
|
||||||
}
|
}
|
||||||
|
@ -342,7 +334,7 @@ void FSService::openFileDirectly(u32 messagePointer) {
|
||||||
if (!handle.has_value()) {
|
if (!handle.has_value()) {
|
||||||
Helpers::panic("OpenFileDirectly: Failed to open file with given path");
|
Helpers::panic("OpenFileDirectly: Failed to open file with given path");
|
||||||
} else {
|
} else {
|
||||||
mem.write32(messagePointer + 4, ResultCode::Success);
|
mem.write32(messagePointer + 4, Result::Success);
|
||||||
mem.write32(messagePointer + 12, handle.value());
|
mem.write32(messagePointer + 12, handle.value());
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -360,16 +352,16 @@ void FSService::createFile(u32 messagePointer) {
|
||||||
auto archiveObject = kernel.getObject(archiveHandle, KernelObjectType::Archive);
|
auto archiveObject = kernel.getObject(archiveHandle, KernelObjectType::Archive);
|
||||||
if (archiveObject == nullptr) [[unlikely]] {
|
if (archiveObject == nullptr) [[unlikely]] {
|
||||||
log("FS::OpenFile: Invalid archive handle %d\n", archiveHandle);
|
log("FS::OpenFile: Invalid archive handle %d\n", archiveHandle);
|
||||||
mem.write32(messagePointer + 4, ResultCode::Failure);
|
mem.write32(messagePointer + 4, Result::FailurePlaceholder);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
ArchiveBase* archive = archiveObject->getData<ArchiveSession>()->archive;
|
ArchiveBase* archive = archiveObject->getData<ArchiveSession>()->archive;
|
||||||
auto filePath = readPath(filePathType, filePathPointer, filePathSize);
|
auto filePath = readPath(filePathType, filePathPointer, filePathSize);
|
||||||
|
|
||||||
FSResult res = archive->createFile(filePath, size);
|
Result::HorizonResult res = archive->createFile(filePath, size);
|
||||||
mem.write32(messagePointer, IPC::responseHeader(0x808, 1, 0));
|
mem.write32(messagePointer, IPC::responseHeader(0x808, 1, 0));
|
||||||
mem.write32(messagePointer + 4, static_cast<u32>(res));
|
mem.write32(messagePointer + 4, res);
|
||||||
}
|
}
|
||||||
|
|
||||||
void FSService::deleteFile(u32 messagePointer) {
|
void FSService::deleteFile(u32 messagePointer) {
|
||||||
|
@ -382,14 +374,14 @@ void FSService::deleteFile(u32 messagePointer) {
|
||||||
auto archiveObject = kernel.getObject(archiveHandle, KernelObjectType::Archive);
|
auto archiveObject = kernel.getObject(archiveHandle, KernelObjectType::Archive);
|
||||||
if (archiveObject == nullptr) [[unlikely]] {
|
if (archiveObject == nullptr) [[unlikely]] {
|
||||||
log("FS::DeleteFile: Invalid archive handle %d\n", archiveHandle);
|
log("FS::DeleteFile: Invalid archive handle %d\n", archiveHandle);
|
||||||
mem.write32(messagePointer + 4, ResultCode::Failure);
|
mem.write32(messagePointer + 4, Result::FailurePlaceholder);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
ArchiveBase* archive = archiveObject->getData<ArchiveSession>()->archive;
|
ArchiveBase* archive = archiveObject->getData<ArchiveSession>()->archive;
|
||||||
auto filePath = readPath(filePathType, filePathPointer, filePathSize);
|
auto filePath = readPath(filePathType, filePathPointer, filePathSize);
|
||||||
|
|
||||||
FSResult res = archive->deleteFile(filePath);
|
Result::HorizonResult res = archive->deleteFile(filePath);
|
||||||
mem.write32(messagePointer, IPC::responseHeader(0x804, 1, 0));
|
mem.write32(messagePointer, IPC::responseHeader(0x804, 1, 0));
|
||||||
mem.write32(messagePointer + 4, static_cast<u32>(res));
|
mem.write32(messagePointer + 4, static_cast<u32>(res));
|
||||||
}
|
}
|
||||||
|
@ -409,12 +401,12 @@ void FSService::getFormatInfo(u32 messagePointer) {
|
||||||
}
|
}
|
||||||
|
|
||||||
mem.write32(messagePointer, IPC::responseHeader(0x845, 5, 0));
|
mem.write32(messagePointer, IPC::responseHeader(0x845, 5, 0));
|
||||||
Rust::Result<ArchiveBase::FormatInfo, FSResult> res = archive->getFormatInfo(path);
|
Rust::Result<ArchiveBase::FormatInfo, Result::HorizonResult> res = archive->getFormatInfo(path);
|
||||||
|
|
||||||
// If the FormatInfo was returned, write them to the output buffer. Otherwise, write an error code.
|
// If the FormatInfo was returned, write them to the output buffer. Otherwise, write an error code.
|
||||||
if (res.isOk()) {
|
if (res.isOk()) {
|
||||||
ArchiveBase::FormatInfo info = res.unwrap();
|
ArchiveBase::FormatInfo info = res.unwrap();
|
||||||
mem.write32(messagePointer + 4, ResultCode::Success);
|
mem.write32(messagePointer + 4, Result::Success);
|
||||||
mem.write32(messagePointer + 8, info.size);
|
mem.write32(messagePointer + 8, info.size);
|
||||||
mem.write32(messagePointer + 12, info.numOfDirectories);
|
mem.write32(messagePointer + 12, info.numOfDirectories);
|
||||||
mem.write32(messagePointer + 16, info.numOfFiles);
|
mem.write32(messagePointer + 16, info.numOfFiles);
|
||||||
|
@ -458,7 +450,7 @@ void FSService::formatSaveData(u32 messagePointer) {
|
||||||
saveData.format(path, info);
|
saveData.format(path, info);
|
||||||
|
|
||||||
mem.write32(messagePointer, IPC::responseHeader(0x84C, 1, 0));
|
mem.write32(messagePointer, IPC::responseHeader(0x84C, 1, 0));
|
||||||
mem.write32(messagePointer + 4, ResultCode::Success);
|
mem.write32(messagePointer + 4, Result::Success);
|
||||||
}
|
}
|
||||||
|
|
||||||
void FSService::formatThisUserSaveData(u32 messagePointer) {
|
void FSService::formatThisUserSaveData(u32 messagePointer) {
|
||||||
|
@ -478,7 +470,7 @@ void FSService::formatThisUserSaveData(u32 messagePointer) {
|
||||||
.duplicateData = duplicateData
|
.duplicateData = duplicateData
|
||||||
};
|
};
|
||||||
FSPath emptyPath;
|
FSPath emptyPath;
|
||||||
|
|
||||||
mem.write32(messagePointer, IPC::responseHeader(0x080F, 1, 0));
|
mem.write32(messagePointer, IPC::responseHeader(0x080F, 1, 0));
|
||||||
saveData.format(emptyPath, info);
|
saveData.format(emptyPath, info);
|
||||||
}
|
}
|
||||||
|
@ -497,13 +489,13 @@ void FSService::controlArchive(u32 messagePointer) {
|
||||||
mem.write32(messagePointer, IPC::responseHeader(0x80D, 1, 0));
|
mem.write32(messagePointer, IPC::responseHeader(0x80D, 1, 0));
|
||||||
if (archiveObject == nullptr) [[unlikely]] {
|
if (archiveObject == nullptr) [[unlikely]] {
|
||||||
log("FS::ControlArchive: Invalid archive handle %d\n", archiveHandle);
|
log("FS::ControlArchive: Invalid archive handle %d\n", archiveHandle);
|
||||||
mem.write32(messagePointer + 4, ResultCode::Failure);
|
mem.write32(messagePointer + 4, Result::FailurePlaceholder);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
switch (action) {
|
switch (action) {
|
||||||
case 0: // Commit save data changes. Shouldn't need us to do anything
|
case 0: // Commit save data changes. Shouldn't need us to do anything
|
||||||
mem.write32(messagePointer + 4, ResultCode::Success);
|
mem.write32(messagePointer + 4, Result::Success);
|
||||||
break;
|
break;
|
||||||
default:
|
default:
|
||||||
Helpers::panic("Unimplemented action for ControlArchive (action = %X)\n", action);
|
Helpers::panic("Unimplemented action for ControlArchive (action = %X)\n", action);
|
||||||
|
@ -519,7 +511,7 @@ void FSService::getFreeBytes(u32 messagePointer) {
|
||||||
mem.write32(messagePointer, IPC::responseHeader(0x812, 3, 0));
|
mem.write32(messagePointer, IPC::responseHeader(0x812, 3, 0));
|
||||||
if (session == nullptr) [[unlikely]] {
|
if (session == nullptr) [[unlikely]] {
|
||||||
log("FS::GetFreeBytes: Invalid archive handle %d\n", archiveHandle);
|
log("FS::GetFreeBytes: Invalid archive handle %d\n", archiveHandle);
|
||||||
mem.write32(messagePointer + 4, ResultCode::Failure);
|
mem.write32(messagePointer + 4, Result::FailurePlaceholder);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -531,7 +523,7 @@ void FSService::getPriority(u32 messagePointer) {
|
||||||
log("FS::GetPriority\n");
|
log("FS::GetPriority\n");
|
||||||
|
|
||||||
mem.write32(messagePointer, IPC::responseHeader(0x863, 2, 0));
|
mem.write32(messagePointer, IPC::responseHeader(0x863, 2, 0));
|
||||||
mem.write32(messagePointer + 4, ResultCode::Success);
|
mem.write32(messagePointer + 4, Result::Success);
|
||||||
mem.write32(messagePointer + 8, priority);
|
mem.write32(messagePointer + 8, priority);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -540,13 +532,13 @@ void FSService::setPriority(u32 messagePointer) {
|
||||||
log("FS::SetPriority (priority = %d)\n", value);
|
log("FS::SetPriority (priority = %d)\n", value);
|
||||||
|
|
||||||
mem.write32(messagePointer, IPC::responseHeader(0x862, 1, 0));
|
mem.write32(messagePointer, IPC::responseHeader(0x862, 1, 0));
|
||||||
mem.write32(messagePointer + 4, ResultCode::Success);
|
mem.write32(messagePointer + 4, Result::Success);
|
||||||
priority = value;
|
priority = value;
|
||||||
}
|
}
|
||||||
|
|
||||||
void FSService::isSdmcDetected(u32 messagePointer) {
|
void FSService::isSdmcDetected(u32 messagePointer) {
|
||||||
log("FS::IsSdmcDetected\n");
|
log("FS::IsSdmcDetected\n");
|
||||||
mem.write32(messagePointer, IPC::responseHeader(0x817, 2, 0));
|
mem.write32(messagePointer, IPC::responseHeader(0x817, 2, 0));
|
||||||
mem.write32(messagePointer + 4, ResultCode::Success);
|
mem.write32(messagePointer + 4, Result::Success);
|
||||||
mem.write32(messagePointer + 8, 0); // Whether SD is detected. For now we emulate a 3DS without an SD.
|
mem.write32(messagePointer + 8, 0); // Whether SD is detected. For now we emulate a 3DS without an SD.
|
||||||
}
|
}
|
|
@ -30,13 +30,6 @@ namespace GXCommands {
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
namespace Result {
|
|
||||||
enum : u32 {
|
|
||||||
Success = 0,
|
|
||||||
SuccessRegisterIRQ = 0x2A07 // TODO: Is this a reference to the Ricoh 2A07 used in PAL NES systems?
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
void GPUService::reset() {
|
void GPUService::reset() {
|
||||||
privilegedProcess = 0xFFFFFFFF; // Set the privileged process to an invalid handle
|
privilegedProcess = 0xFFFFFFFF; // Set the privileged process to an invalid handle
|
||||||
interruptEvent = std::nullopt;
|
interruptEvent = std::nullopt;
|
||||||
|
@ -100,7 +93,7 @@ void GPUService::registerInterruptRelayQueue(u32 messagePointer) {
|
||||||
}
|
}
|
||||||
|
|
||||||
mem.write32(messagePointer, IPC::responseHeader(0x13, 2, 2));
|
mem.write32(messagePointer, IPC::responseHeader(0x13, 2, 2));
|
||||||
mem.write32(messagePointer + 4, Result::SuccessRegisterIRQ); // First init returns a unique result
|
mem.write32(messagePointer + 4, Result::GSP::SuccessRegisterIRQ); // First init returns a unique result
|
||||||
mem.write32(messagePointer + 8, 0); // TODO: GSP module thread index
|
mem.write32(messagePointer + 8, 0); // TODO: GSP module thread index
|
||||||
mem.write32(messagePointer + 12, 0); // Translation descriptor
|
mem.write32(messagePointer + 12, 0); // Translation descriptor
|
||||||
mem.write32(messagePointer + 16, KernelHandles::GSPSharedMemHandle);
|
mem.write32(messagePointer + 16, KernelHandles::GSPSharedMemHandle);
|
||||||
|
@ -120,7 +113,7 @@ void GPUService::requestInterrupt(GPUInterrupt type) {
|
||||||
u8& interruptCount = sharedMem[1];
|
u8& interruptCount = sharedMem[1];
|
||||||
u8 flagIndex = (index + interruptCount) % 0x34;
|
u8 flagIndex = (index + interruptCount) % 0x34;
|
||||||
interruptCount++;
|
interruptCount++;
|
||||||
|
|
||||||
sharedMem[2] = 0; // Set error code to 0
|
sharedMem[2] = 0; // Set error code to 0
|
||||||
sharedMem[0xC + flagIndex] = static_cast<u8>(type); // Write interrupt type to queue
|
sharedMem[0xC + flagIndex] = static_cast<u8>(type); // Write interrupt type to queue
|
||||||
|
|
||||||
|
@ -187,7 +180,7 @@ void GPUService::writeHwRegsWithMask(u32 messagePointer) {
|
||||||
u32 dataPointer = mem.read32(messagePointer + 16); // Data pointer
|
u32 dataPointer = mem.read32(messagePointer + 16); // Data pointer
|
||||||
u32 maskPointer = mem.read32(messagePointer + 24); // Mask pointer
|
u32 maskPointer = mem.read32(messagePointer + 24); // Mask pointer
|
||||||
|
|
||||||
log("GSP::GPU::writeHwRegsWithMask (GPU address = %08X, size = %X, data address = %08X, mask address = %08X)\n",
|
log("GSP::GPU::writeHwRegsWithMask (GPU address = %08X, size = %X, data address = %08X, mask address = %08X)\n",
|
||||||
ioAddr, size, dataPointer, maskPointer);
|
ioAddr, size, dataPointer, maskPointer);
|
||||||
|
|
||||||
// Check for alignment
|
// Check for alignment
|
||||||
|
@ -202,7 +195,7 @@ void GPUService::writeHwRegsWithMask(u32 messagePointer) {
|
||||||
if (ioAddr >= 0x420000) {
|
if (ioAddr >= 0x420000) {
|
||||||
Helpers::panic("GSP::GPU::writeHwRegs offset too big");
|
Helpers::panic("GSP::GPU::writeHwRegs offset too big");
|
||||||
}
|
}
|
||||||
|
|
||||||
ioAddr += 0x1EB00000;
|
ioAddr += 0x1EB00000;
|
||||||
for (u32 i = 0; i < size; i += 4) {
|
for (u32 i = 0; i < size; i += 4) {
|
||||||
const u32 current = gpu.readReg(ioAddr);
|
const u32 current = gpu.readReg(ioAddr);
|
||||||
|
@ -301,13 +294,13 @@ void GPUService::processCommandBuffer() {
|
||||||
|
|
||||||
commandsLeft--;
|
commandsLeft--;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Fill 2 GPU framebuffers, buf0 and buf1, using a specific word value
|
// Fill 2 GPU framebuffers, buf0 and buf1, using a specific word value
|
||||||
void GPUService::memoryFill(u32* cmd) {
|
void GPUService::memoryFill(u32* cmd) {
|
||||||
u32 control = cmd[7];
|
u32 control = cmd[7];
|
||||||
|
|
||||||
// buf0 parameters
|
// buf0 parameters
|
||||||
u32 start0 = cmd[1]; // Start address for the fill. If 0, don't fill anything
|
u32 start0 = cmd[1]; // Start address for the fill. If 0, don't fill anything
|
||||||
u32 value0 = cmd[2]; // Value to fill the framebuffer with
|
u32 value0 = cmd[2]; // Value to fill the framebuffer with
|
||||||
|
@ -331,9 +324,31 @@ void GPUService::memoryFill(u32* cmd) {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static u32 VaddrToPaddr(u32 addr) {
|
||||||
|
if (addr >= VirtualAddrs::VramStart && addr < (VirtualAddrs::VramStart + VirtualAddrs::VramSize)) [[likely]] {
|
||||||
|
return addr - VirtualAddrs::VramStart + PhysicalAddrs::VRAM;
|
||||||
|
}
|
||||||
|
|
||||||
|
else if (addr >= VirtualAddrs::LinearHeapStartOld && addr < VirtualAddrs::LinearHeapEndOld) {
|
||||||
|
return addr - VirtualAddrs::LinearHeapStartOld + PhysicalAddrs::FCRAM;
|
||||||
|
}
|
||||||
|
|
||||||
|
else if (addr >= VirtualAddrs::LinearHeapStartNew && addr < VirtualAddrs::LinearHeapEndNew) {
|
||||||
|
return addr - VirtualAddrs::LinearHeapStartNew + PhysicalAddrs::FCRAM;
|
||||||
|
}
|
||||||
|
|
||||||
|
else if (addr == 0) {
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
Helpers::warn("[GSP::GPU VaddrToPaddr] Unknown virtual address %08X", addr);
|
||||||
|
// Obviously garbage address
|
||||||
|
return 0xF3310932;
|
||||||
|
}
|
||||||
|
|
||||||
void GPUService::triggerDisplayTransfer(u32* cmd) {
|
void GPUService::triggerDisplayTransfer(u32* cmd) {
|
||||||
const u32 inputAddr = cmd[1];
|
const u32 inputAddr = VaddrToPaddr(cmd[1]);
|
||||||
const u32 outputAddr = cmd[2];
|
const u32 outputAddr = VaddrToPaddr(cmd[2]);
|
||||||
const u32 inputSize = cmd[3];
|
const u32 inputSize = cmd[3];
|
||||||
const u32 outputSize = cmd[4];
|
const u32 outputSize = cmd[4];
|
||||||
const u32 flags = cmd[5];
|
const u32 flags = cmd[5];
|
||||||
|
|
|
@ -6,12 +6,6 @@ namespace LCDCommands {
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
namespace Result {
|
|
||||||
enum : u32 {
|
|
||||||
Success = 0,
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
void LCDService::reset() {}
|
void LCDService::reset() {}
|
||||||
|
|
||||||
void LCDService::handleSyncRequest(u32 messagePointer) {
|
void LCDService::handleSyncRequest(u32 messagePointer) {
|
||||||
|
|
|
@ -13,13 +13,6 @@ namespace HIDCommands {
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
namespace Result {
|
|
||||||
enum : u32 {
|
|
||||||
Success = 0,
|
|
||||||
Failure = 0xFFFFFFFF
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
void HIDService::reset() {
|
void HIDService::reset() {
|
||||||
sharedMem = nullptr;
|
sharedMem = nullptr;
|
||||||
accelerometerEnabled = false;
|
accelerometerEnabled = false;
|
||||||
|
@ -90,7 +83,7 @@ void HIDService::getGyroscopeCoefficient(u32 messagePointer) {
|
||||||
constexpr float gyroscopeCoeff = 14.375f; // Same as retail 3DS
|
constexpr float gyroscopeCoeff = 14.375f; // Same as retail 3DS
|
||||||
mem.write32(messagePointer, IPC::responseHeader(0x15, 2, 0));
|
mem.write32(messagePointer, IPC::responseHeader(0x15, 2, 0));
|
||||||
mem.write32(messagePointer + 4, Result::Success);
|
mem.write32(messagePointer + 4, Result::Success);
|
||||||
mem.write32(messagePointer + 8, std::bit_cast<u32, float>(gyroscopeCoeff));
|
mem.write32(messagePointer + 8, Helpers::bit_cast<u32, float>(gyroscopeCoeff));
|
||||||
}
|
}
|
||||||
|
|
||||||
void HIDService::getIPCHandles(u32 messagePointer) {
|
void HIDService::getIPCHandles(u32 messagePointer) {
|
||||||
|
@ -155,7 +148,7 @@ void HIDService::updateInputs(u64 currentTick) {
|
||||||
writeSharedMem<u16>(touchEntryOffset, touchScreenX);
|
writeSharedMem<u16>(touchEntryOffset, touchScreenX);
|
||||||
writeSharedMem<u16>(touchEntryOffset + 2, touchScreenY);
|
writeSharedMem<u16>(touchEntryOffset + 2, touchScreenY);
|
||||||
writeSharedMem<u8>(touchEntryOffset + 4, touchScreenPressed ? 1 : 0);
|
writeSharedMem<u8>(touchEntryOffset + 4, touchScreenPressed ? 1 : 0);
|
||||||
|
|
||||||
// Next, update accelerometer state
|
// Next, update accelerometer state
|
||||||
if (nextAccelerometerIndex == 0) {
|
if (nextAccelerometerIndex == 0) {
|
||||||
writeSharedMem<u64>(0x110, readSharedMem<u64>(0x108)); // Copy previous tick count
|
writeSharedMem<u64>(0x110, readSharedMem<u64>(0x108)); // Copy previous tick count
|
||||||
|
|
|
@ -8,12 +8,6 @@ namespace LDRCommands {
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
namespace Result {
|
|
||||||
enum : u32 {
|
|
||||||
Success = 0,
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
void LDRService::reset() {}
|
void LDRService::reset() {}
|
||||||
|
|
||||||
void LDRService::handleSyncRequest(u32 messagePointer) {
|
void LDRService::handleSyncRequest(u32 messagePointer) {
|
||||||
|
|
|
@ -13,12 +13,6 @@ namespace MICCommands {
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
namespace Result {
|
|
||||||
enum : u32 {
|
|
||||||
Success = 0,
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
void MICService::reset() {
|
void MICService::reset() {
|
||||||
micEnabled = false;
|
micEnabled = false;
|
||||||
shouldClamp = false;
|
shouldClamp = false;
|
||||||
|
|
|
@ -11,12 +11,6 @@ namespace NDMCommands {
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
namespace Result {
|
|
||||||
enum : u32 {
|
|
||||||
Success = 0,
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
void NDMService::reset() {}
|
void NDMService::reset() {}
|
||||||
|
|
||||||
void NDMService::handleSyncRequest(u32 messagePointer) {
|
void NDMService::handleSyncRequest(u32 messagePointer) {
|
||||||
|
|
|
@ -10,12 +10,6 @@ namespace NFCCommands {
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
namespace Result {
|
|
||||||
enum : u32 {
|
|
||||||
Success = 0,
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
void NFCService::reset() {
|
void NFCService::reset() {
|
||||||
tagInRangeEvent = std::nullopt;
|
tagInRangeEvent = std::nullopt;
|
||||||
tagOutOfRangeEvent = std::nullopt;
|
tagOutOfRangeEvent = std::nullopt;
|
||||||
|
|
|
@ -7,12 +7,6 @@ namespace NIMCommands {
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
namespace Result {
|
|
||||||
enum : u32 {
|
|
||||||
Success = 0,
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
void NIMService::reset() {}
|
void NIMService::reset() {}
|
||||||
|
|
||||||
void NIMService::handleSyncRequest(u32 messagePointer) {
|
void NIMService::handleSyncRequest(u32 messagePointer) {
|
||||||
|
|
|
@ -3,18 +3,12 @@
|
||||||
|
|
||||||
namespace PTMCommands {
|
namespace PTMCommands {
|
||||||
enum : u32 {
|
enum : u32 {
|
||||||
GetStepHistory = 0x000B00C2,
|
GetStepHistory = 0x000B00C2,
|
||||||
GetTotalStepCount = 0x000C0000,
|
GetTotalStepCount = 0x000C0000,
|
||||||
ConfigureNew3DSCPU = 0x08180040
|
ConfigureNew3DSCPU = 0x08180040
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
namespace Result {
|
|
||||||
enum : u32 {
|
|
||||||
Success = 0,
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
void PTMService::reset() {}
|
void PTMService::reset() {}
|
||||||
|
|
||||||
void PTMService::handleSyncRequest(u32 messagePointer) {
|
void PTMService::handleSyncRequest(u32 messagePointer) {
|
||||||
|
|
|
@ -1,13 +1,15 @@
|
||||||
#include "services/service_manager.hpp"
|
#include "services/service_manager.hpp"
|
||||||
|
|
||||||
#include <map>
|
#include <map>
|
||||||
|
|
||||||
#include "ipc.hpp"
|
#include "ipc.hpp"
|
||||||
#include "kernel.hpp"
|
#include "kernel.hpp"
|
||||||
|
|
||||||
ServiceManager::ServiceManager(std::array<u32, 16>& regs, Memory& mem, GPU& gpu, u32& currentPID, Kernel& kernel)
|
ServiceManager::ServiceManager(std::span<u32, 16> regs, Memory& mem, GPU& gpu, u32& currentPID, Kernel& kernel)
|
||||||
: regs(regs), mem(mem), kernel(kernel), ac(mem), am(mem), boss(mem), act(mem), apt(mem, kernel), cam(mem),
|
: regs(regs), mem(mem), kernel(kernel), ac(mem), am(mem), boss(mem), act(mem), apt(mem, kernel), cam(mem),
|
||||||
cecd(mem, kernel), cfg(mem), dlp_srvr(mem), dsp(mem, kernel), hid(mem, kernel), frd(mem), fs(mem, kernel),
|
cecd(mem, kernel), cfg(mem), dlp_srvr(mem), dsp(mem, kernel), hid(mem, kernel), frd(mem), fs(mem, kernel),
|
||||||
gsp_gpu(mem, gpu, kernel, currentPID), gsp_lcd(mem), ldr(mem), mic(mem), nfc(mem, kernel), nim(mem), ndm(mem),
|
gsp_gpu(mem, gpu, kernel, currentPID), gsp_lcd(mem), ldr(mem), mic(mem), nfc(mem, kernel), nim(mem), ndm(mem),
|
||||||
ptm(mem), y2r(mem, kernel) {}
|
ptm(mem), y2r(mem, kernel) {}
|
||||||
|
|
||||||
static constexpr int MAX_NOTIFICATION_COUNT = 16;
|
static constexpr int MAX_NOTIFICATION_COUNT = 16;
|
||||||
|
|
||||||
|
@ -58,12 +60,6 @@ namespace Commands {
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
namespace Result {
|
|
||||||
enum : u32 {
|
|
||||||
Success = 0
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
// Handle an IPC message issued using the SendSyncRequest SVC
|
// Handle an IPC message issued using the SendSyncRequest SVC
|
||||||
// The parameters are stored in thread-local storage in this format: https://www.3dbrew.org/wiki/IPC#Message_Structure
|
// The parameters are stored in thread-local storage in this format: https://www.3dbrew.org/wiki/IPC#Message_Structure
|
||||||
// messagePointer: The base pointer for the IPC message
|
// messagePointer: The base pointer for the IPC message
|
||||||
|
|
|
@ -29,12 +29,6 @@ namespace Y2RCommands {
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
namespace Result {
|
|
||||||
enum : u32 {
|
|
||||||
Success = 0,
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
void Y2RService::reset() {
|
void Y2RService::reset() {
|
||||||
transferEndInterruptEnabled = false;
|
transferEndInterruptEnabled = false;
|
||||||
transferEndEvent = std::nullopt;
|
transferEndEvent = std::nullopt;
|
||||||
|
|
|
@ -23,7 +23,6 @@ void Emulator::render() {
|
||||||
|
|
||||||
void Emulator::run() {
|
void Emulator::run() {
|
||||||
while (running) {
|
while (running) {
|
||||||
gpu.getGraphicsContext(); // Give the GPU a rendering context
|
|
||||||
runFrame(); // Run 1 frame of instructions
|
runFrame(); // Run 1 frame of instructions
|
||||||
gpu.display(); // Display graphics
|
gpu.display(); // Display graphics
|
||||||
|
|
||||||
|
|
|
@ -1,11 +1,7 @@
|
||||||
#include "emulator.hpp"
|
#include "emulator.hpp"
|
||||||
#include "gl3w.h"
|
|
||||||
|
|
||||||
int main (int argc, char *argv[]) {
|
int main (int argc, char *argv[]) {
|
||||||
Emulator emu;
|
Emulator emu;
|
||||||
if (gl3wInit()) {
|
|
||||||
Helpers::panic("Failed to initialize OpenGL");
|
|
||||||
}
|
|
||||||
|
|
||||||
emu.initGraphicsContext();
|
emu.initGraphicsContext();
|
||||||
|
|
||||||
|
|
1496
third_party/gl3w/gl3w.cpp
vendored
1496
third_party/gl3w/gl3w.cpp
vendored
File diff suppressed because it is too large
Load diff
2619
third_party/gl3w/gl3w.h
vendored
2619
third_party/gl3w/gl3w.h
vendored
File diff suppressed because it is too large
Load diff
Some files were not shown because too many files have changed in this diff Show more
Loading…
Add table
Reference in a new issue