From e69e95af69a46a059b23402497920fd83073272f Mon Sep 17 00:00:00 2001 From: wheremyfoodat Date: Sun, 12 Mar 2023 03:15:44 +0200 Subject: [PATCH] [FS] Implement SaveData sort of. --- include/PICA/gpu.hpp | 8 +++++ include/renderer_gl/renderer_gl.hpp | 6 ++-- src/core/PICA/gpu.cpp | 1 + src/core/fs/archive_save_data.cpp | 54 +++++++++++++++++----------- src/core/renderer_gl/renderer_gl.cpp | 8 +++++ src/core/services/fs.cpp | 12 +++++-- src/core/services/gsp_gpu.cpp | 17 ++++++--- 7 files changed, 75 insertions(+), 31 deletions(-) diff --git a/include/PICA/gpu.hpp b/include/PICA/gpu.hpp index 801af7c6..4b60b3ef 100644 --- a/include/PICA/gpu.hpp +++ b/include/PICA/gpu.hpp @@ -85,10 +85,18 @@ public: u32 readInternalReg(u32 index); void writeInternalReg(u32 index, u32 value, u32 mask); + // TODO: Emulate the transfer engine & its registers + // Then this can be emulated by just writing the appropriate values there void clearBuffer(u32 startAddress, u32 endAddress, u32 value, u32 control) { renderer.clearBuffer(startAddress, endAddress, value, control); } + // TODO: Emulate the transfer engine & its registers + // Then this can be emulated by just writing the appropriate values there + void displayTransfer(u32 inputAddr, u32 outputAddr, u32 inputSize, u32 outputSize, u32 flags) { + renderer.displayTransfer(inputAddr, outputAddr, inputSize, outputSize, flags); + } + // Read a value of type T from physical address paddr // This is necessary because vertex attribute fetching uses physical addresses template diff --git a/include/renderer_gl/renderer_gl.hpp b/include/renderer_gl/renderer_gl.hpp index f0a092bf..06a84ca0 100644 --- a/include/renderer_gl/renderer_gl.hpp +++ b/include/renderer_gl/renderer_gl.hpp @@ -61,6 +61,8 @@ class Renderer { OpenGL::Texture getTexture(Texture& tex); MAKE_LOG_FUNCTION(log, rendererLogger) + void setupBlending(); + void bindDepthBuffer(); public: Renderer(GPU& gpu, const std::array& internalRegs) : gpu(gpu), regs(internalRegs) {} @@ -70,6 +72,7 @@ public: void initGraphicsContext(); // Initialize graphics context 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, Vertex* vertices, u32 count); // Draw the given vertices void setFBSize(u32 width, u32 height) { @@ -92,8 +95,5 @@ public: void setColourBufferLoc(u32 loc) { colourBufferLoc = loc; } void setDepthBufferLoc(u32 loc) { depthBufferLoc = loc; } - void setupBlending(); - void bindDepthBuffer(); - static constexpr u32 vertexBufferSize = 0x1500; }; \ No newline at end of file diff --git a/src/core/PICA/gpu.cpp b/src/core/PICA/gpu.cpp index 679dd974..9266c28c 100644 --- a/src/core/PICA/gpu.cpp +++ b/src/core/PICA/gpu.cpp @@ -250,6 +250,7 @@ void GPU::fireDMA(u32 dest, u32 source, u32 size) { std::memcpy(&vram[dest - vramStart], &fcram[source - fcramStart], size); } else { printf("Non-trivially optimizable GPU DMA. Falling back to byte-by-byte transfer"); + std::memcpy(&vram[dest - vramStart], mem.getReadPointer(source), size); for (u32 i = 0; i < size; i++) { mem.write8(dest + i, mem.read8(source + i)); diff --git a/src/core/fs/archive_save_data.cpp b/src/core/fs/archive_save_data.cpp index 7f7aabcf..79227275 100644 --- a/src/core/fs/archive_save_data.cpp +++ b/src/core/fs/archive_save_data.cpp @@ -2,6 +2,8 @@ #include #include +namespace fs = std::filesystem; + CreateFileResult SaveDataArchive::createFile(const FSPath& path, u64 size) { Helpers::panic("[SaveData] CreateFile not yet supported"); return CreateFileResult::Success; @@ -18,17 +20,37 @@ FileDescriptor SaveDataArchive::openFile(const FSPath& path, const FilePerms& pe return FileError; } - if (path.type == PathType::UTF16 /* && path.utf16_string == u"/game_header" */) { - printf("Opened file from the SaveData archive \n"); - return NoFile; + if (path.type == PathType::UTF16) { + if (!isPathSafe(path)) + Helpers::panic("Unsafe path in SaveData::OpenFile"); + + if (perms.raw == 0 || (perms.create() && !perms.write())) + Helpers::panic("[SaveData] Unsupported flags for OpenFile"); + + fs::path p = IOFile::getAppData() / "SaveData"; + p += fs::path(path.utf16_string).make_preferred(); + + const char* permString = perms.write() ? "r+b" : "rb"; + + if (fs::exists(p)) { // Return file descriptor if the file exists + IOFile file(p.string().c_str(), permString); + return file.isOpen() ? file.getHandle() : FileError; + } else { + // If the file is not found, create it if the create flag is on + if (perms.create()) { + IOFile file(p.string().c_str(), "wb"); // Create file + file.close(); // Close it + + file.open(p.string().c_str(), permString); // Reopen with proper perms + return file.isOpen() ? file.getHandle() : FileError; + } else { + return FileError; + } + } } - if (path.type != PathType::Binary) { - printf("Unimplemented SaveData path type: %d\n", path.type); - return FileError; - } - - return NoFile; + Helpers::panic("SaveDataArchive::OpenFile: Failed"); + return FileError; } ArchiveBase* SaveDataArchive::openArchive(const FSPath& path) { @@ -54,16 +76,6 @@ std::optional SaveDataArchive::readFile(FileSession* file, u64 offset, u32 return std::nullopt; } - const u64 endOffset = std::min(saveSize, offset + size); // Don't go past the end of the save file - const u32 bytesRead = endOffset - offset; - - if (bytesRead != 0x22) Helpers::panic("Might want to actually implement SaveData"); - - static constexpr std::array saveDataStub = { 0x00, 0x23, 0x3C, 0x77, 0x67, 0x28, 0x30, 0x33, 0x58, 0x61, 0x39, 0x61, 0x48, 0x59, 0x36, 0x55, 0x43, 0x76, 0x58, 0x61, 0x6F, 0x65, 0x48, 0x6D, 0x2B, 0x5E, 0x6F, 0x62, 0x3E, 0x6F, 0x34, 0x00, 0x77, 0x09}; - - for (u32 i = 0; i < bytesRead; i++) { - mem.write8(dataPointer + i, saveDataStub[i]); - } - - return bytesRead; + Helpers::panic("Unimplemented SaveData::ReadFile"); + return 0; } \ No newline at end of file diff --git a/src/core/renderer_gl/renderer_gl.cpp b/src/core/renderer_gl/renderer_gl.cpp index 90b696f7..ac0ac68b 100644 --- a/src/core/renderer_gl/renderer_gl.cpp +++ b/src/core/renderer_gl/renderer_gl.cpp @@ -441,4 +441,12 @@ OpenGL::Texture Renderer::getTexture(Texture& tex) { return newTex.texture; } +} + +void Renderer::displayTransfer(u32 inputAddr, u32 outputAddr, u32 inputSize, u32 outputSize, u32 flags) { + const u32 inputWidth = inputSize & 0xffff; + const u32 inputGap = inputSize >> 16; + + const u32 outputWidth = outputSize & 0xffff; + const u32 outputGap = outputSize >> 16; } \ No newline at end of file diff --git a/src/core/services/fs.cpp b/src/core/services/fs.cpp index 50a46e26..81bf19c3 100644 --- a/src/core/services/fs.cpp +++ b/src/core/services/fs.cpp @@ -38,10 +38,16 @@ void FSService::reset() { // Creates directories for NAND, ExtSaveData, etc if they don't already exist. Should be executed after loading a new ROM. void FSService::initializeFilesystem() { const auto nandPath = IOFile::getAppData() / "NAND"; // Create NAND - // TODO: Savedata, SDMC, etc + const auto savePath = IOFile::getAppData() / "SaveData"; // Create SaveData + namespace fs = std::filesystem; + // TODO: SDMC, etc - if (!std::filesystem::is_directory(nandPath)) { - std::filesystem::create_directories(nandPath); + if (!fs::is_directory(nandPath)) { + fs::create_directories(nandPath); + } + + if (!fs::is_directory(savePath)) { + fs::create_directories(savePath); } } diff --git a/src/core/services/gsp_gpu.cpp b/src/core/services/gsp_gpu.cpp index da31914c..b12adff1 100644 --- a/src/core/services/gsp_gpu.cpp +++ b/src/core/services/gsp_gpu.cpp @@ -284,15 +284,22 @@ void GPUService::memoryFill(u32* cmd) { } void GPUService::triggerDisplayTransfer(u32* cmd) { + const u32 inputAddr = cmd[1]; + const u32 outputAddr = cmd[2]; + const u32 inputSize = cmd[3]; + const u32 outputSize = cmd[4]; + const u32 flags = cmd[5]; + log("GSP::GPU::TriggerDisplayTransfer (Stubbed)\n"); + gpu.displayTransfer(inputAddr, outputAddr, inputSize, outputSize, flags); requestInterrupt(GPUInterrupt::PPF); // Send "Display transfer finished" interrupt } void GPUService::triggerDMARequest(u32* cmd) { - u32 source = cmd[1]; - u32 dest = cmd[2]; - u32 size = cmd[3]; - bool flush = cmd[7] == 1; + const u32 source = cmd[1]; + const u32 dest = cmd[2]; + const u32 size = cmd[3]; + const bool flush = cmd[7] == 1; log("GSP::GPU::TriggerDMARequest (source = %08X, dest = %08X, size = %08X)\n", source, dest, size); gpu.fireDMA(dest, source, size); @@ -315,6 +322,8 @@ void GPUService::processCommandList(u32* cmd) { requestInterrupt(GPUInterrupt::P3D); // Send an IRQ when command list processing is over } +// TODO: Emulate the transfer engine & its registers +// Then this can be emulated by just writing the appropriate values there void GPUService::triggerTextureCopy(u32* cmd) { Helpers::warn("GSP::GPU::TriggerTextureCopy (unimplemented)\n"); // This uses the transfer engine and thus needs to fire a PPF interrupt.