NX-Shell/source/fs.cpp

504 lines
17 KiB
C++
Raw Normal View History

2020-08-03 22:10:06 +00:00
#include <algorithm>
#include <cstdio>
#include <cstring>
#include <filesystem>
#include "config.h"
#include "fs.h"
2020-09-02 21:36:54 +00:00
#include "log.h"
2020-08-03 22:10:06 +00:00
// Global vars
FsFileSystem *fs;
FsFileSystem devices[4];
namespace FS {
static int PREVIOUS_BROWSE_STATE = 0;
typedef struct {
char copy_path[FS_MAX_PATH];
char copy_filename[FS_MAX_PATH];
bool is_dir = false;
} FS_Copy_Struct;
FS_Copy_Struct fs_copy_struct;
2021-09-12 21:08:02 +00:00
bool FileExists(const char path[FS_MAX_PATH]) {
FsFile file;
if (R_SUCCEEDED(fsFsOpenFile(fs, path, FsOpenMode_Read, &file))) {
fsFileClose(&file);
return true;
}
return false;
}
bool DirExists(const char path[FS_MAX_PATH]) {
FsDir dir;
if (R_SUCCEEDED(fsFsOpenDirectory(fs, path, FsDirOpenMode_ReadDirs, &dir))) {
fsDirClose(&dir);
return true;
}
return false;
}
Result GetFileSize(const char path[FS_MAX_PATH], s64 *size) {
Result ret = 0;
FsFile file;
if (R_FAILED(ret = fsFsOpenFile(fs, path, FsOpenMode_Read, &file))) {
Log::Error("fsFsOpenFile(%s) failed: 0x%x\n", path, ret);
return ret;
}
if (R_FAILED(ret = fsFileGetSize(&file, size))) {
Log::Error("fsFileGetSize(%s) failed: 0x%x\n", path, ret);
fsFileClose(&file);
return ret;
}
fsFileClose(&file);
return 0;
}
2021-09-12 21:08:02 +00:00
std::string GetFileExt(const std::string &filename) {
std::string ext = std::filesystem::path(filename).extension();
std::transform(ext.begin(), ext.end(), ext.begin(), ::toupper);
return ext;
}
FileType GetFileType(const std::string &filename) {
std::string ext = FS::GetFileExt(filename);
if ((!ext.compare(".ZIP")) || (!ext.compare(".RAR")) || (!ext.compare(".7Z")))
return FileTypeArchive;
else if ((!ext.compare(".BMP")) || (!ext.compare(".GIF")) || (!ext.compare(".JPG")) || (!ext.compare(".JPEG")) || (!ext.compare(".PGM"))
|| (!ext.compare(".PPM")) || (!ext.compare(".PNG")) || (!ext.compare(".PSD")) || (!ext.compare(".TGA")) || (!ext.compare(".WEBP")))
return FileTypeImage;
else if ((!ext.compare(".JSON")) || (!ext.compare(".LOG")) || (!ext.compare(".TXT")) || (!ext.compare(".CFG")) || (!ext.compare(".INI")))
return FileTypeText;
return FileTypeNone;
}
2021-09-12 21:08:02 +00:00
static bool Sort(const FsDirectoryEntry &entryA, const FsDirectoryEntry &entryB) {
if ((entryA.type == FsDirEntryType_Dir) && !(entryB.type == FsDirEntryType_Dir))
return true;
else if (!(entryA.type == FsDirEntryType_Dir) && (entryB.type == FsDirEntryType_Dir))
return false;
else {
switch(cfg.sort) {
case 0: // Sort alphabetically (ascending - A to Z)
if (strcasecmp(entryA.name, entryB.name) < 0)
return true;
break;
case 1: // Sort alphabetically (descending - Z to A)
if (strcasecmp(entryB.name, entryA.name) < 0)
return true;
break;
case 2: // Sort by file size (largest first)
if (entryB.file_size < entryA.file_size)
return true;
break;
case 3: // Sort by file size (smallest first)
if (entryA.file_size < entryB.file_size)
return true;
break;
}
}
return false;
}
Result GetDirList(const char path[FS_MAX_PATH], std::vector<FsDirectoryEntry> &entries) {
FsDir dir;
Result ret = 0;
2021-09-12 21:08:02 +00:00
s64 read_entries = 0;
const std::string cwd = path;
entries.clear();
2021-09-12 21:08:02 +00:00
if (strncmp(path, "/", std::strlen(path))) {
FsDirectoryEntry entry;
std::strncpy(entry.name, "..", 3);
entry.type = FsDirEntryType_Dir;
entry.file_size = 0;
entries.push_back(entry);
}
if (R_FAILED(ret = fsFsOpenDirectory(fs, path, FsDirOpenMode_ReadDirs | FsDirOpenMode_ReadFiles, &dir))) {
Log::Error("fsFsOpenDirectory(%s) failed: 0x%x\n", path, ret);
return ret;
}
2021-09-12 21:08:02 +00:00
while (true) {
FsDirectoryEntry entry;
if (R_FAILED(ret = fsDirRead(&dir, &read_entries, 1, &entry))) {
fsDirClose(&dir);
Log::Error("fsDirRead(%s) failed: 0x%x\n", path, ret);
return ret;
}
if (read_entries != 1)
break;
2021-09-12 21:08:02 +00:00
entries.push_back(entry);
}
std::sort(entries.begin(), entries.end(), FS::Sort);
fsDirClose(&dir);
return 0;
}
2021-09-12 21:08:02 +00:00
static Result ChangeDir(const char path[FS_MAX_PATH], std::vector<FsDirectoryEntry> &entries) {
Result ret = 0;
std::vector<FsDirectoryEntry> new_entries;
if (R_FAILED(ret = FS::GetDirList(path, new_entries)))
return ret;
// Apply cd after successfully listing new directory
entries.clear();
std::strncpy(cfg.cwd, path, FS_MAX_PATH);
Config::Save(cfg);
entries = new_entries;
return 0;
}
static int GetPrevPath(char path[FS_MAX_PATH]) {
if (std::strlen(cfg.cwd) <= 1 && cfg.cwd[0] == '/')
return -1;
// Remove upmost directory
bool copy = false;
int len = 0;
for (ssize_t i = std::strlen(cfg.cwd); i >= 0; i--) {
if (cfg.cwd[i] == '/')
copy = true;
if (copy) {
path[i] = cfg.cwd[i];
len++;
}
}
// remove trailing slash
if (len > 1 && path[len - 1] == '/')
len--;
path[len] = '\0';
return 0;
}
Result ChangeDirNext(const char path[FS_MAX_PATH], std::vector<FsDirectoryEntry> &entries) {
char new_cwd[FS_MAX_PATH + 1];
const char *sep = !std::strncmp(cfg.cwd, "/", 2) ? "" : "/"; // Don't append / if at /
if ((std::snprintf(new_cwd, FS_MAX_PATH, "%s%s%s", cfg.cwd, sep, path)) > 0)
return FS::ChangeDir(new_cwd, entries);
return 0;
}
Result ChangeDirPrev(std::vector<FsDirectoryEntry> &entries) {
char new_cwd[FS_MAX_PATH];
if (FS::GetPrevPath(new_cwd) < 0)
return -1;
return FS::ChangeDir(new_cwd, entries);
}
int ConstructPath(FsDirectoryEntry *entry, char path[FS_MAX_PATH + 1], const char filename[FS_MAX_PATH]) {
if (entry) {
if ((std::snprintf(path, FS_MAX_PATH, "%s%s%s", cfg.cwd, !std::strncmp(cfg.cwd, "/", 2) ? "" : "/", entry? entry->name : "")) > 0)
return 0;
}
else {
if ((std::snprintf(path, FS_MAX_PATH, "%s%s%s", cfg.cwd, !std::strncmp(cfg.cwd, "/", 2) ? "" : "/", filename[0] != '\0'? filename : "")) > 0)
return 0;
}
return -1;
}
Result GetTimeStamp(FsDirectoryEntry *entry, FsTimeStampRaw *timestamp) {
Result ret = 0;
char path[FS_MAX_PATH];
if (FS::ConstructPath(entry, path, "") < 0)
return -1;
if (R_FAILED(ret = fsFsGetFileTimeStampRaw(fs, path, timestamp))) {
Log::Error("fsFsGetFileTimeStampRaw(%s) failed: 0x%x\n", path, ret);
return ret;
}
return 0;
}
Result Rename(FsDirectoryEntry *entry, const char filename[FS_MAX_PATH]) {
Result ret = 0;
char path[FS_MAX_PATH];
if (FS::ConstructPath(entry, path, "") < 0)
return -1;
char new_path[FS_MAX_PATH];
if (FS::ConstructPath(nullptr, new_path, filename) < 0)
return -1;
if (entry->type == FsDirEntryType_Dir) {
if (R_FAILED(ret = fsFsRenameDirectory(fs, path, new_path))) {
Log::Error("fsFsRenameDirectory(%s, %s) failed: 0x%x\n", path, new_path, ret);
return ret;
}
}
else {
if (R_FAILED(ret = fsFsRenameFile(fs, path, new_path))) {
Log::Error("fsFsRenameFile(%s, %s) failed: 0x%x\n", path, new_path, ret);
return ret;
}
}
return 0;
}
Result Delete(FsDirectoryEntry *entry) {
Result ret = 0;
char path[FS_MAX_PATH];
if (FS::ConstructPath(entry, path, "") < 0)
return -1;
if (entry->type == FsDirEntryType_Dir) {
if (R_FAILED(ret = fsFsDeleteDirectoryRecursively(fs, path))) {
Log::Error("fsFsDeleteDirectoryRecursively(%s) failed: 0x%x\n", path, ret);
return ret;
}
}
else {
if (R_FAILED(ret = fsFsDeleteFile(fs, path))) {
Log::Error("fsFsDeleteFile(%s) failed: 0x%x\n", path, ret);
return ret;
}
}
return 0;
}
Result SetArchiveBit(FsDirectoryEntry *entry) {
Result ret = 0;
char path[FS_MAX_PATH];
if (FS::ConstructPath(entry, path, "") < 0)
return -1;
if (R_FAILED(ret = fsFsSetConcatenationFileAttribute(fs, path))) {
Log::Error("fsFsSetConcatenationFileAttribute(%s) failed: 0x%x\n", path, ret);
return ret;
}
return 0;
}
static Result CopyFile(const char src_path[FS_MAX_PATH], const char dest_path[FS_MAX_PATH]) {
Result ret = 0;
FsFile src_handle, dest_handle;
if (R_FAILED(ret = fsFsOpenFile(&devices[PREVIOUS_BROWSE_STATE], src_path, FsOpenMode_Read, &src_handle))) {
Log::Error("fsFsOpenFile(%s) failed: 0x%x\n", src_path, ret);
return ret;
}
s64 size = 0;
if (R_FAILED(ret = fsFileGetSize(&src_handle, &size))) {
Log::Error("fsFileGetSize(%s) failed: 0x%x\n", src_path, ret);
fsFileClose(&src_handle);
return ret;
}
// This may fail or not, but we don't care -> create the file if it doesn't exist, otherwise continue.
fsFsCreateFile(fs, dest_path, size, 0);
if (R_FAILED(ret = fsFsOpenFile(fs, dest_path, FsOpenMode_Write, &dest_handle))) {
Log::Error("fsFsOpenFile(%s) failed: 0x%x\n", dest_path, ret);
fsFileClose(&src_handle);
return ret;
}
u64 bytes_read = 0;
const u64 buf_size = 0x10000;
s64 offset = 0;
unsigned char *buf = new unsigned char[buf_size];
std::string filename = std::filesystem::path(src_path).filename();
do {
std::memset(buf, 0, buf_size);
if (R_FAILED(ret = fsFileRead(&src_handle, offset, buf, buf_size, FsReadOption_None, &bytes_read))) {
Log::Error("fsFileRead(%s) failed: 0x%x\n", src_path, ret);
delete[] buf;
fsFileClose(&src_handle);
fsFileClose(&dest_handle);
return ret;
}
if (R_FAILED(ret = fsFileWrite(&dest_handle, offset, buf, bytes_read, FsWriteOption_Flush))) {
Log::Error("fsFileWrite(%s) failed: 0x%x\n", dest_path, ret);
delete[] buf;
fsFileClose(&src_handle);
fsFileClose(&dest_handle);
return ret;
}
offset += bytes_read;
//Popups::ProgressPopup(static_cast<float>(offset), static_cast<float>(size), strings[cfg.lang][Lang::OptionsCopying], filename.c_str());
} while(offset < size);
delete[] buf;
fsFileClose(&src_handle);
fsFileClose(&dest_handle);
return 0;
}
static Result CopyDir(const char src_path[FS_MAX_PATH], const char dest_path[FS_MAX_PATH]) {
Result ret = 0;
FsDir dir;
if (R_FAILED(ret = fsFsOpenDirectory(&devices[PREVIOUS_BROWSE_STATE], src_path, FsDirOpenMode_ReadDirs | FsDirOpenMode_ReadFiles, &dir))) {
Log::Error("fsFsOpenDirectory(%s) failed: 0x%x\n", src_path, ret);
return ret;
}
// This may fail or not, but we don't care -> make the dir if it doesn't exist, otherwise continue.
fsFsCreateDirectory(fs, dest_path);
s64 entry_count = 0;
if (R_FAILED(ret = fsDirGetEntryCount(&dir, &entry_count))) {
Log::Error("fsDirGetEntryCount(%s) failed: 0x%x\n", src_path, ret);
return ret;
}
FsDirectoryEntry *entries = new FsDirectoryEntry[entry_count * sizeof(*entries)];
if (R_FAILED(ret = fsDirRead(&dir, nullptr, static_cast<size_t>(entry_count), entries))) {
Log::Error("fsDirRead(%s) failed: 0x%x\n", src_path, ret);
delete[] entries;
return ret;
}
for (s64 i = 0; i < entry_count; i++) {
std::string filename = entries[i].name;
if (!filename.empty()) {
if ((!filename.compare(".")) || (!filename.compare("..")))
continue;
std::string src = src_path;
src.append("/");
src.append(filename);
std::string dest = dest_path;
dest.append("/");
dest.append(filename);
if (entries[i].type == FsDirEntryType_Dir)
FS::CopyDir(src.c_str(), dest.c_str()); // Copy Folder (via recursion)
else
FS::CopyFile(src.c_str(), dest.c_str()); // Copy File
}
}
delete[] entries;
fsDirClose(&dir);
return 0;
}
void Copy(FsDirectoryEntry *entry, const std::string &path) {
std::string full_path = path;
full_path.append("/");
full_path.append(entry->name);
std::strcpy(fs_copy_struct.copy_path, full_path.c_str());
std::strcpy(fs_copy_struct.copy_filename, entry->name);
if (entry->type == FsDirEntryType_Dir)
fs_copy_struct.is_dir = true;
}
Result Paste(void) {
Result ret = 0;
char path[FS_MAX_PATH];
FS::ConstructPath(nullptr, path, fs_copy_struct.copy_filename);
if (fs_copy_struct.is_dir) // Copy folder recursively
ret = FS::CopyDir(fs_copy_struct.copy_path, path);
else // Copy file
ret = FS::CopyFile(fs_copy_struct.copy_path, path);
std::memset(fs_copy_struct.copy_path, 0, FS_MAX_PATH);
std::memset(fs_copy_struct.copy_filename, 0, FS_MAX_PATH);
fs_copy_struct.is_dir = false;
return ret;
}
Result Move(void) {
Result ret = 0;
char path[FS_MAX_PATH];
FS::ConstructPath(nullptr, path, fs_copy_struct.copy_filename);
if (fs_copy_struct.is_dir) {
if (R_FAILED(ret = fsFsRenameDirectory(fs, fs_copy_struct.copy_path, path))) {
Log::Error("fsFsRenameDirectory(%s, %s) failed: 0x%x\n", path, fs_copy_struct.copy_filename, ret);
return ret;
}
}
else {
if (R_FAILED(ret = fsFsRenameFile(fs, fs_copy_struct.copy_path, path))) {
Log::Error("fsFsRenameFile(%s, %s) failed: 0x%x\n", path, fs_copy_struct.copy_filename, ret);
return ret;
}
}
std::memset(fs_copy_struct.copy_path, 0, FS_MAX_PATH);
std::memset(fs_copy_struct.copy_filename, 0, FS_MAX_PATH);
fs_copy_struct.is_dir = false;
return 0;
}
2021-09-12 21:08:02 +00:00
Result GetFreeStorageSpace(s64 &size) {
Result ret = 0;
if (R_FAILED(ret = fsFsGetFreeSpace(fs, "/", &size))) {
Log::Error("fsFsGetFreeSpace() failed: 0x%x\n", ret);
return ret;
}
return 0;
}
Result GetTotalStorageSpace(s64 &size) {
Result ret = 0;
if (R_FAILED(ret = fsFsGetTotalSpace(fs, "/", &size))) {
Log::Error("fsFsGetTotalSpace() failed: 0x%x\n", ret);
return ret;
}
return 0;
}
Result GetUsedStorageSpace(s64 &size) {
Result ret = 0;
s64 free_size = 0, total_size = 0;
if (R_FAILED(ret = FS::GetFreeStorageSpace(free_size)))
return ret;
if (R_FAILED(ret = FS::GetTotalStorageSpace(total_size)))
return ret;
size = (total_size - free_size);
return 0;
}
2020-08-03 22:10:06 +00:00
}