mirror of
https://github.com/wheremyfoodat/Panda3DS.git
synced 2025-04-09 07:35:41 +12:00
[FS] Implement SaveData sort of.
This commit is contained in:
parent
d24a61d5a7
commit
e69e95af69
7 changed files with 75 additions and 31 deletions
|
@ -85,10 +85,18 @@ public:
|
||||||
u32 readInternalReg(u32 index);
|
u32 readInternalReg(u32 index);
|
||||||
void writeInternalReg(u32 index, u32 value, u32 mask);
|
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) {
|
void clearBuffer(u32 startAddress, u32 endAddress, u32 value, u32 control) {
|
||||||
renderer.clearBuffer(startAddress, endAddress, value, 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
|
// Read a value of type T from physical address paddr
|
||||||
// This is necessary because vertex attribute fetching uses physical addresses
|
// This is necessary because vertex attribute fetching uses physical addresses
|
||||||
template <typename T>
|
template <typename T>
|
||||||
|
|
|
@ -61,6 +61,8 @@ class Renderer {
|
||||||
OpenGL::Texture getTexture(Texture& tex);
|
OpenGL::Texture getTexture(Texture& tex);
|
||||||
|
|
||||||
MAKE_LOG_FUNCTION(log, rendererLogger)
|
MAKE_LOG_FUNCTION(log, rendererLogger)
|
||||||
|
void setupBlending();
|
||||||
|
void bindDepthBuffer();
|
||||||
|
|
||||||
public:
|
public:
|
||||||
Renderer(GPU& gpu, const std::array<u32, regNum>& internalRegs) : gpu(gpu), regs(internalRegs) {}
|
Renderer(GPU& gpu, const std::array<u32, regNum>& internalRegs) : gpu(gpu), regs(internalRegs) {}
|
||||||
|
@ -70,6 +72,7 @@ public:
|
||||||
void initGraphicsContext(); // Initialize graphics context
|
void initGraphicsContext(); // Initialize graphics context
|
||||||
void getGraphicsContext(); // Set up graphics context for rendering
|
void getGraphicsContext(); // Set up graphics context for rendering
|
||||||
void clearBuffer(u32 startAddress, u32 endAddress, u32 value, u32 control); // Clear a GPU buffer in VRAM
|
void clearBuffer(u32 startAddress, u32 endAddress, u32 value, u32 control); // Clear a GPU buffer in VRAM
|
||||||
|
void displayTransfer(u32 inputAddr, u32 outputAddr, u32 inputSize, u32 outputSize, u32 flags); // Perform display transfer
|
||||||
void drawVertices(OpenGL::Primitives primType, Vertex* vertices, u32 count); // Draw the given vertices
|
void drawVertices(OpenGL::Primitives primType, Vertex* vertices, u32 count); // Draw the given vertices
|
||||||
|
|
||||||
void setFBSize(u32 width, u32 height) {
|
void setFBSize(u32 width, u32 height) {
|
||||||
|
@ -92,8 +95,5 @@ public:
|
||||||
void setColourBufferLoc(u32 loc) { colourBufferLoc = loc; }
|
void setColourBufferLoc(u32 loc) { colourBufferLoc = loc; }
|
||||||
void setDepthBufferLoc(u32 loc) { depthBufferLoc = loc; }
|
void setDepthBufferLoc(u32 loc) { depthBufferLoc = loc; }
|
||||||
|
|
||||||
void setupBlending();
|
|
||||||
void bindDepthBuffer();
|
|
||||||
|
|
||||||
static constexpr u32 vertexBufferSize = 0x1500;
|
static constexpr u32 vertexBufferSize = 0x1500;
|
||||||
};
|
};
|
|
@ -250,6 +250,7 @@ void GPU::fireDMA(u32 dest, u32 source, u32 size) {
|
||||||
std::memcpy(&vram[dest - vramStart], &fcram[source - fcramStart], size);
|
std::memcpy(&vram[dest - vramStart], &fcram[source - fcramStart], size);
|
||||||
} else {
|
} else {
|
||||||
printf("Non-trivially optimizable GPU DMA. Falling back to byte-by-byte transfer");
|
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++) {
|
for (u32 i = 0; i < size; i++) {
|
||||||
mem.write8(dest + i, mem.read8(source + i));
|
mem.write8(dest + i, mem.read8(source + i));
|
||||||
|
|
|
@ -2,6 +2,8 @@
|
||||||
#include <algorithm>
|
#include <algorithm>
|
||||||
#include <memory>
|
#include <memory>
|
||||||
|
|
||||||
|
namespace fs = std::filesystem;
|
||||||
|
|
||||||
CreateFileResult SaveDataArchive::createFile(const FSPath& path, u64 size) {
|
CreateFileResult SaveDataArchive::createFile(const FSPath& path, u64 size) {
|
||||||
Helpers::panic("[SaveData] CreateFile not yet supported");
|
Helpers::panic("[SaveData] CreateFile not yet supported");
|
||||||
return CreateFileResult::Success;
|
return CreateFileResult::Success;
|
||||||
|
@ -18,17 +20,37 @@ FileDescriptor SaveDataArchive::openFile(const FSPath& path, const FilePerms& pe
|
||||||
return FileError;
|
return FileError;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (path.type == PathType::UTF16 /* && path.utf16_string == u"/game_header" */) {
|
if (path.type == PathType::UTF16) {
|
||||||
printf("Opened file from the SaveData archive \n");
|
if (!isPathSafe<PathType::UTF16>(path))
|
||||||
return NoFile;
|
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) {
|
Helpers::panic("SaveDataArchive::OpenFile: Failed");
|
||||||
printf("Unimplemented SaveData path type: %d\n", path.type);
|
return FileError;
|
||||||
return FileError;
|
|
||||||
}
|
|
||||||
|
|
||||||
return NoFile;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
ArchiveBase* SaveDataArchive::openArchive(const FSPath& path) {
|
ArchiveBase* SaveDataArchive::openArchive(const FSPath& path) {
|
||||||
|
@ -54,16 +76,6 @@ std::optional<u32> SaveDataArchive::readFile(FileSession* file, u64 offset, u32
|
||||||
return std::nullopt;
|
return std::nullopt;
|
||||||
}
|
}
|
||||||
|
|
||||||
const u64 endOffset = std::min<u64>(saveSize, offset + size); // Don't go past the end of the save file
|
Helpers::panic("Unimplemented SaveData::ReadFile");
|
||||||
const u32 bytesRead = endOffset - offset;
|
return 0;
|
||||||
|
|
||||||
if (bytesRead != 0x22) Helpers::panic("Might want to actually implement SaveData");
|
|
||||||
|
|
||||||
static constexpr std::array<u8, 0x22> 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;
|
|
||||||
}
|
}
|
|
@ -441,4 +441,12 @@ OpenGL::Texture Renderer::getTexture(Texture& tex) {
|
||||||
|
|
||||||
return newTex.texture;
|
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;
|
||||||
}
|
}
|
|
@ -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.
|
// Creates directories for NAND, ExtSaveData, etc if they don't already exist. Should be executed after loading a new ROM.
|
||||||
void FSService::initializeFilesystem() {
|
void FSService::initializeFilesystem() {
|
||||||
const auto nandPath = IOFile::getAppData() / "NAND"; // Create NAND
|
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)) {
|
if (!fs::is_directory(nandPath)) {
|
||||||
std::filesystem::create_directories(nandPath);
|
fs::create_directories(nandPath);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!fs::is_directory(savePath)) {
|
||||||
|
fs::create_directories(savePath);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -284,15 +284,22 @@ void GPUService::memoryFill(u32* cmd) {
|
||||||
}
|
}
|
||||||
|
|
||||||
void GPUService::triggerDisplayTransfer(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");
|
log("GSP::GPU::TriggerDisplayTransfer (Stubbed)\n");
|
||||||
|
gpu.displayTransfer(inputAddr, outputAddr, inputSize, outputSize, flags);
|
||||||
requestInterrupt(GPUInterrupt::PPF); // Send "Display transfer finished" interrupt
|
requestInterrupt(GPUInterrupt::PPF); // Send "Display transfer finished" interrupt
|
||||||
}
|
}
|
||||||
|
|
||||||
void GPUService::triggerDMARequest(u32* cmd) {
|
void GPUService::triggerDMARequest(u32* cmd) {
|
||||||
u32 source = cmd[1];
|
const u32 source = cmd[1];
|
||||||
u32 dest = cmd[2];
|
const u32 dest = cmd[2];
|
||||||
u32 size = cmd[3];
|
const u32 size = cmd[3];
|
||||||
bool flush = cmd[7] == 1;
|
const bool flush = cmd[7] == 1;
|
||||||
|
|
||||||
log("GSP::GPU::TriggerDMARequest (source = %08X, dest = %08X, size = %08X)\n", source, dest, size);
|
log("GSP::GPU::TriggerDMARequest (source = %08X, dest = %08X, size = %08X)\n", source, dest, size);
|
||||||
gpu.fireDMA(dest, source, 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
|
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) {
|
void GPUService::triggerTextureCopy(u32* cmd) {
|
||||||
Helpers::warn("GSP::GPU::TriggerTextureCopy (unimplemented)\n");
|
Helpers::warn("GSP::GPU::TriggerTextureCopy (unimplemented)\n");
|
||||||
// This uses the transfer engine and thus needs to fire a PPF interrupt.
|
// This uses the transfer engine and thus needs to fire a PPF interrupt.
|
||||||
|
|
Loading…
Add table
Reference in a new issue