mirror of
https://github.com/wheremyfoodat/Panda3DS.git
synced 2025-04-09 15:45:40 +12:00
Merge pull request #32 from fleroviux/pica-tev-emulation
Implement texture combiner emulation
This commit is contained in:
commit
91c9e6a31a
95 changed files with 23365 additions and 11036 deletions
1
.gitignore
vendored
1
.gitignore
vendored
|
@ -8,6 +8,7 @@
|
|||
|
||||
#Clion Files
|
||||
.idea/
|
||||
cmake-build-*
|
||||
|
||||
Debug/
|
||||
Release/
|
||||
|
|
|
@ -18,7 +18,6 @@ include_directories(${PROJECT_SOURCE_DIR}/include/kernel)
|
|||
include_directories (${FMT_INCLUDE_DIR})
|
||||
include_directories(third_party/boost/)
|
||||
include_directories(third_party/elfio/)
|
||||
include_directories(third_party/gl3w/)
|
||||
include_directories(third_party/imgui/)
|
||||
include_directories(third_party/dynarmic/src)
|
||||
include_directories(third_party/cryptopp/)
|
||||
|
@ -30,6 +29,7 @@ add_compile_definitions(SDL_MAIN_HANDLED)
|
|||
set(SDL_STATIC ON CACHE BOOL "" FORCE)
|
||||
set(SDL_SHARED OFF CACHE BOOL "" FORCE)
|
||||
add_subdirectory(third_party/SDL2)
|
||||
add_subdirectory(third_party/glad)
|
||||
include_directories(${SDL2_INCLUDE_DIR})
|
||||
|
||||
set(BOOST_ROOT "${CMAKE_SOURCE_DIR}/third_party/boost")
|
||||
|
@ -40,7 +40,7 @@ add_compile_definitions(BOOST_NO_CXX98_FUNCTION_BASE) # Forbid Boost from using
|
|||
add_library(boost INTERFACE)
|
||||
target_include_directories(boost SYSTEM INTERFACE ${Boost_INCLUDE_DIR})
|
||||
|
||||
set(CRYPTOPP_BUILD_TESTING OFF)
|
||||
set(CRYPTOPP_BUILD_TESTING OFF)
|
||||
add_subdirectory(third_party/cryptopp)
|
||||
|
||||
# Check for x64
|
||||
|
@ -114,7 +114,9 @@ set(HEADER_FILES include/emulator.hpp include/helpers.hpp include/opengl.hpp inc
|
|||
include/fs/archive_ext_save_data.hpp include/services/shared_font.hpp include/fs/archive_ncch.hpp
|
||||
include/renderer_gl/textures.hpp include/colour.hpp include/services/y2r.hpp include/services/cam.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/system_models.hpp include/services/dlp_srvr.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
|
||||
|
@ -122,7 +124,6 @@ set(THIRD_PARTY_SOURCE_FILES third_party/imgui/imgui.cpp
|
|||
third_party/imgui/imgui_tables.cpp
|
||||
third_party/imgui/imgui_widgets.cpp
|
||||
third_party/imgui/imgui_demo.cpp
|
||||
third_party/gl3w/gl3w.cpp
|
||||
)
|
||||
|
||||
source_group("Header Files\\Core" FILES ${HEADER_FILES})
|
||||
|
@ -137,4 +138,4 @@ source_group("Source Files\\Third Party" FILES ${THIRD_PARTY_SOURCE_FILES})
|
|||
|
||||
add_executable(Alber ${SOURCE_FILES} ${FS_SOURCE_FILES} ${KERNEL_SOURCE_FILES} ${LOADER_SOURCE_FILES} ${SERVICE_SOURCE_FILES}
|
||||
${PICA_SOURCE_FILES} ${RENDERER_GL_SOURCE_FILES} ${THIRD_PARTY_SOURCE_FILES} ${HEADER_FILES})
|
||||
target_link_libraries(Alber PRIVATE dynarmic SDL2-static)
|
||||
target_link_libraries(Alber PRIVATE dynarmic SDL2-static glad)
|
|
@ -47,7 +47,7 @@ class GPU {
|
|||
};
|
||||
|
||||
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
|
||||
|
|
|
@ -1,131 +1,237 @@
|
|||
#pragma once
|
||||
#include "helpers.hpp"
|
||||
|
||||
namespace PICAInternalRegs {
|
||||
enum : u32 {
|
||||
// Rasterizer registers
|
||||
ViewportWidth = 0x41,
|
||||
ViewportInvw = 0x42,
|
||||
ViewportHeight = 0x43,
|
||||
ViewportInvh = 0x44,
|
||||
namespace PICA {
|
||||
namespace InternalRegs {
|
||||
enum : u32 {
|
||||
// Rasterizer registers
|
||||
ViewportWidth = 0x41,
|
||||
ViewportInvw = 0x42,
|
||||
ViewportHeight = 0x43,
|
||||
ViewportInvh = 0x44,
|
||||
|
||||
DepthScale = 0x4D,
|
||||
DepthOffset = 0x4E,
|
||||
ShaderOutputCount = 0x4F,
|
||||
ShaderOutmap0 = 0x50,
|
||||
DepthScale = 0x4D,
|
||||
DepthOffset = 0x4E,
|
||||
ShaderOutputCount = 0x4F,
|
||||
ShaderOutmap0 = 0x50,
|
||||
|
||||
DepthmapEnable = 0x6D,
|
||||
TexUnitCfg = 0x80,
|
||||
DepthmapEnable = 0x6D,
|
||||
|
||||
// Framebuffer registers
|
||||
ColourOperation = 0x100,
|
||||
BlendFunc = 0x101,
|
||||
BlendColour = 0x103,
|
||||
AlphaTestConfig = 0x104,
|
||||
DepthAndColorMask = 0x107,
|
||||
DepthBufferFormat = 0x116,
|
||||
ColourBufferFormat = 0x117,
|
||||
DepthBufferLoc = 0x11C,
|
||||
ColourBufferLoc = 0x11D,
|
||||
FramebufferSize = 0x11E,
|
||||
// Texture registers
|
||||
TexUnitCfg = 0x80,
|
||||
Tex0BorderColor = 0x81,
|
||||
Tex1BorderColor = 0x91,
|
||||
Tex2BorderColor = 0x99,
|
||||
TexEnv0Source = 0xC0,
|
||||
TexEnv1Source = 0xC8,
|
||||
TexEnv2Source = 0xD0,
|
||||
TexEnv3Source = 0xD8,
|
||||
TexEnvUpdateBuffer = 0xE0,
|
||||
TexEnv4Source = 0xF0,
|
||||
TexEnv5Source = 0xF8,
|
||||
TexEnvBufferColor = 0xFD,
|
||||
|
||||
// Geometry pipeline registers
|
||||
VertexAttribLoc = 0x200,
|
||||
AttribFormatLow = 0x201,
|
||||
AttribFormatHigh = 0x202,
|
||||
IndexBufferConfig = 0x227,
|
||||
VertexCountReg = 0x228,
|
||||
VertexOffsetReg = 0x22A,
|
||||
SignalDrawArrays = 0x22E,
|
||||
SignalDrawElements = 0x22F,
|
||||
// Framebuffer registers
|
||||
ColourOperation = 0x100,
|
||||
BlendFunc = 0x101,
|
||||
BlendColour = 0x103,
|
||||
AlphaTestConfig = 0x104,
|
||||
DepthAndColorMask = 0x107,
|
||||
DepthBufferFormat = 0x116,
|
||||
ColourBufferFormat = 0x117,
|
||||
DepthBufferLoc = 0x11C,
|
||||
ColourBufferLoc = 0x11D,
|
||||
FramebufferSize = 0x11E,
|
||||
|
||||
Attrib0Offset = 0x203,
|
||||
Attrib1Offset = 0x206,
|
||||
Attrib2Offset = 0x209,
|
||||
Attrib3Offset = 0x20C,
|
||||
Attrib4Offset = 0x20F,
|
||||
Attrib5Offset = 0x212,
|
||||
Attrib6Offset = 0x215,
|
||||
Attrib7Offset = 0x218,
|
||||
Attrib8Offset = 0x21B,
|
||||
Attrib9Offset = 0x21E,
|
||||
Attrib10Offset = 0x221,
|
||||
Attrib11Offset = 0x224,
|
||||
// Geometry pipeline registers
|
||||
VertexAttribLoc = 0x200,
|
||||
AttribFormatLow = 0x201,
|
||||
AttribFormatHigh = 0x202,
|
||||
IndexBufferConfig = 0x227,
|
||||
VertexCountReg = 0x228,
|
||||
VertexOffsetReg = 0x22A,
|
||||
SignalDrawArrays = 0x22E,
|
||||
SignalDrawElements = 0x22F,
|
||||
|
||||
Attrib0Config2 = 0x205,
|
||||
Attrib1Config2 = 0x208,
|
||||
Attrib2Config2 = 0x20B,
|
||||
Attrib3Config2 = 0x20E,
|
||||
Attrib4Config2 = 0x211,
|
||||
Attrib5Config2 = 0x214,
|
||||
Attrib6Config2 = 0x217,
|
||||
Attrib7Config2 = 0x21A,
|
||||
Attrib8Config2 = 0x21D,
|
||||
Attrib9Config2 = 0x220,
|
||||
Attrib10Config2 = 0x223,
|
||||
Attrib11Config2 = 0x226,
|
||||
Attrib0Offset = 0x203,
|
||||
Attrib1Offset = 0x206,
|
||||
Attrib2Offset = 0x209,
|
||||
Attrib3Offset = 0x20C,
|
||||
Attrib4Offset = 0x20F,
|
||||
Attrib5Offset = 0x212,
|
||||
Attrib6Offset = 0x215,
|
||||
Attrib7Offset = 0x218,
|
||||
Attrib8Offset = 0x21B,
|
||||
Attrib9Offset = 0x21E,
|
||||
Attrib10Offset = 0x221,
|
||||
Attrib11Offset = 0x224,
|
||||
|
||||
AttribInfoStart = Attrib0Offset,
|
||||
AttribInfoEnd = Attrib11Config2,
|
||||
Attrib0Config2 = 0x205,
|
||||
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
|
||||
FixedAttribIndex = 0x232,
|
||||
FixedAttribData0 = 0x233,
|
||||
FixedAttribData1 = 0x234,
|
||||
FixedAttribData2 = 0x235,
|
||||
AttribInfoStart = Attrib0Offset,
|
||||
AttribInfoEnd = Attrib11Config2,
|
||||
|
||||
// Command processor registers
|
||||
CmdBufSize0 = 0x238,
|
||||
CmdBufSize1 = 0x239,
|
||||
CmdBufAddr0 = 0x23A,
|
||||
CmdBufAddr1 = 0x23B,
|
||||
CmdBufTrigger0 = 0x23C,
|
||||
CmdBufTrigger1 = 0x23D,
|
||||
// Fixed attribute registers
|
||||
FixedAttribIndex = 0x232,
|
||||
FixedAttribData0 = 0x233,
|
||||
FixedAttribData1 = 0x234,
|
||||
FixedAttribData2 = 0x235,
|
||||
|
||||
PrimitiveConfig = 0x25E,
|
||||
PrimitiveRestart = 0x25F,
|
||||
// Command processor registers
|
||||
CmdBufSize0 = 0x238,
|
||||
CmdBufSize1 = 0x239,
|
||||
CmdBufAddr0 = 0x23A,
|
||||
CmdBufAddr1 = 0x23B,
|
||||
CmdBufTrigger0 = 0x23C,
|
||||
CmdBufTrigger1 = 0x23D,
|
||||
|
||||
// Vertex shader registers
|
||||
VertexShaderAttrNum = 0x242,
|
||||
VertexBoolUniform = 0x2B0,
|
||||
VertexIntUniform0 = 0x2B1,
|
||||
VertexIntUniform1 = 0x2B2,
|
||||
VertexIntUniform2 = 0x2B3,
|
||||
VertexIntUniform3 = 0x2B4,
|
||||
PrimitiveConfig = 0x25E,
|
||||
PrimitiveRestart = 0x25F,
|
||||
|
||||
VertexShaderEntrypoint = 0x2BA,
|
||||
VertexShaderTransferEnd = 0x2BF,
|
||||
VertexFloatUniformIndex = 0x2C0,
|
||||
VertexFloatUniformData0 = 0x2C1,
|
||||
VertexFloatUniformData1 = 0x2C2,
|
||||
VertexFloatUniformData2 = 0x2C3,
|
||||
VertexFloatUniformData3 = 0x2C4,
|
||||
VertexFloatUniformData4 = 0x2C5,
|
||||
VertexFloatUniformData5 = 0x2C6,
|
||||
VertexFloatUniformData6 = 0x2C7,
|
||||
VertexFloatUniformData7 = 0x2C8,
|
||||
// Vertex shader registers
|
||||
VertexShaderAttrNum = 0x242,
|
||||
VertexBoolUniform = 0x2B0,
|
||||
VertexIntUniform0 = 0x2B1,
|
||||
VertexIntUniform1 = 0x2B2,
|
||||
VertexIntUniform2 = 0x2B3,
|
||||
VertexIntUniform3 = 0x2B4,
|
||||
|
||||
VertexShaderInputBufferCfg = 0x2B9,
|
||||
VertexShaderInputCfgLow = 0x2BB,
|
||||
VertexShaderInputCfgHigh = 0x2BC,
|
||||
VertexShaderEntrypoint = 0x2BA,
|
||||
VertexShaderTransferEnd = 0x2BF,
|
||||
VertexFloatUniformIndex = 0x2C0,
|
||||
VertexFloatUniformData0 = 0x2C1,
|
||||
VertexFloatUniformData1 = 0x2C2,
|
||||
VertexFloatUniformData2 = 0x2C3,
|
||||
VertexFloatUniformData3 = 0x2C4,
|
||||
VertexFloatUniformData4 = 0x2C5,
|
||||
VertexFloatUniformData5 = 0x2C6,
|
||||
VertexFloatUniformData6 = 0x2C7,
|
||||
VertexFloatUniformData7 = 0x2C8,
|
||||
|
||||
VertexShaderTransferIndex = 0x2CB,
|
||||
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,
|
||||
VertexShaderInputBufferCfg = 0x2B9,
|
||||
VertexShaderInputCfgLow = 0x2BB,
|
||||
VertexShaderInputCfgHigh = 0x2BC,
|
||||
|
||||
VertexShaderTransferIndex = 0x2CB,
|
||||
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
|
|
@ -3,6 +3,7 @@
|
|||
#include <filesystem>
|
||||
#include <fstream>
|
||||
#include <SDL.h>
|
||||
#include <glad/gl.h>
|
||||
|
||||
#include "cpu.hpp"
|
||||
#include "io_file.hpp"
|
||||
|
@ -35,7 +36,7 @@ class Emulator {
|
|||
|
||||
public:
|
||||
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");
|
||||
}
|
||||
|
||||
|
@ -45,8 +46,21 @@ public:
|
|||
SDL_GL_SetAttribute(SDL_GL_CONTEXT_MAJOR_VERSION, 4);
|
||||
SDL_GL_SetAttribute(SDL_GL_CONTEXT_MINOR_VERSION, 1);
|
||||
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);
|
||||
|
||||
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();
|
||||
}
|
||||
|
||||
|
|
|
@ -10,6 +10,9 @@
|
|||
#include "helpers.hpp"
|
||||
#include "memory.hpp"
|
||||
#include "result.hpp"
|
||||
#include "result/result.hpp"
|
||||
|
||||
using Result::HorizonResult;
|
||||
|
||||
namespace PathType {
|
||||
enum : u32 {
|
||||
|
@ -97,7 +100,7 @@ struct FileSession {
|
|||
u32 priority = 0; // TODO: What does this even do
|
||||
bool isOpen;
|
||||
|
||||
FileSession(ArchiveBase* archive, const FSPath& filePath, const FSPath& archivePath, FILE* fd, bool isOpen = true) :
|
||||
FileSession(ArchiveBase* archive, const FSPath& filePath, const FSPath& archivePath, FILE* fd, bool isOpen = true) :
|
||||
archive(archive), path(filePath), archivePath(archivePath), fd(fd), isOpen(isOpen), priority(0) {}
|
||||
|
||||
// For cloning a file session
|
||||
|
@ -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)
|
||||
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 {
|
||||
public:
|
||||
struct FormatInfo {
|
||||
|
@ -149,7 +142,7 @@ public:
|
|||
|
||||
protected:
|
||||
using Handle = u32;
|
||||
|
||||
|
||||
static constexpr FileDescriptor NoFile = nullptr;
|
||||
static constexpr FileDescriptor FileError = std::nullopt;
|
||||
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 (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 then this is the FS root.
|
||||
// If it's > 0 then we're in a subdirectory of the root.
|
||||
int level = 0;
|
||||
|
||||
|
||||
// Split the string on / characters and see how many of the substrings are ".."
|
||||
size_t pos = 0;
|
||||
while ((pos = pathString.find(Char('/'))) != String::npos) {
|
||||
|
@ -202,27 +195,27 @@ protected:
|
|||
public:
|
||||
virtual std::string name() = 0;
|
||||
virtual u64 getFreeBytes() = 0;
|
||||
virtual FSResult createFile(const FSPath& path, u64 size) = 0;
|
||||
virtual FSResult deleteFile(const FSPath& path) = 0;
|
||||
virtual HorizonResult createFile(const FSPath& path, u64 size) = 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());
|
||||
// Return a dummy struct just to avoid the UB of not returning anything, even if we panic
|
||||
return Ok(FormatInfo{ .size = 0, .numOfDirectories = 0, .numOfFiles = 0, .duplicateData = false });
|
||||
}
|
||||
|
||||
virtual FSResult createDirectory(const FSPath& path) {
|
||||
virtual HorizonResult createDirectory(const FSPath& path) {
|
||||
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)
|
||||
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());
|
||||
return Err(FSResult::FileNotFound);
|
||||
return Err(Result::FS::FileNotFoundAlt);
|
||||
}
|
||||
|
||||
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
|
||||
// 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;
|
||||
|
||||
|
||||
ArchiveBase(Memory& mem) : mem(mem) {}
|
||||
};
|
|
@ -9,11 +9,11 @@ public:
|
|||
u64 getFreeBytes() override { Helpers::panic("ExtSaveData::GetFreeBytes unimplemented"); return 0; }
|
||||
std::string name() override { return "ExtSaveData::" + backingFolder; }
|
||||
|
||||
FSResult createFile(const FSPath& path, u64 size) override;
|
||||
FSResult deleteFile(const FSPath& path) override;
|
||||
HorizonResult createFile(const FSPath& path, u64 size) override;
|
||||
HorizonResult deleteFile(const FSPath& path) override;
|
||||
|
||||
Rust::Result<ArchiveBase*, FSResult> openArchive(const FSPath& path) override;
|
||||
Rust::Result<DirectorySession, FSResult> openDirectory(const FSPath& path) override;
|
||||
Rust::Result<ArchiveBase*, HorizonResult> openArchive(const FSPath& path) override;
|
||||
Rust::Result<DirectorySession, HorizonResult> openDirectory(const FSPath& path) override;
|
||||
FileDescriptor openFile(const FSPath& path, const FilePerms& perms) 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; }
|
||||
std::string name() override { return "NCCH"; }
|
||||
|
||||
FSResult createFile(const FSPath& path, u64 size) override;
|
||||
FSResult deleteFile(const FSPath& path) override;
|
||||
HorizonResult createFile(const FSPath& path, u64 size) 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;
|
||||
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; }
|
||||
std::string name() override { return "SaveData"; }
|
||||
|
||||
FSResult createDirectory(const FSPath& path) override;
|
||||
FSResult createFile(const FSPath& path, u64 size) override;
|
||||
FSResult deleteFile(const FSPath& path) override;
|
||||
HorizonResult createDirectory(const FSPath& path) override;
|
||||
HorizonResult createFile(const FSPath& path, u64 size) override;
|
||||
HorizonResult deleteFile(const FSPath& path) override;
|
||||
|
||||
Rust::Result<ArchiveBase*, FSResult> openArchive(const FSPath& path) override;
|
||||
Rust::Result<DirectorySession, FSResult> openDirectory(const FSPath& path) override;
|
||||
Rust::Result<ArchiveBase*, HorizonResult> openArchive(const FSPath& path) override;
|
||||
Rust::Result<DirectorySession, HorizonResult> openDirectory(const FSPath& path) override;
|
||||
FileDescriptor openFile(const FSPath& path, const FilePerms& perms) override;
|
||||
std::optional<u32> readFile(FileSession* file, u64 offset, u32 size, u32 dataPointer) 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() {
|
||||
return IOFile::getAppData() / "FormatInfo" / "SaveData.format";
|
||||
|
|
|
@ -1,5 +1,8 @@
|
|||
#pragma once
|
||||
#include "archive_base.hpp"
|
||||
#include "result/result.hpp"
|
||||
|
||||
using Result::HorizonResult;
|
||||
|
||||
class SDMCArchive : public ArchiveBase {
|
||||
public:
|
||||
|
@ -8,10 +11,10 @@ public:
|
|||
u64 getFreeBytes() override { Helpers::panic("SDMC::GetFreeBytes unimplemented"); return 0; }
|
||||
std::string name() override { return "SDMC"; }
|
||||
|
||||
FSResult createFile(const FSPath& path, u64 size) override;
|
||||
FSResult deleteFile(const FSPath& path) override;
|
||||
HorizonResult createFile(const FSPath& path, u64 size) 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;
|
||||
std::optional<u32> readFile(FileSession* file, u64 offset, u32 size, u32 dataPointer) override;
|
||||
};
|
|
@ -8,10 +8,10 @@ public:
|
|||
u64 getFreeBytes() override { return 0; }
|
||||
std::string name() override { return "SelfNCCH"; }
|
||||
|
||||
FSResult createFile(const FSPath& path, u64 size) override;
|
||||
FSResult deleteFile(const FSPath& path) override;
|
||||
HorizonResult createFile(const FSPath& path, u64 size) 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;
|
||||
std::optional<u32> readFile(FileSession* file, u64 offset, u32 size, u32 dataPointer) override;
|
||||
|
||||
|
|
|
@ -1,14 +1,23 @@
|
|||
#pragma once
|
||||
#include <cstdarg>
|
||||
#include <climits>
|
||||
#include <cstdarg>
|
||||
#include <cstdint>
|
||||
#include <fstream>
|
||||
#include <iostream>
|
||||
#include <iterator>
|
||||
#include <type_traits>
|
||||
#include <utility>
|
||||
#include <vector>
|
||||
|
||||
#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 u16 = std::uint16_t;
|
||||
using u32 = std::uint32_t;
|
||||
|
@ -22,78 +31,74 @@ using s32 = std::int32_t;
|
|||
using s64 = std::int64_t;
|
||||
|
||||
namespace Helpers {
|
||||
[[noreturn]] static void panic(const char* fmt, ...) {
|
||||
std::va_list args;
|
||||
va_start(args, fmt);
|
||||
std::cout << termcolor::on_red << "[FATAL] ";
|
||||
std::vprintf (fmt, args);
|
||||
std::cout << termcolor::reset << "\n";
|
||||
va_end(args);
|
||||
[[noreturn]] static void panic(const char* fmt, ...) {
|
||||
std::va_list args;
|
||||
va_start(args, fmt);
|
||||
std::cout << termcolor::on_red << "[FATAL] ";
|
||||
std::vprintf(fmt, args);
|
||||
std::cout << termcolor::reset << "\n";
|
||||
va_end(args);
|
||||
|
||||
exit(1);
|
||||
}
|
||||
exit(1);
|
||||
}
|
||||
|
||||
static void warn(const char* fmt, ...) {
|
||||
std::va_list args;
|
||||
va_start(args, fmt);
|
||||
std::cout << termcolor::on_red << "[Warning] ";
|
||||
std::vprintf (fmt, args);
|
||||
std::cout << termcolor::reset << "\n";
|
||||
va_end(args);
|
||||
}
|
||||
static void warn(const char* fmt, ...) {
|
||||
std::va_list args;
|
||||
va_start(args, fmt);
|
||||
std::cout << termcolor::on_red << "[Warning] ";
|
||||
std::vprintf(fmt, args);
|
||||
std::cout << termcolor::reset << "\n";
|
||||
va_end(args);
|
||||
}
|
||||
|
||||
static std::vector <u8> loadROM(std::string directory) {
|
||||
std::ifstream file (directory, std::ios::binary);
|
||||
if (file.fail())
|
||||
panic("Couldn't read %s", directory.c_str());
|
||||
static std::vector<u8> loadROM(std::string directory) {
|
||||
std::ifstream file(directory, std::ios::binary);
|
||||
if (file.fail()) panic("Couldn't read %s", directory.c_str());
|
||||
|
||||
std::vector<u8> ROM;
|
||||
std::vector<u8> ROM;
|
||||
|
||||
file.unsetf(std::ios::skipws);
|
||||
ROM.insert(ROM.begin(),
|
||||
std::istream_iterator<uint8_t>(file),
|
||||
std::istream_iterator<uint8_t>());
|
||||
file.unsetf(std::ios::skipws);
|
||||
ROM.insert(ROM.begin(), std::istream_iterator<uint8_t>(file), std::istream_iterator<uint8_t>());
|
||||
|
||||
file.close();
|
||||
file.close();
|
||||
|
||||
printf ("%s loaded successfully\n", directory.c_str());
|
||||
return ROM;
|
||||
}
|
||||
printf("%s loaded successfully\n", directory.c_str());
|
||||
return ROM;
|
||||
}
|
||||
|
||||
static constexpr bool buildingInDebugMode() {
|
||||
#ifdef NDEBUG
|
||||
return false;
|
||||
#endif
|
||||
static constexpr bool buildingInDebugMode() {
|
||||
#ifdef NDEBUG
|
||||
return false;
|
||||
#endif
|
||||
return true;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
static void debug_printf (const char* fmt, ...) {
|
||||
if constexpr (buildingInDebugMode()) {
|
||||
std::va_list args;
|
||||
va_start(args, fmt);
|
||||
std::vprintf (fmt, args);
|
||||
va_end(args);
|
||||
}
|
||||
}
|
||||
static void debug_printf(const char* fmt, ...) {
|
||||
if constexpr (buildingInDebugMode()) {
|
||||
std::va_list args;
|
||||
va_start(args, fmt);
|
||||
std::vprintf(fmt, args);
|
||||
va_end(args);
|
||||
}
|
||||
}
|
||||
|
||||
/// Sign extend an arbitrary-size value to 32 bits
|
||||
static constexpr u32 inline signExtend32 (u32 value, u32 startingSize) {
|
||||
auto temp = (s32) value;
|
||||
auto bitsToShift = 32 - startingSize;
|
||||
return (u32) (temp << bitsToShift >> bitsToShift);
|
||||
}
|
||||
/// Sign extend an arbitrary-size value to 32 bits
|
||||
static constexpr u32 inline signExtend32(u32 value, u32 startingSize) {
|
||||
auto temp = (s32)value;
|
||||
auto bitsToShift = 32 - startingSize;
|
||||
return (u32)(temp << bitsToShift >> bitsToShift);
|
||||
}
|
||||
|
||||
/// Sign extend an arbitrary-size value to 16 bits
|
||||
static constexpr u16 signExtend16 (u16 value, u32 startingSize) {
|
||||
auto temp = (s16) value;
|
||||
auto bitsToShift = 16 - startingSize;
|
||||
return (u16) (temp << bitsToShift >> bitsToShift);
|
||||
}
|
||||
/// Sign extend an arbitrary-size value to 16 bits
|
||||
static constexpr u16 signExtend16(u16 value, u32 startingSize) {
|
||||
auto temp = (s16)value;
|
||||
auto bitsToShift = 16 - startingSize;
|
||||
return (u16)(temp << bitsToShift >> bitsToShift);
|
||||
}
|
||||
|
||||
/// Create a mask with `count` number of one bits.
|
||||
template<typename T, usize count>
|
||||
static constexpr T ones () {
|
||||
template <typename T, usize count>
|
||||
static constexpr T ones() {
|
||||
constexpr usize bitsize = CHAR_BIT * sizeof(T);
|
||||
static_assert(count <= bitsize, "count larger than bitsize of T");
|
||||
|
||||
|
@ -104,74 +109,74 @@ namespace Helpers {
|
|||
}
|
||||
|
||||
/// Extract bits from an integer-type
|
||||
template<usize offset, typename T>
|
||||
static constexpr T getBit (T value) {
|
||||
return (value >> offset) & T(1);
|
||||
template <usize offset, typename T>
|
||||
static constexpr T getBit(T value) {
|
||||
return (value >> offset) & T(1);
|
||||
}
|
||||
|
||||
/// Extract bits from an integer-type
|
||||
template<usize offset, usize bits, typename T>
|
||||
static constexpr T getBits (T value) {
|
||||
return (value >> offset) & ones<T, bits>();
|
||||
template <usize offset, usize bits, typename T>
|
||||
static constexpr T getBits(T value) {
|
||||
return (value >> offset) & ones<T, bits>();
|
||||
}
|
||||
|
||||
/// Check if a bit "bit" of value is set
|
||||
static constexpr bool isBitSet (u32 value, int bit) {
|
||||
return (value >> bit) & 1;
|
||||
}
|
||||
/// Check if a bit "bit" of value is set
|
||||
static constexpr bool isBitSet(u32 value, int bit) { return (value >> bit) & 1; }
|
||||
|
||||
/// rotate number right
|
||||
template <typename T>
|
||||
static constexpr T rotr (T value, int bits) {
|
||||
constexpr auto bitWidth = sizeof(T) * 8;
|
||||
bits &= bitWidth - 1;
|
||||
return (value >> bits) | (value << (bitWidth - bits));
|
||||
}
|
||||
/// rotate number right
|
||||
template <typename T>
|
||||
static constexpr T rotr(T value, int bits) {
|
||||
constexpr auto bitWidth = sizeof(T) * 8;
|
||||
bits &= bitWidth - 1;
|
||||
return (value >> bits) | (value << (bitWidth - bits));
|
||||
}
|
||||
|
||||
// rotate number left
|
||||
template <typename T>
|
||||
static constexpr T rotl (T value, int bits) {
|
||||
constexpr auto bitWidth = sizeof(T) * 8;
|
||||
bits &= bitWidth - 1;
|
||||
return (value << bits) | (value >> (bitWidth - bits));
|
||||
}
|
||||
// rotate number left
|
||||
template <typename T>
|
||||
static constexpr T rotl(T value, int bits) {
|
||||
constexpr auto bitWidth = sizeof(T) * 8;
|
||||
bits &= bitWidth - 1;
|
||||
return (value << bits) | (value >> (bitWidth - bits));
|
||||
}
|
||||
|
||||
/// Used to make the compiler evaluate beeg loops at compile time for the tablegen
|
||||
template <typename T, T Begin, class Func, T ...Is>
|
||||
static constexpr void static_for_impl( Func&& f, std::integer_sequence<T, Is...> ) {
|
||||
( f( std::integral_constant<T, Begin + Is>{ } ),... );
|
||||
}
|
||||
/// Used to make the compiler evaluate beeg loops at compile time for the tablegen
|
||||
template <typename T, T Begin, class Func, T... Is>
|
||||
static constexpr void static_for_impl(Func&& f, std::integer_sequence<T, Is...>) {
|
||||
(f(std::integral_constant<T, Begin + Is>{}), ...);
|
||||
}
|
||||
|
||||
template <typename T, T Begin, T End, class Func>
|
||||
static constexpr void static_for(Func&& f) {
|
||||
static_for_impl<T, Begin>( std::forward<Func>(f), std::make_integer_sequence<T, End - Begin>{ } );
|
||||
}
|
||||
template <typename T, T Begin, T End, class Func>
|
||||
static constexpr void static_for(Func&& f) {
|
||||
static_for_impl<T, Begin>(std::forward<Func>(f), std::make_integer_sequence<T, End - Begin>{});
|
||||
}
|
||||
|
||||
// For values < 0x99
|
||||
static constexpr inline u8 incBCDByte(u8 value) {
|
||||
return ((value & 0xf) == 0x9) ? value + 7 : value + 1;
|
||||
}
|
||||
};
|
||||
// For values < 0x99
|
||||
static constexpr inline u8 incBCDByte(u8 value) { 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
|
||||
constexpr size_t operator""_KB(unsigned long long int 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""_KB(unsigned long long int 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; }
|
||||
|
||||
// useful macros
|
||||
// likely/unlikely
|
||||
#ifdef __GNUC__
|
||||
#define likely(x) __builtin_expect((x),1)
|
||||
#define unlikely(x) __builtin_expect((x),0)
|
||||
#else
|
||||
#define likely(x) (x)
|
||||
#define unlikely(x) (x)
|
||||
#endif
|
||||
#define likely(x) __builtin_expect((x), 1)
|
||||
#define unlikely(x) __builtin_expect((x), 0)
|
||||
#else
|
||||
#define likely(x) (x)
|
||||
#define unlikely(x) (x)
|
||||
#endif
|
||||
|
|
|
@ -4,33 +4,7 @@
|
|||
#include "fs/archive_base.hpp"
|
||||
#include "handles.hpp"
|
||||
#include "helpers.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
|
||||
};
|
||||
}
|
||||
#include "result/result.hpp"
|
||||
|
||||
enum class KernelObjectType : u8 {
|
||||
AddressArbiter, Archive, Directory, File, MemoryBlock, Process, ResourceLimit, Session, Dummy,
|
||||
|
@ -125,7 +99,7 @@ struct Thread {
|
|||
ThreadStatus status;
|
||||
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
|
||||
|
||||
|
||||
// The waiting address for threads that are waiting on an AddressArbiter
|
||||
u32 waitingAddress;
|
||||
|
||||
|
|
|
@ -28,7 +28,7 @@
|
|||
#include <type_traits>
|
||||
#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...
|
||||
|
@ -523,8 +523,6 @@ namespace OpenGL {
|
|||
static GLint getProgram() { return get<GLint>(GL_CURRENT_PROGRAM); }
|
||||
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) {
|
||||
return glGetUniformLocation(program, name);
|
||||
}
|
||||
|
|
|
@ -2,11 +2,13 @@
|
|||
#include <array>
|
||||
#include <span>
|
||||
|
||||
#include "PICA/float_types.hpp"
|
||||
#include "helpers.hpp"
|
||||
#include "logger.hpp"
|
||||
#include "opengl.hpp"
|
||||
#include "surface_cache.hpp"
|
||||
#include "textures.hpp"
|
||||
#include "PICA/regs.hpp"
|
||||
|
||||
// More circular dependencies!
|
||||
class GPU;
|
||||
|
@ -14,7 +16,11 @@ class GPU;
|
|||
struct Vertex {
|
||||
OpenGL::vec4 position;
|
||||
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 {
|
||||
|
@ -27,6 +33,15 @@ class Renderer {
|
|||
GLint alphaControlLoc = -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
|
||||
GLint depthOffsetLoc = -1;
|
||||
GLint depthScaleLoc = -1;
|
||||
|
@ -45,12 +60,12 @@ class Renderer {
|
|||
|
||||
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
|
||||
ColourBuffer::Formats colourBufferFormat; // Format of the colours stored in the colour buffer
|
||||
u32 colourBufferLoc; // Location in 3DS VRAM for the colour buffer
|
||||
PICA::ColorFmt colourBufferFormat; // Format of the colours stored in the colour buffer
|
||||
|
||||
// Same for the depth/stencil buffer
|
||||
u32 depthBufferLoc;
|
||||
DepthBuffer::Formats depthBufferFormat;
|
||||
PICA::DepthFmt depthBufferFormat;
|
||||
|
||||
// Dummy VAO/VBO for blitting the final output
|
||||
OpenGL::VertexArray dummyVAO;
|
||||
|
@ -68,6 +83,8 @@ class Renderer {
|
|||
MAKE_LOG_FUNCTION(log, rendererLogger)
|
||||
void setupBlending();
|
||||
void bindDepthBuffer();
|
||||
void setupTextureEnvState();
|
||||
void bindTexturesToSlots();
|
||||
|
||||
public:
|
||||
Renderer(GPU& gpu, const std::array<u32, regNum>& internalRegs) : gpu(gpu), regs(internalRegs) {}
|
||||
|
@ -78,23 +95,19 @@ class Renderer {
|
|||
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 displayTransfer(u32 inputAddr, u32 outputAddr, u32 inputSize, u32 outputSize, u32 flags); // Perform display transfer
|
||||
void drawVertices(OpenGL::Primitives primType, std::span<const Vertex> vertices); // Draw the given vertices
|
||||
void drawVertices(PICA::PrimType primType, std::span<const Vertex> vertices); // Draw the given vertices
|
||||
|
||||
void setFBSize(u32 width, u32 height) {
|
||||
fbSize.x() = width;
|
||||
fbSize.y() = height;
|
||||
}
|
||||
|
||||
void setColourFormat(ColourBuffer::Formats format) { colourBufferFormat = format; }
|
||||
void setColourFormat(u32 format) { colourBufferFormat = static_cast<ColourBuffer::Formats>(format); }
|
||||
|
||||
void setDepthFormat(DepthBuffer::Formats format) { depthBufferFormat = format; }
|
||||
void setDepthFormat(u32 format) {
|
||||
if (format == 1) {
|
||||
void setColourFormat(PICA::ColorFmt format) { colourBufferFormat = format; }
|
||||
void setDepthFormat(PICA::DepthFmt format) {
|
||||
if (format == PICA::DepthFmt::Unknown1) {
|
||||
Helpers::panic("[PICA] Undocumented depth-stencil mode!");
|
||||
}
|
||||
|
||||
depthBufferFormat = static_cast<DepthBuffer::Formats>(format);
|
||||
depthBufferFormat = format;
|
||||
}
|
||||
|
||||
void setColourBufferLoc(u32 loc) { colourBufferLoc = loc; }
|
||||
|
|
|
@ -1,4 +1,5 @@
|
|||
#pragma once
|
||||
#include "PICA/regs.hpp"
|
||||
#include "boost/icl/interval.hpp"
|
||||
#include "helpers.hpp"
|
||||
#include "opengl.hpp"
|
||||
|
@ -7,18 +8,8 @@ template <typename T>
|
|||
using Interval = boost::icl::right_open_interval<T>;
|
||||
|
||||
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;
|
||||
Formats format;
|
||||
PICA::ColorFmt format;
|
||||
OpenGL::uvec2 size;
|
||||
bool valid;
|
||||
|
||||
|
@ -30,7 +21,7 @@ struct ColourBuffer {
|
|||
|
||||
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) {
|
||||
|
||||
u64 endLoc = (u64)loc + sizeInBytes();
|
||||
|
@ -78,31 +69,14 @@ struct ColourBuffer {
|
|||
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() {
|
||||
return (size_t)size.x() * (size_t)size.y() * sizePerPixel();
|
||||
return (size_t)size.x() * (size_t)size.y() * PICA::sizePerPixel(format);
|
||||
}
|
||||
};
|
||||
|
||||
struct DepthBuffer {
|
||||
enum class Formats : u32 {
|
||||
Depth16 = 0,
|
||||
Garbage = 1,
|
||||
Depth24 = 2,
|
||||
Depth24Stencil8 = 3
|
||||
};
|
||||
|
||||
u32 location;
|
||||
Formats format;
|
||||
PICA::DepthFmt format;
|
||||
OpenGL::uvec2 size; // Implicitly set to the size of the framebuffer
|
||||
bool valid;
|
||||
|
||||
|
@ -113,7 +87,7 @@ struct DepthBuffer {
|
|||
|
||||
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) {
|
||||
|
||||
u64 endLoc = (u64)loc + sizeInBytes();
|
||||
|
@ -121,10 +95,6 @@ struct DepthBuffer {
|
|||
range = Interval<u32>(loc, (u32)endLoc);
|
||||
}
|
||||
|
||||
bool hasStencil() {
|
||||
return format == Formats::Depth24Stencil8;
|
||||
}
|
||||
|
||||
void allocate() {
|
||||
// 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.
|
||||
|
@ -167,18 +137,7 @@ struct DepthBuffer {
|
|||
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() {
|
||||
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
|
||||
#include <array>
|
||||
#include <string>
|
||||
#include "PICA/regs.hpp"
|
||||
#include "boost/icl/interval.hpp"
|
||||
#include "helpers.hpp"
|
||||
#include "opengl.hpp"
|
||||
|
@ -9,28 +10,9 @@ template <typename T>
|
|||
using Interval = boost::icl::right_open_interval<T>;
|
||||
|
||||
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 config; // Magnification/minification filter, wrapping configs, etc
|
||||
Formats format;
|
||||
PICA::TextureFmt format;
|
||||
OpenGL::uvec2 size;
|
||||
bool valid;
|
||||
|
||||
|
@ -41,7 +23,7 @@ struct Texture {
|
|||
|
||||
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) {
|
||||
|
||||
u64 endLoc = (u64)loc + sizeInBytes();
|
||||
|
@ -62,7 +44,7 @@ struct Texture {
|
|||
void free();
|
||||
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
|
||||
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_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
|
||||
std::string formatToString() {
|
||||
return textureFormatToString(format);
|
||||
return PICA::textureFormatToString(format);
|
||||
}
|
||||
|
||||
// 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 "logger.hpp"
|
||||
#include "memory.hpp"
|
||||
#include "result/result.hpp"
|
||||
|
||||
class ACService {
|
||||
Handle handle = KernelHandles::AC;
|
||||
|
|
|
@ -3,6 +3,7 @@
|
|||
#include "kernel_types.hpp"
|
||||
#include "logger.hpp"
|
||||
#include "memory.hpp"
|
||||
#include "result/result.hpp"
|
||||
|
||||
class ACTService {
|
||||
Handle handle = KernelHandles::ACT;
|
||||
|
|
|
@ -3,6 +3,7 @@
|
|||
#include "kernel_types.hpp"
|
||||
#include "logger.hpp"
|
||||
#include "memory.hpp"
|
||||
#include "result/result.hpp"
|
||||
|
||||
class AMService {
|
||||
Handle handle = KernelHandles::AM;
|
||||
|
|
|
@ -4,6 +4,7 @@
|
|||
#include "kernel_types.hpp"
|
||||
#include "logger.hpp"
|
||||
#include "memory.hpp"
|
||||
#include "result/result.hpp"
|
||||
|
||||
// Yay, more circular dependencies
|
||||
class Kernel;
|
||||
|
|
|
@ -3,6 +3,7 @@
|
|||
#include "kernel_types.hpp"
|
||||
#include "logger.hpp"
|
||||
#include "memory.hpp"
|
||||
#include "result/result.hpp"
|
||||
|
||||
class BOSSService {
|
||||
Handle handle = KernelHandles::BOSS;
|
||||
|
|
|
@ -3,6 +3,7 @@
|
|||
#include "kernel_types.hpp"
|
||||
#include "logger.hpp"
|
||||
#include "memory.hpp"
|
||||
#include "result/result.hpp"
|
||||
|
||||
class CAMService {
|
||||
Handle handle = KernelHandles::CAM;
|
||||
|
|
|
@ -4,6 +4,7 @@
|
|||
#include "kernel_types.hpp"
|
||||
#include "logger.hpp"
|
||||
#include "memory.hpp"
|
||||
#include "result/result.hpp"
|
||||
|
||||
class Kernel;
|
||||
|
||||
|
|
|
@ -4,6 +4,7 @@
|
|||
#include "logger.hpp"
|
||||
#include "memory.hpp"
|
||||
#include "region_codes.hpp"
|
||||
#include "result/result.hpp"
|
||||
|
||||
class CFGService {
|
||||
Handle handle = KernelHandles::CFG;
|
||||
|
|
|
@ -3,6 +3,7 @@
|
|||
#include "kernel_types.hpp"
|
||||
#include "logger.hpp"
|
||||
#include "memory.hpp"
|
||||
#include "result/result.hpp"
|
||||
|
||||
// Please forgive me for how everything in this file is named
|
||||
// "dlp:SRVR" is not a nice name to work with
|
||||
|
|
|
@ -4,6 +4,7 @@
|
|||
#include "helpers.hpp"
|
||||
#include "logger.hpp"
|
||||
#include "memory.hpp"
|
||||
#include "result/result.hpp"
|
||||
|
||||
namespace DSPPipeType {
|
||||
enum : u32 {
|
||||
|
|
|
@ -4,6 +4,7 @@
|
|||
#include "kernel_types.hpp"
|
||||
#include "logger.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
|
||||
struct FriendKey {
|
||||
|
|
|
@ -8,6 +8,7 @@
|
|||
#include "kernel_types.hpp"
|
||||
#include "logger.hpp"
|
||||
#include "memory.hpp"
|
||||
#include "result/result.hpp"
|
||||
|
||||
// Yay, more circular dependencies
|
||||
class Kernel;
|
||||
|
@ -29,8 +30,8 @@ class FSService {
|
|||
ExtSaveDataArchive sharedExtSaveData_nand;
|
||||
|
||||
ArchiveBase* getArchiveFromID(u32 id, const FSPath& archivePath);
|
||||
Rust::Result<Handle, FSResult> openArchiveHandle(u32 archiveID, const FSPath& path);
|
||||
Rust::Result<Handle, FSResult> openDirectoryHandle(ArchiveBase* archive, const FSPath& path);
|
||||
Rust::Result<Handle, HorizonResult> openArchiveHandle(u32 archiveID, 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);
|
||||
FSPath readPath(u32 type, u32 pointer, u32 size);
|
||||
|
||||
|
@ -62,7 +63,7 @@ public:
|
|||
sharedExtSaveData_nand(mem, "../SharedFiles/NAND", true), extSaveData_sdmc(mem, "SDMC"),
|
||||
sdmc(mem), selfNcch(mem), ncch(mem), kernel(kernel)
|
||||
{}
|
||||
|
||||
|
||||
void reset();
|
||||
void handleSyncRequest(u32 messagePointer);
|
||||
// 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 "logger.hpp"
|
||||
#include "memory.hpp"
|
||||
#include "result/result.hpp"
|
||||
|
||||
enum class GPUInterrupt : u8 {
|
||||
PSC0 = 0, // Memory fill completed
|
||||
|
|
|
@ -3,6 +3,7 @@
|
|||
#include "kernel_types.hpp"
|
||||
#include "logger.hpp"
|
||||
#include "memory.hpp"
|
||||
#include "result/result.hpp"
|
||||
|
||||
class LCDService {
|
||||
Handle handle = KernelHandles::LCD;
|
||||
|
|
|
@ -5,6 +5,7 @@
|
|||
#include "kernel_types.hpp"
|
||||
#include "logger.hpp"
|
||||
#include "memory.hpp"
|
||||
#include "result/result.hpp"
|
||||
|
||||
namespace HID::Keys {
|
||||
enum : u32 {
|
||||
|
|
|
@ -3,6 +3,7 @@
|
|||
#include "kernel_types.hpp"
|
||||
#include "logger.hpp"
|
||||
#include "memory.hpp"
|
||||
#include "result/result.hpp"
|
||||
|
||||
class LDRService {
|
||||
Handle handle = KernelHandles::LDR_RO;
|
||||
|
|
|
@ -3,6 +3,7 @@
|
|||
#include "kernel_types.hpp"
|
||||
#include "logger.hpp"
|
||||
#include "memory.hpp"
|
||||
#include "result/result.hpp"
|
||||
|
||||
class MICService {
|
||||
Handle handle = KernelHandles::MIC;
|
||||
|
|
|
@ -3,6 +3,7 @@
|
|||
#include "kernel_types.hpp"
|
||||
#include "logger.hpp"
|
||||
#include "memory.hpp"
|
||||
#include "result/result.hpp"
|
||||
|
||||
class NDMService {
|
||||
Handle handle = KernelHandles::NDM;
|
||||
|
|
|
@ -3,6 +3,7 @@
|
|||
#include "kernel_types.hpp"
|
||||
#include "logger.hpp"
|
||||
#include "memory.hpp"
|
||||
#include "result/result.hpp"
|
||||
|
||||
// You know the drill
|
||||
class Kernel;
|
||||
|
|
|
@ -3,6 +3,7 @@
|
|||
#include "kernel_types.hpp"
|
||||
#include "logger.hpp"
|
||||
#include "memory.hpp"
|
||||
#include "result/result.hpp"
|
||||
|
||||
class NIMService {
|
||||
Handle handle = KernelHandles::NIM;
|
||||
|
|
|
@ -3,6 +3,7 @@
|
|||
#include "kernel_types.hpp"
|
||||
#include "logger.hpp"
|
||||
#include "memory.hpp"
|
||||
#include "result/result.hpp"
|
||||
|
||||
class PTMService {
|
||||
Handle handle = KernelHandles::PTM;
|
||||
|
|
|
@ -88,44 +88,45 @@ template <bool indexed>
|
|||
void GPU::drawArrays() {
|
||||
// Base address for vertex attributes
|
||||
// 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 vertexCount = regs[PICAInternalRegs::VertexCountReg]; // Total # of vertices to transfer
|
||||
const u32 vertexBase = ((regs[PICA::InternalRegs::VertexAttribLoc] >> 1) & 0xfffffff) * 16;
|
||||
const u32 vertexCount = regs[PICA::InternalRegs::VertexCountReg]; // Total # of vertices to transfer
|
||||
|
||||
// Configures the type of primitive and the number of vertex shader outputs
|
||||
const u32 primConfig = regs[PICAInternalRegs::PrimitiveConfig];
|
||||
const u32 primType = Helpers::getBits<8, 2>(primConfig);
|
||||
if (primType != 0 && primType != 1 && primType != 3) Helpers::panic("[PICA] Tried to draw unimplemented shape %d\n", primType);
|
||||
const u32 primConfig = regs[PICA::InternalRegs::PrimitiveConfig];
|
||||
const PICA::PrimType primType = static_cast<PICA::PrimType>(Helpers::getBits<8, 2>(primConfig));
|
||||
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 ((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);
|
||||
}
|
||||
|
||||
// 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);
|
||||
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
|
||||
// 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) {
|
||||
u32 offset = regs[PICAInternalRegs::VertexOffsetReg];
|
||||
u32 offset = regs[PICA::InternalRegs::VertexOffsetReg];
|
||||
log("PICA::DrawArrays(vertex count = %d, vertexOffset = %d)\n", vertexCount, offset);
|
||||
} else {
|
||||
log("PICA::DrawElements(vertex count = %d, index buffer config = %08X)\n", vertexCount, indexBufferConfig);
|
||||
}
|
||||
|
||||
// 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();
|
||||
|
||||
for (u32 i = 0; i < vertexCount; i++) {
|
||||
u32 vertexIndex; // Index of the vertex in the VBO
|
||||
|
||||
if constexpr (!indexed) {
|
||||
vertexIndex = i + regs[PICAInternalRegs::VertexOffsetReg];
|
||||
vertexIndex = i + regs[PICA::InternalRegs::VertexOffsetReg];
|
||||
} else {
|
||||
if (shortIndex) {
|
||||
auto ptr = getPointerPhys<u16>(indexBufferPointer);
|
||||
|
@ -235,9 +236,9 @@ void GPU::drawArrays() {
|
|||
OutputVertex out;
|
||||
|
||||
// Map shader outputs to fixed function properties
|
||||
const u32 totalShaderOutputs = regs[PICAInternalRegs::ShaderOutputCount] & 7;
|
||||
const u32 totalShaderOutputs = regs[PICA::InternalRegs::ShaderOutputCount] & 7;
|
||||
for (int i = 0; i < totalShaderOutputs; i++) {
|
||||
const u32 config = regs[PICAInternalRegs::ShaderOutmap0 + 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;
|
||||
|
@ -247,24 +248,22 @@ void GPU::drawArrays() {
|
|||
|
||||
std::memcpy(&vertices[i].position, &out.s.positions, sizeof(vec4f));
|
||||
std::memcpy(&vertices[i].colour, &out.s.colour, sizeof(vec4f));
|
||||
std::memcpy(&vertices[i].UVs, &out.s.texcoord0, 2 * sizeof(f24));
|
||||
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("(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());
|
||||
}
|
||||
|
||||
// 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 shape = primTypes[primType];
|
||||
renderer.drawVertices(shape, std::span(vertices).first(vertexCount));
|
||||
renderer.drawVertices(primType, std::span(vertices).first(vertexCount));
|
||||
}
|
||||
|
||||
Vertex GPU::getImmediateModeVertex() {
|
||||
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
|
||||
for (int i = 0; i < totalAttrCount; i++) {
|
||||
|
@ -275,11 +274,11 @@ Vertex GPU::getImmediateModeVertex() {
|
|||
shaderUnit.vs.run();
|
||||
std::memcpy(&v.position, &shaderUnit.vs.outputs[0], 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("(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;
|
||||
}
|
||||
|
|
|
@ -33,7 +33,7 @@ u32 GPU::readInternalReg(u32 index) {
|
|||
}
|
||||
|
||||
void GPU::writeInternalReg(u32 index, u32 value, u32 mask) {
|
||||
using namespace PICAInternalRegs;
|
||||
using namespace PICA::InternalRegs;
|
||||
|
||||
if (index > regNum) {
|
||||
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: {
|
||||
u32 format = getBits<16, 3>(value);
|
||||
renderer.setColourFormat(format);
|
||||
renderer.setColourFormat(static_cast<PICA::ColorFmt>(format));
|
||||
break;
|
||||
}
|
||||
|
||||
|
@ -79,8 +79,8 @@ void GPU::writeInternalReg(u32 index, u32 value, u32 mask) {
|
|||
}
|
||||
|
||||
case DepthBufferFormat: {
|
||||
u32 fmt = value & 0x3;
|
||||
renderer.setDepthFormat(fmt);
|
||||
u32 format = value & 0x3;
|
||||
renderer.setDepthFormat(static_cast<PICA::DepthFmt>(format));
|
||||
break;
|
||||
}
|
||||
|
||||
|
@ -137,7 +137,7 @@ void GPU::writeInternalReg(u32 index, u32 value, u32 mask) {
|
|||
if (fixedAttribIndex < 12) [[likely]] {
|
||||
shaderUnit.vs.fixedAttributes[fixedAttribIndex++] = attr;
|
||||
} 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) {
|
||||
printf("Broken state in the immediate mode vertex submission pipeline. Failing silently\n");
|
||||
immediateModeAttrIndex = 0;
|
||||
|
@ -151,13 +151,13 @@ void GPU::writeInternalReg(u32 index, u32 value, u32 mask) {
|
|||
immediateModeVertices[immediateModeVertIndex++] = v;
|
||||
|
||||
// Get primitive type
|
||||
const u32 primConfig = regs[PICAInternalRegs::PrimitiveConfig];
|
||||
const u32 primConfig = regs[PICA::InternalRegs::PrimitiveConfig];
|
||||
const u32 primType = getBits<8, 2>(primConfig);
|
||||
|
||||
// If we've reached 3 verts, issue a draw call
|
||||
// Handle rendering depending on the primitive type
|
||||
if (immediateModeVertIndex == 3) {
|
||||
renderer.drawVertices(OpenGL::Triangle, immediateModeVertices);
|
||||
renderer.drawVertices(PICA::PrimType::TriangleList, immediateModeVertices);
|
||||
|
||||
switch (primType) {
|
||||
// Triangle or geometry primitive. Draw a triangle and discard all vertices
|
||||
|
|
|
@ -3,7 +3,7 @@
|
|||
|
||||
namespace fs = std::filesystem;
|
||||
|
||||
FSResult ExtSaveDataArchive::createFile(const FSPath& path, u64 size) {
|
||||
HorizonResult ExtSaveDataArchive::createFile(const FSPath& path, u64 size) {
|
||||
if (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();
|
||||
|
||||
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
|
||||
IOFile file(p.string().c_str(), "wb");
|
||||
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");
|
||||
return FSResult::Success;
|
||||
return Result::Success;
|
||||
}
|
||||
|
||||
FSResult ExtSaveDataArchive::deleteFile(const FSPath& path) {
|
||||
HorizonResult ExtSaveDataArchive::deleteFile(const FSPath& path) {
|
||||
if (path.type == PathType::UTF16) {
|
||||
if (!isPathSafe<PathType::UTF16>(path))
|
||||
Helpers::panic("Unsafe path in ExtSaveData::DeleteFile");
|
||||
|
@ -43,7 +43,7 @@ FSResult ExtSaveDataArchive::deleteFile(const FSPath& path) {
|
|||
}
|
||||
|
||||
if (!fs::is_regular_file(p)) {
|
||||
return FSResult::FileNotFound;
|
||||
return Result::FS::FileNotFoundAlt;
|
||||
}
|
||||
|
||||
std::error_code ec;
|
||||
|
@ -55,11 +55,11 @@ FSResult ExtSaveDataArchive::deleteFile(const FSPath& path) {
|
|||
Helpers::warn("ExtSaveData::DeleteFile: fs::remove failed\n");
|
||||
}
|
||||
|
||||
return FSResult::Success;
|
||||
return Result::Success;
|
||||
}
|
||||
|
||||
Helpers::panic("ExtSaveDataArchive::DeleteFile: Unknown path type");
|
||||
return FSResult::Success;
|
||||
return Result::Success;
|
||||
}
|
||||
|
||||
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);
|
||||
}
|
||||
|
||||
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) {
|
||||
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");
|
||||
// Format info not found so the archive is not formatted
|
||||
// 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);
|
||||
}
|
||||
|
||||
Rust::Result<DirectorySession, FSResult> ExtSaveDataArchive::openDirectory(const FSPath& path) {
|
||||
Rust::Result<DirectorySession, HorizonResult> ExtSaveDataArchive::openDirectory(const FSPath& path) {
|
||||
if (path.type == PathType::UTF16) {
|
||||
if (!isPathSafe<PathType::UTF16>(path))
|
||||
Helpers::panic("Unsafe path in ExtSaveData::OpenDirectory");
|
||||
|
@ -121,18 +121,18 @@ Rust::Result<DirectorySession, FSResult> ExtSaveDataArchive::openDirectory(const
|
|||
|
||||
if (fs::is_regular_file(p)) {
|
||||
printf("ExtSaveData: OpenArchive used with a file path");
|
||||
return Err(FSResult::UnexpectedFileOrDir);
|
||||
return Err(Result::FS::UnexpectedFileOrDir);
|
||||
}
|
||||
|
||||
if (fs::is_directory(p)) {
|
||||
return Ok(DirectorySession(this, p));
|
||||
} else {
|
||||
return Err(FSResult::FileNotFound);
|
||||
return Err(Result::FS::FileNotFoundAlt);
|
||||
}
|
||||
}
|
||||
|
||||
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) {
|
||||
|
|
|
@ -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");
|
||||
return FSResult::Success;
|
||||
return Result::Success;
|
||||
}
|
||||
|
||||
FSResult NCCHArchive::deleteFile(const FSPath& path) {
|
||||
HorizonResult NCCHArchive::deleteFile(const FSPath& path) {
|
||||
Helpers::panic("[NCCH] Unimplemented DeleteFile");
|
||||
return FSResult::Success;
|
||||
return Result::Success;
|
||||
}
|
||||
|
||||
FileDescriptor NCCHArchive::openFile(const FSPath& path, const FilePerms& perms) {
|
||||
|
@ -48,7 +48,7 @@ FileDescriptor NCCHArchive::openFile(const FSPath& path, const FilePerms& perms)
|
|||
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) {
|
||||
Helpers::panic("NCCHArchive::OpenArchive: Invalid path");
|
||||
}
|
||||
|
|
|
@ -4,7 +4,7 @@
|
|||
|
||||
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 (!isPathSafe<PathType::UTF16>(path))
|
||||
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();
|
||||
|
||||
if (fs::exists(p))
|
||||
return FSResult::AlreadyExists;
|
||||
|
||||
return Result::FS::AlreadyExists;
|
||||
|
||||
IOFile file(p.string().c_str(), "wb");
|
||||
|
||||
|
||||
// If the size is 0, leave the file empty and return success
|
||||
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"
|
||||
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");
|
||||
return FSResult::Success;
|
||||
return Result::Success;
|
||||
}
|
||||
|
||||
FSResult SaveDataArchive::createDirectory(const FSPath& path) {
|
||||
HorizonResult SaveDataArchive::createDirectory(const FSPath& path) {
|
||||
if (path.type == PathType::UTF16) {
|
||||
if (!isPathSafe<PathType::UTF16>(path))
|
||||
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();
|
||||
|
||||
if (fs::is_directory(p))
|
||||
return FSResult::AlreadyExists;
|
||||
return Result::FS::AlreadyExists;
|
||||
if (fs::is_regular_file(p)) {
|
||||
Helpers::panic("File path passed to SaveData::CreateDirectory");
|
||||
}
|
||||
|
||||
bool success = fs::create_directory(p);
|
||||
return success ? FSResult::Success : FSResult::UnexpectedFileOrDir;
|
||||
return success ? Result::Success : Result::FS::UnexpectedFileOrDir;
|
||||
} else {
|
||||
Helpers::panic("Unimplemented SaveData::CreateDirectory");
|
||||
}
|
||||
}
|
||||
|
||||
FSResult SaveDataArchive::deleteFile(const FSPath& path) {
|
||||
HorizonResult SaveDataArchive::deleteFile(const FSPath& path) {
|
||||
if (path.type == PathType::UTF16) {
|
||||
if (!isPathSafe<PathType::UTF16>(path))
|
||||
Helpers::panic("Unsafe path in SaveData::DeleteFile");
|
||||
|
@ -68,7 +68,7 @@ FSResult SaveDataArchive::deleteFile(const FSPath& path) {
|
|||
}
|
||||
|
||||
if (!fs::is_regular_file(p)) {
|
||||
return FSResult::FileNotFound;
|
||||
return Result::FS::FileNotFoundAlt;
|
||||
}
|
||||
|
||||
std::error_code ec;
|
||||
|
@ -80,11 +80,11 @@ FSResult SaveDataArchive::deleteFile(const FSPath& path) {
|
|||
Helpers::warn("SaveData::DeleteFile: fs::remove failed\n");
|
||||
}
|
||||
|
||||
return FSResult::Success;
|
||||
return Result::Success;
|
||||
}
|
||||
|
||||
Helpers::panic("SaveDataArchive::DeleteFile: Unknown path type");
|
||||
return FSResult::Success;
|
||||
return Result::Success;
|
||||
}
|
||||
|
||||
FileDescriptor SaveDataArchive::openFile(const FSPath& path, const FilePerms& perms) {
|
||||
|
@ -121,7 +121,7 @@ FileDescriptor SaveDataArchive::openFile(const FSPath& path, const FilePerms& pe
|
|||
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 (!isPathSafe<PathType::UTF16>(path))
|
||||
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)) {
|
||||
printf("SaveData: OpenDirectory used with a file path");
|
||||
return Err(FSResult::UnexpectedFileOrDir);
|
||||
return Err(Result::FS::UnexpectedFileOrDir);
|
||||
}
|
||||
|
||||
if (fs::is_directory(p)) {
|
||||
return Ok(DirectorySession(this, p));
|
||||
} else {
|
||||
return Err(FSResult::FileNotFound);
|
||||
return Err(Result::FS::FileNotFoundAlt);
|
||||
}
|
||||
}
|
||||
|
||||
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();
|
||||
IOFile file(formatInfoPath, "rb");
|
||||
|
||||
// If the file failed to open somehow, we return that the archive is not formatted
|
||||
if (!file.isOpen()) {
|
||||
return Err(FSResult::NotFormatted);
|
||||
return Err(Result::FS::NotFormatted);
|
||||
}
|
||||
|
||||
FormatInfo ret;
|
||||
auto [success, bytesRead] = file.readBytes(&ret, sizeof(FormatInfo));
|
||||
if (!success || bytesRead != sizeof(FormatInfo)) {
|
||||
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);
|
||||
|
@ -168,7 +168,7 @@ void SaveDataArchive::format(const FSPath& path, const ArchiveBase::FormatInfo&
|
|||
const fs::path saveDataPath = IOFile::getAppData() / "SaveData";
|
||||
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::create_directories(saveDataPath);
|
||||
|
||||
|
@ -177,16 +177,16 @@ void SaveDataArchive::format(const FSPath& path, const ArchiveBase::FormatInfo&
|
|||
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) {
|
||||
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();
|
||||
// Format info not found so the archive is not formatted
|
||||
if (!fs::is_regular_file(formatInfoPath)) {
|
||||
return Err(FSResult::NotFormatted);
|
||||
return Err(Result::FS::NotFormatted);
|
||||
}
|
||||
|
||||
return Ok((ArchiveBase*)this);
|
||||
|
|
|
@ -1,14 +1,14 @@
|
|||
#include "fs/archive_sdmc.hpp"
|
||||
#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");
|
||||
return FSResult::Success;
|
||||
return Result::Success;
|
||||
}
|
||||
|
||||
FSResult SDMCArchive::deleteFile(const FSPath& path) {
|
||||
HorizonResult SDMCArchive::deleteFile(const FSPath& path) {
|
||||
Helpers::panic("[SDMC] Unimplemented DeleteFile");
|
||||
return FSResult::Success;
|
||||
return Result::Success;
|
||||
}
|
||||
|
||||
FileDescriptor SDMCArchive::openFile(const FSPath& path, const FilePerms& perms) {
|
||||
|
@ -16,9 +16,9 @@ FileDescriptor SDMCArchive::openFile(const FSPath& path, const FilePerms& perms)
|
|||
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");
|
||||
return Err(FSResult::NotFormatted);
|
||||
return Err(Result::FS::NotFormatted);
|
||||
}
|
||||
|
||||
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");
|
||||
return FSResult::Success;
|
||||
return Result::Success;
|
||||
}
|
||||
|
||||
FSResult SelfNCCHArchive::deleteFile(const FSPath& path) {
|
||||
HorizonResult SelfNCCHArchive::deleteFile(const FSPath& path) {
|
||||
Helpers::panic("[SelfNCCH] Unimplemented DeleteFile");
|
||||
return FSResult::Success;
|
||||
return Result::Success;
|
||||
}
|
||||
|
||||
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
|
||||
}
|
||||
|
||||
Rust::Result<ArchiveBase*, FSResult> SelfNCCHArchive::openArchive(const FSPath& path) {
|
||||
Rust::Result<ArchiveBase*, HorizonResult> SelfNCCHArchive::openArchive(const FSPath& path) {
|
||||
if (path.type != PathType::Empty) {
|
||||
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);
|
||||
|
|
|
@ -26,7 +26,7 @@ Handle Kernel::makeArbiter() {
|
|||
// Result CreateAddressArbiter(Handle* arbiter)
|
||||
void Kernel::createAddressArbiter() {
|
||||
logSVC("CreateAddressArbiter\n");
|
||||
regs[0] = SVCResult::Success;
|
||||
regs[0] = Result::Success;
|
||||
regs[1] = makeArbiter();
|
||||
}
|
||||
|
||||
|
@ -43,7 +43,7 @@ void Kernel::arbitrateAddress() {
|
|||
|
||||
const auto arbiter = getObject(handle, KernelObjectType::AddressArbiter);
|
||||
if (arbiter == nullptr) [[unlikely]] {
|
||||
regs[0] = SVCResult::BadHandle;
|
||||
regs[0] = Result::Kernel::InvalidHandle;
|
||||
return;
|
||||
}
|
||||
|
||||
|
@ -52,16 +52,16 @@ void Kernel::arbitrateAddress() {
|
|||
}
|
||||
|
||||
if (type > 4) [[unlikely]] {
|
||||
regs[0] = SVCResult::InvalidEnumValueAlt;
|
||||
regs[0] = Result::FND::InvalidEnumValue;
|
||||
return;
|
||||
}
|
||||
// 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)) {
|
||||
// Puts this thread to sleep if word < value until another thread arbitrates the address using SIGNAL
|
||||
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) {
|
||||
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
|
||||
// If the thread is put to sleep, the arbiter address is decremented
|
||||
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) {
|
||||
mem.write32(address, word - 1);
|
||||
sleepThreadOnArbiter(address);
|
||||
|
|
|
@ -7,12 +7,6 @@ namespace DirectoryOps {
|
|||
};
|
||||
}
|
||||
|
||||
namespace Result {
|
||||
enum : u32 {
|
||||
Success = 0
|
||||
};
|
||||
}
|
||||
|
||||
void Kernel::handleDirectoryOperation(u32 messagePointer, Handle directory) {
|
||||
const u32 cmd = mem.read32(messagePointer);
|
||||
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
|
||||
if (event->resetType == ResetType::OneShot) {
|
||||
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;
|
||||
} else {
|
||||
wakeupAllThreads(event->waitlist, handle);
|
||||
|
@ -64,11 +64,11 @@ void Kernel::svcCreateEvent() {
|
|||
|
||||
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));
|
||||
}
|
||||
|
||||
// Result ClearEvent(Handle event)
|
||||
// Result ClearEvent(Handle event)
|
||||
void Kernel::svcClearEvent() {
|
||||
const Handle handle = regs[0];
|
||||
const auto event = getObject(handle, KernelObjectType::Event);
|
||||
|
@ -76,15 +76,15 @@ void Kernel::svcClearEvent() {
|
|||
|
||||
if (event == nullptr) [[unlikely]] {
|
||||
Helpers::panic("Tried to clear non-existent event (handle = %X)", handle);
|
||||
regs[0] = SVCResult::BadHandle;
|
||||
regs[0] = Result::Kernel::InvalidHandle;
|
||||
return;
|
||||
}
|
||||
|
||||
event->getData<Event>()->fired = false;
|
||||
regs[0] = SVCResult::Success;
|
||||
regs[0] = Result::Success;
|
||||
}
|
||||
|
||||
// Result SignalEvent(Handle event)
|
||||
// Result SignalEvent(Handle event)
|
||||
void Kernel::svcSignalEvent() {
|
||||
const Handle handle = regs[0];
|
||||
logSVC("SignalEvent(event handle = %X)\n", handle);
|
||||
|
@ -92,15 +92,15 @@ void Kernel::svcSignalEvent() {
|
|||
|
||||
if (object == nullptr) {
|
||||
Helpers::panic("Signalled non-existent event: %X\n", handle);
|
||||
regs[0] = SVCResult::BadHandle;
|
||||
regs[0] = Result::Kernel::InvalidHandle;
|
||||
} else {
|
||||
// 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);
|
||||
}
|
||||
}
|
||||
|
||||
// Result WaitSynchronization1(Handle handle, s64 timeout_nanoseconds)
|
||||
// Result WaitSynchronization1(Handle handle, s64 timeout_nanoseconds)
|
||||
void Kernel::waitSynchronization1() {
|
||||
const Handle handle = regs[0];
|
||||
const s64 ns = s64(u64(regs[1]) | (u64(regs[2]) << 32));
|
||||
|
@ -110,7 +110,7 @@ void Kernel::waitSynchronization1() {
|
|||
|
||||
if (object == nullptr) [[unlikely]] {
|
||||
Helpers::panic("WaitSynchronization1: Bad event handle %X\n", handle);
|
||||
regs[0] = SVCResult::BadHandle;
|
||||
regs[0] = Result::Kernel::InvalidHandle;
|
||||
return;
|
||||
}
|
||||
|
||||
|
@ -120,16 +120,16 @@ void Kernel::waitSynchronization1() {
|
|||
|
||||
if (!shouldWaitOnObject(object)) {
|
||||
acquireSyncObject(object, threads[currentThreadIndex]); // Acquire the object since it's ready
|
||||
regs[0] = SVCResult::Success;
|
||||
regs[0] = Result::Success;
|
||||
rescheduleThreads();
|
||||
} else {
|
||||
// Timeout is 0, don't bother waiting, instantly timeout
|
||||
if (ns == 0) {
|
||||
regs[0] = SVCResult::Timeout;
|
||||
regs[0] = Result::OS::Timeout;
|
||||
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];
|
||||
t.waitList.resize(1);
|
||||
|
@ -180,7 +180,7 @@ void Kernel::waitSynchronizationN() {
|
|||
// Panic if one of the objects is not even an object
|
||||
if (object == nullptr) [[unlikely]] {
|
||||
Helpers::panic("WaitSynchronizationN: Bad object handle %X\n", handle);
|
||||
regs[0] = SVCResult::BadHandle;
|
||||
regs[0] = Result::Kernel::InvalidHandle;
|
||||
return;
|
||||
}
|
||||
|
||||
|
@ -206,16 +206,16 @@ void Kernel::waitSynchronizationN() {
|
|||
|
||||
// We only need to wait on one object. Easy...?!
|
||||
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) {
|
||||
regs[0] = SVCResult::Success;
|
||||
regs[0] = Result::Success;
|
||||
regs[1] = firstReadyObjectIndex; // Return index of the acquired object
|
||||
acquireSyncObject(waitObjects[firstReadyObjectIndex].second, t); // Acquire object
|
||||
rescheduleThreads();
|
||||
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
|
||||
regs[1] = 0xFFFFFFFF;
|
||||
t.waitList.resize(handleCount);
|
||||
|
@ -223,7 +223,7 @@ void Kernel::waitSynchronizationN() {
|
|||
t.outPointer = outPointer;
|
||||
t.waitingNanoseconds = ns;
|
||||
t.sleepTick = cpu.getTicks();
|
||||
|
||||
|
||||
for (s32 i = 0; i < handleCount; i++) {
|
||||
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
|
||||
|
|
|
@ -14,12 +14,6 @@ namespace FileOps {
|
|||
};
|
||||
}
|
||||
|
||||
namespace Result {
|
||||
enum : u32 {
|
||||
Success = 0
|
||||
};
|
||||
}
|
||||
|
||||
|
||||
void Kernel::handleFileOperation(u32 messagePointer, Handle file) {
|
||||
const u32 cmd = mem.read32(messagePointer);
|
||||
|
@ -90,7 +84,7 @@ void Kernel::readFile(u32 messagePointer, Handle fileHandle) {
|
|||
if (!file->isOpen) {
|
||||
Helpers::panic("Tried to read closed file");
|
||||
}
|
||||
|
||||
|
||||
// Handle files with their own file descriptors by just fread'ing the data
|
||||
if (file->fd) {
|
||||
std::unique_ptr<u8[]> data(new u8[size]);
|
||||
|
|
|
@ -149,7 +149,7 @@ u32 Kernel::getTLSPointer() {
|
|||
// Result CloseHandle(Handle handle)
|
||||
void Kernel::svcCloseHandle() {
|
||||
logSVC("CloseHandle(handle = %d) (Unimplemented)\n", regs[0]);
|
||||
regs[0] = SVCResult::Success;
|
||||
regs[0] = Result::Success;
|
||||
}
|
||||
|
||||
// u64 GetSystemTick()
|
||||
|
@ -169,7 +169,7 @@ void Kernel::outputDebugString() {
|
|||
|
||||
std::string message = mem.readString(pointer, size);
|
||||
logDebugString("[OutputDebugString] %s\n", message.c_str());
|
||||
regs[0] = SVCResult::Success;
|
||||
regs[0] = Result::Success;
|
||||
}
|
||||
|
||||
void Kernel::getProcessID() {
|
||||
|
@ -178,11 +178,11 @@ void Kernel::getProcessID() {
|
|||
logSVC("GetProcessID(process: %s)\n", getProcessName(pid).c_str());
|
||||
|
||||
if (process == nullptr) [[unlikely]] {
|
||||
regs[0] = SVCResult::BadHandle;
|
||||
regs[0] = Result::Kernel::InvalidHandle;
|
||||
return;
|
||||
}
|
||||
|
||||
regs[0] = SVCResult::Success;
|
||||
regs[0] = Result::Success;
|
||||
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);
|
||||
|
||||
if (process == nullptr) [[unlikely]] {
|
||||
regs[0] = SVCResult::BadHandle;
|
||||
regs[0] = Result::Kernel::InvalidHandle;
|
||||
return;
|
||||
}
|
||||
|
||||
|
@ -215,7 +215,7 @@ void Kernel::getProcessInfo() {
|
|||
Helpers::panic("GetProcessInfo: unimplemented type %d", type);
|
||||
}
|
||||
|
||||
regs[0] = SVCResult::Success;
|
||||
regs[0] = Result::Success;
|
||||
}
|
||||
|
||||
// Result DuplicateHandle(Handle* out, Handle original)
|
||||
|
@ -224,7 +224,7 @@ void Kernel::duplicateHandle() {
|
|||
logSVC("DuplicateHandle(handle = %X)\n", original);
|
||||
|
||||
if (original == KernelHandles::CurrentThread) {
|
||||
regs[0] = SVCResult::Success;
|
||||
regs[0] = Result::Success;
|
||||
Handle ret = makeObject(KernelObjectType::Thread);
|
||||
objects[ret].data = &threads[currentThreadIndex];
|
||||
|
||||
|
|
|
@ -36,7 +36,7 @@ static constexpr bool isAligned(u32 value) {
|
|||
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)
|
||||
// This has a weird ABI documented here https://www.3dbrew.org/wiki/Kernel_ABI
|
||||
// TODO: Does this need to write to outaddr?
|
||||
|
@ -64,7 +64,7 @@ void Kernel::controlMemory() {
|
|||
if (!isAligned(addr0) || !isAligned(addr1) || !isAligned(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",
|
||||
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);
|
||||
}
|
||||
|
||||
regs[0] = SVCResult::Success;
|
||||
regs[0] = Result::Success;
|
||||
}
|
||||
|
||||
// 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);
|
||||
|
||||
const auto info = mem.queryMemory(addr);
|
||||
regs[0] = SVCResult::Success;
|
||||
regs[0] = Result::Success;
|
||||
regs[1] = info.baseAddr;
|
||||
regs[2] = info.size;
|
||||
regs[3] = info.perms;
|
||||
|
@ -110,7 +110,7 @@ void Kernel::queryMemory() {
|
|||
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() {
|
||||
const Handle block = regs[0];
|
||||
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");
|
||||
}
|
||||
|
||||
regs[0] = SVCResult::Success;
|
||||
regs[0] = Result::Success;
|
||||
}
|
||||
|
||||
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
|
||||
if (!isAligned(size)) {
|
||||
regs[0] = SVCResult::UnalignedSize;
|
||||
regs[0] = Result::OS::MisalignedSize;
|
||||
return;
|
||||
}
|
||||
|
||||
// Throw error if one of the permissions is not valid
|
||||
if (!isPermValid(myPermission) || !isPermValid(otherPermission)) {
|
||||
regs[0] = SVCResult::InvalidCombination;
|
||||
regs[0] = Result::OS::InvalidCombination;
|
||||
return;
|
||||
}
|
||||
|
||||
|
@ -199,6 +199,6 @@ void Kernel::createMemoryBlock() {
|
|||
if (myPermission == MemoryPermissions::DontCare) myPermission = MemoryPermissions::ReadWrite;
|
||||
if (otherPermission == MemoryPermissions::DontCare) otherPermission = MemoryPermissions::ReadWrite;
|
||||
|
||||
regs[0] = SVCResult::Success;
|
||||
regs[0] = Result::Success;
|
||||
regs[1] = makeMemoryBlock(addr, size, myPermission, otherPermission);
|
||||
}
|
|
@ -43,7 +43,7 @@ void Kernel::connectToPort() {
|
|||
|
||||
if (port.size() > Port::maxNameLen) {
|
||||
Helpers::panic("ConnectToPort: Port name too long\n");
|
||||
regs[0] = SVCResult::PortNameTooLong;
|
||||
regs[0] = Result::OS::PortNameTooLong;
|
||||
return;
|
||||
}
|
||||
|
||||
|
@ -51,7 +51,7 @@ void Kernel::connectToPort() {
|
|||
std::optional<Handle> optionalHandle = getPortHandle(port.c_str());
|
||||
if (!optionalHandle.has_value()) [[unlikely]] {
|
||||
Helpers::panic("ConnectToPort: Port doesn't exist\n");
|
||||
regs[0] = SVCResult::ObjectNotFound;
|
||||
regs[0] = Result::Kernel::NotFound;
|
||||
return;
|
||||
}
|
||||
|
||||
|
@ -65,7 +65,7 @@ void Kernel::connectToPort() {
|
|||
// TODO: Actually create session
|
||||
Handle sessionHandle = makeSession(portHandle);
|
||||
|
||||
regs[0] = SVCResult::Success;
|
||||
regs[0] = Result::Success;
|
||||
regs[1] = sessionHandle;
|
||||
}
|
||||
|
||||
|
@ -80,7 +80,7 @@ void Kernel::sendSyncRequest() {
|
|||
if (KernelHandles::isServiceHandle(handle)) {
|
||||
// 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!!
|
||||
regs[0] = SVCResult::Success;
|
||||
regs[0] = Result::Success;
|
||||
serviceManager.sendCommandToService(messagePointer, handle);
|
||||
return;
|
||||
}
|
||||
|
@ -88,7 +88,7 @@ void Kernel::sendSyncRequest() {
|
|||
// Check if our sync request is targetting a file instead of a service
|
||||
bool isFileOperation = getObject(handle, KernelObjectType::File) != nullptr;
|
||||
if (isFileOperation) {
|
||||
regs[0] = SVCResult::Success; // r0 goes first here too
|
||||
regs[0] = Result::Success; // r0 goes first here too
|
||||
handleFileOperation(messagePointer, handle);
|
||||
return;
|
||||
}
|
||||
|
@ -96,7 +96,7 @@ void Kernel::sendSyncRequest() {
|
|||
// Check if our sync request is targetting a directory instead of a service
|
||||
bool isDirectoryOperation = getObject(handle, KernelObjectType::Directory) != nullptr;
|
||||
if (isDirectoryOperation) {
|
||||
regs[0] = SVCResult::Success; // r0 goes first here too
|
||||
regs[0] = Result::Success; // r0 goes first here too
|
||||
handleDirectoryOperation(messagePointer, handle);
|
||||
return;
|
||||
}
|
||||
|
@ -105,7 +105,7 @@ void Kernel::sendSyncRequest() {
|
|||
const auto session = getObject(handle, KernelObjectType::Session);
|
||||
if (session == nullptr) [[unlikely]] {
|
||||
Helpers::panic("SendSyncRequest: Invalid handle");
|
||||
regs[0] = SVCResult::BadHandle;
|
||||
regs[0] = Result::Kernel::InvalidHandle;
|
||||
return;
|
||||
}
|
||||
|
||||
|
@ -113,10 +113,10 @@ void Kernel::sendSyncRequest() {
|
|||
const Handle portHandle = sessionData->portHandle;
|
||||
|
||||
if (portHandle == srvHandle) { // Special-case SendSyncRequest targetting the "srv: port"
|
||||
regs[0] = SVCResult::Success;
|
||||
regs[0] = Result::Success;
|
||||
serviceManager.handleSyncRequest(messagePointer);
|
||||
} else if (portHandle == errorPortHandle) { // Special-case "err:f" for juicy logs too
|
||||
regs[0] = SVCResult::Success;
|
||||
regs[0] = Result::Success;
|
||||
handleErrorSyncRequest(messagePointer);
|
||||
} else {
|
||||
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());
|
||||
|
||||
if (process == nullptr) [[unlikely]] {
|
||||
regs[0] = SVCResult::BadHandle;
|
||||
regs[0] = Result::Kernel::InvalidHandle;
|
||||
return;
|
||||
}
|
||||
|
||||
const auto processData = static_cast<Process*>(process->data);
|
||||
|
||||
regs[0] = SVCResult::Success;
|
||||
regs[0] = Result::Success;
|
||||
regs[1] = processData->limits.handle;
|
||||
}
|
||||
|
||||
|
@ -29,7 +29,7 @@ void Kernel::getResourceLimitLimitValues() {
|
|||
|
||||
const KernelObject* limit = getObject(resourceLimit, KernelObjectType::ResourceLimit);
|
||||
if (limit == nullptr) [[unlikely]] {
|
||||
regs[0] = SVCResult::BadHandle;
|
||||
regs[0] = Result::Kernel::InvalidHandle;
|
||||
return;
|
||||
}
|
||||
|
||||
|
@ -46,7 +46,7 @@ void Kernel::getResourceLimitLimitValues() {
|
|||
count--;
|
||||
}
|
||||
|
||||
regs[0] = SVCResult::Success;
|
||||
regs[0] = Result::Success;
|
||||
}
|
||||
|
||||
// Result GetResourceLimitCurrentValues(s64* values, Handle resourceLimit, LimitableResource* names, s32 nameCount)
|
||||
|
@ -59,7 +59,7 @@ void Kernel::getResourceLimitCurrentValues() {
|
|||
|
||||
const KernelObject* limit = getObject(resourceLimit, KernelObjectType::ResourceLimit);
|
||||
if (limit == nullptr) [[unlikely]] {
|
||||
regs[0] = SVCResult::BadHandle;
|
||||
regs[0] = Result::Kernel::InvalidHandle;
|
||||
return;
|
||||
}
|
||||
|
||||
|
@ -75,7 +75,7 @@ void Kernel::getResourceLimitCurrentValues() {
|
|||
count--;
|
||||
}
|
||||
|
||||
regs[0] = SVCResult::Success;
|
||||
regs[0] = Result::Success;
|
||||
}
|
||||
|
||||
s32 Kernel::getCurrentResourceValue(const KernelObject* limit, u32 resourceName) {
|
||||
|
|
|
@ -1,3 +1,4 @@
|
|||
#include <bit>
|
||||
#include <cassert>
|
||||
#include <cstring>
|
||||
|
||||
|
@ -49,7 +50,7 @@ void Kernel::sortThreads() {
|
|||
bool Kernel::canThreadRun(const Thread& t) {
|
||||
if (t.status == ThreadStatus::Ready) {
|
||||
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) {
|
||||
const u64 elapsedTicks = cpu.getTicks() - t.sleepTick;
|
||||
|
||||
|
@ -83,7 +84,7 @@ std::optional<int> Kernel::getNextThread() {
|
|||
|
||||
void Kernel::switchToNextThread() {
|
||||
std::optional<int> newThreadIndex = getNextThread();
|
||||
|
||||
|
||||
if (!newThreadIndex.has_value()) {
|
||||
log("Kernel tried to switch to the next thread but none found. Switching to random thread\n");
|
||||
assert(aliveThreadCount != 0);
|
||||
|
@ -103,7 +104,7 @@ void Kernel::switchToNextThread() {
|
|||
// See if there;s a higher priority, ready thread and switch to that
|
||||
void Kernel::rescheduleThreads() {
|
||||
std::optional<int> newThreadIndex = getNextThread();
|
||||
|
||||
|
||||
if (newThreadIndex.has_value() && newThreadIndex.value() != currentThreadIndex) {
|
||||
threads[currentThreadIndex].status = ThreadStatus::Ready;
|
||||
switchThread(newThreadIndex.value());
|
||||
|
@ -275,12 +276,12 @@ int Kernel::wakeupOneThread(u64 waitlist, Handle handle) {
|
|||
switch (t.status) {
|
||||
case ThreadStatus::WaitSync1:
|
||||
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;
|
||||
|
||||
case ThreadStatus::WaitSyncAny:
|
||||
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
|
||||
for (size_t i = 0; i < t.waitList.size(); i++) {
|
||||
|
@ -310,12 +311,12 @@ void Kernel::wakeupAllThreads(u64 waitlist, Handle handle) {
|
|||
switch (t.status) {
|
||||
case ThreadStatus::WaitSync1:
|
||||
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;
|
||||
|
||||
case ThreadStatus::WaitSyncAny:
|
||||
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
|
||||
for (size_t i = 0; i < t.waitList.size(); i++) {
|
||||
|
@ -354,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() {
|
||||
u32 priority = regs[0];
|
||||
u32 entrypoint = regs[1];
|
||||
|
@ -367,11 +368,11 @@ void Kernel::createThread() {
|
|||
|
||||
if (priority > 0x3F) [[unlikely]] {
|
||||
Helpers::panic("Created thread with bad priority value %X", priority);
|
||||
regs[0] = SVCResult::BadThreadPriority;
|
||||
regs[0] = Result::OS::OutOfRange;
|
||||
return;
|
||||
}
|
||||
|
||||
regs[0] = SVCResult::Success;
|
||||
regs[0] = Result::Success;
|
||||
regs[1] = makeThread(entrypoint, initialSP, priority, id, arg, ThreadStatus::Ready);
|
||||
rescheduleThreads();
|
||||
}
|
||||
|
@ -381,7 +382,7 @@ void Kernel::svcSleepThread() {
|
|||
const s64 ns = s64(u64(regs[0]) | (u64(regs[1]) << 32));
|
||||
//logSVC("SleepThread(ns = %lld)\n", ns);
|
||||
|
||||
regs[0] = SVCResult::Success;
|
||||
regs[0] = Result::Success;
|
||||
sleepThread(ns);
|
||||
}
|
||||
|
||||
|
@ -390,18 +391,18 @@ void Kernel::getThreadID() {
|
|||
logSVC("GetThreadID(handle = %X)\n", handle);
|
||||
|
||||
if (handle == KernelHandles::CurrentThread) {
|
||||
regs[0] = SVCResult::Success;
|
||||
regs[0] = Result::Success;
|
||||
regs[1] = currentThreadIndex;
|
||||
return;
|
||||
}
|
||||
|
||||
const auto thread = getObject(handle, KernelObjectType::Thread);
|
||||
if (thread == nullptr) [[unlikely]] {
|
||||
regs[0] = SVCResult::BadHandle;
|
||||
regs[0] = Result::Kernel::InvalidHandle;
|
||||
return;
|
||||
}
|
||||
|
||||
regs[0] = SVCResult::Success;
|
||||
regs[0] = Result::Success;
|
||||
regs[1] = thread->getData<Thread>()->index;
|
||||
}
|
||||
|
||||
|
@ -410,14 +411,14 @@ void Kernel::getThreadPriority() {
|
|||
logSVC("GetThreadPriority (handle = %X)\n", handle);
|
||||
|
||||
if (handle == KernelHandles::CurrentThread) {
|
||||
regs[0] = SVCResult::Success;
|
||||
regs[0] = Result::Success;
|
||||
regs[1] = threads[currentThreadIndex].priority;
|
||||
} else {
|
||||
auto object = getObject(handle, KernelObjectType::Thread);
|
||||
if (object == nullptr) [[unlikely]] {
|
||||
regs[0] = SVCResult::BadHandle;
|
||||
regs[0] = Result::Kernel::InvalidHandle;
|
||||
} else {
|
||||
regs[0] = SVCResult::Success;
|
||||
regs[0] = Result::Success;
|
||||
regs[1] = object->getData<Thread>()->priority;
|
||||
}
|
||||
}
|
||||
|
@ -429,20 +430,20 @@ void Kernel::setThreadPriority() {
|
|||
logSVC("SetThreadPriority (handle = %X, priority = %X)\n", handle, priority);
|
||||
|
||||
if (priority > 0x3F) {
|
||||
regs[0] = SVCResult::BadThreadPriority;
|
||||
regs[0] = Result::OS::OutOfRange;
|
||||
return;
|
||||
}
|
||||
|
||||
if (handle == KernelHandles::CurrentThread) {
|
||||
regs[0] = SVCResult::Success;
|
||||
regs[0] = Result::Success;
|
||||
threads[currentThreadIndex].priority = priority;
|
||||
} else {
|
||||
auto object = getObject(handle, KernelObjectType::Thread);
|
||||
if (object == nullptr) [[unlikely]] {
|
||||
regs[0] = SVCResult::BadHandle;
|
||||
regs[0] = Result::Kernel::InvalidHandle;
|
||||
return;
|
||||
} else {
|
||||
regs[0] = SVCResult::Success;
|
||||
regs[0] = Result::Success;
|
||||
object->getData<Thread>()->priority = priority;
|
||||
}
|
||||
}
|
||||
|
@ -478,7 +479,7 @@ void Kernel::svcCreateMutex() {
|
|||
bool locked = regs[1] != 0;
|
||||
logSVC("CreateMutex (locked = %s)\n", locked ? "yes" : "no");
|
||||
|
||||
regs[0] = SVCResult::Success;
|
||||
regs[0] = Result::Success;
|
||||
regs[1] = makeMutex(locked);
|
||||
}
|
||||
|
||||
|
@ -489,18 +490,18 @@ void Kernel::svcReleaseMutex() {
|
|||
const auto object = getObject(handle, KernelObjectType::Mutex);
|
||||
if (object == nullptr) [[unlikely]] {
|
||||
Helpers::panic("Tried to release non-existent mutex");
|
||||
regs[0] = SVCResult::BadHandle;
|
||||
regs[0] = Result::Kernel::InvalidHandle;
|
||||
return;
|
||||
}
|
||||
|
||||
Mutex* moo = object->getData<Mutex>();
|
||||
// A thread can't release a mutex it does not own
|
||||
if (!moo->locked || moo->ownerThread != currentThreadIndex) {
|
||||
regs[0] = SVCResult::InvalidMutexRelease;
|
||||
regs[0] = Result::Kernel::InvalidMutexRelease;
|
||||
return;
|
||||
}
|
||||
|
||||
regs[0] = SVCResult::Success;
|
||||
regs[0] = Result::Success;
|
||||
releaseMutex(moo);
|
||||
}
|
||||
|
||||
|
@ -515,7 +516,7 @@ void Kernel::svcCreateSemaphore() {
|
|||
if (initialCount < 0 || maxCount < 0)
|
||||
Helpers::panic("CreateSemaphore: Negative count value");
|
||||
|
||||
regs[0] = SVCResult::Success;
|
||||
regs[0] = Result::Success;
|
||||
regs[1] = makeSemaphore(initialCount, maxCount);
|
||||
}
|
||||
|
||||
|
@ -527,7 +528,7 @@ void Kernel::svcReleaseSemaphore() {
|
|||
const auto object = getObject(handle, KernelObjectType::Semaphore);
|
||||
if (object == nullptr) [[unlikely]] {
|
||||
Helpers::panic("Tried to release non-existent semaphore");
|
||||
regs[0] = SVCResult::BadHandle;
|
||||
regs[0] = Result::Kernel::InvalidHandle;
|
||||
return;
|
||||
}
|
||||
|
||||
|
@ -539,7 +540,7 @@ void Kernel::svcReleaseSemaphore() {
|
|||
Helpers::panic("ReleaseSemaphore: Release count too high");
|
||||
|
||||
// Write success and old available count to r0 and r1 respectively
|
||||
regs[0] = SVCResult::Success;
|
||||
regs[0] = Result::Success;
|
||||
regs[1] = s->availableCount;
|
||||
// Bump available count
|
||||
s->availableCount += releaseCount;
|
||||
|
|
|
@ -11,45 +11,221 @@ using namespace Helpers;
|
|||
const char* vertexShader = R"(
|
||||
#version 410 core
|
||||
|
||||
layout (location = 0) in vec4 coords;
|
||||
layout (location = 1) in vec4 vertexColour;
|
||||
layout (location = 2) in vec2 inUVs_texture0;
|
||||
layout (location = 0) in vec4 a_coords;
|
||||
layout (location = 1) in vec4 a_vertexColour;
|
||||
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 vec2 tex0_UVs;
|
||||
out vec4 v_colour;
|
||||
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() {
|
||||
gl_Position = coords;
|
||||
colour = vertexColour;
|
||||
gl_Position = a_coords;
|
||||
v_colour = a_vertexColour;
|
||||
|
||||
// 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"(
|
||||
#version 410 core
|
||||
|
||||
in vec4 colour;
|
||||
in vec2 tex0_UVs;
|
||||
in vec4 v_colour;
|
||||
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;
|
||||
|
||||
uniform uint u_alphaControl;
|
||||
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
|
||||
uniform float u_depthScale;
|
||||
uniform float u_depthOffset;
|
||||
uniform bool u_depthmapEnable;
|
||||
|
||||
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() {
|
||||
if ((u_textureConfig & 1u) != 0) { // Render texture 0 if enabled
|
||||
fragColour = texture(u_tex0, tex0_UVs);
|
||||
} else {
|
||||
fragColour = colour;
|
||||
vec2 tex2UV = (u_textureConfig & (1u << 13)) != 0u ? v_texcoord1 : v_texcoord2;
|
||||
|
||||
// TODO: what do invalid sources and disabled textures read as?
|
||||
// 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]
|
||||
|
@ -119,10 +295,10 @@ const char* displayVertexShader = R"(
|
|||
vec2(1.0, 0.0), // Bottom-right
|
||||
vec2(0.0, 1.0), // Top-left
|
||||
vec2(0.0, 0.0) // Bottom-left
|
||||
);
|
||||
);
|
||||
|
||||
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
|
||||
colourBufferLoc = 0;
|
||||
colourBufferFormat = ColourBuffer::Formats::RGBA8;
|
||||
colourBufferFormat = PICA::ColorFmt::RGBA8;
|
||||
|
||||
depthBufferLoc = 0;
|
||||
depthBufferFormat = DepthBuffer::Formats::Depth16;
|
||||
depthBufferFormat = PICA::DepthFmt::Depth16;
|
||||
|
||||
if (triangleProgram.exists()) {
|
||||
const auto oldProgram = OpenGL::getProgram();
|
||||
|
@ -179,10 +355,22 @@ void Renderer::initGraphicsContext() {
|
|||
alphaControlLoc = OpenGL::uniformLocation(triangleProgram, "u_alphaControl");
|
||||
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");
|
||||
depthOffsetLoc = OpenGL::uniformLocation(triangleProgram, "u_depthOffset");
|
||||
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 fragDisplay(displayFragmentShader, OpenGL::Fragment);
|
||||
|
@ -202,9 +390,18 @@ void Renderer::initGraphicsContext() {
|
|||
// Colour attribute
|
||||
vao.setAttributeFloat<float>(1, 4, sizeof(Vertex), offsetof(Vertex, colour));
|
||||
vao.enableAttribute(1);
|
||||
// UV attribute
|
||||
vao.setAttributeFloat<float>(2, 2, sizeof(Vertex), offsetof(Vertex, UVs));
|
||||
// UV 0 attribute
|
||||
vao.setAttributeFloat<float>(2, 2, sizeof(Vertex), offsetof(Vertex, texcoord0));
|
||||
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();
|
||||
dummyVAO.create();
|
||||
|
@ -239,7 +436,7 @@ void Renderer::initGraphicsContext() {
|
|||
|
||||
// Set up the OpenGL blending context to match the emulated PICA
|
||||
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)
|
||||
static constexpr std::array<GLenum, 8> blendingEquations = {
|
||||
|
@ -259,7 +456,7 @@ void Renderer::setupBlending() {
|
|||
OpenGL::enableBlend();
|
||||
|
||||
// Get blending equations
|
||||
const u32 blendControl = regs[PICAInternalRegs::BlendFunc];
|
||||
const u32 blendControl = regs[PICA::InternalRegs::BlendFunc];
|
||||
const u32 rgbEquation = blendControl & 0x7;
|
||||
const u32 alphaEquation = getBits<8, 3>(blendControl);
|
||||
|
||||
|
@ -269,7 +466,7 @@ void Renderer::setupBlending() {
|
|||
const u32 alphaSourceFunc = getBits<24, 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 g = getBits<8, 8>(constantColor);
|
||||
const u32 b = getBits<16, 8>(constantColor);
|
||||
|
@ -282,7 +479,82 @@ void Renderer::setupBlending() {
|
|||
}
|
||||
}
|
||||
|
||||
void Renderer::drawVertices(OpenGL::Primitives primType, std::span<const Vertex> vertices) {
|
||||
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
|
||||
|
@ -293,7 +565,7 @@ void Renderer::drawVertices(OpenGL::Primitives primType, std::span<const Vertex>
|
|||
triangleProgram.use();
|
||||
|
||||
// Adjust alpha test if necessary
|
||||
const u32 alphaControl = regs[PICAInternalRegs::AlphaTestConfig];
|
||||
const u32 alphaControl = regs[PICA::InternalRegs::AlphaTestConfig];
|
||||
if (alphaControl != oldAlphaControl) {
|
||||
oldAlphaControl = alphaControl;
|
||||
glUniform1ui(alphaControlLoc, alphaControl);
|
||||
|
@ -303,7 +575,7 @@ void Renderer::drawVertices(OpenGL::Primitives primType, std::span<const Vertex>
|
|||
OpenGL::Framebuffer poop = getColourFBO();
|
||||
poop.bind(OpenGL::DrawAndReadFramebuffer);
|
||||
|
||||
const u32 depthControl = regs[PICAInternalRegs::DepthAndColorMask];
|
||||
const u32 depthControl = regs[PICA::InternalRegs::DepthAndColorMask];
|
||||
const bool depthEnable = depthControl & 1;
|
||||
const bool depthWriteEnable = getBit<12>(depthControl);
|
||||
const int depthFunc = getBits<4, 3>(depthControl);
|
||||
|
@ -314,9 +586,9 @@ void Renderer::drawVertices(OpenGL::Primitives primType, std::span<const Vertex>
|
|||
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 depthOffset = f24::fromRaw(regs[PICAInternalRegs::DepthOffset] & 0xffffff).toFloat32();
|
||||
const bool depthMapEnable = regs[PICAInternalRegs::DepthmapEnable] & 1;
|
||||
const float depthScale = f24::fromRaw(regs[PICA::InternalRegs::DepthScale] & 0xffffff).toFloat32();
|
||||
const float depthOffset = f24::fromRaw(regs[PICA::InternalRegs::DepthOffset] & 0xffffff).toFloat32();
|
||||
const bool depthMapEnable = regs[PICA::InternalRegs::DepthmapEnable] & 1;
|
||||
|
||||
// Update depth uniforms
|
||||
if (oldDepthScale != depthScale) {
|
||||
|
@ -334,30 +606,12 @@ void Renderer::drawVertices(OpenGL::Primitives primType, std::span<const Vertex>
|
|||
glUniform1i(depthmapEnableLoc, depthMapEnable);
|
||||
}
|
||||
|
||||
// Hack for rendering texture 1
|
||||
if (regs[0x80] & 1) {
|
||||
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);
|
||||
}
|
||||
setupTextureEnvState();
|
||||
bindTexturesToSlots();
|
||||
|
||||
// TODO: Actually use this
|
||||
float viewportWidth = f24::fromRaw(regs[PICAInternalRegs::ViewportWidth] & 0xffffff).toFloat32() * 2.0;
|
||||
float viewportHeight = f24::fromRaw(regs[PICAInternalRegs::ViewportHeight] & 0xffffff).toFloat32() * 2.0;
|
||||
float viewportWidth = f24::fromRaw(regs[PICA::InternalRegs::ViewportWidth] & 0xffffff).toFloat32() * 2.0;
|
||||
float viewportHeight = f24::fromRaw(regs[PICA::InternalRegs::ViewportHeight] & 0xffffff).toFloat32() * 2.0;
|
||||
OpenGL::setViewport(viewportWidth, viewportHeight);
|
||||
|
||||
// Note: The code below must execute after we've bound the colour buffer & its framebuffer
|
||||
|
@ -379,7 +633,7 @@ void Renderer::drawVertices(OpenGL::Primitives primType, std::span<const Vertex>
|
|||
}
|
||||
|
||||
vbo.bufferVertsSub(vertices);
|
||||
OpenGL::draw(primType, vertices.size());
|
||||
OpenGL::draw(primitiveTopology, vertices.size());
|
||||
}
|
||||
|
||||
constexpr u32 topScreenBuffer = 0x1f000000;
|
||||
|
@ -441,8 +695,8 @@ void Renderer::bindDepthBuffer() {
|
|||
tex = depthBufferCache.add(sampleBuffer).texture.m_handle;
|
||||
}
|
||||
|
||||
if (DepthBuffer::Formats::Depth24Stencil8 != depthBufferFormat) Helpers::panic("TODO: Should we remove stencil attachment?");
|
||||
auto attachment = depthBufferFormat == DepthBuffer::Formats::Depth24Stencil8 ? GL_DEPTH_STENCIL_ATTACHMENT : GL_DEPTH_ATTACHMENT;
|
||||
if (PICA::DepthFmt::Depth24Stencil8 != depthBufferFormat) Helpers::panic("TODO: Should we remove stencil attachment?");
|
||||
auto attachment = depthBufferFormat == PICA::DepthFmt::Depth24Stencil8 ? GL_DEPTH_STENCIL_ATTACHMENT : GL_DEPTH_ATTACHMENT;
|
||||
glFramebufferTexture2D(GL_FRAMEBUFFER, attachment, GL_TEXTURE_2D, tex, 0);
|
||||
}
|
||||
|
||||
|
|
|
@ -43,34 +43,34 @@ u64 Texture::sizeInBytes() {
|
|||
u64 pixelCount = u64(size.x()) * u64(size.y());
|
||||
|
||||
switch (format) {
|
||||
case Formats::RGBA8: // 4 bytes per pixel
|
||||
case PICA::TextureFmt::RGBA8: // 4 bytes per pixel
|
||||
return pixelCount * 4;
|
||||
|
||||
case Formats::RGB8: // 3 bytes per pixel
|
||||
case PICA::TextureFmt::RGB8: // 3 bytes per pixel
|
||||
return pixelCount * 3;
|
||||
|
||||
case Formats::RGBA5551: // 2 bytes per pixel
|
||||
case Formats::RGB565:
|
||||
case Formats::RGBA4:
|
||||
case Formats::RG8:
|
||||
case Formats::IA8:
|
||||
case PICA::TextureFmt::RGBA5551: // 2 bytes per pixel
|
||||
case PICA::TextureFmt::RGB565:
|
||||
case PICA::TextureFmt::RGBA4:
|
||||
case PICA::TextureFmt::RG8:
|
||||
case PICA::TextureFmt::IA8:
|
||||
return pixelCount * 2;
|
||||
|
||||
case Formats::A8: // 1 byte per pixel
|
||||
case Formats::I8:
|
||||
case Formats::IA4:
|
||||
case PICA::TextureFmt::A8: // 1 byte per pixel
|
||||
case PICA::TextureFmt::I8:
|
||||
case PICA::TextureFmt::IA4:
|
||||
return pixelCount;
|
||||
|
||||
case Formats::I4: // 4 bits per pixel
|
||||
case Formats::A4:
|
||||
case PICA::TextureFmt::I4: // 4 bits per pixel
|
||||
case PICA::TextureFmt::A4:
|
||||
return pixelCount / 2;
|
||||
|
||||
case Formats::ETC1: // Compressed formats
|
||||
case Formats::ETC1A4: {
|
||||
case PICA::TextureFmt::ETC1: // Compressed formats
|
||||
case PICA::TextureFmt::ETC1A4: {
|
||||
// Number of 4x4 tiles
|
||||
const u64 tileCount = pixelCount / 16;
|
||||
// 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;
|
||||
}
|
||||
|
||||
|
@ -111,9 +111,9 @@ u32 Texture::getSwizzledOffset_4bpp(u32 u, u32 v, u32 width) {
|
|||
// Get the texel at position (u, v)
|
||||
// fmt: format 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) {
|
||||
case Formats::RGBA4: {
|
||||
case PICA::TextureFmt::RGBA4: {
|
||||
u32 offset = getSwizzledOffset(u, v, size.u(), 2);
|
||||
auto ptr = static_cast<const u8*>(data);
|
||||
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;
|
||||
}
|
||||
|
||||
case Formats::RGBA5551: {
|
||||
case PICA::TextureFmt::RGBA5551: {
|
||||
u32 offset = getSwizzledOffset(u, v, size.u(), 2);
|
||||
auto ptr = static_cast<const u8*>(data);
|
||||
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;
|
||||
}
|
||||
|
||||
case Formats::RGB565: {
|
||||
case PICA::TextureFmt::RGB565: {
|
||||
u32 offset = getSwizzledOffset(u, v, size.u(), 2);
|
||||
auto ptr = static_cast<const u8*>(data);
|
||||
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;
|
||||
}
|
||||
|
||||
case Formats::RG8: {
|
||||
case PICA::TextureFmt::RG8: {
|
||||
u32 offset = getSwizzledOffset(u, v, size.u(), 2);
|
||||
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;
|
||||
}
|
||||
|
||||
case Formats::RGB8: {
|
||||
case PICA::TextureFmt::RGB8: {
|
||||
u32 offset = getSwizzledOffset(u, v, size.u(), 3);
|
||||
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;
|
||||
}
|
||||
|
||||
case Formats::RGBA8: {
|
||||
case PICA::TextureFmt::RGBA8: {
|
||||
u32 offset = getSwizzledOffset(u, v, size.u(), 4);
|
||||
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;
|
||||
}
|
||||
|
||||
case Formats::IA4: {
|
||||
case PICA::TextureFmt::IA4: {
|
||||
u32 offset = getSwizzledOffset(u, v, size.u(), 1);
|
||||
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;
|
||||
}
|
||||
|
||||
case Formats::A4: {
|
||||
case PICA::TextureFmt::A4: {
|
||||
u32 offset = getSwizzledOffset_4bpp(u, v, size.u());
|
||||
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;
|
||||
}
|
||||
|
||||
case Formats::A8: {
|
||||
case PICA::TextureFmt::A8: {
|
||||
u32 offset = getSwizzledOffset(u, v, size.u(), 1);
|
||||
auto ptr = static_cast<const u8*>(data);
|
||||
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;
|
||||
}
|
||||
|
||||
case Formats::I4: {
|
||||
case PICA::TextureFmt::I4: {
|
||||
u32 offset = getSwizzledOffset_4bpp(u, v, size.u());
|
||||
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;
|
||||
}
|
||||
|
||||
case Formats::I8: {
|
||||
case PICA::TextureFmt::I8: {
|
||||
u32 offset = getSwizzledOffset(u, v, size.u(), 1);
|
||||
auto ptr = static_cast<const u8*>(data);
|
||||
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;
|
||||
}
|
||||
|
||||
case Formats::IA8: {
|
||||
case PICA::TextureFmt::IA8: {
|
||||
u32 offset = getSwizzledOffset(u, v, size.u(), 2);
|
||||
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;
|
||||
}
|
||||
|
||||
case Formats::ETC1: return getTexelETC(false, u, v, size.u(), data);
|
||||
case Formats::ETC1A4: return getTexelETC(true, u, v, size.u(), data);
|
||||
case PICA::TextureFmt::ETC1: return getTexelETC(false, u, v, size.u(), data);
|
||||
case PICA::TextureFmt::ETC1A4: return getTexelETC(true, u, v, size.u(), data);
|
||||
|
||||
default:
|
||||
Helpers::panic("[Texture::DecodeTexel] Unimplemented format = %d", static_cast<int>(fmt));
|
||||
|
@ -271,24 +271,4 @@ void Texture::decodeTexture(const void* data) {
|
|||
|
||||
texture.bind();
|
||||
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::handleSyncRequest(u32 messagePointer) {
|
||||
|
|
|
@ -7,12 +7,6 @@ namespace ACTCommands {
|
|||
};
|
||||
}
|
||||
|
||||
namespace Result {
|
||||
enum : u32 {
|
||||
Success = 0,
|
||||
};
|
||||
}
|
||||
|
||||
void ACTService::reset() {}
|
||||
|
||||
void ACTService::handleSyncRequest(u32 messagePointer) {
|
||||
|
|
|
@ -8,12 +8,6 @@ namespace AMCommands {
|
|||
};
|
||||
}
|
||||
|
||||
namespace Result {
|
||||
enum : u32 {
|
||||
Success = 0,
|
||||
};
|
||||
}
|
||||
|
||||
void AMService::reset() {}
|
||||
|
||||
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
|
||||
namespace APTTransitions {
|
||||
enum : u32 {
|
||||
|
@ -154,7 +148,7 @@ void APTService::inquireNotification(u32 messagePointer) {
|
|||
|
||||
mem.write32(messagePointer, IPC::responseHeader(0xB, 2, 0));
|
||||
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) {
|
||||
|
|
|
@ -14,12 +14,6 @@ namespace BOSSCommands {
|
|||
};
|
||||
}
|
||||
|
||||
namespace Result {
|
||||
enum : u32 {
|
||||
Success = 0,
|
||||
};
|
||||
}
|
||||
|
||||
void BOSSService::reset() {
|
||||
optoutFlag = 0;
|
||||
}
|
||||
|
|
|
@ -8,12 +8,6 @@ namespace CAMCommands {
|
|||
};
|
||||
}
|
||||
|
||||
namespace Result {
|
||||
enum : u32 {
|
||||
Success = 0,
|
||||
};
|
||||
}
|
||||
|
||||
void CAMService::reset() {}
|
||||
|
||||
void CAMService::handleSyncRequest(u32 messagePointer) {
|
||||
|
|
|
@ -8,12 +8,6 @@ namespace CECDCommands {
|
|||
};
|
||||
}
|
||||
|
||||
namespace Result {
|
||||
enum : u32 {
|
||||
Success = 0,
|
||||
};
|
||||
}
|
||||
|
||||
void CECDService::reset() {
|
||||
infoEvent = std::nullopt;
|
||||
}
|
||||
|
|
|
@ -16,12 +16,6 @@ namespace CFGCommands {
|
|||
};
|
||||
}
|
||||
|
||||
namespace Result {
|
||||
enum : u32 {
|
||||
Success = 0,
|
||||
};
|
||||
}
|
||||
|
||||
void CFGService::reset() {}
|
||||
|
||||
void CFGService::handleSyncRequest(u32 messagePointer) {
|
||||
|
@ -79,7 +73,7 @@ void CFGService::getConfigInfoBlk2(u32 messagePointer) {
|
|||
};
|
||||
|
||||
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
|
||||
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)
|
||||
log("Read EULA info\n");
|
||||
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
|
||||
constexpr size_t languageCount = 16;
|
||||
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
|
||||
|
||||
for (int i = 0; i < languageCount; i++) {
|
||||
u32 pointer = output + i * nameSize;
|
||||
u32 pointer = output + i * nameSize;
|
||||
writeStringU16(pointer, name);
|
||||
}
|
||||
} 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::handleSyncRequest(u32 messagePointer) {
|
||||
|
|
|
@ -25,7 +25,6 @@ namespace DSPCommands {
|
|||
|
||||
namespace Result {
|
||||
enum : u32 {
|
||||
Success = 0,
|
||||
HeadphonesNotInserted = 0,
|
||||
HeadphonesInserted = 1
|
||||
};
|
||||
|
@ -215,7 +214,7 @@ void DSPService::registerInterruptEvents(u32 messagePointer) {
|
|||
const u32 channel = mem.read32(messagePointer + 8);
|
||||
const u32 eventHandle = mem.read32(messagePointer + 16);
|
||||
log("DSP::RegisterInterruptEvents (interrupt = %d, channel = %d, event = %d)\n", interrupt, channel, eventHandle);
|
||||
|
||||
|
||||
// The event handle being 0 means we're removing an event
|
||||
if (eventHandle == 0) {
|
||||
DSPEvent& e = getEventRef(interrupt, channel); // Get event
|
||||
|
|
|
@ -17,12 +17,6 @@ namespace FRDCommands {
|
|||
};
|
||||
}
|
||||
|
||||
namespace Result {
|
||||
enum : u32 {
|
||||
Success = 0,
|
||||
};
|
||||
}
|
||||
|
||||
void FRDService::reset() {}
|
||||
|
||||
void FRDService::handleSyncRequest(u32 messagePointer) {
|
||||
|
@ -91,7 +85,7 @@ void FRDService::getMyProfile(u32 messagePointer) {
|
|||
mem.write32(messagePointer + 4, Result::Success);
|
||||
|
||||
// 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 + 10, 2); // Area (this should be Washington)
|
||||
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() {
|
||||
priority = 0;
|
||||
}
|
||||
|
@ -96,15 +88,15 @@ std::optional<Handle> FSService::openFileHandle(ArchiveBase* archive, const FSPa
|
|||
|
||||
auto& file = kernel.getObjects()[handle];
|
||||
file.data = new FileSession(archive, path, archivePath, opened.value());
|
||||
|
||||
|
||||
return handle;
|
||||
} else {
|
||||
return std::nullopt;
|
||||
}
|
||||
}
|
||||
|
||||
Rust::Result<Handle, FSResult> FSService::openDirectoryHandle(ArchiveBase* archive, const FSPath& path) {
|
||||
Rust::Result<DirectorySession, FSResult> opened = archive->openDirectory(path);
|
||||
Rust::Result<Handle, Result::HorizonResult> FSService::openDirectoryHandle(ArchiveBase* archive, const FSPath& 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
|
||||
auto handle = kernel.makeObject(KernelObjectType::Directory);
|
||||
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);
|
||||
|
||||
if (archive == nullptr) [[unlikely]] {
|
||||
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()) {
|
||||
auto handle = kernel.makeObject(KernelObjectType::Archive);
|
||||
auto& archiveObject = kernel.getObjects()[handle];
|
||||
|
@ -175,7 +167,7 @@ void FSService::handleSyncRequest(u32 messagePointer) {
|
|||
void FSService::initialize(u32 messagePointer) {
|
||||
log("FS::Initialize\n");
|
||||
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
|
||||
|
@ -184,7 +176,7 @@ void FSService::initializeWithSdkVersion(u32 messagePointer) {
|
|||
log("FS::InitializeWithSDKVersion(version = %d)\n", version);
|
||||
|
||||
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) {
|
||||
|
@ -196,10 +188,10 @@ void FSService::closeArchive(u32 messagePointer) {
|
|||
|
||||
if (object == nullptr) {
|
||||
log("FSService::CloseArchive: Tried to close invalid archive %X\n", handle);
|
||||
mem.write32(messagePointer + 4, ResultCode::Failure);
|
||||
mem.write32(messagePointer + 4, Result::FailurePlaceholder);
|
||||
} else {
|
||||
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);
|
||||
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));
|
||||
if (res.isOk()) {
|
||||
mem.write32(messagePointer + 4, ResultCode::Success);
|
||||
mem.write32(messagePointer + 4, Result::Success);
|
||||
mem.write64(messagePointer + 8, res.unwrap());
|
||||
} else {
|
||||
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);
|
||||
}
|
||||
}
|
||||
|
@ -237,7 +229,7 @@ void FSService::openFile(u32 messagePointer) {
|
|||
auto archiveObject = kernel.getObject(archiveHandle, KernelObjectType::Archive);
|
||||
if (archiveObject == nullptr) [[unlikely]] {
|
||||
log("FS::OpenFile: Invalid archive handle %d\n", archiveHandle);
|
||||
mem.write32(messagePointer + 4, ResultCode::Failure);
|
||||
mem.write32(messagePointer + 4, Result::FailurePlaceholder);
|
||||
return;
|
||||
}
|
||||
|
||||
|
@ -251,9 +243,9 @@ void FSService::openFile(u32 messagePointer) {
|
|||
mem.write32(messagePointer, IPC::responseHeader(0x802, 1, 2));
|
||||
if (!handle.has_value()) {
|
||||
printf("OpenFile failed\n");
|
||||
mem.write32(messagePointer + 4, ResultCode::FileNotFound);
|
||||
mem.write32(messagePointer + 4, Result::FS::FileNotFound);
|
||||
} else {
|
||||
mem.write32(messagePointer + 4, ResultCode::Success);
|
||||
mem.write32(messagePointer + 4, Result::Success);
|
||||
mem.write32(messagePointer + 8, 0x10); // "Move handle descriptor"
|
||||
mem.write32(messagePointer + 12, handle.value());
|
||||
}
|
||||
|
@ -270,13 +262,13 @@ void FSService::createDirectory(u32 messagePointer) {
|
|||
KernelObject* archiveObject = kernel.getObject(archiveHandle, KernelObjectType::Archive);
|
||||
if (archiveObject == nullptr) [[unlikely]] {
|
||||
log("FS::CreateDirectory: Invalid archive handle %d\n", archiveHandle);
|
||||
mem.write32(messagePointer + 4, ResultCode::Failure);
|
||||
mem.write32(messagePointer + 4, Result::FailurePlaceholder);
|
||||
return;
|
||||
}
|
||||
|
||||
ArchiveBase* archive = archiveObject->getData<ArchiveSession>()->archive;
|
||||
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 + 4, static_cast<u32>(res));
|
||||
|
@ -292,7 +284,7 @@ void FSService::openDirectory(u32 messagePointer) {
|
|||
KernelObject* archiveObject = kernel.getObject(archiveHandle, KernelObjectType::Archive);
|
||||
if (archiveObject == nullptr) [[unlikely]] {
|
||||
log("FS::OpenDirectory: Invalid archive handle %d\n", archiveHandle);
|
||||
mem.write32(messagePointer + 4, ResultCode::Failure);
|
||||
mem.write32(messagePointer + 4, Result::FailurePlaceholder);
|
||||
return;
|
||||
}
|
||||
|
||||
|
@ -302,7 +294,7 @@ void FSService::openDirectory(u32 messagePointer) {
|
|||
|
||||
mem.write32(messagePointer, IPC::responseHeader(0x80B, 1, 2));
|
||||
if (dir.isOk()) {
|
||||
mem.write32(messagePointer + 4, ResultCode::Success);
|
||||
mem.write32(messagePointer + 4, Result::Success);
|
||||
mem.write32(messagePointer + 12, dir.unwrap());
|
||||
} else {
|
||||
printf("FS::OpenDirectory failed\n");
|
||||
|
@ -324,14 +316,14 @@ void FSService::openFileDirectly(u32 messagePointer) {
|
|||
|
||||
auto archivePath = readPath(archivePathType, archivePathPointer, archivePathSize);
|
||||
ArchiveBase* archive = getArchiveFromID(archiveID, archivePath);
|
||||
|
||||
|
||||
if (archive == nullptr) [[unlikely]] {
|
||||
Helpers::panic("OpenFileDirectly: Tried to open unknown archive %d.", archiveID);
|
||||
}
|
||||
auto filePath = readPath(filePathType, filePathPointer, filePathSize);
|
||||
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]] {
|
||||
Helpers::panic("OpenFileDirectly: Failed to open archive with given path");
|
||||
}
|
||||
|
@ -342,7 +334,7 @@ void FSService::openFileDirectly(u32 messagePointer) {
|
|||
if (!handle.has_value()) {
|
||||
Helpers::panic("OpenFileDirectly: Failed to open file with given path");
|
||||
} else {
|
||||
mem.write32(messagePointer + 4, ResultCode::Success);
|
||||
mem.write32(messagePointer + 4, Result::Success);
|
||||
mem.write32(messagePointer + 12, handle.value());
|
||||
}
|
||||
}
|
||||
|
@ -360,16 +352,16 @@ void FSService::createFile(u32 messagePointer) {
|
|||
auto archiveObject = kernel.getObject(archiveHandle, KernelObjectType::Archive);
|
||||
if (archiveObject == nullptr) [[unlikely]] {
|
||||
log("FS::OpenFile: Invalid archive handle %d\n", archiveHandle);
|
||||
mem.write32(messagePointer + 4, ResultCode::Failure);
|
||||
mem.write32(messagePointer + 4, Result::FailurePlaceholder);
|
||||
return;
|
||||
}
|
||||
|
||||
ArchiveBase* archive = archiveObject->getData<ArchiveSession>()->archive;
|
||||
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 + 4, static_cast<u32>(res));
|
||||
mem.write32(messagePointer + 4, res);
|
||||
}
|
||||
|
||||
void FSService::deleteFile(u32 messagePointer) {
|
||||
|
@ -382,14 +374,14 @@ void FSService::deleteFile(u32 messagePointer) {
|
|||
auto archiveObject = kernel.getObject(archiveHandle, KernelObjectType::Archive);
|
||||
if (archiveObject == nullptr) [[unlikely]] {
|
||||
log("FS::DeleteFile: Invalid archive handle %d\n", archiveHandle);
|
||||
mem.write32(messagePointer + 4, ResultCode::Failure);
|
||||
mem.write32(messagePointer + 4, Result::FailurePlaceholder);
|
||||
return;
|
||||
}
|
||||
|
||||
ArchiveBase* archive = archiveObject->getData<ArchiveSession>()->archive;
|
||||
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 + 4, static_cast<u32>(res));
|
||||
}
|
||||
|
@ -409,12 +401,12 @@ void FSService::getFormatInfo(u32 messagePointer) {
|
|||
}
|
||||
|
||||
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 (res.isOk()) {
|
||||
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 + 12, info.numOfDirectories);
|
||||
mem.write32(messagePointer + 16, info.numOfFiles);
|
||||
|
@ -458,7 +450,7 @@ void FSService::formatSaveData(u32 messagePointer) {
|
|||
saveData.format(path, info);
|
||||
|
||||
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) {
|
||||
|
@ -478,7 +470,7 @@ void FSService::formatThisUserSaveData(u32 messagePointer) {
|
|||
.duplicateData = duplicateData
|
||||
};
|
||||
FSPath emptyPath;
|
||||
|
||||
|
||||
mem.write32(messagePointer, IPC::responseHeader(0x080F, 1, 0));
|
||||
saveData.format(emptyPath, info);
|
||||
}
|
||||
|
@ -497,13 +489,13 @@ void FSService::controlArchive(u32 messagePointer) {
|
|||
mem.write32(messagePointer, IPC::responseHeader(0x80D, 1, 0));
|
||||
if (archiveObject == nullptr) [[unlikely]] {
|
||||
log("FS::ControlArchive: Invalid archive handle %d\n", archiveHandle);
|
||||
mem.write32(messagePointer + 4, ResultCode::Failure);
|
||||
mem.write32(messagePointer + 4, Result::FailurePlaceholder);
|
||||
return;
|
||||
}
|
||||
|
||||
switch (action) {
|
||||
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;
|
||||
default:
|
||||
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));
|
||||
if (session == nullptr) [[unlikely]] {
|
||||
log("FS::GetFreeBytes: Invalid archive handle %d\n", archiveHandle);
|
||||
mem.write32(messagePointer + 4, ResultCode::Failure);
|
||||
mem.write32(messagePointer + 4, Result::FailurePlaceholder);
|
||||
return;
|
||||
}
|
||||
|
||||
|
@ -531,7 +523,7 @@ void FSService::getPriority(u32 messagePointer) {
|
|||
log("FS::GetPriority\n");
|
||||
|
||||
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);
|
||||
}
|
||||
|
||||
|
@ -540,13 +532,13 @@ void FSService::setPriority(u32 messagePointer) {
|
|||
log("FS::SetPriority (priority = %d)\n", value);
|
||||
|
||||
mem.write32(messagePointer, IPC::responseHeader(0x862, 1, 0));
|
||||
mem.write32(messagePointer + 4, ResultCode::Success);
|
||||
mem.write32(messagePointer + 4, Result::Success);
|
||||
priority = value;
|
||||
}
|
||||
|
||||
void FSService::isSdmcDetected(u32 messagePointer) {
|
||||
log("FS::IsSdmcDetected\n");
|
||||
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.
|
||||
}
|
|
@ -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() {
|
||||
privilegedProcess = 0xFFFFFFFF; // Set the privileged process to an invalid handle
|
||||
interruptEvent = std::nullopt;
|
||||
|
@ -100,7 +93,7 @@ void GPUService::registerInterruptRelayQueue(u32 messagePointer) {
|
|||
}
|
||||
|
||||
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 + 12, 0); // Translation descriptor
|
||||
mem.write32(messagePointer + 16, KernelHandles::GSPSharedMemHandle);
|
||||
|
@ -120,7 +113,7 @@ void GPUService::requestInterrupt(GPUInterrupt type) {
|
|||
u8& interruptCount = sharedMem[1];
|
||||
u8 flagIndex = (index + interruptCount) % 0x34;
|
||||
interruptCount++;
|
||||
|
||||
|
||||
sharedMem[2] = 0; // Set error code to 0
|
||||
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 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);
|
||||
|
||||
// Check for alignment
|
||||
|
@ -202,7 +195,7 @@ void GPUService::writeHwRegsWithMask(u32 messagePointer) {
|
|||
if (ioAddr >= 0x420000) {
|
||||
Helpers::panic("GSP::GPU::writeHwRegs offset too big");
|
||||
}
|
||||
|
||||
|
||||
ioAddr += 0x1EB00000;
|
||||
for (u32 i = 0; i < size; i += 4) {
|
||||
const u32 current = gpu.readReg(ioAddr);
|
||||
|
@ -301,13 +294,13 @@ void GPUService::processCommandBuffer() {
|
|||
|
||||
commandsLeft--;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Fill 2 GPU framebuffers, buf0 and buf1, using a specific word value
|
||||
void GPUService::memoryFill(u32* cmd) {
|
||||
u32 control = cmd[7];
|
||||
|
||||
|
||||
// buf0 parameters
|
||||
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
|
||||
|
|
|
@ -6,12 +6,6 @@ namespace LCDCommands {
|
|||
};
|
||||
}
|
||||
|
||||
namespace Result {
|
||||
enum : u32 {
|
||||
Success = 0,
|
||||
};
|
||||
}
|
||||
|
||||
void LCDService::reset() {}
|
||||
|
||||
void LCDService::handleSyncRequest(u32 messagePointer) {
|
||||
|
|
|
@ -13,13 +13,6 @@ namespace HIDCommands {
|
|||
};
|
||||
}
|
||||
|
||||
namespace Result {
|
||||
enum : u32 {
|
||||
Success = 0,
|
||||
Failure = 0xFFFFFFFF
|
||||
};
|
||||
}
|
||||
|
||||
void HIDService::reset() {
|
||||
sharedMem = nullptr;
|
||||
accelerometerEnabled = false;
|
||||
|
@ -90,7 +83,7 @@ void HIDService::getGyroscopeCoefficient(u32 messagePointer) {
|
|||
constexpr float gyroscopeCoeff = 14.375f; // Same as retail 3DS
|
||||
mem.write32(messagePointer, IPC::responseHeader(0x15, 2, 0));
|
||||
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) {
|
||||
|
@ -155,7 +148,7 @@ void HIDService::updateInputs(u64 currentTick) {
|
|||
writeSharedMem<u16>(touchEntryOffset, touchScreenX);
|
||||
writeSharedMem<u16>(touchEntryOffset + 2, touchScreenY);
|
||||
writeSharedMem<u8>(touchEntryOffset + 4, touchScreenPressed ? 1 : 0);
|
||||
|
||||
|
||||
// Next, update accelerometer state
|
||||
if (nextAccelerometerIndex == 0) {
|
||||
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::handleSyncRequest(u32 messagePointer) {
|
||||
|
|
|
@ -13,12 +13,6 @@ namespace MICCommands {
|
|||
};
|
||||
}
|
||||
|
||||
namespace Result {
|
||||
enum : u32 {
|
||||
Success = 0,
|
||||
};
|
||||
}
|
||||
|
||||
void MICService::reset() {
|
||||
micEnabled = false;
|
||||
shouldClamp = false;
|
||||
|
|
|
@ -11,12 +11,6 @@ namespace NDMCommands {
|
|||
};
|
||||
}
|
||||
|
||||
namespace Result {
|
||||
enum : u32 {
|
||||
Success = 0,
|
||||
};
|
||||
}
|
||||
|
||||
void NDMService::reset() {}
|
||||
|
||||
void NDMService::handleSyncRequest(u32 messagePointer) {
|
||||
|
|
|
@ -10,12 +10,6 @@ namespace NFCCommands {
|
|||
};
|
||||
}
|
||||
|
||||
namespace Result {
|
||||
enum : u32 {
|
||||
Success = 0,
|
||||
};
|
||||
}
|
||||
|
||||
void NFCService::reset() {
|
||||
tagInRangeEvent = std::nullopt;
|
||||
tagOutOfRangeEvent = std::nullopt;
|
||||
|
|
|
@ -7,12 +7,6 @@ namespace NIMCommands {
|
|||
};
|
||||
}
|
||||
|
||||
namespace Result {
|
||||
enum : u32 {
|
||||
Success = 0,
|
||||
};
|
||||
}
|
||||
|
||||
void NIMService::reset() {}
|
||||
|
||||
void NIMService::handleSyncRequest(u32 messagePointer) {
|
||||
|
|
|
@ -3,18 +3,12 @@
|
|||
|
||||
namespace PTMCommands {
|
||||
enum : u32 {
|
||||
GetStepHistory = 0x000B00C2,
|
||||
GetStepHistory = 0x000B00C2,
|
||||
GetTotalStepCount = 0x000C0000,
|
||||
ConfigureNew3DSCPU = 0x08180040
|
||||
};
|
||||
}
|
||||
|
||||
namespace Result {
|
||||
enum : u32 {
|
||||
Success = 0,
|
||||
};
|
||||
}
|
||||
|
||||
void PTMService::reset() {}
|
||||
|
||||
void PTMService::handleSyncRequest(u32 messagePointer) {
|
||||
|
|
|
@ -60,12 +60,6 @@ namespace Commands {
|
|||
};
|
||||
}
|
||||
|
||||
namespace Result {
|
||||
enum : u32 {
|
||||
Success = 0
|
||||
};
|
||||
}
|
||||
|
||||
// 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
|
||||
// messagePointer: The base pointer for the IPC message
|
||||
|
|
|
@ -29,12 +29,6 @@ namespace Y2RCommands {
|
|||
};
|
||||
}
|
||||
|
||||
namespace Result {
|
||||
enum : u32 {
|
||||
Success = 0,
|
||||
};
|
||||
}
|
||||
|
||||
void Y2RService::reset() {
|
||||
transferEndInterruptEnabled = false;
|
||||
transferEndEvent = std::nullopt;
|
||||
|
|
|
@ -1,11 +1,7 @@
|
|||
#include "emulator.hpp"
|
||||
#include "gl3w.h"
|
||||
|
||||
int main (int argc, char *argv[]) {
|
||||
Emulator emu;
|
||||
if (gl3wInit()) {
|
||||
Helpers::panic("Failed to initialize OpenGL");
|
||||
}
|
||||
|
||||
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
5991
third_party/gl3w/glcorearb.h
vendored
5991
third_party/gl3w/glcorearb.h
vendored
File diff suppressed because it is too large
Load diff
7
third_party/glad/CMakeLists.txt
vendored
Normal file
7
third_party/glad/CMakeLists.txt
vendored
Normal file
|
@ -0,0 +1,7 @@
|
|||
add_library(glad STATIC
|
||||
src/gl.c
|
||||
include/glad/gl.h
|
||||
include/KHR/khrplatform.h
|
||||
)
|
||||
|
||||
target_include_directories(glad PUBLIC "include")
|
7
third_party/glad/README.md
vendored
Normal file
7
third_party/glad/README.md
vendored
Normal file
|
@ -0,0 +1,7 @@
|
|||
# glad
|
||||
|
||||
This was generated with [glad2](https://github.com/Dav1dde/glad) using the following command:
|
||||
|
||||
```bash
|
||||
python -m glad --api gl:core=4.3 --out-path glad --reproducible c --loader
|
||||
```
|
|
@ -94,14 +94,14 @@
|
|||
# define KHRONOS_STATIC 1
|
||||
#endif
|
||||
|
||||
/*-------------------------------------------------------------------------
|
||||
* Definition of KHRONOS_APICALL
|
||||
*-------------------------------------------------------------------------
|
||||
* This precedes the return type of the function in the function prototype.
|
||||
*/
|
||||
/*-------------------------------------------------------------------------
|
||||
* Definition of KHRONOS_APICALL
|
||||
*-------------------------------------------------------------------------
|
||||
* This precedes the return type of the function in the function prototype.
|
||||
*/
|
||||
#if defined(KHRONOS_STATIC)
|
||||
/* If the preprocessor constant KHRONOS_STATIC is defined, make the
|
||||
* header compatible with static linking. */
|
||||
/* If the preprocessor constant KHRONOS_STATIC is defined, make the
|
||||
* header compatible with static linking. */
|
||||
# define KHRONOS_APICALL
|
||||
#elif defined(_WIN32)
|
||||
# define KHRONOS_APICALL __declspec(dllimport)
|
||||
|
@ -113,39 +113,39 @@
|
|||
# define KHRONOS_APICALL
|
||||
#endif
|
||||
|
||||
/*-------------------------------------------------------------------------
|
||||
* Definition of KHRONOS_APIENTRY
|
||||
*-------------------------------------------------------------------------
|
||||
* This follows the return type of the function and precedes the function
|
||||
* name in the function prototype.
|
||||
*/
|
||||
/*-------------------------------------------------------------------------
|
||||
* Definition of KHRONOS_APIENTRY
|
||||
*-------------------------------------------------------------------------
|
||||
* This follows the return type of the function and precedes the function
|
||||
* name in the function prototype.
|
||||
*/
|
||||
#if defined(_WIN32) && !defined(_WIN32_WCE) && !defined(__SCITECH_SNAP__)
|
||||
/* Win32 but not WinCE */
|
||||
/* Win32 but not WinCE */
|
||||
# define KHRONOS_APIENTRY __stdcall
|
||||
#else
|
||||
# define KHRONOS_APIENTRY
|
||||
#endif
|
||||
|
||||
/*-------------------------------------------------------------------------
|
||||
* Definition of KHRONOS_APIATTRIBUTES
|
||||
*-------------------------------------------------------------------------
|
||||
* This follows the closing parenthesis of the function prototype arguments.
|
||||
*/
|
||||
/*-------------------------------------------------------------------------
|
||||
* Definition of KHRONOS_APIATTRIBUTES
|
||||
*-------------------------------------------------------------------------
|
||||
* This follows the closing parenthesis of the function prototype arguments.
|
||||
*/
|
||||
#if defined (__ARMCC_2__)
|
||||
#define KHRONOS_APIATTRIBUTES __softfp
|
||||
#else
|
||||
#define KHRONOS_APIATTRIBUTES
|
||||
#endif
|
||||
|
||||
/*-------------------------------------------------------------------------
|
||||
* basic type definitions
|
||||
*-----------------------------------------------------------------------*/
|
||||
/*-------------------------------------------------------------------------
|
||||
* basic type definitions
|
||||
*-----------------------------------------------------------------------*/
|
||||
#if (defined(__STDC_VERSION__) && __STDC_VERSION__ >= 199901L) || defined(__GNUC__) || defined(__SCO__) || defined(__USLC__)
|
||||
|
||||
|
||||
/*
|
||||
* Using <stdint.h>
|
||||
*/
|
||||
/*
|
||||
* Using <stdint.h>
|
||||
*/
|
||||
#include <stdint.h>
|
||||
typedef int32_t khronos_int32_t;
|
||||
typedef uint32_t khronos_uint32_t;
|
||||
|
@ -170,9 +170,9 @@ typedef uint64_t khronos_uint64_t;
|
|||
|
||||
#elif defined(__VMS ) || defined(__sgi)
|
||||
|
||||
/*
|
||||
* Using <inttypes.h>
|
||||
*/
|
||||
/*
|
||||
* Using <inttypes.h>
|
||||
*/
|
||||
#include <inttypes.h>
|
||||
typedef int32_t khronos_int32_t;
|
||||
typedef uint32_t khronos_uint32_t;
|
||||
|
@ -183,9 +183,9 @@ typedef uint64_t khronos_uint64_t;
|
|||
|
||||
#elif defined(_WIN32) && !defined(__SCITECH_SNAP__)
|
||||
|
||||
/*
|
||||
* Win32
|
||||
*/
|
||||
/*
|
||||
* Win32
|
||||
*/
|
||||
typedef __int32 khronos_int32_t;
|
||||
typedef unsigned __int32 khronos_uint32_t;
|
||||
typedef __int64 khronos_int64_t;
|
||||
|
@ -195,9 +195,9 @@ typedef unsigned __int64 khronos_uint64_t;
|
|||
|
||||
#elif defined(__sun__) || defined(__digital__)
|
||||
|
||||
/*
|
||||
* Sun or Digital
|
||||
*/
|
||||
/*
|
||||
* Sun or Digital
|
||||
*/
|
||||
typedef int khronos_int32_t;
|
||||
typedef unsigned int khronos_uint32_t;
|
||||
#if defined(__arch64__) || defined(_LP64)
|
||||
|
@ -212,9 +212,9 @@ typedef unsigned long long int khronos_uint64_t;
|
|||
|
||||
#elif 0
|
||||
|
||||
/*
|
||||
* Hypothetical platform with no float or int64 support
|
||||
*/
|
||||
/*
|
||||
* Hypothetical platform with no float or int64 support
|
||||
*/
|
||||
typedef int khronos_int32_t;
|
||||
typedef unsigned int khronos_uint32_t;
|
||||
#define KHRONOS_SUPPORT_INT64 0
|
||||
|
@ -222,9 +222,9 @@ typedef unsigned int khronos_uint32_t;
|
|||
|
||||
#else
|
||||
|
||||
/*
|
||||
* Generic fallback
|
||||
*/
|
||||
/*
|
||||
* Generic fallback
|
||||
*/
|
||||
#include <stdint.h>
|
||||
typedef int32_t khronos_int32_t;
|
||||
typedef uint32_t khronos_uint32_t;
|
||||
|
@ -296,15 +296,15 @@ typedef khronos_int64_t khronos_stime_nanoseconds_t;
|
|||
#define KHRONOS_MAX_ENUM 0x7FFFFFFF
|
||||
#endif
|
||||
|
||||
/*
|
||||
* Enumerated boolean type
|
||||
*
|
||||
* Values other than zero should be considered to be true. Therefore
|
||||
* comparisons should not be made against KHRONOS_TRUE.
|
||||
*/
|
||||
/*
|
||||
* Enumerated boolean type
|
||||
*
|
||||
* Values other than zero should be considered to be true. Therefore
|
||||
* comparisons should not be made against KHRONOS_TRUE.
|
||||
*/
|
||||
typedef enum {
|
||||
KHRONOS_FALSE = 0,
|
||||
KHRONOS_TRUE = 1,
|
||||
KHRONOS_TRUE = 1,
|
||||
KHRONOS_BOOLEAN_ENUM_FORCE_SIZE = KHRONOS_MAX_ENUM
|
||||
} khronos_boolean_enum_t;
|
||||
|
13602
third_party/glad/include/glad/gl.h
vendored
Normal file
13602
third_party/glad/include/glad/gl.h
vendored
Normal file
File diff suppressed because one or more lines are too long
8394
third_party/glad/src/gl.c
vendored
Normal file
8394
third_party/glad/src/gl.c
vendored
Normal file
File diff suppressed because it is too large
Load diff
Loading…
Add table
Reference in a new issue