add support for mounting stdio (hdd) in the file browser.
This commit is contained in:
@@ -121,12 +121,18 @@ Result CreateDirectoryRecursively(FsFileSystem* fs, const FsPath& _path, bool ig
|
||||
auto path_view = std::string_view{_path};
|
||||
// todo: fix this for sdmc: and ums0:
|
||||
FsPath path{"/"};
|
||||
if (auto s = std::strchr(_path.s, ':')) {
|
||||
const int len = (s - _path.s) + 1;
|
||||
std::snprintf(path, sizeof(path), "%.*s/", len, _path.s);
|
||||
path_view = path_view.substr(len);
|
||||
}
|
||||
|
||||
for (const auto dir : std::views::split(path_view, '/')) {
|
||||
if (dir.empty()) {
|
||||
continue;
|
||||
}
|
||||
std::strncat(path, dir.data(), dir.size());
|
||||
log_write("[FS] dir creation path is now: %s\n", path.s);
|
||||
|
||||
Result rc;
|
||||
if (fs) {
|
||||
@@ -447,4 +453,233 @@ Result copy_entire_file(const FsPath& dst, const FsPath& src, bool ignore_read_o
|
||||
return write_entire_file(dst, data, ignore_read_only);
|
||||
}
|
||||
|
||||
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));
|
||||
} else {
|
||||
if ((mode & FsOpenMode_Read) && (mode & FsOpenMode_Write)) {
|
||||
// todo:
|
||||
R_THROW(0x1);
|
||||
} else if (mode & FsOpenMode_Read) {
|
||||
f->m_stdio = fopen(path, "rb");
|
||||
} else if (mode & FsOpenMode_Write) {
|
||||
f->m_stdio = fopen(path, "wb");
|
||||
}
|
||||
|
||||
R_UNLESS(f->m_stdio, 0x1);
|
||||
}
|
||||
|
||||
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);
|
||||
}
|
||||
|
||||
*bytes_read = std::fread(buf, 1, read_size, f->m_stdio);
|
||||
f->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));
|
||||
} else {
|
||||
if (f->m_stdio_off != off) {
|
||||
log_write("[FS] diff seek\n");
|
||||
f->m_stdio_off = off;
|
||||
std::fseek(f->m_stdio, off, SEEK_SET);
|
||||
}
|
||||
|
||||
const auto result = std::fwrite(buf, 1, write_size, f->m_stdio);
|
||||
// log_write("[FS] fwrite res: %zu vs %zu\n", result, write_size);
|
||||
R_UNLESS(result == write_size, 0x1);
|
||||
|
||||
f->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));
|
||||
} else {
|
||||
R_SUCCEED();
|
||||
// const auto fd = fileno(f->m_stdio);
|
||||
// R_UNLESS(fd > 0, 0x1);
|
||||
// R_UNLESS(!ftruncate(fd, sz), 0x1);
|
||||
}
|
||||
|
||||
R_SUCCEED();
|
||||
}
|
||||
|
||||
Result FileGetSize(File* f, s64* out) {
|
||||
if (f->m_fs->IsNative()) {
|
||||
R_TRY(fsFileGetSize(&f->m_native, out));
|
||||
} else {
|
||||
struct stat st;
|
||||
const auto fd = fileno(f->m_stdio);
|
||||
bool did_stat{};
|
||||
|
||||
if (fd && !fstat(fd, &st)) {
|
||||
did_stat = true;
|
||||
}
|
||||
|
||||
if (!did_stat) {
|
||||
R_UNLESS(!lstat(f->m_path, &st), 0x1);
|
||||
}
|
||||
|
||||
*out = st.st_size;
|
||||
}
|
||||
|
||||
R_SUCCEED();
|
||||
}
|
||||
|
||||
void FileClose(File* f) {
|
||||
if (f->m_fs->IsNative()) {
|
||||
fsFileClose(&f->m_native);
|
||||
} else {
|
||||
std::fclose(f->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));
|
||||
} else {
|
||||
d->m_stdio = opendir(path);
|
||||
R_UNLESS(d->m_stdio, 0x1);
|
||||
}
|
||||
|
||||
R_SUCCEED();
|
||||
}
|
||||
|
||||
Result DirReadAll(Dir* d, std::vector<FsDirectoryEntry>& buf) {
|
||||
buf.clear();
|
||||
|
||||
if (d->m_fs->IsNative()) {
|
||||
auto fs = (fs::FsNative*)d->m_fs;
|
||||
|
||||
s64 count;
|
||||
R_TRY(fs->DirGetEntryCount(&d->m_native, &count));
|
||||
|
||||
buf.resize(count);
|
||||
R_TRY(fs->DirRead(&d->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, "..")) {
|
||||
continue;
|
||||
}
|
||||
|
||||
FsDirectoryEntry entry{};
|
||||
|
||||
if (dirent->d_type == DT_DIR) {
|
||||
if (!(d->m_mode & FsDirOpenMode_ReadDirs)) {
|
||||
continue;
|
||||
}
|
||||
entry.type = FsDirEntryType_Dir;
|
||||
} else if (dirent->d_type == DT_REG) {
|
||||
if (!(d->m_mode & FsDirOpenMode_ReadFiles)) {
|
||||
continue;
|
||||
}
|
||||
entry.type = FsDirEntryType_File;
|
||||
}
|
||||
|
||||
std::strcpy(entry.name, dirent->d_name);
|
||||
buf.emplace_back(entry);
|
||||
}
|
||||
}
|
||||
|
||||
R_SUCCEED();
|
||||
}
|
||||
|
||||
void DirClose(Dir* d) {
|
||||
if (d->m_fs->IsNative()) {
|
||||
fsDirClose(&d->m_native);
|
||||
} else {
|
||||
closedir(d->m_stdio);
|
||||
}
|
||||
|
||||
*d = {};
|
||||
}
|
||||
|
||||
Result FileGetSizeAndTimestamp(fs::Fs* m_fs, const FsPath& path, FsTimeStampRaw* ts, s64* size) {
|
||||
*ts = {};
|
||||
*size = {};
|
||||
|
||||
if (m_fs->IsNative()) {
|
||||
auto fs = (fs::FsNative*)m_fs;
|
||||
R_TRY(fs->GetFileTimeStampRaw(path, ts));
|
||||
|
||||
File f;
|
||||
R_TRY(m_fs->OpenFile(path, FsOpenMode_Read, &f));
|
||||
ON_SCOPE_EXIT(fs->FileClose(&f));
|
||||
|
||||
R_TRY(m_fs->FileGetSize(&f, size));
|
||||
} else {
|
||||
struct stat st;
|
||||
R_UNLESS(!lstat(path, &st), 0x1);
|
||||
|
||||
ts->is_valid = true;
|
||||
ts->created = st.st_ctim.tv_sec;
|
||||
ts->modified = st.st_mtim.tv_sec;
|
||||
ts->accessed = st.st_atim.tv_sec;
|
||||
*size = st.st_size;
|
||||
}
|
||||
|
||||
R_SUCCEED();
|
||||
}
|
||||
|
||||
Result IsDirEmpty(fs::Fs* m_fs, const fs::FsPath& path, bool* out) {
|
||||
*out = true;
|
||||
|
||||
if (m_fs->IsNative()) {
|
||||
auto fs = (fs::FsNative*)m_fs;
|
||||
|
||||
s64 count;
|
||||
R_TRY(fs->DirGetEntryCount(path, FsDirOpenMode_ReadDirs | FsDirOpenMode_ReadFiles | FsDirOpenMode_NoFileSize, &count));
|
||||
*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, "..")) {
|
||||
continue;
|
||||
}
|
||||
|
||||
*out = false;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
R_SUCCEED();
|
||||
}
|
||||
|
||||
} // namespace fs
|
||||
|
||||
@@ -42,6 +42,16 @@
|
||||
namespace sphaira::ui::menu::filebrowser {
|
||||
namespace {
|
||||
|
||||
constexpr FsEntry FS_ENTRY_DEFAULT{
|
||||
"Sd", "/", FsType::Sd, FsEntryFlag_Assoc,
|
||||
};
|
||||
|
||||
constexpr FsEntry FS_ENTRIES[]{
|
||||
FS_ENTRY_DEFAULT,
|
||||
{ "Image System memory", "/", FsType::ImageNand },
|
||||
{ "Image microSD card", "/", FsType::ImageSd},
|
||||
};
|
||||
|
||||
struct ExtDbEntry {
|
||||
std::string_view db_name;
|
||||
std::span<const std::string_view> ext;
|
||||
@@ -198,7 +208,7 @@ auto GetRomDatabaseFromPath(std::string_view path) -> RomDatabaseIndexs {
|
||||
}
|
||||
|
||||
//
|
||||
auto GetRomIcon(fs::FsNative* fs, ProgressBox* pbox, std::string filename, const RomDatabaseIndexs& db_indexs, const NroEntry& nro) {
|
||||
auto GetRomIcon(fs::Fs* fs, ProgressBox* pbox, std::string filename, const RomDatabaseIndexs& db_indexs, const NroEntry& nro) {
|
||||
// if no db entries, use nro icon
|
||||
if (db_indexs.empty()) {
|
||||
log_write("using nro image\n");
|
||||
@@ -315,7 +325,7 @@ Menu::Menu(const std::vector<NroEntry>& nro_entries) : MenuBase{"FileBrowser"_i1
|
||||
return;
|
||||
}
|
||||
|
||||
if (m_fs_type == FsType::Sd && m_is_update_folder && m_daybreak_path.has_value()) {
|
||||
if (IsSd() && m_is_update_folder && m_daybreak_path.has_value()) {
|
||||
App::Push(std::make_shared<OptionBox>("Open with DayBreak?"_i18n, "No"_i18n, "Yes"_i18n, 1, [this](auto op_index){
|
||||
if (op_index && *op_index) {
|
||||
// daybreak uses native fs so do not use nro_add_arg_file
|
||||
@@ -330,9 +340,9 @@ Menu::Menu(const std::vector<NroEntry>& nro_entries) : MenuBase{"FileBrowser"_i1
|
||||
|
||||
if (entry.type == FsDirEntryType_Dir) {
|
||||
Scan(GetNewPathCurrent());
|
||||
} else if (m_fs_type == FsType::Sd) {
|
||||
} else {
|
||||
// special case for nro
|
||||
if (entry.GetExtension() == "nro") {
|
||||
if (IsSd() && entry.GetExtension() == "nro") {
|
||||
App::Push(std::make_shared<OptionBox>("Launch "_i18n + entry.GetName() + '?',
|
||||
"No"_i18n, "Launch"_i18n, 1, [this](auto op_index){
|
||||
if (op_index && *op_index) {
|
||||
@@ -341,7 +351,7 @@ Menu::Menu(const std::vector<NroEntry>& nro_entries) : MenuBase{"FileBrowser"_i1
|
||||
}));
|
||||
} else if (App::GetInstallEnable() && IsExtension(entry.GetExtension(), INSTALL_EXTENSIONS)) {
|
||||
InstallFiles();
|
||||
} else {
|
||||
} else if (IsSd()) {
|
||||
const auto assoc_list = FindFileAssocFor();
|
||||
if (!assoc_list.empty()) {
|
||||
// for (auto&e : assoc_list) {
|
||||
@@ -374,12 +384,12 @@ Menu::Menu(const std::vector<NroEntry>& nro_entries) : MenuBase{"FileBrowser"_i1
|
||||
|
||||
std::make_pair(Button::B, Action{"Back"_i18n, [this](){
|
||||
std::string_view view{m_path};
|
||||
if (view != "/") {
|
||||
if (view != m_fs->Root()) {
|
||||
const auto end = view.find_last_of('/');
|
||||
assert(end != view.npos);
|
||||
|
||||
if (end == 0) {
|
||||
Scan("/", true);
|
||||
Scan(m_fs->Root(), true);
|
||||
} else {
|
||||
Scan(view.substr(0, end), true);
|
||||
}
|
||||
@@ -519,7 +529,7 @@ Menu::Menu(const std::vector<NroEntry>& nro_entries) : MenuBase{"FileBrowser"_i1
|
||||
}
|
||||
}
|
||||
|
||||
if (m_fs_type == FsType::Sd && m_entries_current.size()) {
|
||||
if (IsSd() && m_entries_current.size()) {
|
||||
if (App::GetInstallEnable() && HasTypeInSelectedEntries(FsDirEntryType_File) && !m_selected_count && (GetEntry().GetExtension() == "nro" || !FindFileAssocFor().empty())) {
|
||||
options->Add(std::make_shared<SidebarEntryCallback>("Install Forwarder"_i18n, [this](){;
|
||||
if (App::GetInstallPrompt()) {
|
||||
@@ -552,7 +562,7 @@ Menu::Menu(const std::vector<NroEntry>& nro_entries) : MenuBase{"FileBrowser"_i1
|
||||
App::Push(std::make_shared<OptionBox>("Are you sure you want to extract to root?"_i18n,
|
||||
"No"_i18n, "Yes"_i18n, 0, [this](auto op_index){
|
||||
if (op_index && *op_index) {
|
||||
UnzipFiles("/");
|
||||
UnzipFiles(m_fs->Root());
|
||||
}
|
||||
}));
|
||||
}));
|
||||
@@ -591,11 +601,11 @@ Menu::Menu(const std::vector<NroEntry>& nro_entries) : MenuBase{"FileBrowser"_i1
|
||||
|
||||
options->Add(std::make_shared<SidebarEntryCallback>("Create File"_i18n, [this](){
|
||||
std::string out;
|
||||
if (R_SUCCEEDED(swkbd::ShowText(out, "Set File Name"_i18n.c_str())) && !out.empty()) {
|
||||
if (R_SUCCEEDED(swkbd::ShowText(out, "Set File Name"_i18n.c_str(), fs::AppendPath(m_path, ""))) && !out.empty()) {
|
||||
App::PopToMenu();
|
||||
|
||||
fs::FsPath full_path;
|
||||
if (out[0] == '/') {
|
||||
if (out.starts_with(m_fs_entry.root.s)) {
|
||||
full_path = out;
|
||||
} else {
|
||||
full_path = fs::AppendPath(m_path, out);
|
||||
@@ -617,7 +627,7 @@ Menu::Menu(const std::vector<NroEntry>& nro_entries) : MenuBase{"FileBrowser"_i1
|
||||
App::PopToMenu();
|
||||
|
||||
fs::FsPath full_path;
|
||||
if (out[0] == '/') {
|
||||
if (out.starts_with(m_fs_entry.root.s)) {
|
||||
full_path = out;
|
||||
} else {
|
||||
full_path = fs::AppendPath(m_path, out);
|
||||
@@ -632,13 +642,13 @@ Menu::Menu(const std::vector<NroEntry>& nro_entries) : MenuBase{"FileBrowser"_i1
|
||||
}
|
||||
}));
|
||||
|
||||
if (m_fs_type == FsType::Sd && m_entries_current.size() && !m_selected_count && GetEntry().IsFile() && GetEntry().file_size < 1024*64) {
|
||||
if (IsSd() && m_entries_current.size() && !m_selected_count && GetEntry().IsFile() && GetEntry().file_size < 1024*64) {
|
||||
options->Add(std::make_shared<SidebarEntryCallback>("View as text (unfinished)"_i18n, [this](){
|
||||
App::Push(std::make_shared<fileview::Menu>(GetNewPathCurrent()));
|
||||
}));
|
||||
}
|
||||
|
||||
if (m_fs_type == FsType::Sd && m_entries_current.size()) {
|
||||
if (m_entries_current.size()) {
|
||||
options->Add(std::make_shared<SidebarEntryCallback>("Upload"_i18n, [this](){
|
||||
UploadFiles();
|
||||
}));
|
||||
@@ -650,15 +660,27 @@ Menu::Menu(const std::vector<NroEntry>& nro_entries) : MenuBase{"FileBrowser"_i1
|
||||
}));
|
||||
|
||||
SidebarEntryArray::Items mount_items;
|
||||
mount_items.push_back("Sd"_i18n);
|
||||
mount_items.push_back("Image System memory"_i18n);
|
||||
mount_items.push_back("Image microSD card"_i18n);
|
||||
std::vector<FsEntry> fs_entries;
|
||||
for (const auto& e: FS_ENTRIES) {
|
||||
fs_entries.emplace_back(e);
|
||||
mount_items.push_back(i18n::get(e.name));
|
||||
}
|
||||
|
||||
options->Add(std::make_shared<SidebarEntryArray>("Mount"_i18n, mount_items, [this](s64& index_out){
|
||||
const auto stdio_locations = location::GetStdio(false);
|
||||
for (const auto& e: stdio_locations) {
|
||||
u32 flags{};
|
||||
if (e.write_protect) {
|
||||
flags |= FsEntryFlag_ReadOnly;
|
||||
}
|
||||
|
||||
fs_entries.emplace_back(e.name, e.mount, FsType::Stdio, flags);
|
||||
mount_items.push_back(e.name);
|
||||
}
|
||||
|
||||
options->Add(std::make_shared<SidebarEntryArray>("Mount"_i18n, mount_items, [this, fs_entries](s64& index_out){
|
||||
App::PopToMenu();
|
||||
m_mount.Set(index_out);
|
||||
SetFs("/", index_out);
|
||||
}, m_mount.Get()));
|
||||
SetFs(fs_entries[index_out].root, fs_entries[index_out]);
|
||||
}, m_fs_entry.name));
|
||||
}));
|
||||
}})
|
||||
);
|
||||
@@ -668,11 +690,14 @@ Menu::Menu(const std::vector<NroEntry>& nro_entries) : MenuBase{"FileBrowser"_i1
|
||||
|
||||
fs::FsPath buf;
|
||||
ini_gets("paths", "last_path", "/", buf, sizeof(buf), App::CONFIG_PATH);
|
||||
SetFs(buf, m_mount.Get());
|
||||
SetFs(buf, FS_ENTRY_DEFAULT);
|
||||
}
|
||||
|
||||
Menu::~Menu() {
|
||||
ini_puts("paths", "last_path", m_path, App::CONFIG_PATH);
|
||||
// don't store mount points for non-sd card paths.
|
||||
if (IsSd()) {
|
||||
ini_puts("paths", "last_path", m_path, App::CONFIG_PATH);
|
||||
}
|
||||
}
|
||||
|
||||
void Menu::Update(Controller* controller, TouchInfo* touch) {
|
||||
@@ -704,10 +729,10 @@ void Menu::Draw(NVGcontext* vg, Theme* theme) {
|
||||
auto& e = GetEntry(i);
|
||||
|
||||
if (e.IsDir()) {
|
||||
if (e.file_count == -1 && e.dir_count == -1) {
|
||||
if (m_fs->IsNative() && e.file_count == -1 && e.dir_count == -1) {
|
||||
const auto full_path = GetNewPath(e);
|
||||
m_fs->DirGetEntryCount(full_path, FsDirOpenMode_ReadFiles | FsDirOpenMode_NoFileSize, &e.file_count);
|
||||
m_fs->DirGetEntryCount(full_path, FsDirOpenMode_ReadDirs | FsDirOpenMode_NoFileSize, &e.dir_count);
|
||||
GetNative()->DirGetEntryCount(full_path, FsDirOpenMode_ReadFiles | FsDirOpenMode_NoFileSize, &e.file_count);
|
||||
GetNative()->DirGetEntryCount(full_path, FsDirOpenMode_ReadDirs | FsDirOpenMode_NoFileSize, &e.dir_count);
|
||||
}
|
||||
} else if (!e.checked_extension) {
|
||||
e.checked_extension = true;
|
||||
@@ -759,13 +784,17 @@ void Menu::Draw(NVGcontext* vg, Theme* theme) {
|
||||
gfx::drawText(vg, x + text_xoffset+65, y + (h / 2.f), 20.f, e.name, NULL, NVG_ALIGN_LEFT | NVG_ALIGN_MIDDLE, theme->GetColour(text_id));
|
||||
nvgRestore(vg);
|
||||
|
||||
if (e.IsDir()) {
|
||||
if (m_fs->IsNative() && 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 {
|
||||
} else if (e.IsFile()) {
|
||||
if (!e.time_stamp.is_valid) {
|
||||
const auto path = GetNewPath(e);
|
||||
m_fs->GetFileTimeStampRaw(path, &e.time_stamp);
|
||||
if (m_fs->IsNative()) {
|
||||
m_fs->GetFileTimeStampRaw(path, &e.time_stamp);
|
||||
} else {
|
||||
m_fs->FileGetSizeAndTimestamp(path, &e.time_stamp, &e.file_size);
|
||||
}
|
||||
}
|
||||
const auto t = (time_t)(e.time_stamp.modified);
|
||||
struct tm tm{};
|
||||
@@ -784,7 +813,7 @@ void Menu::OnFocusGained() {
|
||||
MenuBase::OnFocusGained();
|
||||
if (m_entries.empty()) {
|
||||
if (m_path.empty()) {
|
||||
Scan("/");
|
||||
Scan(m_fs->Root());
|
||||
} else {
|
||||
Scan(m_path);
|
||||
}
|
||||
@@ -899,7 +928,11 @@ void Menu::InstallFiles() {
|
||||
|
||||
App::Push(std::make_shared<ui::ProgressBox>(0, "Installing "_i18n, "", [this, targets](auto pbox) -> Result {
|
||||
for (auto& e : targets) {
|
||||
R_TRY(yati::InstallFromFile(pbox, &m_fs->m_fs, GetNewPath(e)));
|
||||
if (m_fs->IsNative()) {
|
||||
R_TRY(yati::InstallFromFile(pbox, &GetNative()->m_fs, GetNewPath(e)));
|
||||
} else {
|
||||
R_TRY(yati::InstallFromStdioFile(pbox, GetNewPath(e)));
|
||||
}
|
||||
App::Notify("Installed " + e.GetName());
|
||||
}
|
||||
|
||||
@@ -919,7 +952,6 @@ void Menu::UnzipFiles(fs::FsPath dir_path) {
|
||||
|
||||
App::Push(std::make_shared<ui::ProgressBox>(0, "Extracting "_i18n, "", [this, dir_path, targets](auto pbox) -> Result {
|
||||
constexpr auto chunk_size = 1024 * 512; // 512KiB
|
||||
auto& fs = *m_fs.get();
|
||||
|
||||
for (auto& e : targets) {
|
||||
pbox->SetTitle(e.GetName());
|
||||
@@ -959,19 +991,19 @@ void Menu::UnzipFiles(fs::FsPath dir_path) {
|
||||
pbox->NewTransfer(name);
|
||||
|
||||
// create directories
|
||||
fs.CreateDirectoryRecursivelyWithPath(file_path);
|
||||
m_fs->CreateDirectoryRecursivelyWithPath(file_path);
|
||||
|
||||
Result rc;
|
||||
if (R_FAILED(rc = fs.CreateFile(file_path, info.uncompressed_size, 0)) && rc != FsError_PathAlreadyExists) {
|
||||
if (R_FAILED(rc = m_fs->CreateFile(file_path, info.uncompressed_size, 0)) && rc != FsError_PathAlreadyExists) {
|
||||
log_write("failed to create file: %s 0x%04X\n", file_path.s, rc);
|
||||
R_THROW(rc);
|
||||
}
|
||||
|
||||
FsFile f;
|
||||
R_TRY(fs.OpenFile(file_path, FsOpenMode_Write, &f));
|
||||
ON_SCOPE_EXIT(fsFileClose(&f));
|
||||
fs::File f;
|
||||
R_TRY(m_fs->OpenFile(file_path, FsOpenMode_Write, &f));
|
||||
ON_SCOPE_EXIT(m_fs->FileClose(&f));
|
||||
|
||||
R_TRY(fsFileSetSize(&f, info.uncompressed_size));
|
||||
R_TRY(m_fs->FileSetSize(&f, info.uncompressed_size));
|
||||
|
||||
std::vector<char> buf(chunk_size);
|
||||
s64 offset{};
|
||||
@@ -984,7 +1016,7 @@ void Menu::UnzipFiles(fs::FsPath dir_path) {
|
||||
R_THROW(0x1);
|
||||
}
|
||||
|
||||
R_TRY(fsFileWrite(&f, offset, buf.data(), bytes_read, FsWriteOption_None));
|
||||
R_TRY(m_fs->FileWrite(&f, offset, buf.data(), bytes_read, FsWriteOption_None));
|
||||
|
||||
pbox->UpdateTransfer(offset, info.uncompressed_size);
|
||||
offset += bytes_read;
|
||||
@@ -1030,7 +1062,7 @@ void Menu::ZipFiles(fs::FsPath zip_out) {
|
||||
}
|
||||
|
||||
zip_out = fs::AppendPath(m_path, file_path);
|
||||
if (!fs::FileExists(&m_fs->m_fs, zip_out)) {
|
||||
if (!m_fs->FileExists(zip_out)) {
|
||||
break;
|
||||
}
|
||||
}
|
||||
@@ -1043,7 +1075,6 @@ void Menu::ZipFiles(fs::FsPath zip_out) {
|
||||
|
||||
App::Push(std::make_shared<ui::ProgressBox>(0, "Compressing "_i18n, "", [this, zip_out, targets](auto pbox) -> Result {
|
||||
constexpr auto chunk_size = 1024 * 512; // 512KiB
|
||||
auto& fs = *m_fs.get();
|
||||
|
||||
const auto t = std::time(NULL);
|
||||
const auto tm = std::localtime(&t);
|
||||
@@ -1080,12 +1111,12 @@ void Menu::ZipFiles(fs::FsPath zip_out) {
|
||||
}
|
||||
ON_SCOPE_EXIT(zipCloseFileInZip(zfile));
|
||||
|
||||
FsFile f;
|
||||
R_TRY(fs.OpenFile(file_path, FsOpenMode_Read, &f));
|
||||
ON_SCOPE_EXIT(fsFileClose(&f));
|
||||
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(fsFileGetSize(&f, &file_size));
|
||||
R_TRY(m_fs->FileGetSize(&f, &file_size));
|
||||
|
||||
std::vector<char> buf(chunk_size);
|
||||
s64 offset{};
|
||||
@@ -1093,7 +1124,7 @@ void Menu::ZipFiles(fs::FsPath zip_out) {
|
||||
R_TRY(pbox->ShouldExitResult());
|
||||
|
||||
u64 bytes_read;
|
||||
R_TRY(fsFileRead(&f, offset, buf.data(), buf.size(), FsReadOption_None, &bytes_read));
|
||||
R_TRY(m_fs->FileRead(&f, 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);
|
||||
@@ -1168,13 +1199,13 @@ void Menu::UploadFiles() {
|
||||
pbox->SetTitle(name);
|
||||
pbox->NewTransfer(relative_file_name);
|
||||
|
||||
FsFile file;
|
||||
R_TRY(m_fs->OpenFile(file_path, FsOpenMode_Read, &file));
|
||||
ON_SCOPE_EXIT(fsFileClose(&file));
|
||||
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 fsFileRead(&file, off, data, size, FsReadOption_None, bytes_read);
|
||||
return m_fs->FileRead(&f, off, data, size, FsReadOption_None, bytes_read);
|
||||
},
|
||||
[&](thread::PullCallback pull) -> Result {
|
||||
s64 offset{};
|
||||
@@ -1256,20 +1287,16 @@ auto Menu::Scan(const fs::FsPath& new_path, bool is_walk_up) -> Result {
|
||||
ResetSelection();
|
||||
}
|
||||
|
||||
FsDir d;
|
||||
fs::Dir d;
|
||||
R_TRY(m_fs->OpenDirectory(new_path, FsDirOpenMode_ReadDirs | FsDirOpenMode_ReadFiles, &d));
|
||||
ON_SCOPE_EXIT(fsDirClose(&d));
|
||||
|
||||
s64 count;
|
||||
R_TRY(m_fs->DirGetEntryCount(&d, &count));
|
||||
ON_SCOPE_EXIT(m_fs->DirClose(&d));
|
||||
|
||||
// we won't run out of memory here (tm)
|
||||
std::vector<FsDirectoryEntry> dir_entries(count);
|
||||
std::vector<FsDirectoryEntry> dir_entries;
|
||||
|
||||
R_TRY(m_fs->DirRead(&d, &count, dir_entries.size(), dir_entries.data()));
|
||||
R_TRY(m_fs->DirReadAll(&d, dir_entries));
|
||||
|
||||
// size may of changed
|
||||
dir_entries.resize(count);
|
||||
const auto count = dir_entries.size();
|
||||
m_entries.reserve(count);
|
||||
|
||||
m_entries_index.clear();
|
||||
@@ -1581,9 +1608,9 @@ void Menu::OnDeleteCallback() {
|
||||
const auto full_path = GetNewPath(m_selected_path, entry.name);
|
||||
|
||||
if (entry.IsDir()) {
|
||||
s64 count{};
|
||||
m_fs->DirGetEntryCount(full_path, FsDirOpenMode_ReadDirs | FsDirOpenMode_ReadFiles | FsDirOpenMode_NoFileSize, &count);
|
||||
if (!count) {
|
||||
bool empty{};
|
||||
m_fs->IsDirEmpty(full_path, &empty);
|
||||
if (empty) {
|
||||
m_fs->DeleteDirectory(full_path);
|
||||
use_progress_box = false;
|
||||
}
|
||||
@@ -1726,7 +1753,7 @@ void Menu::OnPasteCallback() {
|
||||
m_fs->CreateDirectory(dst_path);
|
||||
} else {
|
||||
pbox->NewTransfer("Copying "_i18n + src_path);
|
||||
R_TRY(pbox->CopyFile(src_path, dst_path));
|
||||
R_TRY(pbox->CopyFile(m_fs.get(), src_path, dst_path));
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1755,7 +1782,7 @@ void Menu::OnPasteCallback() {
|
||||
|
||||
pbox->NewTransfer("Copying "_i18n + src_path);
|
||||
log_write("copying: %s to %s\n", src_path.s, dst_path.s);
|
||||
R_TRY(pbox->CopyFile(src_path, dst_path));
|
||||
R_TRY(pbox->CopyFile(m_fs.get(), src_path, dst_path));
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1776,7 +1803,8 @@ void Menu::OnRenameCallback() {
|
||||
}
|
||||
|
||||
auto Menu::CheckIfUpdateFolder() -> Result {
|
||||
R_UNLESS(m_fs_type == FsType::Sd, FsError_InvalidMountName);
|
||||
R_UNLESS(IsSd(), FsError_InvalidMountName);
|
||||
R_UNLESS(m_fs->IsNative(), 0x1);
|
||||
|
||||
// check if we have already tried to find daybreak
|
||||
if (m_daybreak_path.has_value() && m_daybreak_path.value().empty()) {
|
||||
@@ -1799,12 +1827,8 @@ auto Menu::CheckIfUpdateFolder() -> Result {
|
||||
log_write("found daybreak in: %s\n", m_daybreak_path.value().s);
|
||||
}
|
||||
|
||||
FsDir d;
|
||||
R_TRY(m_fs->OpenDirectory(m_path, FsDirOpenMode_ReadDirs, &d));
|
||||
ON_SCOPE_EXIT(m_fs->DirClose(&d));
|
||||
|
||||
s64 count;
|
||||
R_TRY(m_fs->DirGetEntryCount(&d, &count));
|
||||
R_TRY(GetNative()->DirGetEntryCount(m_path, FsDirOpenMode_ReadDirs, &count));
|
||||
|
||||
// check that we are at the bottom level
|
||||
R_UNLESS(count == 0, 0x1);
|
||||
@@ -1826,15 +1850,10 @@ auto Menu::get_collection(const fs::FsPath& path, const fs::FsPath& parent_name,
|
||||
out.parent_name = parent_name;
|
||||
|
||||
const auto fetch = [this, &path](std::vector<FsDirectoryEntry>& out, u32 flags) -> Result {
|
||||
FsDir d;
|
||||
fs::Dir d;
|
||||
R_TRY(m_fs->OpenDirectory(path, flags, &d));
|
||||
ON_SCOPE_EXIT(m_fs->DirClose(&d));
|
||||
|
||||
s64 count;
|
||||
R_TRY(m_fs->DirGetEntryCount(&d, &count));
|
||||
|
||||
out.resize(count);
|
||||
return m_fs->DirRead(&d, &count, out.size(), out.data());
|
||||
return m_fs->DirReadAll(&d, out);
|
||||
};
|
||||
|
||||
if (inc_file) {
|
||||
@@ -1871,9 +1890,9 @@ auto Menu::get_collections(const fs::FsPath& path, const fs::FsPath& parent_name
|
||||
R_SUCCEED();
|
||||
}
|
||||
|
||||
void Menu::SetFs(const fs::FsPath& new_path, u32 _new_type) {
|
||||
const auto new_type = static_cast<FsType>(_new_type);
|
||||
if (m_fs && new_type == m_fs_type) {
|
||||
void Menu::SetFs(const fs::FsPath& new_path, const FsEntry& new_entry) {
|
||||
if (m_fs && m_fs_entry.root == new_entry.root && m_fs_entry.type == new_entry.type) {
|
||||
log_write("same fs, ignoring\n");
|
||||
return;
|
||||
}
|
||||
|
||||
@@ -1888,28 +1907,26 @@ void Menu::SetFs(const fs::FsPath& new_path, u32 _new_type) {
|
||||
m_selected_path.clear();
|
||||
m_selected_count = 0;
|
||||
m_selected_type = SelectedType::None;
|
||||
m_fs_entry = new_entry;
|
||||
|
||||
switch (new_type) {
|
||||
default: case FsType::Sd:
|
||||
switch (new_entry.type) {
|
||||
case FsType::Sd:
|
||||
m_fs = std::make_unique<fs::FsNativeSd>(m_ignore_read_only.Get());
|
||||
m_fs_type = FsType::Sd;
|
||||
log_write("doing fs: %u\n", _new_type);
|
||||
break;
|
||||
case FsType::ImageNand:
|
||||
m_fs = std::make_unique<fs::FsNativeImage>(FsImageDirectoryId_Nand);
|
||||
m_fs_type = FsType::ImageNand;
|
||||
log_write("doing image nand\n");
|
||||
break;
|
||||
case FsType::ImageSd:
|
||||
m_fs = std::make_unique<fs::FsNativeImage>(FsImageDirectoryId_Sd);
|
||||
m_fs_type = FsType::ImageSd;
|
||||
log_write("doing image sd\n");
|
||||
break;
|
||||
case FsType::Stdio:
|
||||
m_fs = std::make_unique<fs::FsStdio>(true, new_entry.root);
|
||||
break;
|
||||
}
|
||||
|
||||
if (HasFocus()) {
|
||||
if (m_path.empty()) {
|
||||
Scan("/");
|
||||
Scan(m_fs->Root());
|
||||
} else {
|
||||
Scan(m_path);
|
||||
}
|
||||
|
||||
@@ -219,41 +219,36 @@ auto ProgressBox::ShouldExitResult() -> Result {
|
||||
R_SUCCEED();
|
||||
}
|
||||
|
||||
auto ProgressBox::CopyFile(const fs::FsPath& src_path, const fs::FsPath& dst_path) -> Result {
|
||||
fs::FsNativeSd fs;
|
||||
R_TRY(fs.GetFsOpenResult());
|
||||
|
||||
FsFile src_file;
|
||||
R_TRY(fs.OpenFile(src_path, FsOpenMode_Read, &src_file));
|
||||
ON_SCOPE_EXIT(fsFileClose(&src_file));
|
||||
auto ProgressBox::CopyFile(fs::Fs* fs, const fs::FsPath& src_path, const fs::FsPath& dst_path) -> Result {
|
||||
fs::File src_file;
|
||||
R_TRY(fs->OpenFile(src_path, FsOpenMode_Read, &src_file));
|
||||
ON_SCOPE_EXIT(fs->FileClose(&src_file));
|
||||
|
||||
s64 src_size;
|
||||
R_TRY(fsFileGetSize(&src_file, &src_size));
|
||||
R_TRY(fs->FileGetSize(&src_file, &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
|
||||
// handled when we try and open it for writing.
|
||||
fs.CreateFile(dst_path, src_size, 0);
|
||||
fs->CreateFile(dst_path, src_size, 0);
|
||||
|
||||
FsFile dst_file;
|
||||
R_TRY(fs.OpenFile(dst_path, FsOpenMode_Write, &dst_file));
|
||||
ON_SCOPE_EXIT(fsFileClose(&dst_file));
|
||||
fs::File dst_file;
|
||||
R_TRY(fs->OpenFile(dst_path, FsOpenMode_Write, &dst_file));
|
||||
ON_SCOPE_EXIT(fs->FileClose(&dst_file));
|
||||
|
||||
R_TRY(fsFileSetSize(&dst_file, src_size));
|
||||
R_TRY(fs->FileSetSize(&dst_file, src_size));
|
||||
|
||||
s64 offset{};
|
||||
std::vector<u8> buf(1024*1024*4); // 4MiB
|
||||
|
||||
while (offset < src_size) {
|
||||
if (ShouldExit()) {
|
||||
R_THROW(0xFFFF);
|
||||
}
|
||||
R_TRY(ShouldExitResult());
|
||||
|
||||
u64 bytes_read;
|
||||
R_TRY(fsFileRead(&src_file, offset, buf.data(), buf.size(), 0, &bytes_read));
|
||||
R_TRY(fs->FileRead(&src_file, offset, buf.data(), buf.size(), 0, &bytes_read));
|
||||
Yield();
|
||||
|
||||
R_TRY(fsFileWrite(&dst_file, offset, buf.data(), bytes_read, FsWriteOption_None));
|
||||
R_TRY(fs->FileWrite(&dst_file, offset, buf.data(), bytes_read, FsWriteOption_None));
|
||||
Yield();
|
||||
|
||||
UpdateTransfer(offset, src_size);
|
||||
@@ -263,6 +258,12 @@ auto ProgressBox::CopyFile(const fs::FsPath& src_path, const fs::FsPath& dst_pat
|
||||
R_SUCCEED();
|
||||
}
|
||||
|
||||
auto ProgressBox::CopyFile(const fs::FsPath& src_path, const fs::FsPath& dst_path) -> Result {
|
||||
fs::FsNativeSd fs;
|
||||
R_TRY(fs.GetFsOpenResult());
|
||||
return CopyFile(&fs, src_path, dst_path);
|
||||
}
|
||||
|
||||
void ProgressBox::Yield() {
|
||||
svcSleepThread(YieldType_WithoutCoreMigration);
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user