diff --git a/Libraries/FsLib b/Libraries/FsLib index 7156c8a..30f5674 160000 --- a/Libraries/FsLib +++ b/Libraries/FsLib @@ -1 +1 @@ -Subproject commit 7156c8a8828096205cecc3421413bda75987fe78 +Subproject commit 30f56742733a350a1f4788d6fa2bcf26712ae669 diff --git a/include/appstates/FileOptionState.hpp b/include/appstates/FileOptionState.hpp index 46ebea1..9a3a22a 100644 --- a/include/appstates/FileOptionState.hpp +++ b/include/appstates/FileOptionState.hpp @@ -117,6 +117,13 @@ class FileOptionState final : public BaseState /// @brief Gets the properties of a file/folder. void get_show_target_properties(); + /// @brief Gets info and creates a MessageState displaying the properties of the target directory. + void get_show_directory_properties(const fslib::Path &path); + + /// @brief Gets info and creates a message displaying the properties of the target file. + /// @param path + void get_show_file_properties(const fslib::Path &path); + /// @brief Closes and hides the state. void close(); diff --git a/include/fs/directory_functions.hpp b/include/fs/directory_functions.hpp index 03f4bf1..634a95e 100644 --- a/include/fs/directory_functions.hpp +++ b/include/fs/directory_functions.hpp @@ -3,9 +3,16 @@ namespace fs { - /// @brief Retrieves the total size of the contents of the directory at targetPath. - /// @param targetPath Directory to calculate. - uint64_t get_directory_total_size(const fslib::Path &targetPath); + /// @brief Recursively runs through the path passed and retrieves information. + /// @param directoryPath Path of directory. + /// @param subDirCount Int64_t to track number of subdirectories with. + /// @param fileCount Int64_t to track the number of files. + /// @param totalSize Int64_t to track the size of everything. + /// @return True on success. False or crash on what I would assume is a stack overflow. + bool get_directory_information(const fslib::Path &directoryPath, + int64_t &subDirCount, + int64_t &fileCount, + int64_t &totalSize); /// @brief Checks if directory is empty. Didn't feel like this needs its own source file. /// @param directoryPath Path to directory to check. diff --git a/include/strings/names.hpp b/include/strings/names.hpp index c7c797b..062bc50 100644 --- a/include/strings/names.hpp +++ b/include/strings/names.hpp @@ -13,6 +13,8 @@ namespace strings::names inline constexpr std::string_view EXTRASMENU_POPS = "ExtrasPops"; inline constexpr std::string_view FILEOPTION_MENU = "FileOptionMenu"; inline constexpr std::string_view FILEOPTION_CONFS = "FileOptionConfs"; + inline constexpr std::string_view FILEOPTION_MESSAGES = "FileOptionMessages"; + inline constexpr std::string_view FILEOPTION_STATUS = "FileOptionStatus"; inline constexpr std::string_view FILEMODE_POPS = "FileModePops"; inline constexpr std::string_view GENERAL_POPS = "GeneralPops"; inline constexpr std::string_view GOOGLE_DRIVE = "GoogleDriveStrings"; diff --git a/romfs/Text/ENUS.json b/romfs/Text/ENUS.json index e34ccfd..2c4ad0c 100644 --- a/romfs/Text/ENUS.json +++ b/romfs/Text/ENUS.json @@ -71,6 +71,13 @@ "0: Are you sure you want to copy #%s# to #%s#?", "1: Are you sure you want to delete #%s#?" ], + "FileOptionMessages": [ + "0: #%s#:\n\t\tSub Directories: %lli\n\t\tFile count: %lli\n\t\tTotal Size: %s", + "1: #%s#:\n\t\tSize: %s\n\t\tFirst created: %s\n\t\tLast modified: %s\n\t\tLast Accessed: %s" + ], + "FileOptionStatus": [ + "0: Reading #%s#'s data..." + ], "FileModePops": [ "0: Copied #%s#!", "1: Failed to copy #%s#!", diff --git a/source/appstates/FileOptionState.cpp b/source/appstates/FileOptionState.cpp index 6140bde..980f40c 100644 --- a/source/appstates/FileOptionState.cpp +++ b/source/appstates/FileOptionState.cpp @@ -1,10 +1,12 @@ #include "appstates/FileOptionState.hpp" #include "appstates/ConfirmState.hpp" +#include "appstates/MessageState.hpp" #include "appstates/ProgressState.hpp" #include "appstates/TaskState.hpp" #include "config/config.hpp" #include "error.hpp" +#include "fs/fs.hpp" #include "fslib.hpp" #include "input.hpp" #include "keyboard.hpp" @@ -14,6 +16,8 @@ #include "stringutil.hpp" #include "tasks/fileoptions.hpp" +#include + namespace { enum @@ -31,6 +35,9 @@ namespace using ProgressConfirm = ConfirmState; } +// Defined at bottom. +static std::string get_size_string(int64_t totalSize); + FileOptionState::FileOptionState(FileModeState *spawningState) : m_spawningState(spawningState) , m_dataStruct(std::make_shared()) @@ -305,9 +312,85 @@ void FileOptionState::create_directory() void FileOptionState::get_show_target_properties() { - const fslib::Path &targetPath = m_spawningState->get_source_path(); - const fslib::Directory &targetDir = m_spawningState->get_source_directory(); - const ui::Menu &targeMenu = m_spawningState->get_source_menu(); + const fslib::Path &sourcePath = m_spawningState->get_source_path(); + const fslib::Directory &sourceDir = m_spawningState->get_source_directory(); + const ui::Menu &sourceMenu = m_spawningState->get_source_menu(); + + fslib::Path targetPath{sourcePath}; + const int selected = sourceMenu.get_selected(); + if (selected > 1) + { + const int dirIndex = selected - 2; + targetPath /= sourceDir[dirIndex]; + } + + const bool isDir = fslib::directory_exists(targetPath); + if (isDir) { FileOptionState::get_show_directory_properties(targetPath); } + else { FileOptionState::get_show_file_properties(targetPath); } +} + +void FileOptionState::get_show_directory_properties(const fslib::Path &path) +{ + + int64_t subDirCount{}; + int64_t fileCount{}; + int64_t totalSize{}; + const bool getInfo = fs::get_directory_information(path, subDirCount, fileCount, totalSize); + if (!getInfo) { return; } + + const char *messageFormat = strings::get_by_name(strings::names::FILEOPTION_MESSAGES, 0); + const char *filename = path.get_filename(); + const char *messagePath = filename; + const std::string sizeString = get_size_string(totalSize); + const std::string message = + stringutil::get_formatted_string(messageFormat, filename, subDirCount, fileCount, sizeString.c_str()); + + MessageState::create_and_push(message); +} + +void FileOptionState::get_show_file_properties(const fslib::Path &path) +{ + + static constexpr size_t BUFFER_SIZE = 0x40; + + FsTimeStampRaw timestamp{}; + const int64_t fileSize = fslib::get_file_size(path); + + const bool stampError = error::fslib(fslib::get_file_timestamp(path, timestamp)); + if (fileSize == -1 || stampError) { return; } + + logger::log("%lli, %lli, %lli", timestamp.created, timestamp.modified, timestamp.accessed); + + // I don't like this, but it's the easiest way to pull this off. + const std::time_t created = static_cast(timestamp.created); + const std::time_t modified = static_cast(timestamp.modified); + const std::time_t accessed = static_cast(timestamp.accessed); + logger::log("%lli, %lli, %lli", created, modified, accessed); + + std::tm createdTm{}, modifiedTm{}, accessedTm{}; + localtime_r(&created, &createdTm); + localtime_r(&modified, &modifiedTm); + localtime_r(&accessed, &accessedTm); + + char createdBuffer[BUFFER_SIZE] = {0}; + char lastModified[BUFFER_SIZE] = {0}; + char lastAccessed[BUFFER_SIZE] = {0}; + + std::strftime(createdBuffer, BUFFER_SIZE, "%c", &createdTm); + std::strftime(lastModified, BUFFER_SIZE, "%c", &modifiedTm); + std::strftime(lastAccessed, BUFFER_SIZE, "%c", &accessedTm); + + const char *messageFormat = strings::get_by_name(strings::names::FILEOPTION_MESSAGES, 1); + const char *filename = path.get_filename(); + const std::string sizeString = get_size_string(fileSize); + const std::string message = stringutil::get_formatted_string(messageFormat, + filename, + sizeString.c_str(), + createdBuffer, + lastModified, + lastAccessed); + + MessageState::create_and_push(message); } void FileOptionState::close() @@ -324,4 +407,31 @@ void FileOptionState::deactivate_state() { sm_copyMenu->set_selected(0); BaseState::deactivate(); +} + +static std::string get_size_string(int64_t totalSize) +{ + static constexpr int64_t THRESHOLD_BYTES = 0x400; + static constexpr int64_t THRESHOLD_KB = 0x100000; + static constexpr int64_t THRESHOLD_MB = 0x40000000; + + std::string sizeString{}; + if (totalSize > THRESHOLD_MB) + { + const double gigabytes = static_cast(totalSize) / static_cast(THRESHOLD_MB); + sizeString = stringutil::get_formatted_string("%.02f GB", gigabytes); + } + else if (totalSize < THRESHOLD_BYTES) { sizeString = stringutil::get_formatted_string("%lli bytes", totalSize); } + else if (totalSize < THRESHOLD_KB) + { + const double kilobytes = static_cast(totalSize) / static_cast(THRESHOLD_BYTES); + sizeString = stringutil::get_formatted_string("%.02f KB", kilobytes); + } + else + { + const double megabytes = static_cast(totalSize) / static_cast(THRESHOLD_KB); + sizeString = stringutil::get_formatted_string("%.02f MB", megabytes); + } + + return sizeString; } \ No newline at end of file diff --git a/source/fs/directory_functions.cpp b/source/fs/directory_functions.cpp index 989b49d..3d85092 100644 --- a/source/fs/directory_functions.cpp +++ b/source/fs/directory_functions.cpp @@ -1,24 +1,34 @@ #include "fs/directory_functions.hpp" +#include "error.hpp" #include "fs/SaveMetaData.hpp" +#include "logging/logger.hpp" -uint64_t fs::get_directory_total_size(const fslib::Path &targetPath) +bool fs::get_directory_information(const fslib::Path &directoryPath, + int64_t &subDirCount, + int64_t &fileCount, + int64_t &totalSize) { - fslib::Directory targetDir{targetPath}; - if (!targetDir.is_open()) { return 0; } + fslib::Directory dir{directoryPath}; + if (error::fslib(dir.is_open())) { return false; } - uint64_t directorySize = 0; - for (const fslib::DirectoryEntry &entry : targetDir) + for (const fslib::DirectoryEntry &entry : dir) { if (entry.is_directory()) { - const fslib::Path newTarget{targetPath / entry}; - directorySize += fs::get_directory_total_size(newTarget); + const fslib::Path newPath{directoryPath / entry}; + const bool getInfo = fs::get_directory_information(newPath, subDirCount, fileCount, totalSize); + if (!getInfo) { return false; } + ++subDirCount; + } + else + { + totalSize += entry.get_size(); + ++fileCount; } - else { directorySize += entry.get_size(); } } - return directorySize; + return true; } bool fs::directory_has_contents(const fslib::Path &directoryPath) diff --git a/source/stringutil.cpp b/source/stringutil.cpp index 536584e..907faa0 100644 --- a/source/stringutil.cpp +++ b/source/stringutil.cpp @@ -83,9 +83,9 @@ std::string stringutil::get_date_string(stringutil::DateFormat format) { char stringBuffer[0x80] = {0}; - std::time_t timer; + std::time_t timer{}; std::time(&timer); - std::tm *localTime = std::localtime(&timer); + const std::tm *localTime = std::localtime(&timer); switch (format) { diff --git a/source/tasks/fileoptions.cpp b/source/tasks/fileoptions.cpp index d0b5726..259d8c2 100644 --- a/source/tasks/fileoptions.cpp +++ b/source/tasks/fileoptions.cpp @@ -1,9 +1,9 @@ #include "tasks/fileoptions.hpp" +#include "appstates/MessageState.hpp" #include "error.hpp" #include "fs/fs.hpp" #include "fslib.hpp" -#include "logging/logger.hpp" #include "strings/strings.hpp" #include "stringutil.hpp" @@ -69,4 +69,4 @@ void tasks::fileoptions::delete_target(sys::Task *task, FileOptionState::TaskDat spawningState->update_source(); task->complete(); -} \ No newline at end of file +}