mirror of
https://github.com/wheremyfoodat/Panda3DS.git
synced 2025-04-08 07:05:40 +12:00
Merge pull request #673 from wheremyfoodat/TWL-Photo
FS: Add the Twilight Zone
This commit is contained in:
commit
2e9bbae46d
9 changed files with 299 additions and 146 deletions
|
@ -338,6 +338,7 @@ set(LOADER_SOURCE_FILES src/core/loader/elf.cpp src/core/loader/ncsd.cpp src/cor
|
||||||
set(FS_SOURCE_FILES src/core/fs/archive_self_ncch.cpp src/core/fs/archive_save_data.cpp src/core/fs/archive_sdmc.cpp
|
set(FS_SOURCE_FILES src/core/fs/archive_self_ncch.cpp src/core/fs/archive_save_data.cpp src/core/fs/archive_sdmc.cpp
|
||||||
src/core/fs/archive_ext_save_data.cpp src/core/fs/archive_ncch.cpp src/core/fs/romfs.cpp
|
src/core/fs/archive_ext_save_data.cpp src/core/fs/archive_ncch.cpp src/core/fs/romfs.cpp
|
||||||
src/core/fs/ivfc.cpp src/core/fs/archive_user_save_data.cpp src/core/fs/archive_system_save_data.cpp
|
src/core/fs/ivfc.cpp src/core/fs/archive_user_save_data.cpp src/core/fs/archive_system_save_data.cpp
|
||||||
|
src/core/fs/archive_twl_photo.cpp src/core/fs/archive_twl_sound.cpp
|
||||||
)
|
)
|
||||||
|
|
||||||
set(APPLET_SOURCE_FILES src/core/applets/applet.cpp src/core/applets/mii_selector.cpp src/core/applets/software_keyboard.cpp src/core/applets/applet_manager.cpp
|
set(APPLET_SOURCE_FILES src/core/applets/applet.cpp src/core/applets/mii_selector.cpp src/core/applets/software_keyboard.cpp src/core/applets/applet_manager.cpp
|
||||||
|
@ -387,7 +388,8 @@ set(HEADER_FILES include/emulator.hpp include/helpers.hpp include/termcolor.hpp
|
||||||
include/PICA/pica_vert_config.hpp include/sdl_sensors.hpp include/PICA/draw_acceleration.hpp include/renderdoc.hpp
|
include/PICA/pica_vert_config.hpp include/sdl_sensors.hpp include/PICA/draw_acceleration.hpp include/renderdoc.hpp
|
||||||
include/align.hpp include/audio/aac_decoder.hpp include/PICA/pica_simd.hpp include/services/fonts.hpp
|
include/align.hpp include/audio/aac_decoder.hpp include/PICA/pica_simd.hpp include/services/fonts.hpp
|
||||||
include/audio/audio_interpolation.hpp include/audio/hle_mixer.hpp include/audio/dsp_simd.hpp
|
include/audio/audio_interpolation.hpp include/audio/hle_mixer.hpp include/audio/dsp_simd.hpp
|
||||||
include/services/dsp_firmware_db.hpp include/frontend_settings.hpp
|
include/services/dsp_firmware_db.hpp include/frontend_settings.hpp include/fs/archive_twl_photo.hpp
|
||||||
|
include/fs/archive_twl_sound.hpp
|
||||||
)
|
)
|
||||||
|
|
||||||
cmrc_add_resource_library(
|
cmrc_add_resource_library(
|
||||||
|
|
|
@ -7,6 +7,7 @@
|
||||||
#include <string>
|
#include <string>
|
||||||
#include <type_traits>
|
#include <type_traits>
|
||||||
#include <vector>
|
#include <vector>
|
||||||
|
|
||||||
#include "helpers.hpp"
|
#include "helpers.hpp"
|
||||||
#include "memory.hpp"
|
#include "memory.hpp"
|
||||||
#include "result.hpp"
|
#include "result.hpp"
|
||||||
|
@ -15,13 +16,13 @@
|
||||||
using Result::HorizonResult;
|
using Result::HorizonResult;
|
||||||
|
|
||||||
namespace PathType {
|
namespace PathType {
|
||||||
enum : u32 {
|
enum : u32 {
|
||||||
Invalid = 0,
|
Invalid = 0,
|
||||||
Empty = 1,
|
Empty = 1,
|
||||||
Binary = 2,
|
Binary = 2,
|
||||||
ASCII = 3,
|
ASCII = 3,
|
||||||
UTF16 = 4,
|
UTF16 = 4,
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
namespace ArchiveID {
|
namespace ArchiveID {
|
||||||
|
@ -40,85 +41,88 @@ namespace ArchiveID {
|
||||||
UserSaveData1 = 0x567890B2,
|
UserSaveData1 = 0x567890B2,
|
||||||
// 3DBrew: Similar to 0x567890B2 but can only access Accessible Save specified in exheader?
|
// 3DBrew: Similar to 0x567890B2 but can only access Accessible Save specified in exheader?
|
||||||
UserSaveData2 = 0x567890B4,
|
UserSaveData2 = 0x567890B4,
|
||||||
|
|
||||||
|
TwlPhoto = 0x567890AC,
|
||||||
|
TwlSound = 0x567890AD,
|
||||||
};
|
};
|
||||||
|
|
||||||
static std::string toString(u32 id) {
|
static std::string toString(u32 id) {
|
||||||
switch (id) {
|
switch (id) {
|
||||||
case SelfNCCH: return "SelfNCCH";
|
case SelfNCCH: return "SelfNCCH";
|
||||||
case SaveData: return "SaveData";
|
case SaveData: return "SaveData";
|
||||||
case ExtSaveData: return "ExtSaveData";
|
case ExtSaveData: return "ExtSaveData";
|
||||||
case SharedExtSaveData: return "SharedExtSaveData";
|
case SharedExtSaveData: return "SharedExtSaveData";
|
||||||
case SystemSaveData: return "SystemSaveData";
|
case SystemSaveData: return "SystemSaveData";
|
||||||
case SDMC: return "SDMC";
|
case SDMC: return "SDMC";
|
||||||
case SDMCWriteOnly: return "SDMC (Write-only)";
|
case SDMCWriteOnly: return "SDMC (Write-only)";
|
||||||
case SavedataAndNcch: return "Savedata & NCCH (archive 0x2345678A)";
|
case SavedataAndNcch: return "Savedata & NCCH (archive 0x2345678A)";
|
||||||
default: return "Unknown archive";
|
case TwlPhoto: return "TWL_PHOTO";
|
||||||
}
|
case TwlSound: return "TWL_SOUND";
|
||||||
}
|
default: return "Unknown archive";
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
} // namespace ArchiveID
|
||||||
|
|
||||||
struct FSPath {
|
struct FSPath {
|
||||||
u32 type = PathType::Invalid;
|
u32 type = PathType::Invalid;
|
||||||
|
|
||||||
std::vector<u8> binary; // Path data for binary paths
|
std::vector<u8> binary; // Path data for binary paths
|
||||||
std::string string; // Path data for ASCII paths
|
std::string string; // Path data for ASCII paths
|
||||||
std::u16string utf16_string;
|
std::u16string utf16_string;
|
||||||
|
|
||||||
FSPath() {}
|
FSPath() {}
|
||||||
|
|
||||||
FSPath(u32 type, const std::vector<u8>& vec) : type(type) {
|
FSPath(u32 type, const std::vector<u8>& vec) : type(type) {
|
||||||
switch (type) {
|
switch (type) {
|
||||||
case PathType::Binary:
|
case PathType::Binary: binary = std::move(vec); break;
|
||||||
binary = std::move(vec);
|
|
||||||
break;
|
|
||||||
|
|
||||||
case PathType::ASCII:
|
case PathType::ASCII:
|
||||||
string.resize(vec.size() - 1); // -1 because of the null terminator
|
string.resize(vec.size() - 1); // -1 because of the null terminator
|
||||||
std::memcpy(string.data(), vec.data(), vec.size() - 1); // Copy string data
|
std::memcpy(string.data(), vec.data(), vec.size() - 1); // Copy string data
|
||||||
break;
|
break;
|
||||||
|
|
||||||
case PathType::UTF16: {
|
case PathType::UTF16: {
|
||||||
const size_t size = vec.size() / sizeof(u16) - 1; // Character count. -1 because null terminator here too
|
const size_t size = vec.size() / sizeof(u16) - 1; // Character count. -1 because null terminator here too
|
||||||
utf16_string.resize(size);
|
utf16_string.resize(size);
|
||||||
std::memcpy(utf16_string.data(), vec.data(), size * sizeof(u16));
|
std::memcpy(utf16_string.data(), vec.data(), size * sizeof(u16));
|
||||||
break;
|
break;
|
||||||
}
|
};
|
||||||
; }
|
}
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
struct FilePerms {
|
struct FilePerms {
|
||||||
u32 raw;
|
u32 raw;
|
||||||
|
|
||||||
FilePerms(u32 val) : raw(val) {}
|
FilePerms(u32 val) : raw(val) {}
|
||||||
bool read() const { return (raw & 1) != 0; }
|
bool read() const { return (raw & 1) != 0; }
|
||||||
bool write() const { return (raw & 2) != 0; }
|
bool write() const { return (raw & 2) != 0; }
|
||||||
bool create() const { return (raw & 4) != 0; }
|
bool create() const { return (raw & 4) != 0; }
|
||||||
};
|
};
|
||||||
|
|
||||||
class ArchiveBase;
|
class ArchiveBase;
|
||||||
struct FileSession {
|
struct FileSession {
|
||||||
ArchiveBase* archive = nullptr;
|
ArchiveBase* archive = nullptr;
|
||||||
FILE* fd = nullptr; // File descriptor for file sessions that require them.
|
FILE* fd = nullptr; // File descriptor for file sessions that require them.
|
||||||
FSPath path;
|
FSPath path;
|
||||||
FSPath archivePath;
|
FSPath archivePath;
|
||||||
u32 priority = 0; // TODO: What does this even do
|
u32 priority = 0; // TODO: What does this even do
|
||||||
bool isOpen;
|
bool isOpen;
|
||||||
|
|
||||||
FileSession(ArchiveBase* archive, const FSPath& filePath, const FSPath& archivePath, FILE* fd, bool isOpen = true) :
|
FileSession(ArchiveBase* archive, const FSPath& filePath, const FSPath& archivePath, FILE* fd, bool isOpen = true)
|
||||||
archive(archive), path(filePath), archivePath(archivePath), fd(fd), isOpen(isOpen), priority(0) {}
|
: archive(archive), path(filePath), archivePath(archivePath), fd(fd), isOpen(isOpen), priority(0) {}
|
||||||
|
|
||||||
// For cloning a file session
|
// For cloning a file session
|
||||||
FileSession(const FileSession& other) : archive(other.archive), path(other.path),
|
FileSession(const FileSession& other)
|
||||||
archivePath(other.archivePath), fd(other.fd), isOpen(other.isOpen), priority(other.priority) {}
|
: archive(other.archive), path(other.path), archivePath(other.archivePath), fd(other.fd), isOpen(other.isOpen), priority(other.priority) {}
|
||||||
};
|
};
|
||||||
|
|
||||||
struct ArchiveSession {
|
struct ArchiveSession {
|
||||||
ArchiveBase* archive = nullptr;
|
ArchiveBase* archive = nullptr;
|
||||||
FSPath path;
|
FSPath path;
|
||||||
bool isOpen;
|
bool isOpen;
|
||||||
|
|
||||||
ArchiveSession(ArchiveBase* archive, const FSPath& filePath, bool isOpen = true) : archive(archive), path(filePath), isOpen(isOpen) {}
|
ArchiveSession(ArchiveBase* archive, const FSPath& filePath, bool isOpen = true) : archive(archive), path(filePath), isOpen(isOpen) {}
|
||||||
};
|
};
|
||||||
|
|
||||||
struct DirectoryEntry {
|
struct DirectoryEntry {
|
||||||
|
@ -156,106 +160,104 @@ struct DirectorySession {
|
||||||
using FileDescriptor = std::optional<FILE*>;
|
using FileDescriptor = std::optional<FILE*>;
|
||||||
|
|
||||||
class ArchiveBase {
|
class ArchiveBase {
|
||||||
public:
|
public:
|
||||||
struct FormatInfo {
|
struct FormatInfo {
|
||||||
u32 size; // Archive size
|
u32 size; // Archive size
|
||||||
u32 numOfDirectories; // Number of directories
|
u32 numOfDirectories; // Number of directories
|
||||||
u32 numOfFiles; // Number of files
|
u32 numOfFiles; // Number of files
|
||||||
bool duplicateData; // Whether to duplicate data or not
|
bool duplicateData; // Whether to duplicate data or not
|
||||||
};
|
};
|
||||||
|
|
||||||
protected:
|
protected:
|
||||||
using Handle = u32;
|
using Handle = u32;
|
||||||
|
|
||||||
static constexpr FileDescriptor NoFile = nullptr;
|
static constexpr FileDescriptor NoFile = nullptr;
|
||||||
static constexpr FileDescriptor FileError = std::nullopt;
|
static constexpr FileDescriptor FileError = std::nullopt;
|
||||||
Memory& mem;
|
Memory& mem;
|
||||||
|
|
||||||
// Returns if a specified 3DS path in UTF16 or ASCII format is safe or not
|
// Returns if a specified 3DS path in UTF16 or ASCII format is safe or not
|
||||||
// A 3DS path is considered safe if its first character is '/' which means we're not trying to access anything outside the root of the fs
|
// A 3DS path is considered safe if its first character is '/' which means we're not trying to access anything outside the root of the fs
|
||||||
// And if it doesn't contain enough instances of ".." (Indicating "climb up a folder" in filesystems) to let the software climb up the directory tree
|
// And if it doesn't contain enough instances of ".." (Indicating "climb up a folder" in filesystems) to let the software climb up the directory
|
||||||
// And access files outside of the emulator's app data folder
|
// tree And access files outside of the emulator's app data folder
|
||||||
template <u32 format>
|
template <u32 format>
|
||||||
bool isPathSafe(const FSPath& path) {
|
bool isPathSafe(const FSPath& path) {
|
||||||
static_assert(format == PathType::ASCII || format == PathType::UTF16);
|
static_assert(format == PathType::ASCII || format == PathType::UTF16);
|
||||||
using String = typename std::conditional<format == PathType::UTF16, std::u16string, std::string>::type; // String type for the path
|
using String = typename std::conditional<format == PathType::UTF16, std::u16string, std::string>::type; // String type for the path
|
||||||
using Char = typename String::value_type; // Char type for the path
|
using Char = typename String::value_type; // Char type for the path
|
||||||
|
|
||||||
String pathString, dots;
|
String pathString, dots;
|
||||||
if constexpr (std::is_same<String, std::u16string>()) {
|
if constexpr (std::is_same<String, std::u16string>()) {
|
||||||
pathString = path.utf16_string;
|
pathString = path.utf16_string;
|
||||||
dots = u"..";
|
dots = u"..";
|
||||||
} else {
|
} else {
|
||||||
pathString = path.string;
|
pathString = path.string;
|
||||||
dots = "..";
|
dots = "..";
|
||||||
}
|
}
|
||||||
|
|
||||||
// If the path string doesn't begin with / then that means it's accessing outside the FS root, which is invalid & unsafe
|
// If the path string doesn't begin with / then that means it's accessing outside the FS root, which is invalid & unsafe
|
||||||
if (pathString[0] != Char('/')) return false;
|
if (pathString[0] != Char('/')) return false;
|
||||||
|
|
||||||
// Counts how many folders sans the root our file is nested under.
|
// Counts how many folders sans the root our file is nested under.
|
||||||
// If it's < 0 at any point of parsing, then the path is unsafe and tries to crawl outside our file sandbox.
|
// If it's < 0 at any point of parsing, then the path is unsafe and tries to crawl outside our file sandbox.
|
||||||
// If it's 0 then this is the FS root.
|
// If it's 0 then this is the FS root.
|
||||||
// If it's > 0 then we're in a subdirectory of the root.
|
// If it's > 0 then we're in a subdirectory of the root.
|
||||||
int level = 0;
|
int level = 0;
|
||||||
|
|
||||||
// Split the string on / characters and see how many of the substrings are ".."
|
// Split the string on / characters and see how many of the substrings are ".."
|
||||||
size_t pos = 0;
|
size_t pos = 0;
|
||||||
while ((pos = pathString.find(Char('/'))) != String::npos) {
|
while ((pos = pathString.find(Char('/'))) != String::npos) {
|
||||||
String token = pathString.substr(0, pos);
|
String token = pathString.substr(0, pos);
|
||||||
pathString.erase(0, pos + 1);
|
pathString.erase(0, pos + 1);
|
||||||
|
|
||||||
if (token == dots) {
|
if (token == dots) {
|
||||||
level--;
|
level--;
|
||||||
if (level < 0) return false;
|
if (level < 0) return false;
|
||||||
} else {
|
} else {
|
||||||
level++;
|
level++;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
public:
|
public:
|
||||||
virtual std::string name() = 0;
|
virtual std::string name() = 0;
|
||||||
virtual u64 getFreeBytes() = 0;
|
virtual u64 getFreeBytes() = 0;
|
||||||
virtual HorizonResult createFile(const FSPath& path, u64 size) = 0;
|
virtual HorizonResult createFile(const FSPath& path, u64 size) = 0;
|
||||||
virtual HorizonResult deleteFile(const FSPath& path) = 0;
|
virtual HorizonResult deleteFile(const FSPath& path) = 0;
|
||||||
|
|
||||||
virtual Rust::Result<FormatInfo, HorizonResult> getFormatInfo(const FSPath& path) {
|
virtual Rust::Result<FormatInfo, HorizonResult> getFormatInfo(const FSPath& path) {
|
||||||
Helpers::panic("Unimplemented GetFormatInfo for %s archive", name().c_str());
|
Helpers::panic("Unimplemented GetFormatInfo for %s archive", name().c_str());
|
||||||
// Return a dummy struct just to avoid the UB of not returning anything, even if we panic
|
// Return a dummy struct just to avoid the UB of not returning anything, even if we panic
|
||||||
return Ok(FormatInfo{ .size = 0, .numOfDirectories = 0, .numOfFiles = 0, .duplicateData = false });
|
return Ok(FormatInfo{.size = 0, .numOfDirectories = 0, .numOfFiles = 0, .duplicateData = false});
|
||||||
}
|
}
|
||||||
|
|
||||||
virtual HorizonResult createDirectory(const FSPath& path) {
|
virtual HorizonResult createDirectory(const FSPath& path) {
|
||||||
Helpers::panic("Unimplemented CreateDirectory for %s archive", name().c_str());
|
Helpers::panic("Unimplemented CreateDirectory for %s archive", name().c_str());
|
||||||
return Result::FS::AlreadyExists;
|
return Result::FS::AlreadyExists;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Returns nullopt if opening the file failed, otherwise returns a file descriptor to it (nullptr if none is needed)
|
// Returns nullopt if opening the file failed, otherwise returns a file descriptor to it (nullptr if none is needed)
|
||||||
virtual FileDescriptor openFile(const FSPath& path, const FilePerms& perms) = 0;
|
virtual FileDescriptor openFile(const FSPath& path, const FilePerms& perms) = 0;
|
||||||
virtual Rust::Result<ArchiveBase*, HorizonResult> openArchive(const FSPath& path) = 0;
|
virtual Rust::Result<ArchiveBase*, HorizonResult> openArchive(const FSPath& path) = 0;
|
||||||
|
|
||||||
virtual Rust::Result<DirectorySession, HorizonResult> openDirectory(const FSPath& path) {
|
virtual Rust::Result<DirectorySession, HorizonResult> openDirectory(const FSPath& path) {
|
||||||
Helpers::panic("Unimplemented OpenDirectory for %s archive", name().c_str());
|
Helpers::panic("Unimplemented OpenDirectory for %s archive", name().c_str());
|
||||||
return Err(Result::FS::FileNotFoundAlt);
|
return Err(Result::FS::FileNotFoundAlt);
|
||||||
}
|
}
|
||||||
|
|
||||||
virtual void format(const FSPath& path, const FormatInfo& info) {
|
virtual void format(const FSPath& path, const FormatInfo& info) { Helpers::panic("Unimplemented Format for %s archive", name().c_str()); }
|
||||||
Helpers::panic("Unimplemented Format for %s archive", name().c_str());
|
|
||||||
}
|
|
||||||
|
|
||||||
virtual HorizonResult renameFile(const FSPath& oldPath, const FSPath& newPath) {
|
virtual HorizonResult renameFile(const FSPath& oldPath, const FSPath& newPath) {
|
||||||
Helpers::panic("Unimplemented RenameFile for %s archive", name().c_str());
|
Helpers::panic("Unimplemented RenameFile for %s archive", name().c_str());
|
||||||
return Result::Success;
|
return Result::Success;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Read size bytes from a file starting at offset "offset" into a certain buffer in memory
|
// Read size bytes from a file starting at offset "offset" into a certain buffer in memory
|
||||||
// Returns the number of bytes read, or nullopt if the read failed
|
// Returns the number of bytes read, or nullopt if the read failed
|
||||||
virtual std::optional<u32> readFile(FileSession* file, u64 offset, u32 size, u32 dataPointer) = 0;
|
virtual std::optional<u32> readFile(FileSession* file, u64 offset, u32 size, u32 dataPointer) = 0;
|
||||||
|
|
||||||
ArchiveBase(Memory& mem) : mem(mem) {}
|
ArchiveBase(Memory& mem) : mem(mem) {}
|
||||||
};
|
};
|
||||||
|
|
||||||
struct ArchiveResource {
|
struct ArchiveResource {
|
||||||
|
|
30
include/fs/archive_twl_photo.hpp
Normal file
30
include/fs/archive_twl_photo.hpp
Normal file
|
@ -0,0 +1,30 @@
|
||||||
|
#pragma once
|
||||||
|
#include "archive_base.hpp"
|
||||||
|
#include "result/result.hpp"
|
||||||
|
|
||||||
|
using Result::HorizonResult;
|
||||||
|
|
||||||
|
class TWLPhotoArchive : public ArchiveBase {
|
||||||
|
public:
|
||||||
|
TWLPhotoArchive(Memory& mem) : ArchiveBase(mem) {}
|
||||||
|
std::string name() override { return "TWL_PHOTO"; }
|
||||||
|
|
||||||
|
u64 getFreeBytes() override {
|
||||||
|
Helpers::warn("Unimplemented GetFreeBytes for TWLPhoto archive");
|
||||||
|
return 32_MB;
|
||||||
|
}
|
||||||
|
|
||||||
|
HorizonResult createDirectory(const FSPath& path) override;
|
||||||
|
HorizonResult createFile(const FSPath& path, u64 size) override;
|
||||||
|
HorizonResult deleteFile(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 {
|
||||||
|
Helpers::panic("Unimplemented ReadFile for TWL_PHOTO archive");
|
||||||
|
return {};
|
||||||
|
};
|
||||||
|
};
|
30
include/fs/archive_twl_sound.hpp
Normal file
30
include/fs/archive_twl_sound.hpp
Normal file
|
@ -0,0 +1,30 @@
|
||||||
|
#pragma once
|
||||||
|
#include "archive_base.hpp"
|
||||||
|
#include "result/result.hpp"
|
||||||
|
|
||||||
|
using Result::HorizonResult;
|
||||||
|
|
||||||
|
class TWLSoundArchive : public ArchiveBase {
|
||||||
|
public:
|
||||||
|
TWLSoundArchive(Memory& mem) : ArchiveBase(mem) {}
|
||||||
|
std::string name() override { return "TWL_SOUND"; }
|
||||||
|
|
||||||
|
u64 getFreeBytes() override {
|
||||||
|
Helpers::warn("Unimplemented GetFreeBytes for TWLSound archive");
|
||||||
|
return 32_MB;
|
||||||
|
}
|
||||||
|
|
||||||
|
HorizonResult createDirectory(const FSPath& path) override;
|
||||||
|
HorizonResult createFile(const FSPath& path, u64 size) override;
|
||||||
|
HorizonResult deleteFile(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 {
|
||||||
|
Helpers::panic("Unimplemented ReadFile for TWL_SOUND archive");
|
||||||
|
return {};
|
||||||
|
};
|
||||||
|
};
|
|
@ -6,6 +6,8 @@
|
||||||
#include "fs/archive_sdmc.hpp"
|
#include "fs/archive_sdmc.hpp"
|
||||||
#include "fs/archive_self_ncch.hpp"
|
#include "fs/archive_self_ncch.hpp"
|
||||||
#include "fs/archive_system_save_data.hpp"
|
#include "fs/archive_system_save_data.hpp"
|
||||||
|
#include "fs/archive_twl_photo.hpp"
|
||||||
|
#include "fs/archive_twl_sound.hpp"
|
||||||
#include "fs/archive_user_save_data.hpp"
|
#include "fs/archive_user_save_data.hpp"
|
||||||
#include "helpers.hpp"
|
#include "helpers.hpp"
|
||||||
#include "kernel_types.hpp"
|
#include "kernel_types.hpp"
|
||||||
|
@ -39,6 +41,9 @@ class FSService {
|
||||||
ExtSaveDataArchive sharedExtSaveData_nand;
|
ExtSaveDataArchive sharedExtSaveData_nand;
|
||||||
SystemSaveDataArchive systemSaveData;
|
SystemSaveDataArchive systemSaveData;
|
||||||
|
|
||||||
|
TWLPhotoArchive twlPhoto;
|
||||||
|
TWLSoundArchive twlSound;
|
||||||
|
|
||||||
ArchiveBase* getArchiveFromID(u32 id, const FSPath& archivePath);
|
ArchiveBase* getArchiveFromID(u32 id, const FSPath& archivePath);
|
||||||
Rust::Result<Handle, HorizonResult> openArchiveHandle(u32 archiveID, const FSPath& path);
|
Rust::Result<Handle, HorizonResult> openArchiveHandle(u32 archiveID, const FSPath& path);
|
||||||
Rust::Result<Handle, HorizonResult> openDirectoryHandle(ArchiveBase* archive, const FSPath& path);
|
Rust::Result<Handle, HorizonResult> openDirectoryHandle(ArchiveBase* archive, const FSPath& path);
|
||||||
|
@ -87,7 +92,7 @@ class FSService {
|
||||||
FSService(Memory& mem, Kernel& kernel, const EmulatorConfig& config)
|
FSService(Memory& mem, Kernel& kernel, const EmulatorConfig& config)
|
||||||
: mem(mem), saveData(mem), sharedExtSaveData_nand(mem, "../SharedFiles/NAND", true), extSaveData_sdmc(mem, "SDMC"), sdmc(mem),
|
: mem(mem), saveData(mem), sharedExtSaveData_nand(mem, "../SharedFiles/NAND", true), extSaveData_sdmc(mem, "SDMC"), sdmc(mem),
|
||||||
sdmcWriteOnly(mem, true), selfNcch(mem), ncch(mem), userSaveData1(mem, ArchiveID::UserSaveData1),
|
sdmcWriteOnly(mem, true), selfNcch(mem), ncch(mem), userSaveData1(mem, ArchiveID::UserSaveData1),
|
||||||
userSaveData2(mem, ArchiveID::UserSaveData2), kernel(kernel), config(config), systemSaveData(mem) {}
|
userSaveData2(mem, ArchiveID::UserSaveData2), systemSaveData(mem), twlPhoto(mem), twlSound(mem), kernel(kernel), config(config) {}
|
||||||
|
|
||||||
void reset();
|
void reset();
|
||||||
void handleSyncRequest(u32 messagePointer);
|
void handleSyncRequest(u32 messagePointer);
|
||||||
|
|
|
@ -87,7 +87,7 @@ HorizonResult SystemSaveDataArchive::createFile(const FSPath& path, u64 size) {
|
||||||
HorizonResult SystemSaveDataArchive::createDirectory(const FSPath& path) {
|
HorizonResult SystemSaveDataArchive::createDirectory(const FSPath& path) {
|
||||||
if (path.type == PathType::UTF16) {
|
if (path.type == PathType::UTF16) {
|
||||||
if (!isPathSafe<PathType::UTF16>(path)) {
|
if (!isPathSafe<PathType::UTF16>(path)) {
|
||||||
Helpers::panic("Unsafe path in SystemSaveData::OpenFile");
|
Helpers::panic("Unsafe path in SystemSaveData::CreateDirectory");
|
||||||
}
|
}
|
||||||
|
|
||||||
fs::path p = IOFile::getAppData() / ".." / "SharedFiles" / "SystemSaveData";
|
fs::path p = IOFile::getAppData() / ".." / "SharedFiles" / "SystemSaveData";
|
||||||
|
|
40
src/core/fs/archive_twl_photo.cpp
Normal file
40
src/core/fs/archive_twl_photo.cpp
Normal file
|
@ -0,0 +1,40 @@
|
||||||
|
#include <algorithm>
|
||||||
|
#include <memory>
|
||||||
|
|
||||||
|
#include "fs/archive_twl_photo.hpp"
|
||||||
|
|
||||||
|
namespace fs = std::filesystem;
|
||||||
|
|
||||||
|
HorizonResult TWLPhotoArchive::createFile(const FSPath& path, u64 size) {
|
||||||
|
Helpers::panic("[TWL_PHOTO] CreateFile not yet supported");
|
||||||
|
return Result::Success;
|
||||||
|
}
|
||||||
|
|
||||||
|
HorizonResult TWLPhotoArchive::deleteFile(const FSPath& path) {
|
||||||
|
Helpers::panic("[TWL_PHOTO] Unimplemented DeleteFile");
|
||||||
|
return Result::Success;
|
||||||
|
}
|
||||||
|
|
||||||
|
HorizonResult TWLPhotoArchive::createDirectory(const FSPath& path) {
|
||||||
|
Helpers::panic("[TWL_PHOTO] CreateDirectory not yet supported");
|
||||||
|
return Result::Success;
|
||||||
|
}
|
||||||
|
|
||||||
|
FileDescriptor TWLPhotoArchive::openFile(const FSPath& path, const FilePerms& perms) {
|
||||||
|
Helpers::panic("[TWL_PHOTO] OpenFile not yet supported");
|
||||||
|
return FileError;
|
||||||
|
}
|
||||||
|
|
||||||
|
Rust::Result<ArchiveBase*, HorizonResult> TWLPhotoArchive::openArchive(const FSPath& path) {
|
||||||
|
if (path.type != PathType::Empty) {
|
||||||
|
Helpers::panic("Unimplemented path type for TWLPhotoArchive::OpenArchive");
|
||||||
|
}
|
||||||
|
|
||||||
|
Helpers::warn("Unimplemented: TWL_PHOTO archive");
|
||||||
|
return Err(Result::FailurePlaceholder);
|
||||||
|
}
|
||||||
|
|
||||||
|
Rust::Result<DirectorySession, HorizonResult> TWLPhotoArchive::openDirectory(const FSPath& path) {
|
||||||
|
Helpers::panic("[TWL_PHOTO] OpenDirectory not yet supported");
|
||||||
|
return Err(Result::FailurePlaceholder);
|
||||||
|
}
|
40
src/core/fs/archive_twl_sound.cpp
Normal file
40
src/core/fs/archive_twl_sound.cpp
Normal file
|
@ -0,0 +1,40 @@
|
||||||
|
#include <algorithm>
|
||||||
|
#include <memory>
|
||||||
|
|
||||||
|
#include "fs/archive_twl_sound.hpp"
|
||||||
|
|
||||||
|
namespace fs = std::filesystem;
|
||||||
|
|
||||||
|
HorizonResult TWLSoundArchive::createFile(const FSPath& path, u64 size) {
|
||||||
|
Helpers::panic("[TWL_SOUND] CreateFile not yet supported");
|
||||||
|
return Result::Success;
|
||||||
|
}
|
||||||
|
|
||||||
|
HorizonResult TWLSoundArchive::deleteFile(const FSPath& path) {
|
||||||
|
Helpers::panic("[TWL_SOUND] Unimplemented DeleteFile");
|
||||||
|
return Result::Success;
|
||||||
|
}
|
||||||
|
|
||||||
|
HorizonResult TWLSoundArchive::createDirectory(const FSPath& path) {
|
||||||
|
Helpers::panic("[TWL_SOUND] CreateDirectory not yet supported");
|
||||||
|
return Result::Success;
|
||||||
|
}
|
||||||
|
|
||||||
|
FileDescriptor TWLSoundArchive::openFile(const FSPath& path, const FilePerms& perms) {
|
||||||
|
Helpers::panic("[TWL_SOUND] OpenFile not yet supported");
|
||||||
|
return FileError;
|
||||||
|
}
|
||||||
|
|
||||||
|
Rust::Result<ArchiveBase*, HorizonResult> TWLSoundArchive::openArchive(const FSPath& path) {
|
||||||
|
if (path.type != PathType::Empty) {
|
||||||
|
Helpers::panic("Unimplemented path type for TWLSoundArchive::OpenArchive");
|
||||||
|
}
|
||||||
|
|
||||||
|
Helpers::warn("Unimplemented: TWL_SOUND archive");
|
||||||
|
return Err(Result::FailurePlaceholder);
|
||||||
|
}
|
||||||
|
|
||||||
|
Rust::Result<DirectorySession, HorizonResult> TWLSoundArchive::openDirectory(const FSPath& path) {
|
||||||
|
Helpers::panic("[TWL_SOUND] OpenDirectory not yet supported");
|
||||||
|
return Err(Result::FailurePlaceholder);
|
||||||
|
}
|
|
@ -99,6 +99,10 @@ ArchiveBase* FSService::getArchiveFromID(u32 id, const FSPath& archivePath) {
|
||||||
case ArchiveID::SDMC: return &sdmc;
|
case ArchiveID::SDMC: return &sdmc;
|
||||||
case ArchiveID::SDMCWriteOnly: return &sdmcWriteOnly;
|
case ArchiveID::SDMCWriteOnly: return &sdmcWriteOnly;
|
||||||
case ArchiveID::SavedataAndNcch: return &ncch; // This can only access NCCH outside of FSPXI
|
case ArchiveID::SavedataAndNcch: return &ncch; // This can only access NCCH outside of FSPXI
|
||||||
|
|
||||||
|
case ArchiveID::TwlPhoto: return &twlPhoto;
|
||||||
|
case ArchiveID::TwlSound: return &twlSound;
|
||||||
|
|
||||||
default:
|
default:
|
||||||
Helpers::panic("Unknown archive. ID: %d\n", id);
|
Helpers::panic("Unknown archive. ID: %d\n", id);
|
||||||
return nullptr;
|
return nullptr;
|
||||||
|
|
Loading…
Add table
Reference in a new issue