many more optimisations. cleaned up fs code. bug fixes etc (see below).

- fix usb using the wrong year when polling the timestamp.
- fs file/dir has been re-written to allow for simplified calling and remove the need of manually closing.
- add SetSize for stdio by using ftruncate.
- don't truncate the file when opened in stdio.
- add getcount api for stdio.
- display file/dir count in filebrowser for non-native fs.
- allow hash to be used on non-native fs.
- slightly optimise nro parsing by manually calculating nro size rather than doing an os call.
- slightly optimise nro parsing by keeping the fs struct alive between calls, rather than creating a new one on the stack.
- fix filebrowser peeking into zip files that are stored on non-sd fs.
- set the timestamp of a file moved to a stdio location (cut/paste).
- slightly optimise daybreak update folder detection by skipping opening/polling the dir size.
- set the fullpath of the file thats being hashed in progress box.
This commit is contained in:
ITotalJustice
2025-05-26 17:06:04 +01:00
parent 3e3ec71329
commit a9931a975d
23 changed files with 390 additions and 387 deletions

View File

@@ -197,7 +197,7 @@ FetchContent_Declare(zstd
FetchContent_Declare(libusbhsfs
GIT_REPOSITORY https://github.com/ITotalJustice/libusbhsfs.git
GIT_TAG d2395e9
GIT_TAG b963156
)
set(USE_NEW_ZSTD ON)

View File

@@ -174,20 +174,34 @@ static_assert(FsPath::TestFrom(FsPath{"abc"}));
struct Fs;
struct File {
fs::Fs* m_fs;
FsFile m_native;
std::FILE* m_stdio;
s64 m_stdio_off;
~File();
Result Read(s64 off, void* buf, u64 read_size, u32 option, u64* bytes_read);
Result Write(s64 off, const void* buf, u64 write_size, u32 option);
Result SetSize(s64 sz);
Result GetSize(s64* out);
void Close();
fs::Fs* m_fs{};
FsFile m_native{};
std::FILE* m_stdio{};
s64 m_stdio_off{};
// sadly, fatfs doesn't support fstat, so we have to manually
// stat the file to get it's size.
FsPath m_path;
FsPath m_path{};
};
struct Dir {
fs::Fs* m_fs;
FsDir m_native;
DIR* m_stdio;
u32 m_mode;
~Dir();
Result GetEntryCount(s64* out);
Result ReadAll(std::vector<FsDirectoryEntry>& buf);
void Close();
fs::Fs* m_fs{};
FsDir m_native{};
DIR* m_stdio{};
u32 m_mode{};
};
FsPath AppendPath(const fs::FsPath& root_path, const fs::FsPath& file_path);
@@ -203,6 +217,7 @@ Result RenameFile(FsFileSystem* fs, const FsPath& src, const FsPath& dst, bool i
Result RenameDirectory(FsFileSystem* fs, const FsPath& src, const FsPath& dst, bool ignore_read_only = true);
Result GetEntryType(FsFileSystem* fs, const FsPath& path, FsDirEntryType* out);
Result GetFileTimeStampRaw(FsFileSystem* fs, const FsPath& path, FsTimeStampRaw *out);
Result SetTimestamp(FsFileSystem* fs, const FsPath& path, const FsTimeStampRaw* ts);
bool FileExists(FsFileSystem* fs, const FsPath& path);
bool DirExists(FsFileSystem* fs, const FsPath& path);
Result read_entire_file(FsFileSystem* fs, const FsPath& path, std::vector<u8>& out);
@@ -220,6 +235,7 @@ Result RenameFile(const FsPath& src, const FsPath& dst, bool ignore_read_only =
Result RenameDirectory(const FsPath& src, const FsPath& dst, bool ignore_read_only = true);
Result GetEntryType(const FsPath& path, FsDirEntryType* out);
Result GetFileTimeStampRaw(const FsPath& path, FsTimeStampRaw *out);
Result SetTimestamp(const FsPath& path, const FsTimeStampRaw* ts);
bool FileExists(const FsPath& path);
bool DirExists(const FsPath& path);
Result read_entire_file(const FsPath& path, std::vector<u8>& out);
@@ -227,15 +243,15 @@ Result write_entire_file(const FsPath& path, const std::vector<u8>& in, bool ign
Result copy_entire_file(const FsPath& dst, const FsPath& src, bool ignore_read_only = true);
Result OpenFile(fs::Fs* fs, const fs::FsPath& path, u32 mode, File* f);
Result FileRead(File* f, s64 off, void* buf, u64 read_size, u32 option, u64* bytes_read);
Result FileWrite(File* f, s64 off, const void* buf, u64 write_size, u32 option);
Result FileSetSize(File* f, s64 sz);
Result FileGetSize(File* f, s64* out);
void FileClose(File* f);
Result OpenDirectory(fs::Fs* fs, const fs::FsPath& path, u32 mode, Dir* d);
Result DirReadAll(Dir* d, std::vector<FsDirectoryEntry>& buf);
void DirClose(Dir* d);
// opens dir, fetches count for all entries.
// NOTE: this function will be slow on non-native fs, due to multiple
// readdir() functions being needed!
Result DirGetEntryCount(fs::Fs* fs, const fs::FsPath& path, s64* count, u32 mode);
// same as the above, but fetches file and folder count in a single pass
// this is faster when using native, and *much* faster for stdio.
Result DirGetEntryCount(fs::Fs* fs, const fs::FsPath& path, s64* file_count, s64* dir_count, u32 mode = FsDirOpenMode_ReadDirs|FsDirOpenMode_ReadFiles);
// optimised for stdio calls as stat returns size and timestamp in a single call.
// whereas for native, this is 2 function calls.
@@ -275,6 +291,7 @@ struct Fs {
virtual Result RenameDirectory(const FsPath& src, const FsPath& dst) = 0;
virtual Result GetEntryType(const FsPath& path, FsDirEntryType* out) = 0;
virtual Result GetFileTimeStampRaw(const FsPath& path, FsTimeStampRaw *out) = 0;
virtual Result SetTimestamp(const FsPath& path, const FsTimeStampRaw* ts) = 0;
virtual bool FileExists(const FsPath& path) = 0;
virtual bool DirExists(const FsPath& path) = 0;
virtual bool IsNative() const = 0;
@@ -286,32 +303,15 @@ struct Fs {
Result OpenFile(const fs::FsPath& path, u32 mode, File* f) {
return fs::OpenFile(this, path, mode, f);
}
Result FileRead(File* f, s64 off, void* buf, u64 read_size, u32 option, u64* bytes_read) {
return fs::FileRead(f, off, buf, read_size, option, bytes_read);
}
Result FileWrite(File* f, s64 off, const void* buf, u64 write_size, u32 option) {
return fs::FileWrite(f, off, buf, write_size, option);
}
Result FileSetSize(File* f, s64 sz) {
return fs::FileSetSize(f, sz);
}
Result FileGetSize(File* f, s64* out) {
return fs::FileGetSize(f, out);
}
void FileClose(File* f) {
fs::FileClose(f);
}
Result OpenDirectory(const fs::FsPath& path, u32 mode, Dir* d) {
return fs::OpenDirectory(this, path, mode, d);
}
Result DirReadAll(Dir* d, std::vector<FsDirectoryEntry>& buf) {
return fs::DirReadAll(d, buf);
Result DirGetEntryCount(const fs::FsPath& path, s64* count, u32 mode) {
return fs::DirGetEntryCount(this, path, count, mode);
}
void DirClose(Dir* d) {
fs::DirClose(d);
Result DirGetEntryCount(const fs::FsPath& path, s64* file_count, s64* dir_count, u32 mode = FsDirOpenMode_ReadDirs|FsDirOpenMode_ReadFiles) {
return fs::DirGetEntryCount(this, path, file_count, dir_count, mode);
}
Result FileGetSizeAndTimestamp(const FsPath& path, FsTimeStampRaw* ts, s64* size) {
return fs::FileGetSizeAndTimestamp(this, path, ts, size);
}
@@ -364,6 +364,9 @@ struct FsStdio : Fs {
Result GetFileTimeStampRaw(const FsPath& path, FsTimeStampRaw *out) override {
return fs::GetFileTimeStampRaw(path, out);
}
Result SetTimestamp(const FsPath& path, const FsTimeStampRaw *ts) override {
return fs::SetTimestamp(path, ts);
}
bool FileExists(const FsPath& path) override {
return fs::FileExists(path);
}
@@ -411,39 +414,35 @@ struct FsNative : Fs {
return fsFsGetTotalSpace(&m_fs, path, out);
}
Result OpenFile(const FsPath& path, u32 mode, FsFile *out) {
return fsFsOpenFile(&m_fs, path, mode, out);
}
// Result OpenDirectory(const FsPath& path, u32 mode, FsDir *out) {
// return fsFsOpenDirectory(&m_fs, path, mode, out);
// }
Result OpenDirectory(const FsPath& path, u32 mode, FsDir *out) {
return fsFsOpenDirectory(&m_fs, path, mode, out);
}
// void DirClose(FsDir *d) {
// fsDirClose(d);
// }
void DirClose(FsDir *d) {
fsDirClose(d);
}
// Result DirGetEntryCount(FsDir *d, s64* out) {
// return fsDirGetEntryCount(d, out);
// }
Result DirGetEntryCount(FsDir *d, s64* out) {
return fsDirGetEntryCount(d, out);
}
// Result DirGetEntryCount(const FsPath& path, u32 mode, s64* out) {
// FsDir d;
// R_TRY(OpenDirectory(path, mode, &d));
// ON_SCOPE_EXIT(DirClose(&d));
// return DirGetEntryCount(&d, out);
// }
Result DirGetEntryCount(const FsPath& path, u32 mode, s64* out) {
FsDir d;
R_TRY(OpenDirectory(path, mode, &d));
ON_SCOPE_EXIT(DirClose(&d));
return DirGetEntryCount(&d, out);
}
// Result DirRead(FsDir *d, s64 *total_entries, size_t max_entries, FsDirectoryEntry *buf) {
// return fsDirRead(d, total_entries, max_entries, buf);
// }
Result DirRead(FsDir *d, s64 *total_entries, size_t max_entries, FsDirectoryEntry *buf) {
return fsDirRead(d, total_entries, max_entries, buf);
}
Result DirRead(const FsPath& path, u32 mode, s64 *total_entries, size_t max_entries, FsDirectoryEntry *buf) {
FsDir d;
R_TRY(OpenDirectory(path, mode, &d));
ON_SCOPE_EXIT(DirClose(&d));
return DirRead(&d, total_entries, max_entries, buf);
}
// Result DirRead(const FsPath& path, u32 mode, s64 *total_entries, size_t max_entries, FsDirectoryEntry *buf) {
// FsDir d;
// R_TRY(OpenDirectory(path, mode, &d));
// ON_SCOPE_EXIT(DirClose(&d));
// return DirRead(&d, total_entries, max_entries, buf);
// }
virtual bool IsFsActive() {
return serviceIsActive(&m_fs.s);
@@ -486,6 +485,9 @@ struct FsNative : Fs {
Result GetFileTimeStampRaw(const FsPath& path, FsTimeStampRaw *out) override {
return fs::GetFileTimeStampRaw(&m_fs, path, out);
}
Result SetTimestamp(const FsPath& path, const FsTimeStampRaw *ts) override {
return fs::SetTimestamp(&m_fs, path, ts);
}
bool FileExists(const FsPath& path) override {
return fs::FileExists(&m_fs, path);
}

View File

@@ -8,7 +8,6 @@ namespace sphaira::ui::menu::fileview {
struct Menu final : MenuBase {
Menu(const fs::FsPath& path);
~Menu();
auto GetShortTitle() const -> const char* override { return "File"; };
void Update(Controller* controller, TouchInfo* touch) override;
@@ -18,7 +17,7 @@ struct Menu final : MenuBase {
private:
const fs::FsPath m_path;
fs::FsNativeSd m_fs{};
FsFile m_file{};
fs::File m_file{};
s64 m_file_size{};
s64 m_file_offset{};

View File

@@ -8,14 +8,11 @@
namespace sphaira::yati::source {
struct File final : Base {
File(FsFileSystem* fs, const fs::FsPath& path);
File(const fs::FsPath& path);
~File();
File(fs::Fs* fs, const fs::FsPath& path);
Result Read(void* buf, s64 off, s64 size, u64* bytes_read) override;
private:
std::unique_ptr<fs::Fs> m_fs{};
fs::Fs* m_fs{};
fs::File m_file{};
};

View File

@@ -10,14 +10,11 @@
namespace sphaira::yati::source {
struct StreamFile final : Stream {
StreamFile(FsFileSystem* fs, const fs::FsPath& path);
StreamFile(const fs::FsPath& path);
~StreamFile();
StreamFile(fs::Fs* fs, const fs::FsPath& path);
Result ReadChunk(void* buf, s64 size, u64* bytes_read) override;
private:
std::unique_ptr<fs::Fs> m_fs{};
fs::Fs* m_fs{};
fs::File m_file{};
s64 m_offset{};
};

View File

@@ -125,8 +125,7 @@ struct ConfigOverride {
std::optional<bool> lower_system_version{};
};
Result InstallFromFile(ui::ProgressBox* pbox, FsFileSystem* fs, const fs::FsPath& path, const ConfigOverride& override = {});
Result InstallFromFile(ui::ProgressBox* pbox, const fs::FsPath& path, const ConfigOverride& override = {});
Result InstallFromFile(ui::ProgressBox* pbox, fs::Fs* fs, const fs::FsPath& path, const ConfigOverride& override = {});
Result InstallFromSource(ui::ProgressBox* pbox, std::shared_ptr<source::Base> source, const fs::FsPath& path, const ConfigOverride& override = {});
Result InstallFromContainer(ui::ProgressBox* pbox, std::shared_ptr<container::Base> container, const ConfigOverride& override = {});
Result InstallFromCollections(ui::ProgressBox* pbox, std::shared_ptr<source::Base> source, const container::Collections& collections, const ConfigOverride& override = {});

View File

@@ -46,13 +46,13 @@ struct UploadStruct {
std::span<const u8> data;
s64 offset{};
s64 size{};
FsFile f{};
fs::File f{};
};
struct DataStruct {
std::vector<u8> data;
s64 offset{};
FsFile f{};
fs::File f{};
s64 file_offset{};
};
@@ -439,7 +439,7 @@ auto ReadFileCallback(char *ptr, size_t size, size_t nmemb, void *userp) -> size
const auto realsize = size * nmemb;
u64 bytes_read;
if (R_FAILED(fsFileRead(&data_struct->f, data_struct->offset, ptr, realsize, FsReadOption_None, &bytes_read))) {
if (R_FAILED(data_struct->f.Read(data_struct->offset, ptr, realsize, FsReadOption_None, &bytes_read))) {
log_write("reading file error\n");
return 0;
}
@@ -509,7 +509,7 @@ auto WriteFileCallback(void *contents, size_t size, size_t num_files, void *user
// flush data if incomming data would overflow the buffer
if (data_struct->offset && data_struct->data.size() < data_struct->offset + realsize) {
if (R_FAILED(fsFileWrite(&data_struct->f, data_struct->file_offset, data_struct->data.data(), data_struct->offset, FsWriteOption_None))) {
if (R_FAILED(data_struct->f.Write(data_struct->file_offset, data_struct->data.data(), data_struct->offset, FsWriteOption_None))) {
return 0;
}
@@ -519,7 +519,7 @@ auto WriteFileCallback(void *contents, size_t size, size_t num_files, void *user
// we have a huge chunk! write it directly to file
if (data_struct->data.size() < realsize) {
if (R_FAILED(fsFileWrite(&data_struct->f, data_struct->file_offset, contents, realsize, FsWriteOption_None))) {
if (R_FAILED(data_struct->f.Write(data_struct->file_offset, contents, realsize, FsWriteOption_None))) {
return 0;
}
@@ -762,10 +762,10 @@ auto DownloadInternal(CURL* curl, const Api& e) -> ApiResult {
if (has_file) {
ON_SCOPE_EXIT( fs.DeleteFile(tmp_buf) );
if (res == CURLE_OK && chunk.offset) {
fsFileWrite(&chunk.f, chunk.file_offset, chunk.data.data(), chunk.offset, FsWriteOption_None);
chunk.f.Write(chunk.file_offset, chunk.data.data(), chunk.offset, FsWriteOption_None);
}
fsFileClose(&chunk.f);
chunk.f.Close();
if (res == CURLE_OK) {
if (http_code == 304) {
@@ -835,7 +835,7 @@ auto UploadInternal(CURL* curl, const Api& e) -> ApiResult {
return {};
}
fsFileGetSize(&chunk.f, &chunk.size);
chunk.f.GetSize(&chunk.size);
log_write("got chunk size: %zd\n", chunk.size);
} else {
if (info.m_callback) {
@@ -929,7 +929,7 @@ auto UploadInternal(CURL* curl, const Api& e) -> ApiResult {
curl_easy_getinfo(curl, CURLINFO_RESPONSE_CODE, &http_code);
if (has_file) {
fsFileClose(&chunk.f);
chunk.f.Close();
}
log_write("Uploaded %s code: %ld %s\n", url.c_str(), http_code, curl_easy_strerror(res));

View File

@@ -124,14 +124,13 @@ Result DumpToFile(ui::ProgressBox* pbox, fs::Fs* fs, const fs::FsPath& root, Bas
{
fs::File file;
R_TRY(fs->OpenFile(temp_path, FsOpenMode_Write, &file));
ON_SCOPE_EXIT(fs->FileClose(&file));
R_TRY(thread::Transfer(pbox, file_size,
[&](void* data, s64 off, s64 size, u64* bytes_read) -> Result {
return source->Read(path, data, off, size, bytes_read);
},
[&](const void* data, s64 off, s64 size) -> Result {
return fs->FileWrite(&file, off, data, size, FsWriteOption_None);
return file.Write(off, data, size, FsWriteOption_None);
}
));
}

View File

@@ -221,6 +221,11 @@ Result GetFileTimeStampRaw(FsFileSystem* fs, const FsPath& path, FsTimeStampRaw
return fsFsGetFileTimeStampRaw(fs, path, out);
}
Result SetTimestamp(FsFileSystem* fs, const FsPath& path, const FsTimeStampRaw* ts) {
// unsuported.
R_SUCCEED();
}
bool FileExists(FsFileSystem* fs, const FsPath& path) {
FsDirEntryType type;
R_TRY_RESULT(GetEntryType(fs, path, &type), false);
@@ -237,16 +242,16 @@ Result read_entire_file(FsFileSystem* _fs, const FsPath& path, std::vector<u8>&
FsNative fs{_fs, false};
R_TRY(fs.GetFsOpenResult());
FsFile f;
File f;
R_TRY(fs.OpenFile(path, FsOpenMode_Read, &f));
ON_SCOPE_EXIT(fsFileClose(&f));
ON_SCOPE_EXIT(f.Close());
s64 size;
R_TRY(fsFileGetSize(&f, &size));
R_TRY(f.GetSize(&size));
out.resize(size);
u64 bytes_read;
R_TRY(fsFileRead(&f, 0, out.data(), out.size(), FsReadOption_None, &bytes_read));
R_TRY(f.Read(0, out.data(), out.size(), FsReadOption_None, &bytes_read));
R_UNLESS(bytes_read == out.size(), 1);
R_SUCCEED();
@@ -262,12 +267,12 @@ Result write_entire_file(FsFileSystem* _fs, const FsPath& path, const std::vecto
return rc;
}
FsFile f;
File f;
R_TRY(fs.OpenFile(path, FsOpenMode_Write, &f));
ON_SCOPE_EXIT(fsFileClose(&f));
ON_SCOPE_EXIT(f.Close());
R_TRY(fsFileSetSize(&f, in.size()));
R_TRY(fsFileWrite(&f, 0, in.data(), in.size(), FsWriteOption_None));
R_TRY(f.SetSize(in.size()));
R_TRY(f.Write(0, in.data(), in.size(), FsWriteOption_None));
R_SUCCEED();
}
@@ -292,7 +297,12 @@ Result CreateFile(const FsPath& path, u64 size, u32 option, bool ignore_read_onl
R_TRY(fsdevGetLastResult());
return Fs::ResultUnknownStdioError;
}
close(fd);
ON_SCOPE_EXIT(close(fd));
if (size) {
R_UNLESS(!ftruncate(fd, size), Fs::ResultUnknownStdioError);
}
R_SUCCEED();
}
@@ -405,6 +415,20 @@ Result GetFileTimeStampRaw(const FsPath& path, FsTimeStampRaw *out) {
R_SUCCEED();
}
Result SetTimestamp(const FsPath& path, const FsTimeStampRaw* ts) {
if (ts->is_valid) {
timeval val[2]{};
val[0].tv_sec = ts->accessed;
val[1].tv_sec = ts->modified;
if (utimes(path, val)) {
log_write("utimes() failed: %d %s\n", errno, strerror(errno));
}
}
R_SUCCEED();
}
bool FileExists(const FsPath& path) {
FsDirEntryType type;
R_TRY_RESULT(GetEntryType(path, &type), false);
@@ -458,92 +482,95 @@ Result copy_entire_file(const FsPath& dst, const FsPath& src, bool ignore_read_o
}
Result OpenFile(fs::Fs* fs, const fs::FsPath& path, u32 mode, File* f) {
*f = {};
f->m_fs = fs;
if (f->m_fs->IsNative()) {
auto fs = (fs::FsNative*)f->m_fs;
R_TRY(fs->OpenFile(path, mode, &f->m_native));
R_TRY(fsFsOpenFile(&fs->m_fs, path, mode, &f->m_native));
} else {
if ((mode & FsOpenMode_Read) && (mode & FsOpenMode_Write)) {
// todo:
R_THROW(0x1);
f->m_stdio = std::fopen(path, "rb+");
} else if (mode & FsOpenMode_Read) {
f->m_stdio = std::fopen(path, "rb");
} else if (mode & FsOpenMode_Write) {
f->m_stdio = std::fopen(path, "wb");
}
R_UNLESS(f->m_stdio, 0x1);
// not possible to open file with just write and not append
// or create or truncate. So rw it is!
f->m_stdio = std::fopen(path, "rb+");
}
R_UNLESS(f->m_stdio, Fs::ResultUnknownStdioError);
std::strcpy(f->m_path, path);
}
R_SUCCEED();
}
Result FileRead(File* f, s64 off, void* buf, u64 read_size, u32 option, u64* bytes_read) {
if (f->m_fs->IsNative()) {
R_TRY(fsFileRead(&f->m_native, off, buf, read_size, option, bytes_read));
} else {
if (f->m_stdio_off != off) {
f->m_stdio_off = off;
std::fseek(f->m_stdio, off, SEEK_SET);
File::~File() {
Close();
}
*bytes_read = std::fread(buf, 1, read_size, f->m_stdio);
Result File::Read( s64 off, void* buf, u64 read_size, u32 option, u64* bytes_read) {
if (m_fs->IsNative()) {
R_TRY(fsFileRead(&m_native, off, buf, read_size, option, bytes_read));
} else {
if (m_stdio_off != off) {
m_stdio_off = off;
std::fseek(m_stdio, off, SEEK_SET);
}
*bytes_read = std::fread(buf, 1, read_size, m_stdio);
// if we read less bytes than expected, check if there was an error (ignoring eof).
if (*bytes_read < read_size) {
if (!std::feof(f->m_stdio) && std::ferror(f->m_stdio)) {
R_THROW(0x1);
if (!std::feof(m_stdio) && std::ferror(m_stdio)) {
R_THROW(Fs::ResultUnknownStdioError);
}
}
f->m_stdio_off += *bytes_read;
m_stdio_off += *bytes_read;
}
R_SUCCEED();
}
Result FileWrite(File* f, s64 off, const void* buf, u64 write_size, u32 option) {
if (f->m_fs->IsNative()) {
R_TRY(fsFileWrite(&f->m_native, off, buf, write_size, option));
Result File::Write(s64 off, const void* buf, u64 write_size, u32 option) {
if (m_fs->IsNative()) {
R_TRY(fsFileWrite(&m_native, off, buf, write_size, option));
} else {
if (f->m_stdio_off != off) {
if (m_stdio_off != off) {
log_write("[FS] diff seek\n");
f->m_stdio_off = off;
std::fseek(f->m_stdio, off, SEEK_SET);
m_stdio_off = off;
std::fseek(m_stdio, off, SEEK_SET);
}
const auto result = std::fwrite(buf, 1, write_size, f->m_stdio);
const auto result = std::fwrite(buf, 1, write_size, m_stdio);
// log_write("[FS] fwrite res: %zu vs %zu\n", result, write_size);
R_UNLESS(result == write_size, 0x1);
R_UNLESS(result == write_size, Fs::ResultUnknownStdioError);
f->m_stdio_off += write_size;
m_stdio_off += write_size;
}
R_SUCCEED();
}
Result FileSetSize(File* f, s64 sz) {
if (f->m_fs->IsNative()) {
R_TRY(fsFileSetSize(&f->m_native, sz));
Result File::SetSize(s64 sz) {
if (m_fs->IsNative()) {
R_TRY(fsFileSetSize(&m_native, sz));
} else {
R_SUCCEED();
// const auto fd = fileno(f->m_stdio);
// R_UNLESS(fd > 0, 0x1);
// R_UNLESS(!ftruncate(fd, sz), 0x1);
const auto fd = fileno(m_stdio);
R_UNLESS(fd > 0, Fs::ResultUnknownStdioError);
R_UNLESS(!ftruncate(fd, sz), Fs::ResultUnknownStdioError);
}
R_SUCCEED();
}
Result FileGetSize(File* f, s64* out) {
if (f->m_fs->IsNative()) {
R_TRY(fsFileGetSize(&f->m_native, out));
Result File::GetSize(s64* out) {
if (m_fs->IsNative()) {
R_TRY(fsFileGetSize(&m_native, out));
} else {
struct stat st;
const auto fd = fileno(f->m_stdio);
const auto fd = fileno(m_stdio);
bool did_stat{};
if (fd && !fstat(fd, &st)) {
@@ -551,7 +578,7 @@ Result FileGetSize(File* f, s64* out) {
}
if (!did_stat) {
R_UNLESS(!lstat(f->m_path, &st), 0x1);
R_UNLESS(!lstat(m_path, &st), Fs::ResultUnknownStdioError);
}
*out = st.st_size;
@@ -560,68 +587,142 @@ Result FileGetSize(File* f, s64* out) {
R_SUCCEED();
}
void FileClose(File* f) {
if (f->m_fs->IsNative()) {
fsFileClose(&f->m_native);
} else {
std::fclose(f->m_stdio);
void File::Close() {
if (m_fs->IsNative()) {
if (serviceIsActive(&m_native.s)) {
fsFileClose(&m_native);
m_native = {};
}
} else {
if (m_stdio) {
std::fclose(m_stdio);
m_stdio = {};
}
}
*f = {};
}
Result OpenDirectory(fs::Fs* fs, const fs::FsPath& path, u32 mode, Dir* d) {
*d = {};
d->m_fs = fs;
d->m_mode = mode;
if (d->m_fs->IsNative()) {
auto fs = (fs::FsNative*)d->m_fs;
R_TRY(fs->OpenDirectory(path, mode, &d->m_native));
R_TRY(fsFsOpenDirectory(&fs->m_fs, path, mode, &d->m_native));
} else {
d->m_stdio = opendir(path);
R_UNLESS(d->m_stdio, 0x1);
R_UNLESS(d->m_stdio, Fs::ResultUnknownStdioError);
}
R_SUCCEED();
}
Result DirReadAll(Dir* d, std::vector<FsDirectoryEntry>& buf) {
Result DirGetEntryCount(fs::Fs* m_fs, const fs::FsPath& path, s64* count, u32 mode) {
s64 file_count, dir_count;
R_TRY(DirGetEntryCount(m_fs, path, &file_count, &dir_count, mode));
*count = file_count + dir_count;
R_SUCCEED();
}
Result DirGetEntryCount(fs::Fs* m_fs, const fs::FsPath& path, s64* file_count, s64* dir_count, u32 mode) {
*file_count = *dir_count = 0;
if (m_fs->IsNative()) {
if (mode & FsDirOpenMode_ReadDirs){
fs::Dir dir;
R_TRY(m_fs->OpenDirectory(path, FsDirOpenMode_ReadDirs|FsDirOpenMode_NoFileSize, &dir));
R_TRY(dir.GetEntryCount(file_count));
}
if (mode & FsDirOpenMode_ReadFiles){
fs::Dir dir;
R_TRY(m_fs->OpenDirectory(path, FsDirOpenMode_ReadFiles|FsDirOpenMode_NoFileSize, &dir));
R_TRY(dir.GetEntryCount(file_count));
}
} else {
fs::Dir dir;
R_TRY(m_fs->OpenDirectory(path, mode, &dir));
while (auto d = readdir(dir.m_stdio)) {
if (!std::strcmp(d->d_name, ".") || !std::strcmp(d->d_name, "..")) {
continue;
}
if (d->d_type == DT_DIR) {
if (!(mode & FsDirOpenMode_ReadDirs)) {
continue;
}
(*dir_count)++;
} else if (d->d_type == DT_REG) {
if (!(mode & FsDirOpenMode_ReadFiles)) {
continue;
}
(*file_count)++;
}
}
}
R_SUCCEED();
}
Dir::~Dir() {
Close();
}
Result Dir::GetEntryCount(s64* out) {
*out = 0;
if (m_fs->IsNative()) {
R_TRY(fsDirGetEntryCount(&m_native, out));
} else {
while (auto d = readdir(m_stdio)) {
if (!std::strcmp(d->d_name, ".") || !std::strcmp(d->d_name, "..")) {
continue;
}
(*out)++;
}
// NOTE: this will *not* work for native mounted folders!!!
rewinddir(m_stdio);
}
R_SUCCEED();
}
Result Dir::ReadAll(std::vector<FsDirectoryEntry>& buf) {
buf.clear();
if (d->m_fs->IsNative()) {
auto fs = (fs::FsNative*)d->m_fs;
if (m_fs->IsNative()) {
s64 count;
R_TRY(fs->DirGetEntryCount(&d->m_native, &count));
R_TRY(GetEntryCount(&count));
buf.resize(count);
R_TRY(fs->DirRead(&d->m_native, &count, buf.size(), buf.data()));
R_TRY(fsDirRead(&m_native, &count, buf.size(), buf.data()));
buf.resize(count);
} else {
buf.reserve(1000);
struct dirent* dirent;
while ((dirent = readdir(d->m_stdio))) {
if (!std::strcmp(dirent->d_name, ".") || !std::strcmp(dirent->d_name, "..")) {
while (auto d = readdir(m_stdio)) {
if (!std::strcmp(d->d_name, ".") || !std::strcmp(d->d_name, "..")) {
continue;
}
FsDirectoryEntry entry{};
if (dirent->d_type == DT_DIR) {
if (!(d->m_mode & FsDirOpenMode_ReadDirs)) {
if (d->d_type == DT_DIR) {
if (!(m_mode & FsDirOpenMode_ReadDirs)) {
continue;
}
entry.type = FsDirEntryType_Dir;
} else if (dirent->d_type == DT_REG) {
if (!(d->m_mode & FsDirOpenMode_ReadFiles)) {
} else if (d->d_type == DT_REG) {
if (!(m_mode & FsDirOpenMode_ReadFiles)) {
continue;
}
entry.type = FsDirEntryType_File;
} else {
log_write("[FS] WARNING: unknown type when reading dir: %u\n", d->d_type);
continue;
}
std::strcpy(entry.name, dirent->d_name);
std::strcpy(entry.name, d->d_name);
buf.emplace_back(entry);
}
}
@@ -629,14 +730,18 @@ Result DirReadAll(Dir* d, std::vector<FsDirectoryEntry>& buf) {
R_SUCCEED();
}
void DirClose(Dir* d) {
if (d->m_fs->IsNative()) {
fsDirClose(&d->m_native);
} else {
closedir(d->m_stdio);
void Dir::Close() {
if (m_fs->IsNative()) {
if (serviceIsActive(&m_native.s)) {
fsDirClose(&m_native);
m_native = {};
}
} else {
if (m_stdio) {
closedir(m_stdio);
m_stdio = {};
}
}
*d = {};
}
Result FileGetSizeAndTimestamp(fs::Fs* m_fs, const FsPath& path, FsTimeStampRaw* ts, s64* size) {
@@ -649,9 +754,9 @@ Result FileGetSizeAndTimestamp(fs::Fs* m_fs, const FsPath& path, FsTimeStampRaw*
File f;
R_TRY(m_fs->OpenFile(path, FsOpenMode_Read, &f));
ON_SCOPE_EXIT(fs->FileClose(&f));
ON_SCOPE_EXIT(f.Close());
R_TRY(m_fs->FileGetSize(&f, size));
R_TRY(f.GetSize(size));
} else {
struct stat st;
R_UNLESS(!lstat(path, &st), 0x1);
@@ -673,16 +778,15 @@ Result IsDirEmpty(fs::Fs* m_fs, const fs::FsPath& path, bool* out) {
auto fs = (fs::FsNative*)m_fs;
s64 count;
R_TRY(fs->DirGetEntryCount(path, FsDirOpenMode_ReadDirs | FsDirOpenMode_ReadFiles | FsDirOpenMode_NoFileSize, &count));
R_TRY(fs->DirGetEntryCount(path, &count, FsDirOpenMode_ReadDirs | FsDirOpenMode_ReadFiles));
*out = !count;
} else {
auto dir = opendir(path);
R_UNLESS(dir, 0x1);
ON_SCOPE_EXIT(closedir(dir));
struct dirent* dirent;
while ((dirent = readdir(dir))) {
if (!std::strcmp(dirent->d_name, ".") || !std::strcmp(dirent->d_name, "..")) {
while (auto d = readdir(dir)) {
if (!std::strcmp(d->d_name, ".") || !std::strcmp(d->d_name, "..")) {
continue;
}

View File

@@ -13,16 +13,12 @@ struct FileSource final : BaseSource {
m_open_result = m_fs->OpenFile(path, FsOpenMode_Read, std::addressof(m_file));
}
~FileSource() {
m_fs->FileClose(std::addressof(m_file));
}
Result Size(s64* out) {
return m_fs->FileGetSize(std::addressof(m_file), out);
return m_file.GetSize(out);
}
Result Read(void* buf, s64 off, s64 size, u64* bytes_read) {
return m_fs->FileRead(std::addressof(m_file), off, buf, size, 0, bytes_read);
return m_file.Read(off, buf, size, 0, bytes_read);
}
private:

View File

@@ -27,34 +27,35 @@ struct NroData {
NroHeader header;
};
auto nro_parse_internal(fs::FsNative& fs, const fs::FsPath& path, NroEntry& entry) -> Result {
auto nro_parse_internal(fs::Fs* fs, const fs::FsPath& path, NroEntry& entry) -> Result {
entry.path = path;
// todo: special sorting for fw 2.0.0 to make it not look like shit
if (hosversionAtLeast(3,0,0)) {
// it doesn't matter if we fail
entry.timestamp.is_valid = false;
fs.GetFileTimeStampRaw(entry.path, &entry.timestamp);
fs->GetFileTimeStampRaw(entry.path, &entry.timestamp);
// if (R_FAILED(fsFsGetFileTimeStampRaw(fs, entry.path, &entry.timestamp))) {
// // log_write("failed to get timestamp for: %s\n", path);
// }
}
FsFile f;
R_TRY(fs.OpenFile(entry.path, FsOpenMode_Read, &f));
ON_SCOPE_EXIT(fsFileClose(&f));
R_TRY(fsFileGetSize(&f, &entry.size));
fs::File f;
R_TRY(fs->OpenFile(entry.path, FsOpenMode_Read, &f));
// todo: buffer reads to 16k to avoid 2 fs read calls per entry.
NroData data;
u64 bytes_read;
R_TRY(fsFileRead(&f, 0, &data, sizeof(data), FsReadOption_None, &bytes_read));
R_TRY(f.Read(0, &data, sizeof(data), FsReadOption_None, &bytes_read));
R_UNLESS(data.header.magic == NROHEADER_MAGIC, NroError_BadMagic);
NroAssetHeader asset;
R_TRY(fsFileRead(&f, data.header.size, &asset, sizeof(asset), FsReadOption_None, &bytes_read));
R_TRY(f.Read(data.header.size, &asset, sizeof(asset), FsReadOption_None, &bytes_read));
// R_UNLESS(asset.magic == NROASSETHEADER_MAGIC, NroError_BadMagic);
// we can avoid a GetSize() call by calculating the size manually.
entry.size = data.header.size;
// some .nro (vgedit) have bad nacp, fake the nacp
auto& nacp = entry.nacp;
if (asset.magic != NROASSETHEADER_MAGIC || asset.nacp.offset == 0 || asset.nacp.size != sizeof(NacpStruct)) {
@@ -69,8 +70,9 @@ auto nro_parse_internal(fs::FsNative& fs, const fs::FsPath& path, NroEntry& entr
std::strcpy(nacp.display_version, "Unknown");
entry.is_nacp_valid = false;
} else {
R_TRY(fsFileRead(&f, data.header.size + asset.nacp.offset, &nacp.lang, sizeof(nacp.lang), FsReadOption_None, &bytes_read));
R_TRY(fsFileRead(&f, data.header.size + asset.nacp.offset + offsetof(NacpStruct, display_version), nacp.display_version, sizeof(nacp.display_version), FsReadOption_None, &bytes_read));
entry.size += sizeof(asset) + asset.icon.size + asset.nacp.size + asset.romfs.size;
R_TRY(f.Read(data.header.size + asset.nacp.offset, &nacp.lang, sizeof(nacp.lang), FsReadOption_None, &bytes_read));
R_TRY(f.Read(data.header.size + asset.nacp.offset + offsetof(NacpStruct, display_version), nacp.display_version, sizeof(nacp.display_version), FsReadOption_None, &bytes_read));
entry.is_nacp_valid = true;
}
@@ -84,32 +86,19 @@ auto nro_parse_internal(fs::FsNative& fs, const fs::FsPath& path, NroEntry& entr
// this function is recursive by 1 level deep
// if the nro is in switch/folder/folder2/app.nro it will NOT be found
// switch/folder/app.nro for example will work fine.
auto nro_scan_internal(const fs::FsPath& path, std::vector<NroEntry>& nros, bool hide_sphaira, bool nested, bool scan_all_dir, bool root) -> Result {
fs::FsNativeSd fs;
R_TRY(fs.GetFsOpenResult());
auto nro_scan_internal(fs::Fs* fs, const fs::FsPath& path, std::vector<NroEntry>& nros, bool hide_sphaira, bool nested, bool scan_all_dir, bool root) -> Result {
// we don't need to scan for folders if we are not root
u32 dir_open_type = FsDirOpenMode_ReadFiles | FsDirOpenMode_NoFileSize;
if (root) {
dir_open_type |= FsDirOpenMode_ReadDirs;
}
FsDir d;
R_TRY(fs.OpenDirectory(path, dir_open_type, &d));
ON_SCOPE_EXIT(fs.DirClose(&d));
s64 count;
R_TRY(fs.DirGetEntryCount(&d, &count));
// return early if empty
R_UNLESS(count > 0, 0x0);
fs::Dir d;
R_TRY(fs->OpenDirectory(path, dir_open_type, &d));
// we won't run out of memory here
std::vector<FsDirectoryEntry> entries(count);
R_TRY(fs.DirRead(&d, &count, entries.size(), entries.data()));
// size may of changed
entries.resize(count);
std::vector<FsDirectoryEntry> entries;
R_TRY(d.ReadAll(entries));
for (const auto& e : entries) {
// skip hidden files / folders
@@ -135,7 +124,7 @@ auto nro_scan_internal(const fs::FsPath& path, std::vector<NroEntry>& nros, bool
} else {
// slow path...
std::snprintf(fullpath, sizeof(fullpath), "%s/%s", path.s, e.name);
nro_scan_internal(fullpath, nros, hide_sphaira, nested, scan_all_dir, false);
nro_scan_internal(fs, fullpath, nros, hide_sphaira, nested, scan_all_dir, false);
}
} else if (e.type == FsDirEntryType_File && std::string_view{e.name}.ends_with(".nro")) {
fs::FsPath fullpath;
@@ -157,7 +146,12 @@ auto nro_scan_internal(const fs::FsPath& path, std::vector<NroEntry>& nros, bool
R_SUCCEED();
}
auto nro_get_icon_internal(FsFile* f, u64 size, u64 offset) -> std::vector<u8> {
auto nro_scan_internal(const fs::FsPath& path, std::vector<NroEntry>& nros, bool hide_sphaira, bool nested, bool scan_all_dir, bool root) -> Result {
fs::FsNativeSd fs;
return nro_scan_internal(&fs, path, nros, hide_sphaira, nested, scan_all_dir, root);
}
auto nro_get_icon_internal(fs::File* f, u64 size, u64 offset) -> std::vector<u8> {
// protect again really messed up sizes.
if (size > 1024 * 1024) {
return {};
@@ -167,7 +161,7 @@ auto nro_get_icon_internal(FsFile* f, u64 size, u64 offset) -> std::vector<u8> {
u64 bytes_read{};
icon.resize(size);
R_TRY_RESULT(fsFileRead(f, offset, icon.data(), icon.size(), FsReadOption_None, &bytes_read), {});
R_TRY_RESULT(f->Read(offset, icon.data(), icon.size(), FsReadOption_None, &bytes_read), {});
R_UNLESS(bytes_read == icon.size(), {});
return icon;
@@ -208,9 +202,7 @@ auto nro_verify(std::span<const u8> data) -> Result {
auto nro_parse(const fs::FsPath& path, NroEntry& entry) -> Result {
fs::FsNativeSd fs;
R_TRY(fs.GetFsOpenResult());
return nro_parse_internal(fs, path, entry);
return nro_parse_internal(&fs, path, entry);
}
auto nro_scan(const fs::FsPath& path, std::vector<NroEntry>& nros, bool hide_sphaira, bool nested, bool scan_all_dir) -> Result {
@@ -219,31 +211,23 @@ auto nro_scan(const fs::FsPath& path, std::vector<NroEntry>& nros, bool hide_sph
auto nro_get_icon(const fs::FsPath& path, u64 size, u64 offset) -> std::vector<u8> {
fs::FsNativeSd fs;
FsFile f;
R_TRY_RESULT(fs.GetFsOpenResult(), {});
fs::File f;
R_TRY_RESULT(fs.OpenFile(path, FsOpenMode_Read, &f), {});
ON_SCOPE_EXIT(fsFileClose(&f));
return nro_get_icon_internal(&f, size, offset);
}
auto nro_get_icon(const fs::FsPath& path) -> std::vector<u8> {
fs::FsNativeSd fs;
FsFile f;
NroData data;
NroAssetHeader asset;
u64 bytes_read;
R_TRY_RESULT(fs.GetFsOpenResult(), {});
fs::File f;
R_TRY_RESULT(fs.OpenFile(path, FsOpenMode_Read, &f), {});
ON_SCOPE_EXIT(fsFileClose(&f));
R_TRY_RESULT(fsFileRead(&f, 0, &data, sizeof(data), FsReadOption_None, &bytes_read), {});
R_TRY_RESULT(f.Read(0, &data, sizeof(data), FsReadOption_None, &bytes_read), {});
R_UNLESS(data.header.magic == NROHEADER_MAGIC, {});
R_TRY_RESULT(fsFileRead(&f, data.header.size, &asset, sizeof(asset), FsReadOption_None, &bytes_read), {});
R_TRY_RESULT(f.Read(data.header.size, &asset, sizeof(asset), FsReadOption_None, &bytes_read), {});
R_UNLESS(asset.magic == NROASSETHEADER_MAGIC, {});
return nro_get_icon_internal(&f, asset.icon.size, data.header.size + asset.icon.offset);
@@ -251,21 +235,18 @@ auto nro_get_icon(const fs::FsPath& path) -> std::vector<u8> {
auto nro_get_nacp(const fs::FsPath& path, NacpStruct& nacp) -> Result {
fs::FsNativeSd fs;
FsFile f;
NroData data;
NroAssetHeader asset;
u64 bytes_read;
R_TRY_RESULT(fs.GetFsOpenResult(), {});
fs::File f;
R_TRY(fs.OpenFile(path, FsOpenMode_Read, &f));
ON_SCOPE_EXIT(fsFileClose(&f));
R_TRY(fsFileRead(&f, 0, &data, sizeof(data), FsReadOption_None, &bytes_read));
R_TRY(f.Read(0, &data, sizeof(data), FsReadOption_None, &bytes_read));
R_UNLESS(data.header.magic == NROHEADER_MAGIC, NroError_BadMagic);
R_TRY(fsFileRead(&f, data.header.size, &asset, sizeof(asset), FsReadOption_None, &bytes_read));
R_TRY(f.Read(data.header.size, &asset, sizeof(asset), FsReadOption_None, &bytes_read));
R_UNLESS(asset.magic == NROASSETHEADER_MAGIC, NroError_BadMagic);
R_TRY(fsFileRead(&f, data.header.size + asset.nacp.offset, &nacp, sizeof(nacp), FsReadOption_None, &bytes_read));
R_TRY(f.Read(data.header.size + asset.nacp.offset, &nacp, sizeof(nacp), FsReadOption_None, &bytes_read));
R_SUCCEED();
}

View File

@@ -357,15 +357,14 @@ void loop(void* args) {
ON_SCOPE_EXIT(fs.DeleteFile(temp_path));
{
FsFile f;
fs::File f;
if (R_FAILED(rc = fs.OpenFile(temp_path, FsOpenMode_Write, &f))) {
sendall(connfd, &ERR_FILE, sizeof(ERR_FILE));
log_write("failed to open file %X\n", rc);
continue;
}
ON_SCOPE_EXIT(fsFileClose(&f));
if (R_FAILED(rc = fsFileSetSize(&f, file_data.size()))) {
if (R_FAILED(rc = f.SetSize(file_data.size()))) {
sendall(connfd, &ERR_FILE, sizeof(ERR_FILE));
log_write("failed to set file size: 0x%X\n", socketGetLastResult());
continue;
@@ -379,7 +378,7 @@ void loop(void* args) {
if (offset + chunk_size > file_data.size()) {
chunk_size = file_data.size() - offset;
}
if (R_FAILED(rc = fsFileWrite(&f, offset, file_data.data() + offset, chunk_size, FsWriteOption_None))) {
if (R_FAILED(rc = f.Write(offset, file_data.data() + offset, chunk_size, FsWriteOption_None))) {
break;
}
offset += chunk_size;

View File

@@ -448,11 +448,9 @@ auto InstallApp(ProgressBox* pbox, const Entry& entry) -> Result {
R_THROW(rc);
}
FsFile f;
fs::File f;
R_TRY(fs.OpenFile(output, FsOpenMode_Write, &f));
ON_SCOPE_EXIT(fsFileClose(&f));
R_TRY(fsFileSetSize(&f, info.uncompressed_size));
R_TRY(f.SetSize(info.uncompressed_size));
u64 offset{};
while (offset < info.uncompressed_size) {
@@ -464,7 +462,7 @@ auto InstallApp(ProgressBox* pbox, const Entry& entry) -> Result {
R_THROW(0x1);
}
R_TRY(fsFileWrite(&f, offset, buf.data(), bytes_read, FsWriteOption_None));
R_TRY(f.Write(offset, buf.data(), bytes_read, FsWriteOption_None));
pbox->UpdateTransfer(offset, info.uncompressed_size);
offset += bytes_read;

View File

@@ -13,21 +13,17 @@ Menu::Menu(const fs::FsPath& path) : MenuBase{path}, m_path{path} {
std::string buf;
if (R_SUCCEEDED(m_fs.OpenFile(m_path, FsOpenMode_Read, &m_file))) {
fsFileGetSize(&m_file, &m_file_size);
m_file.GetSize(&m_file_size);
buf.resize(m_file_size + 1);
u64 read_bytes;
fsFileRead(&m_file, m_file_offset, buf.data(), buf.size(), 0, &read_bytes);
m_file.Read(m_file_offset, buf.data(), buf.size(), 0, &read_bytes);
buf[m_file_size] = '\0';
}
m_scroll_text = std::make_unique<ScrollableText>(buf, 0, 120, 500, 1150-110, 18);
}
Menu::~Menu() {
fsFileClose(&m_file);
}
void Menu::Update(Controller* controller, TouchInfo* touch) {
MenuBase::Update(controller, touch);

View File

@@ -675,7 +675,7 @@ FsView::FsView(Menu* menu, const fs::FsPath& path, const FsEntry& entry, ViewSid
}));
}
if (IsSd() && m_entries_current.size() && !m_selected_count && GetEntry().IsFile()) {
if (m_entries_current.size() && !m_selected_count && GetEntry().IsFile()) {
options->Add(std::make_shared<SidebarEntryCallback>("Hash"_i18n, [this](){
auto options = std::make_shared<Sidebar>("Hash Options"_i18n, Sidebar::Side::RIGHT);
ON_SCOPE_EXIT(App::Push(options));
@@ -751,10 +751,10 @@ void FsView::Draw(NVGcontext* vg, Theme* theme) {
auto& e = GetEntry(i);
if (e.IsDir()) {
if (m_fs->IsNative() && e.file_count == -1 && e.dir_count == -1) {
const auto full_path = GetNewPath(e);
GetNative()->DirGetEntryCount(full_path, FsDirOpenMode_ReadFiles | FsDirOpenMode_NoFileSize, &e.file_count);
GetNative()->DirGetEntryCount(full_path, FsDirOpenMode_ReadDirs | FsDirOpenMode_NoFileSize, &e.dir_count);
// NOTE: make this native only if hdd dir scan is too slow.
// if (m_fs->IsNative() && e.file_count == -1 && e.dir_count == -1) {
if (e.file_count == -1 && e.dir_count == -1) {
m_fs->DirGetEntryCount(GetNewPath(e), &e.file_count, &e.dir_count);
}
} else if (!e.checked_extension) {
e.checked_extension = true;
@@ -803,7 +803,8 @@ void FsView::Draw(NVGcontext* vg, Theme* theme) {
m_scroll_name.Draw(vg, selected, x + text_xoffset+65, y + (h / 2.f), w-(75+text_xoffset+65+50), 20, NVG_ALIGN_LEFT | NVG_ALIGN_MIDDLE, theme->GetColour(text_id), e.name);
if (m_fs->IsNative() && e.IsDir()) {
// NOTE: make this native only if i disable dir scan from above.
if (e.IsDir()) {
gfx::drawTextArgs(vg, x + w - text_xoffset, y + (h / 2.f) - 3, 16.f, NVG_ALIGN_RIGHT | NVG_ALIGN_BOTTOM, theme->GetColour(text_id), "%zd files"_i18n.c_str(), e.file_count);
gfx::drawTextArgs(vg, x + w - text_xoffset, y + (h / 2.f) + 3, 16.f, NVG_ALIGN_RIGHT | NVG_ALIGN_TOP, theme->GetColour(text_id), "%zd dirs"_i18n.c_str(), e.dir_count);
} else if (e.IsFile()) {
@@ -881,14 +882,13 @@ void FsView::SetIndex(s64 index) {
m_list->SetYoff();
}
if (!m_entries_current.empty() && !GetEntry().checked_internal_extension && GetEntry().extension == "zip") {
if (IsSd() && !m_entries_current.empty() && !GetEntry().checked_internal_extension && GetEntry().extension == "zip") {
GetEntry().checked_internal_extension = true;
if (auto zfile = unzOpen64(GetNewPathCurrent())) {
ON_SCOPE_EXIT(unzClose(zfile));
unz_global_info gi{};
// only check first entry (i think RA does the same)
if (UNZ_OK == unzGetGlobalInfo(zfile, &gi) && gi.number_entry >= 1) {
fs::FsPath filename_inzip{};
unz_file_info64 file_info{};
if (UNZ_OK == unzOpenCurrentFile(zfile)) {
@@ -902,7 +902,6 @@ void FsView::SetIndex(s64 index) {
}
}
}
}
m_menu->UpdateSubheading();
}
@@ -980,11 +979,7 @@ void FsView::InstallFiles() {
App::Push(std::make_shared<ui::ProgressBox>(0, "Installing "_i18n, "", [this, targets](auto pbox) -> Result {
for (auto& e : targets) {
if (m_fs->IsNative()) {
R_TRY(yati::InstallFromFile(pbox, &GetNative()->m_fs, GetNewPath(e)));
} else {
R_TRY(yati::InstallFromFile(pbox, GetNewPath(e)));
}
R_TRY(yati::InstallFromFile(pbox, m_fs.get(), GetNewPath(e)));
App::Notify("Installed "_i18n + e.GetName());
}
@@ -1053,9 +1048,7 @@ void FsView::UnzipFiles(fs::FsPath dir_path) {
fs::File f;
R_TRY(m_fs->OpenFile(file_path, FsOpenMode_Write, &f));
ON_SCOPE_EXIT(m_fs->FileClose(&f));
R_TRY(m_fs->FileSetSize(&f, info.uncompressed_size));
R_TRY(f.SetSize(info.uncompressed_size));
std::vector<char> buf(chunk_size);
s64 offset{};
@@ -1068,7 +1061,7 @@ void FsView::UnzipFiles(fs::FsPath dir_path) {
R_THROW(0x1);
}
R_TRY(m_fs->FileWrite(&f, offset, buf.data(), bytes_read, FsWriteOption_None));
R_TRY(f.Write(offset, buf.data(), bytes_read, FsWriteOption_None));
pbox->UpdateTransfer(offset, info.uncompressed_size);
offset += bytes_read;
@@ -1165,10 +1158,9 @@ void FsView::ZipFiles(fs::FsPath zip_out) {
fs::File f;
R_TRY(m_fs->OpenFile(file_path, FsOpenMode_Read, &f));
ON_SCOPE_EXIT(m_fs->FileClose(&f));
s64 file_size;
R_TRY(m_fs->FileGetSize(&f, &file_size));
R_TRY(f.GetSize(&file_size));
std::vector<char> buf(chunk_size);
s64 offset{};
@@ -1176,7 +1168,7 @@ void FsView::ZipFiles(fs::FsPath zip_out) {
R_TRY(pbox->ShouldExitResult());
u64 bytes_read;
R_TRY(m_fs->FileRead(&f, offset, buf.data(), buf.size(), FsReadOption_None, &bytes_read));
R_TRY(f.Read(offset, buf.data(), buf.size(), FsReadOption_None, &bytes_read));
if (ZIP_OK != zipWriteInFileInZip(zfile, buf.data(), bytes_read)) {
log_write("failed to write zip file: %s\n", file_path.s);
@@ -1253,11 +1245,10 @@ void FsView::UploadFiles() {
fs::File f;
R_TRY(m_fs->OpenFile(file_path, FsOpenMode_Read, &f));
ON_SCOPE_EXIT(m_fs->FileClose(&f));
return thread::TransferPull(pbox, file_size,
[&](void* data, s64 off, s64 size, u64* bytes_read) -> Result {
return m_fs->FileRead(&f, off, data, size, FsReadOption_None, bytes_read);
return f.Read(off, data, size, FsReadOption_None, bytes_read);
},
[&](thread::PullCallback pull) -> Result {
s64 offset{};
@@ -1340,21 +1331,14 @@ auto FsView::Scan(const fs::FsPath& new_path, bool is_walk_up) -> Result {
m_index = 0;
m_list->SetYoff(0);
m_menu->SetTitleSubHeading(m_path);
// todo: verify this works as expected.
m_selected_count = 0;
// if (m_selected_type == SelectedType::None) {
// ResetSelection();
// }
fs::Dir d;
R_TRY(m_fs->OpenDirectory(new_path, FsDirOpenMode_ReadDirs | FsDirOpenMode_ReadFiles, &d));
ON_SCOPE_EXIT(m_fs->DirClose(&d));
// we won't run out of memory here (tm)
std::vector<FsDirectoryEntry> dir_entries;
R_TRY(m_fs->DirReadAll(&d, dir_entries));
R_TRY(d.ReadAll(dir_entries));
const auto count = dir_entries.size();
m_entries.reserve(count);
@@ -1585,6 +1569,23 @@ void FsView::OnPasteCallback() {
} else {
FsDirCollections collections;
const auto on_paste_file = [&](auto& src_path, auto& dst_path) -> Result {
if (selected.m_type == SelectedType::Cut) {
// update timestamp if possible.
if (!m_fs->IsNative()) {
FsTimeStampRaw ts;
if (R_SUCCEEDED(src_fs->GetFileTimeStampRaw(src_path, &ts))) {
m_fs->SetTimestamp(dst_path, &ts);
}
}
// delete src file. folders are removed after.
R_TRY(src_fs->DeleteFile(src_path));
}
R_SUCCEED();
};
// build list of dirs / files
for (const auto&p : selected.m_files) {
pbox->Yield();
@@ -1612,11 +1613,7 @@ void FsView::OnPasteCallback() {
pbox->SetTitle(p.name);
pbox->NewTransfer("Copying "_i18n + src_path);
R_TRY(pbox->CopyFile(src_fs, m_fs.get(), src_path, dst_path));
// delete src file. folders are removed after.
if (selected.m_type == SelectedType::Cut) {
R_TRY(src_fs->DeleteFile(src_path));
}
R_TRY(on_paste_file(src_path, dst_path));
}
}
@@ -1646,11 +1643,7 @@ void FsView::OnPasteCallback() {
pbox->SetTitle(p.name);
pbox->NewTransfer("Copying "_i18n + src_path);
R_TRY(pbox->CopyFile(src_fs, m_fs.get(), src_path, dst_path));
// delete src file. folders are removed after.
if (selected.m_type == SelectedType::Cut) {
R_TRY(src_fs->DeleteFile(src_path));
}
R_TRY(on_paste_file(src_path, dst_path));
}
}
@@ -1704,17 +1697,15 @@ auto FsView::CheckIfUpdateFolder() -> Result {
log_write("found daybreak in: %s\n", m_daybreak_path.value().s);
}
s64 count;
R_TRY(GetNative()->DirGetEntryCount(m_path, FsDirOpenMode_ReadDirs, &count));
// check that we are at the bottom level
R_UNLESS(count == 0, 0x1);
// check that we have enough ncas and not too many
R_UNLESS(m_entries.size() > 150 && m_entries.size() < 300, 0x1);
// check that all entries end in .nca
const auto nca_ext = std::string_view{".nca"};
for (auto& e : m_entries) {
// check that we are at the bottom level
R_UNLESS(e.type == FsDirEntryType_File, 0x1);
const auto ext = std::strrchr(e.name, '.');
R_UNLESS(ext && ext == nca_ext, 0x1);
}
@@ -1729,8 +1720,7 @@ auto FsView::get_collection(fs::Fs* fs, const fs::FsPath& path, const fs::FsPath
const auto fetch = [fs, &path](std::vector<FsDirectoryEntry>& out, u32 flags) -> Result {
fs::Dir d;
R_TRY(fs->OpenDirectory(path, flags, &d));
ON_SCOPE_EXIT(fs->DirClose(&d));
return fs->DirReadAll(&d, out);
return d.ReadAll(out);
};
if (inc_file) {
@@ -1870,7 +1860,9 @@ void FsView::DisplayHash(hash::Type type) {
hash_out.clear();
App::Push(std::make_shared<ProgressBox>(0, "Hashing"_i18n, GetEntry().name, [this, type](auto pbox) -> Result {
R_TRY(hash::Hash(pbox, type, m_fs.get(), GetNewPathCurrent(), hash_out));
const auto full_path = GetNewPathCurrent();
pbox->NewTransfer(full_path);
R_TRY(hash::Hash(pbox, type, m_fs.get(), full_path, hash_out));
R_SUCCEED();
}, [this, type](Result rc){

View File

@@ -278,13 +278,12 @@ Result fsOpenGameCardDetectionEventNotifier(FsEventNotifier* out) {
struct GcSource final : yati::source::Base {
GcSource(const ApplicationEntry& entry, fs::FsNativeGameCard* fs);
~GcSource();
Result Read(void* buf, s64 off, s64 size, u64* bytes_read);
yati::container::Collections m_collections{};
yati::ConfigOverride m_config{};
fs::FsNativeGameCard* m_fs{};
FsFile m_file{};
fs::File m_file{};
s64 m_offset{};
s64 m_size{};
@@ -342,15 +341,10 @@ GcSource::GcSource(const ApplicationEntry& entry, fs::FsNativeGameCard* fs)
m_config.skip_rsa_npdm_fixed_key_verify = true;
}
GcSource::~GcSource() {
fsFileClose(&m_file);
}
Result GcSource::Read(void* buf, s64 off, s64 size, u64* bytes_read) {
// check is we need to open a new file.
if (!InRange(off, m_offset, m_size)) {
fsFileClose(&m_file);
m_file = {};
m_file.Close();
// find new file based on the offset.
bool found = false;
@@ -368,7 +362,7 @@ Result GcSource::Read(void* buf, s64 off, s64 size, u64* bytes_read) {
R_UNLESS(found, 0x1);
}
return fsFileRead(&m_file, off - m_offset, buf, size, 0, bytes_read);
return m_file.Read(off - m_offset, buf, size, 0, bytes_read);
}
} // namespace
@@ -526,17 +520,11 @@ Result Menu::GcMount() {
R_TRY(m_fs->GetFsOpenResult());
FsDir dir;
fs::Dir dir;
R_TRY(m_fs->OpenDirectory("/", FsDirOpenMode_ReadFiles, std::addressof(dir)));
ON_SCOPE_EXIT(fsDirClose(std::addressof(dir)));
s64 count;
R_TRY(m_fs->DirGetEntryCount(std::addressof(dir), std::addressof(count)));
std::vector<FsDirectoryEntry> buf(count);
s64 total_entries;
R_TRY(m_fs->DirRead(std::addressof(dir), std::addressof(total_entries), buf.size(), buf.data()));
R_UNLESS(buf.size() == total_entries, 0x1);
std::vector<FsDirectoryEntry> buf;
R_TRY(dir.ReadAll(buf));
yati::container::Collections ticket_collections;
for (const auto& e : buf) {

View File

@@ -162,11 +162,9 @@ auto DownloadApp(ProgressBox* pbox, const GhApiAsset& gh_asset, const AssetEntry
R_THROW(rc);
}
FsFile f;
fs::File f;
R_TRY(fs.OpenFile(file_path, FsOpenMode_Write, &f));
ON_SCOPE_EXIT(fsFileClose(&f));
R_TRY(fsFileSetSize(&f, info.uncompressed_size));
R_TRY(f.SetSize(info.uncompressed_size));
std::vector<char> buf(chunk_size);
s64 offset{};
@@ -179,7 +177,7 @@ auto DownloadApp(ProgressBox* pbox, const GhApiAsset& gh_asset, const AssetEntry
R_THROW(0x1);
}
R_TRY(fsFileWrite(&f, offset, buf.data(), bytes_read, FsWriteOption_None));
R_TRY(f.Write(offset, buf.data(), bytes_read, FsWriteOption_None));
pbox->UpdateTransfer(offset, info.uncompressed_size);
offset += bytes_read;

View File

@@ -123,11 +123,9 @@ auto InstallUpdate(ProgressBox* pbox, const std::string url, const std::string v
R_THROW(rc);
}
FsFile f;
fs::File f;
R_TRY(fs.OpenFile(file_path, FsOpenMode_Write, &f));
ON_SCOPE_EXIT(fsFileClose(&f));
R_TRY(fsFileSetSize(&f, info.uncompressed_size));
R_TRY(f.SetSize(info.uncompressed_size));
std::vector<char> buf(chunk_size);
s64 offset{};
@@ -138,7 +136,7 @@ auto InstallUpdate(ProgressBox* pbox, const std::string url, const std::string v
R_THROW(0x1);
}
R_TRY(fsFileWrite(&f, offset, buf.data(), bytes_read, FsWriteOption_None));
R_TRY(f.Write(offset, buf.data(), bytes_read, FsWriteOption_None));
pbox->UpdateTransfer(offset, info.uncompressed_size);
offset += bytes_read;

View File

@@ -311,11 +311,9 @@ auto InstallTheme(ProgressBox* pbox, const PackListEntry& entry) -> Result {
R_THROW(rc);
}
FsFile f;
fs::File f;
R_TRY(fs.OpenFile(file_path, FsOpenMode_Write, &f));
ON_SCOPE_EXIT(fsFileClose(&f));
R_TRY(fsFileSetSize(&f, info.uncompressed_size));
R_TRY(f.SetSize(info.uncompressed_size));
std::vector<char> buf(chunk_size);
s64 offset{};
@@ -328,7 +326,7 @@ auto InstallTheme(ProgressBox* pbox, const PackListEntry& entry) -> Result {
R_THROW(0x1);
}
R_TRY(fsFileWrite(&f, offset, buf.data(), bytes_read, FsWriteOption_None));
R_TRY(f.Write(offset, buf.data(), bytes_read, FsWriteOption_None));
pbox->UpdateTransfer(offset, info.uncompressed_size);
offset += bytes_read;

View File

@@ -256,10 +256,9 @@ auto ProgressBox::ShouldExitResult() -> Result {
auto ProgressBox::CopyFile(fs::Fs* fs_src, fs::Fs* fs_dst, const fs::FsPath& src_path, const fs::FsPath& dst_path) -> Result {
fs::File src_file;
R_TRY(fs_src->OpenFile(src_path, FsOpenMode_Read, &src_file));
ON_SCOPE_EXIT(fs_src->FileClose(&src_file));
s64 src_size;
R_TRY(fs_src->FileGetSize(&src_file, &src_size));
R_TRY(src_file.GetSize(&src_size));
// this can fail if it already exists so we ignore the result.
// if the file actually failed to be created, the result is implicitly
@@ -268,9 +267,8 @@ auto ProgressBox::CopyFile(fs::Fs* fs_src, fs::Fs* fs_dst, const fs::FsPath& src
fs::File dst_file;
R_TRY(fs_dst->OpenFile(dst_path, FsOpenMode_Write, &dst_file));
ON_SCOPE_EXIT(fs_dst->FileClose(&dst_file));
R_TRY(fs_dst->FileSetSize(&dst_file, src_size));
R_TRY(dst_file.SetSize(src_size));
s64 offset{};
std::vector<u8> buf(1024*1024*4); // 4MiB
@@ -279,10 +277,10 @@ auto ProgressBox::CopyFile(fs::Fs* fs_src, fs::Fs* fs_dst, const fs::FsPath& src
R_TRY(ShouldExitResult());
u64 bytes_read;
R_TRY(fs_src->FileRead(&src_file, offset, buf.data(), buf.size(), 0, &bytes_read));
R_TRY(src_file.Read(offset, buf.data(), buf.size(), 0, &bytes_read));
Yield();
R_TRY(fs_dst->FileWrite(&dst_file, offset, buf.data(), bytes_read, FsWriteOption_None));
R_TRY(dst_file.Write(offset, buf.data(), bytes_read, FsWriteOption_None));
Yield();
UpdateTransfer(offset, src_size);

View File

@@ -2,29 +2,13 @@
namespace sphaira::yati::source {
File::File(FsFileSystem* fs, const fs::FsPath& path) {
if (fs) {
m_fs = std::make_unique<fs::FsNative>(fs, false);
} else {
m_fs = std::make_unique<fs::FsStdio>();
}
File::File(fs::Fs* fs, const fs::FsPath& path) : m_fs{fs} {
m_open_result = m_fs->OpenFile(path, FsOpenMode_Read, std::addressof(m_file));
}
File::File(const fs::FsPath& path) : File{nullptr, path} {
}
File::~File() {
if (R_SUCCEEDED(GetOpenResult())) {
m_fs->FileClose(std::addressof(m_file));
}
}
Result File::Read(void* buf, s64 off, s64 size, u64* bytes_read) {
R_TRY(GetOpenResult());
return m_fs->FileRead(std::addressof(m_file), off, buf, size, 0, bytes_read);
return m_file.Read(off, buf, size, 0, bytes_read);
}
} // namespace sphaira::yati::source

View File

@@ -3,29 +3,13 @@
namespace sphaira::yati::source {
StreamFile::StreamFile(FsFileSystem* fs, const fs::FsPath& path) {
if (fs) {
m_fs = std::make_unique<fs::FsNative>(fs, false);
} else {
m_fs = std::make_unique<fs::FsStdio>();
}
StreamFile::StreamFile(fs::Fs* fs, const fs::FsPath& path) : m_fs{fs} {
m_open_result = m_fs->OpenFile(path, FsOpenMode_Read, std::addressof(m_file));
}
StreamFile::StreamFile(const fs::FsPath& path) : StreamFile{nullptr, path} {
}
StreamFile::~StreamFile() {
if (R_SUCCEEDED(GetOpenResult())) {
m_fs->FileClose(std::addressof(m_file));
}
}
Result StreamFile::ReadChunk(void* buf, s64 size, u64* bytes_read) {
R_TRY(GetOpenResult());
const auto rc = m_fs->FileRead(std::addressof(m_file), m_offset, buf, size, 0, bytes_read);
const auto rc = m_file.Read(m_offset, buf, size, 0, bytes_read);
m_offset += *bytes_read;
return rc;
}

View File

@@ -1378,15 +1378,11 @@ Result InstallInternalStream(ui::ProgressBox* pbox, std::shared_ptr<source::Base
} // namespace
Result InstallFromFile(ui::ProgressBox* pbox, FsFileSystem* fs, const fs::FsPath& path, const ConfigOverride& override) {
Result InstallFromFile(ui::ProgressBox* pbox, fs::Fs* fs, const fs::FsPath& path, const ConfigOverride& override) {
return InstallFromSource(pbox, std::make_shared<source::File>(fs, path), path, override);
// return InstallFromSource(pbox, std::make_shared<source::StreamFile>(fs, path), path, override);
}
Result InstallFromFile(ui::ProgressBox* pbox, const fs::FsPath& path, const ConfigOverride& override) {
return InstallFromFile(pbox, nullptr, path, override);
}
Result InstallFromSource(ui::ProgressBox* pbox, std::shared_ptr<source::Base> source, const fs::FsPath& path, const ConfigOverride& override) {
const auto ext = std::strrchr(path.s, '.');
R_UNLESS(ext, Result_ContainerNotFound);