File mode progress.

This commit is contained in:
J-D-K 2025-09-03 15:54:54 -04:00
parent 75303dcebb
commit a072c28dfb
82 changed files with 1489 additions and 372 deletions

1
.gitignore vendored
View File

@ -16,3 +16,4 @@ JKSV.pfs0
#sometimes I give jksv to people and forget about this.
JKSV.zip
JKSV.nro.zip

@ -1 +1 @@
Subproject commit f948cecb43c4f1e089442a8c0fd58df7deefb955
Subproject commit 7156c8a8828096205cecc3421413bda75987fe78

@ -1 +1 @@
Subproject commit fa7d0f456c78d7f94b8519260978419b564610b8
Subproject commit 779fc5bd8aa4782e376fbce3baa146837f7d4825

View File

@ -51,7 +51,7 @@ ARCH := -march=armv8-a+crc+crypto -mtune=cortex-a57 -mtp=soft -fPIE
# NOTE: For some reason, devkitpro no longer has freetype-config included? Also, using pkg-config causes conflicts with
# my local pkg-config and I don't feel like dealing with CMake for all of this right now.
CFLAGS := $(INCLUDE) -D__SWITCH__ `sdl2-config --cflags` `curl-config --cflags`\
-g -Wall -O2 -ffunction-sections -ffast-math -fmax-errors=1 \
-g -Wall -O3 -ffunction-sections -ffast-math -fmax-errors=1 \
$(ARCH) $(DEFINES)
CXXFLAGS:= $(CFLAGS) -fno-rtti -fno-exceptions -std=c++23
@ -172,11 +172,12 @@ clean:
@$(MAKE) -C ./Libraries/SDLLib/SDL clean
@rm -fr $(BUILD) $(TARGET).pfs0 $(TARGET).nso $(TARGET).nro $(TARGET).nacp $(TARGET).elf
#---------------------------------------------------------------------------------
send: $(BUILD)
@nxlink $(TARGET).nro
#---------------------------------------------------------------------------------
debug: $(BUILD)
@nxlink -s $(TARGET).nro

View File

@ -16,6 +16,9 @@ class BaseState
/// @brief Every derived class is required to have this function.
virtual void update() = 0;
/// @brief Sub update routine. Meant to handle minor background tasks. Not meant for full update routines.
virtual void sub_update() {};
/// @brief Every derived class is required to have this function.
virtual void render() = 0;

View File

@ -34,4 +34,22 @@ class ExtrasMenuState final : public BaseState
/// @brief This function is called when Reinitialize data is selected.
void reinitialize_data();
/// @brief Opens an SD to SD file browser.
void sd_to_sd_browser();
/// @brief Opens the prodinfo-f to sd.
void prodinfof_to_sd();
/// @brief Opens the safe partition to SD.
void safe_to_sd();
/// @brief Opens the system partition to SD.
void system_to_sd();
/// @brief Opens the user partition to SD.
void user_to_sd();
/// @brief Terminates a process.
void terminate_process();
};

View File

@ -0,0 +1,172 @@
#pragma once
#include "StateManager.hpp"
#include "appstates/BaseState.hpp"
#include "fslib.hpp"
#include "sdl.hpp"
#include "ui/ui.hpp"
#include <string_view>
class FileModeState final : public BaseState
{
public:
/// @brief Constructs a new FileModeState.
FileModeState(std::string_view mountA, std::string_view mountB, int64_t journalSize = 0);
/// @brief Destructor. Closes the filesystems passed.
~FileModeState() {};
static inline std::shared_ptr<FileModeState> create(std::string_view mountA,
std::string_view mountB,
int64_t journalSize = 0)
{
return std::make_shared<FileModeState>(mountA, mountB, journalSize);
}
static inline std::shared_ptr<FileModeState> create_and_push(std::string_view mountA,
std::string_view mountB,
int64_t journalSize = 0)
{
auto newState = std::make_shared<FileModeState>(mountA, mountB, journalSize);
StateManager::push_state(newState);
return newState;
}
/// @brief Update override.
void update() override;
/// @brief Render override.
void render() override;
/// @brief Returns the target/active bool.
bool get_target() const noexcept;
/// @brief Returns the source path. This is used with the FileOptionState.
fslib::Path get_source();
/// @brief Returns the destination path. This is used with the FileOptionState.
fslib::Path get_destination();
/// @brief Returns whether or not committing the transfer is required to FileOptionState.
bool commit_required() const noexcept;
/// @brief Returns the journaling size passed to this for FileOptionState.
int64_t get_journal_size() const noexcept;
/// @brief Renders the control guide in the bottom right.
void render_control_guide() noexcept;
private:
/// @brief These store the mount points to close the filesystems upon construction.
std::string m_mountA;
std::string m_mountB;
/// @brief These are the actual target paths.
fslib::Path m_pathA{};
fslib::Path m_pathB{};
/// @brief Directory listings for each respective path.
fslib::Directory m_dirA{};
fslib::Directory m_dirB{};
/// @brief Menus for the directory listings.
std::shared_ptr<ui::Menu> m_dirMenuA{};
std::shared_ptr<ui::Menu> m_dirMenuB{};
/// @brief Controls which menu/filesystem is currently targetted.
bool m_target{};
/// @brief Stores the size for committing data (if needed) to mountA.
int64_t m_journalSize{};
/// @brief The beginning Y coord of the dialog.
double m_y{720.0f};
/// @brief This is the targetY. Used for the opening and hiding effect.
double m_targetY{91.0f};
/// @brief Config scaling for the "transition"
double m_scaling{};
/// @brief Stores whether or not the dialog has reached its targetted position.
bool m_inPlace{};
/// @brief Frame shared by all instances.
static inline std::shared_ptr<ui::Frame> sm_frame{};
/// @brief This is the render target the browsers are rendered to.
static inline sdl::SharedTexture sm_renderTarget{};
/// @brief Stores a pointer to the guide in the bottom-right corner.
static inline const char *sm_controlGuide{};
/// @brief Calculated X coordinate of the control guide text.
static inline int sm_controlGuideX{};
/// @brief Initializes the members shared by all instances of FileModeState.
void initialize_static_members();
/// @brief Creates valid paths with the mounts passed.
void initialize_paths();
/// @brief Ensures the menus are allocated and setup properly.
void initialize_menus();
/// @brief Loads the current directory listings and menus.
void initialize_directory_menu(const fslib::Path &path, fslib::Directory &directory, ui::Menu &menu);
/// @brief Updates the dialog's coordinates in the beginning.
void update_y_coord() noexcept;
/// @brief Starts the dialog hiding process.
void hide_dialog() noexcept;
/// @brief Returns whether or not the dialog is hidden.
bool is_hidden() noexcept;
/// @brief Handles changing the current directory or opening the options.
void enter_selected(fslib::Path &path, fslib::Directory &directory, ui::Menu &menu);
/// @brief Opens the little option pop-up thingy.
void open_option_menu(fslib::Directory &directory, ui::Menu &menu);
/// @brief Changes the current target/controllable menu.
void change_target() noexcept;
/// @brief Function executed when '..' is selected.
void up_one_directory(fslib::Path &path, fslib::Directory &directory, ui::Menu &menu);
/// @brief Appends the entry passed to the path passed and "enters" the directory.
/// @param path Working path.
/// @param directory Working directory.
/// @param menu Working menu.
/// @param entry Target entry.
void enter_directory(fslib::Path &path,
fslib::Directory &directory,
ui::Menu &menu,
const fslib::DirectoryEntry &entry);
/// @brief Returns a reference to the currently active menu.
ui::Menu &get_source_menu() noexcept;
/// @brief Returns a reference to the currently inactive menu.
ui::Menu &get_destination_menu() noexcept;
/// @brief Returns a reference to the current "active" path.
fslib::Path &get_source_path() noexcept;
/// @brief Returns a reference to the currently "inactive" path.
fslib::Path &get_destination_path() noexcept;
/// @brief Returns a reference to the "active" directory.
fslib::Directory &get_source_directory() noexcept;
/// @brief Returns a reference ot the currently "inactive" directory.
fslib::Directory &get_destination_directory() noexcept;
/// @brief Returns whether or not a path is at the root.
inline bool path_is_root(fslib::Path &path) const { return std::char_traits<char>::length(path.get_path()) <= 1; }
/// @brief Closes the filesystems passed and deactivates the state.
void deactivate_state() noexcept;
};

View File

@ -0,0 +1,110 @@
#pragma once
#include "StateManager.hpp"
#include "appstates/BaseState.hpp"
#include "appstates/FileModeState.hpp"
#include "fslib.hpp"
#include "ui/ui.hpp"
class FileOptionState final : public BaseState
{
public:
/// @brief FileOptionState.
/// @param spawningState Pointer to spawning state to grab its goodies.
FileOptionState(FileModeState *spawningState);
~FileOptionState() {};
/// @brief Inline creation function.
static inline std::shared_ptr<FileOptionState> create(FileModeState *spawningState)
{
return std::make_shared<FileOptionState>(spawningState);
}
/// @brief Same as above. Pushes state before returning it.
static inline std::shared_ptr<FileOptionState> create_and_push(FileModeState *spawningState)
{
auto newState = FileOptionState::create(spawningState);
StateManager::push_state(newState);
return newState;
}
/// @brief Update routine.
void update() override;
/// @brief Render routine.
void render() override;
// clang-format off
struct DataStruct
{
fslib::Path sourcePath{};
fslib::Path destPath{};
int64_t journalSize{};
};
// clang-format on
/// @brief This makes some other stuff easier to read and type.
using TaskData = std::shared_ptr<FileOptionState::DataStruct>;
private:
/// @brief Pointer to spawning FileMode state.
FileModeState *m_spawningState{};
/// @brief Stores whether or not tasks require committing data and changes to the target.
bool m_commitData{};
/// @brief Journal size for when committing is required.
int64_t m_journalSize{};
/// @brief X coordinate. This is set at construction according to the target from the spawning state.
int m_x{};
/// @brief X coordinate for the target to reach.
int m_targetX{};
/// @brief Whether or not the dialog/menu is in place.
bool m_inPlace{};
/// @brief Whether or not the state should be closed.
bool m_close{};
/// @brief This holds the scaling in config.
double m_scaling{};
/// @brief This is the data struct passed to tasks.
std::shared_ptr<FileOptionState::DataStruct> m_dataStruct{};
/// @brief This is shared by all instances.
static inline std::shared_ptr<ui::Menu> sm_copyMenu{};
/// @brief This is shared by all instances.
static inline std::shared_ptr<ui::DialogBox> sm_dialog{};
/// @brief Ensures static members of all instances are allocated.
void initialize_static_members();
/// @brief Sets whether the dialog/menu are positioned left or right depending on the menu active in the spawning state.
void set_menu_side();
/// @brief Updates the Y coordinate
void update_x_coord();
void copy_target();
void delete_target();
void rename_target();
void create_directory();
void get_show_target_properties();
/// @brief Closes and hides the state.
void close();
/// @brief Returns whether or not the state is closed.
bool is_closed();
/// @brief Sets the menu index back to 0 and deactivates the state.
void deactivate_state();
};

View File

@ -39,6 +39,9 @@ class TitleOptionState final : public BaseState
/// @brief Runs update routine.
void update() override;
/// @brief Handles hiding the panel.
void sub_update() override;
/// @brief Runs the render routine.
void render() override;

View File

@ -36,6 +36,9 @@ class UserOptionState final : public BaseState
/// @brief Runs the render routine.
void update() override;
/// @brief Handles hiding the panel.
void sub_update() override;
/// @brief Runs the render routine.
void render() override;

View File

@ -1,34 +1,31 @@
#pragma once
#include <string_view>
namespace config
namespace config::keys
{
namespace keys
{
inline constexpr std::string_view WORKING_DIRECTORY = "WorkingDirectory";
inline constexpr std::string_view INCLUDE_DEVICE_SAVES = "IncludeDeviceSaves";
inline constexpr std::string_view AUTO_BACKUP_ON_RESTORE = "AutoBackupOnRestore";
inline constexpr std::string_view AUTO_NAME_BACKUPS = "AutoNameBackups";
inline constexpr std::string_view AUTO_UPLOAD = "AutoUploadToRemote";
inline constexpr std::string_view USE_TITLE_IDS = "AlwaysUseTitleID";
inline constexpr std::string_view HOLD_FOR_DELETION = "HoldForDeletion";
inline constexpr std::string_view HOLD_FOR_RESTORATION = "HoldForRestoration";
inline constexpr std::string_view HOLD_FOR_OVERWRITE = "HoldForOverWrite";
inline constexpr std::string_view ONLY_LIST_MOUNTABLE = "OnlyListMountable";
inline constexpr std::string_view LIST_ACCOUNT_SYS_SAVES = "ListAccountSystemSaves";
inline constexpr std::string_view ALLOW_WRITING_TO_SYSTEM = "AllowSystemSaveWriting";
inline constexpr std::string_view EXPORT_TO_ZIP = "ExportToZip";
inline constexpr std::string_view ZIP_COMPRESSION_LEVEL = "ZipCompressionLevel";
inline constexpr std::string_view TITLE_SORT_TYPE = "TitleSortType";
inline constexpr std::string_view JKSM_TEXT_MODE = "JKSMTextMode";
inline constexpr std::string_view FORCE_ENGLISH = "ForceEnglish";
inline constexpr std::string_view SHOW_DEVICE_USER = "ShowDevice";
inline constexpr std::string_view SHOW_BCAT_USER = "ShowBCAT";
inline constexpr std::string_view SHOW_CACHE_USER = "ShowCache";
inline constexpr std::string_view SHOW_SYSTEM_USER = "ShowSystem";
inline constexpr std::string_view ENABLE_TRASH_BIN = "EnableTrash";
inline constexpr std::string_view UI_ANIMATION_SCALE = "UIAnimationScaling";
inline constexpr std::string_view FAVORITES = "Favorites";
inline constexpr std::string_view BLACKLIST = "BlackList";
} // namespace keys
inline constexpr std::string_view WORKING_DIRECTORY = "WorkingDirectory";
inline constexpr std::string_view INCLUDE_DEVICE_SAVES = "IncludeDeviceSaves";
inline constexpr std::string_view AUTO_BACKUP_ON_RESTORE = "AutoBackupOnRestore";
inline constexpr std::string_view AUTO_NAME_BACKUPS = "AutoNameBackups";
inline constexpr std::string_view AUTO_UPLOAD = "AutoUploadToRemote";
inline constexpr std::string_view USE_TITLE_IDS = "AlwaysUseTitleID";
inline constexpr std::string_view HOLD_FOR_DELETION = "HoldForDeletion";
inline constexpr std::string_view HOLD_FOR_RESTORATION = "HoldForRestoration";
inline constexpr std::string_view HOLD_FOR_OVERWRITE = "HoldForOverWrite";
inline constexpr std::string_view ONLY_LIST_MOUNTABLE = "OnlyListMountable";
inline constexpr std::string_view LIST_ACCOUNT_SYS_SAVES = "ListAccountSystemSaves";
inline constexpr std::string_view ALLOW_WRITING_TO_SYSTEM = "AllowSystemSaveWriting";
inline constexpr std::string_view EXPORT_TO_ZIP = "ExportToZip";
inline constexpr std::string_view ZIP_COMPRESSION_LEVEL = "ZipCompressionLevel";
inline constexpr std::string_view TITLE_SORT_TYPE = "TitleSortType";
inline constexpr std::string_view JKSM_TEXT_MODE = "JKSMTextMode";
inline constexpr std::string_view FORCE_ENGLISH = "ForceEnglish";
inline constexpr std::string_view SHOW_DEVICE_USER = "ShowDevice";
inline constexpr std::string_view SHOW_BCAT_USER = "ShowBCAT";
inline constexpr std::string_view SHOW_CACHE_USER = "ShowCache";
inline constexpr std::string_view SHOW_SYSTEM_USER = "ShowSystem";
inline constexpr std::string_view ENABLE_TRASH_BIN = "EnableTrash";
inline constexpr std::string_view UI_ANIMATION_SCALE = "UIAnimationScaling";
inline constexpr std::string_view FAVORITES = "Favorites";
inline constexpr std::string_view BLACKLIST = "BlackList";
}

View File

@ -25,13 +25,10 @@ namespace data
/// @brief Initializes a TitleInfo instance using external (cached) NsApplicationControlData
/// @param applicationID Application ID of the title loaded from cache.
/// @param controlData Reference to the control data to init from.
TitleInfo(uint64_t applicationID, std::unique_ptr<NsApplicationControlData> &controlData);
TitleInfo(uint64_t applicationID, NsApplicationControlData &controlData);
/// @brief Move constructor and operator.
TitleInfo(TitleInfo &&titleInfo);
TitleInfo &operator=(TitleInfo &&TitleInfo);
// None of this nonesense around these parts.TitleInfo(const TitleInfo &) = delete;
// None of this nonesense around these parts.
TitleInfo(const TitleInfo &) = delete;
TitleInfo &operator=(const TitleInfo &) = delete;
/// @brief Returns the application ID of the title.
@ -104,8 +101,8 @@ namespace data
/// @brief Stores application ID for easier grabbing since JKSV is all pointers.
uint64_t m_applicationID{};
/// @brief This contains the NACP and the icon.
std::unique_ptr<NsApplicationControlData> m_data{};
/// @brief Where all the good stuff is.
NsApplicationControlData m_data{};
/// @brief Saves whether or not the title has control data.
bool m_hasData{};

View File

@ -6,7 +6,7 @@
namespace fs
{
class MiniUnzip
class MiniUnzip final
{
public:
MiniUnzip() = default;

View File

@ -6,7 +6,7 @@
namespace fs
{
class MiniZip
class MiniZip final
{
public:
MiniZip() = default;
@ -41,6 +41,9 @@ namespace fs
/// @brief Stores whether or not the zipFile was opened successfully.
bool m_isOpen{};
/// @brief Stores the compression level from config to avoid repeated calls.
int m_level{};
/// @brief Underlying ZIP file.
zipFile m_zip{};
};

View File

@ -20,6 +20,6 @@ namespace fs
private:
/// @brief Vector of paths to filter from deletion and backup.
std::vector<std::string> m_paths{};
std::vector<fslib::Path> m_paths{};
};
}

View File

@ -32,9 +32,6 @@ namespace fs
} __attribute__((packed));
// clang-format on
// I didn't want a separate file for this.
bool read_save_data_extra_info(const FsSaveDataInfo *saveInfo, FsSaveDataExtraData &dataOut);
/// @brief Didn't feel like a whole new file just for this. Fills an fs::SaveMetaData struct.
bool fill_save_meta_data(const FsSaveDataInfo *saveInfo, SaveMetaData &meta);

View File

@ -19,7 +19,6 @@ namespace fs
/// @param journalSize Size of the journal area of the save data.
void copy_file_commit(const fslib::Path &source,
const fslib::Path &destination,
std::string_view device,
int64_t journalSize,
sys::ProgressTask *task = nullptr);
@ -36,7 +35,6 @@ namespace fs
/// @param journalSize Size of the journaling area of the save.
void copy_directory_commit(const fslib::Path &source,
const fslib::Path &destination,
std::string_view device,
int64_t journalSize,
sys::ProgressTask *task = nullptr);

View File

@ -1,5 +1,6 @@
#pragma once
#include "data/data.hpp"
#include <switch.h>
namespace fs
@ -27,4 +28,10 @@ namespace fs
/// @return True if it is. False if it isn't.
/// @note The config setting overrides this.
bool is_system_save_data(const FsSaveDataInfo *saveInfo);
/// @brief Reads the extra info of the save container according to the FsSaveDataInfo passed.
/// @param saveInfo Pointer to the save info to read.
/// @param extraOut Reference to the FsSaveDataExtraData to read to.
/// @return True on success. False on failure.
bool read_save_extra_data(const FsSaveDataInfo *saveInfo, FsSaveDataExtraData &extraOut);
} // namespace fs

View File

@ -18,7 +18,6 @@ namespace fs
void copy_zip_to_directory(fs::MiniUnzip &source,
const fslib::Path &dest,
int64_t journalSize,
std::string_view commitDevice,
sys::ProgressTask *Task = nullptr);
/// @brief Returns whether or not zip has files inside besides the save meta.

View File

@ -13,6 +13,7 @@ namespace colors
inline constexpr sdl::Color PINK = {0xFF4444FF};
inline constexpr sdl::Color BLUE_GREEN = {0x00FFC5FF};
inline constexpr sdl::Color CLEAR_COLOR = {0x2D2D2DFF};
inline constexpr sdl::Color CLEAR_PANEL = {0x0D0D0DFF};
inline constexpr sdl::Color DIALOG_DARK = {0x505050FF};
inline constexpr sdl::Color DIALOG_LIGHT = {0xDCDCDCFF};
inline constexpr sdl::Color DIM_BACKGROUND = {0x00000088};

View File

@ -1,44 +1,44 @@
#pragma once
#include <string_view>
namespace strings
namespace strings::names
{
namespace names
{
inline constexpr std::string_view BACKUPMENU_MENU = "BackupMenu";
inline constexpr std::string_view BACKUPMENU_CONFS = "BackupMenuConfirmations";
inline constexpr std::string_view BACKUPMENU_POPS = "BackupMenuPops";
inline constexpr std::string_view BACKUPMENU_STATUS = "BackupMenuStatus";
inline constexpr std::string_view CONTROL_GUIDES = "ControlGuides";
inline constexpr std::string_view DATA_LOADING_STATUS = "DataLoadingStatus";
inline constexpr std::string_view EXTRASMENU_MENU = "ExtrasMenu";
inline constexpr std::string_view EXTRASMENU_POPS = "ExtrasPops";
inline constexpr std::string_view GENERAL_POPS = "GeneralPops";
inline constexpr std::string_view GOOGLE_DRIVE = "GoogleDriveStrings";
inline constexpr std::string_view HOLDING_STRINGS = "HoldingStrings";
inline constexpr std::string_view IO_STATUSES = "IOStatuses";
inline constexpr std::string_view IO_POPS = "IOPops";
inline constexpr std::string_view KEYBOARD = "KeyboardStrings";
inline constexpr std::string_view MAINMENU_CONFS = "MainMenuConfs";
inline constexpr std::string_view MAINMENU_POPS = "MainMenuPops";
inline constexpr std::string_view ON_OFF = "OnOff";
inline constexpr std::string_view REMOTE_POPS = "RemotePops";
inline constexpr std::string_view SAVECREATE_POPS = "SaveCreatePops";
inline constexpr std::string_view SAVE_DATA_TYPES = "SaveDataTypes";
inline constexpr std::string_view SETTINGS_DESCRIPTIONS = "SettingsDescriptions";
inline constexpr std::string_view SETTINGS_MENU = "SettingsMenu";
inline constexpr std::string_view SETTINGS_POPS = "SettingsPops";
inline constexpr std::string_view SORT_TYPES = "SortTypes";
inline constexpr std::string_view TITLEINFO = "TitleInfo";
inline constexpr std::string_view TITLEOPTION_CONFS = "TitleOptionConfirmations";
inline constexpr std::string_view TITLEOPTION_POPS = "TitleOptionPops";
inline constexpr std::string_view TITLEOPTION_STATUS = "TitleOptionStatus";
inline constexpr std::string_view TITLEOPTION = "TitleOptions";
inline constexpr std::string_view TRANSLATION = "TranslationInfo";
inline constexpr std::string_view USEROPTION_CONFS = "UserOptionConfirmations";
inline constexpr std::string_view USEROPTION_STATUS = "UserOptionStatus";
inline constexpr std::string_view USEROPTION_MENU = "UserOptions";
inline constexpr std::string_view WEBDAV = "WebDavStrings";
inline constexpr std::string_view YES_NO_OK = "YesNoOK";
}
inline constexpr std::string_view BACKUPMENU_MENU = "BackupMenu";
inline constexpr std::string_view BACKUPMENU_CONFS = "BackupMenuConfirmations";
inline constexpr std::string_view BACKUPMENU_POPS = "BackupMenuPops";
inline constexpr std::string_view BACKUPMENU_STATUS = "BackupMenuStatus";
inline constexpr std::string_view CONTROL_GUIDES = "ControlGuides";
inline constexpr std::string_view DATA_LOADING_STATUS = "DataLoadingStatus";
inline constexpr std::string_view EXTRASMENU_MENU = "ExtrasMenu";
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 FILEMODE_POPS = "FileModePops";
inline constexpr std::string_view GENERAL_POPS = "GeneralPops";
inline constexpr std::string_view GOOGLE_DRIVE = "GoogleDriveStrings";
inline constexpr std::string_view HOLDING_STRINGS = "HoldingStrings";
inline constexpr std::string_view IO_STATUSES = "IOStatuses";
inline constexpr std::string_view IO_POPS = "IOPops";
inline constexpr std::string_view KEYBOARD = "KeyboardStrings";
inline constexpr std::string_view MAINMENU_CONFS = "MainMenuConfs";
inline constexpr std::string_view MAINMENU_POPS = "MainMenuPops";
inline constexpr std::string_view ON_OFF = "OnOff";
inline constexpr std::string_view REMOTE_POPS = "RemotePops";
inline constexpr std::string_view SAVECREATE_POPS = "SaveCreatePops";
inline constexpr std::string_view SAVE_DATA_TYPES = "SaveDataTypes";
inline constexpr std::string_view SETTINGS_DESCRIPTIONS = "SettingsDescriptions";
inline constexpr std::string_view SETTINGS_MENU = "SettingsMenu";
inline constexpr std::string_view SETTINGS_POPS = "SettingsPops";
inline constexpr std::string_view SORT_TYPES = "SortTypes";
inline constexpr std::string_view TITLEINFO = "TitleInfo";
inline constexpr std::string_view TITLEOPTION_CONFS = "TitleOptionConfirmations";
inline constexpr std::string_view TITLEOPTION_POPS = "TitleOptionPops";
inline constexpr std::string_view TITLEOPTION_STATUS = "TitleOptionStatus";
inline constexpr std::string_view TITLEOPTION = "TitleOptions";
inline constexpr std::string_view TRANSLATION = "TranslationInfo";
inline constexpr std::string_view USEROPTION_CONFS = "UserOptionConfirmations";
inline constexpr std::string_view USEROPTION_STATUS = "UserOptionStatus";
inline constexpr std::string_view USEROPTION_MENU = "UserOptions";
inline constexpr std::string_view WEBDAV = "WebDavStrings";
inline constexpr std::string_view YES_NO_OK = "YesNoOK";
}

18
include/sys/DataTask.hpp Normal file
View File

@ -0,0 +1,18 @@
#pragma once
#include "sys/Task.hpp"
#include <switch.h>
namespace sys
{
class DataTask final : public sys::Task
{
public:
DataTask(ThreadFunc function, bool clearCache);
~DataTask();
private
Thread m_thread{};
}
}

View File

@ -4,40 +4,37 @@
#include <string>
namespace tasks
namespace tasks::backup
{
namespace backup
{
/// @brief Task/thread function executed when a new backup is created.
void create_new_backup_local(sys::ProgressTask *task,
data::User *user,
data::TitleInfo *titleInfo,
fslib::Path target,
BackupMenuState *spawningState,
bool killTask = true);
void create_new_backup_remote(sys::ProgressTask *task,
data::User *user,
data::TitleInfo *titleInfo,
std::string remoteName,
BackupMenuState *spawningState,
bool killTask = true);
/// @brief Task/thread function executed when a new backup is created.
void create_new_backup_local(sys::ProgressTask *task,
data::User *user,
data::TitleInfo *titleInfo,
fslib::Path target,
BackupMenuState *spawningState,
bool killTask = true);
void create_new_backup_remote(sys::ProgressTask *task,
data::User *user,
data::TitleInfo *titleInfo,
std::string remoteName,
BackupMenuState *spawningState,
bool killTask = true);
/// @brief Overwrites a pre-existing backup.
void overwrite_backup_local(sys::ProgressTask *task, BackupMenuState::TaskData taskData);
void overwrite_backup_remote(sys::ProgressTask *task, BackupMenuState::TaskData taskData);
/// @brief Overwrites a pre-existing backup.
void overwrite_backup_local(sys::ProgressTask *task, BackupMenuState::TaskData taskData);
void overwrite_backup_remote(sys::ProgressTask *task, BackupMenuState::TaskData taskData);
/// @brief Restores a backup
void restore_backup_local(sys::ProgressTask *task, BackupMenuState::TaskData taskData);
void restore_backup_remote(sys::ProgressTask *task, BackupMenuState::TaskData taskData);
/// @brief Restores a backup
void restore_backup_local(sys::ProgressTask *task, BackupMenuState::TaskData taskData);
void restore_backup_remote(sys::ProgressTask *task, BackupMenuState::TaskData taskData);
/// @brief Deletes a backup
void delete_backup_local(sys::Task *task, BackupMenuState::TaskData taskData);
void delete_backup_remote(sys::Task *task, BackupMenuState::TaskData taskData);
/// @brief Deletes a backup
void delete_backup_local(sys::Task *task, BackupMenuState::TaskData taskData);
void delete_backup_remote(sys::Task *task, BackupMenuState::TaskData taskData);
/// @brief Uploads a backup
void upload_backup(sys::ProgressTask *task, BackupMenuState::TaskData taskData);
/// @brief Uploads a backup
void upload_backup(sys::ProgressTask *task, BackupMenuState::TaskData taskData);
/// @brief Patches a pre-existing backup on the remote storage.
void patch_backup(sys::ProgressTask *task, BackupMenuState::TaskData taskData);
}
/// @brief Patches a pre-existing backup on the remote storage.
void patch_backup(sys::ProgressTask *task, BackupMenuState::TaskData taskData);
}

View File

@ -0,0 +1,12 @@
#pragma once
#include "appstates/FileOptionState.hpp"
#include "sys/sys.hpp"
namespace tasks::fileoptions
{
/// @brief Copies the source to destination passed through taskData.
void copy_source_to_destination(sys::ProgressTask *task, FileOptionState::TaskData taskData);
/// @brief Deletes the source path passed through taskData
void delete_target(sys::Task *task, FileOptionState::TaskData taskData);
}

View File

@ -2,11 +2,8 @@
#include "appstates/MainMenuState.hpp"
#include "sys/sys.hpp"
namespace tasks
namespace tasks::mainmenu
{
namespace mainmenu
{
void backup_all_for_all_local(sys::ProgressTask *task, MainMenuState::TaskData taskData);
void backup_all_for_all_remote(sys::ProgressTask *task, MainMenuState::TaskData taskData);
}
void backup_all_for_all_local(sys::ProgressTask *task, MainMenuState::TaskData taskData);
void backup_all_for_all_remote(sys::ProgressTask *task, MainMenuState::TaskData taskData);
}

View File

@ -3,13 +3,7 @@
#include "data/data.hpp"
#include "sys/sys.hpp"
namespace tasks
namespace tasks::savecreate
{
namespace savecreate
{
void create_save_data_for(sys::Task *task,
data::User *user,
data::TitleInfo *titleInfo,
SaveCreateState *spawningState);
}
void create_save_data_for(sys::Task *task, data::User *user, data::TitleInfo *titleInfo, SaveCreateState *spawningState);
}

View File

@ -3,26 +3,23 @@
#include "appstates/TitleOptionState.hpp"
#include "sys/sys.hpp"
namespace tasks
namespace tasks::titleoptions
{
namespace titleoptions
{
/// @brief Adds a title to the blacklist. Needs to be task formatted to work with confirmations.
void blacklist_title(sys::Task *task, TitleOptionState::TaskData taskData);
/// @brief Adds a title to the blacklist. Needs to be task formatted to work with confirmations.
void blacklist_title(sys::Task *task, TitleOptionState::TaskData taskData);
/// @brief Wipes deletes all local backups for the current title.
void delete_all_local_backups_for_title(sys::Task *task, TitleOptionState::TaskData taskData);
/// @brief Wipes deletes all local backups for the current title.
void delete_all_local_backups_for_title(sys::Task *task, TitleOptionState::TaskData taskData);
/// @brief Deletes all backups found on the remote storage service.
void delete_all_remote_backups_for_title(sys::Task *task, TitleOptionState::TaskData taskData);
/// @brief Deletes all backups found on the remote storage service.
void delete_all_remote_backups_for_title(sys::Task *task, TitleOptionState::TaskData taskData);
/// @brief Resets save data for the current title.
void reset_save_data(sys::Task *task, TitleOptionState::TaskData taskData);
/// @brief Resets save data for the current title.
void reset_save_data(sys::Task *task, TitleOptionState::TaskData taskData);
/// @brief Deletes the save data from the system the same way Data Management does.
void delete_save_data_from_system(sys::Task *task, TitleOptionState::TaskData taskData);
/// @brief Deletes the save data from the system the same way Data Management does.
void delete_save_data_from_system(sys::Task *task, TitleOptionState::TaskData taskData);
/// @brief Extends the save container for the current save info.
void extend_save_data(sys::Task *task, TitleOptionState::TaskData taskData);
}
/// @brief Extends the save container for the current save info.
void extend_save_data(sys::Task *task, TitleOptionState::TaskData taskData);
}

View File

@ -2,13 +2,10 @@
#include "appstates/UserOptionState.hpp"
#include "sys/sys.hpp"
namespace tasks
namespace tasks::useroptions
{
namespace useroptions
{
void backup_all_for_user_local(sys::ProgressTask *task, UserOptionState::TaskData taskData);
void backup_all_for_user_remote(sys::ProgressTask *task, UserOptionState::TaskData taskData);
void create_all_save_data_for_user(sys::Task *task, UserOptionState::TaskData taskData);
void delete_all_save_data_for_user(sys::Task *task, UserOptionState::TaskData taskData);
}
void backup_all_for_user_local(sys::ProgressTask *task, UserOptionState::TaskData taskData);
void backup_all_for_user_remote(sys::ProgressTask *task, UserOptionState::TaskData taskData);
void create_all_save_data_for_user(sys::Task *task, UserOptionState::TaskData taskData);
void delete_all_save_data_for_user(sys::Task *task, UserOptionState::TaskData taskData);
}

View File

@ -43,14 +43,17 @@ namespace ui
/// @param hasFocus This is ignored.
void render(sdl::SharedTexture &target, bool hasFocus) override;
/// @brief Sets the X and coords for the dialog box.
void set_xy(int x, int y);
/// @brief Sets the X render coord.
void set_x(int x);
/// @brief Sets the width and height of the dialog.
void set_width_height(int width, int height);
/// @brief Sets the X render coord.
void set_y(int y);
/// @brief Pass with the set functions to not change.
static inline constexpr int NO_SET = -1;
/// @brief Sets the width.
void set_width(int width);
/// @brief Sets the height.
void set_height(int height);
private:
/// @brief X render coord.

61
include/ui/Frame.hpp Normal file
View File

@ -0,0 +1,61 @@
#pragma once
#include "sdl.hpp"
#include "ui/Element.hpp"
#include <memory>
namespace ui
{
class Frame final : public ui::Element
{
public:
/// @brief Constructs a new frame.
Frame(int x, int y, int width, int height);
/// @brief Doesn't need to do anything because modern C++.
~Frame() {};
/// @brief Inline function to make constructing nicer.
static inline std::shared_ptr<ui::Frame> create(int x, int y, int width, int height)
{
return std::make_shared<ui::Frame>(x, y, width, height);
}
/// @brief Doesn't need to do anything for this.
void update(bool hasFocus) override {};
/// @brief Renders the frame to the target passed.
void render(sdl::SharedTexture &target, bool hasFocus) override;
/// @brief Sets the X coord.
void set_x(int x);
/// @brief Sets the Y coord.
void set_y(int y);
/// @brief Sets the width of the frame.
void set_width(int width);
/// @brief Sets the height of the frame.
void set_height(int height);
private:
/// @brief X rendering coord.
int m_x{};
/// @brief Y rendering coord.
int m_y{};
/// @brief Rendering width.
int m_width{};
/// @brief Rendering height.
int m_height{};
/// @brief This texture is shared by all instances.
static inline sdl::SharedTexture sm_frameCorners{};
/// @brief Ensures the texture is loading if it hasn't been.
void initialize_static_members();
};
}

View File

@ -61,6 +61,15 @@ namespace ui
/// @param width New width of the menu in pixels.
void set_width(int width);
/// @brief Updates the X render coordinate.
void set_x(int x);
/// @brief Updates the Y render coordinate.
void set_y(int y);
/// @brief Returns if the menu has no options.
bool is_empty() const;
/// @brief Resets the menu and returns it to an empty, default state.
void reset();

View File

@ -35,6 +35,9 @@ namespace ui
/// @param hasFocus Whether or not the calling state has focus.
void update(bool hasFocus) override;
/// @brief Sub update routine. Allows the panel to hide and unhide itself even when not in focus.
void sub_update();
/// @brief Runs the render routine.
/// @param target Target to render to.
/// @param hasFocus Whether or the the calling state has focus.
@ -49,6 +52,12 @@ namespace ui
/// @brief Closes the panel.
void close();
/// @brief Hides the panel temporarily.
void hide();
/// @brief Unhides the panel.
void unhide();
/// @brief Returns if the panel is fully open.
/// @return If the panel is fully open.
bool is_open() const;
@ -57,6 +66,9 @@ namespace ui
/// @return If the panel is fully closed.
bool is_closed();
/// @brief Returns whether or not the panel is hidden.
bool is_hidden() const;
/// @brief Pushes a new element to the element vector.
/// @param newElement New element to push.
void push_new_element(std::shared_ptr<ui::Element> newElement);
@ -75,6 +87,9 @@ namespace ui
/// @brief Whether or not to close panel.
bool m_closePanel{};
/// @brief Whether or not to hide the panel.
bool m_hidePanel{};
/// @brief Current X coordinate to render to. Panels are always 720 pixels in height so no Y is required.
double m_x{};
@ -93,10 +108,16 @@ namespace ui
/// @brief Vector of elements.
std::vector<std::shared_ptr<ui::Element>> m_elements{};
/// @brief Handles sliding out logic.
void slide_out();
/// @brief Slides the panel out from the left side.
void slide_out_left();
/// @brief Slides the panel out from the right side.
void slide_out_right();
int get_absolute_x_distance();
/// @brief Contains the logic for hiding/closing the panel.
void close_hide_panel();
};
} // namespace ui

View File

@ -3,6 +3,7 @@
#include "ui/ColorMod.hpp"
#include "ui/DialogBox.hpp"
#include "ui/Element.hpp"
#include "ui/Frame.hpp"
#include "ui/IconMenu.hpp"
#include "ui/Menu.hpp"
#include "ui/PopMessageManager.hpp"

View File

@ -33,7 +33,8 @@
"0: [A] Select [Y] Dump All Saves [X] User Options",
"1: [A] Select [L] [R] Jump [Y] Favorite [X] Title Options [B] Back",
"2: [A] Select [Y] Restore [X] Delete [ZR] Upload [B] Close",
"3: [A] Toggle [-] Description [X] Defaults [B] Back"
"3: [A] Toggle [-] Description [X] Defaults [B] Back",
"4: [A] Enter [B] Back/Up [X] Options [ZL]/[ZR] Change Target [-] Close"
],
"DataLoadingStatus": [
"0: Loading user accounts from system...",
@ -58,6 +59,28 @@
"0: Data reinitialized!",
"1: Data reinitialization failed!"
],
"FileOptionMenu": [
"0: Copy",
"1: Delete",
"2: Rename",
"3: Create Folder",
"4: Properties",
"5: Close"
],
"FileOptionConfs": [
"0: Are you sure you want to copy #%s# to #%s#?",
"1: Are you sure you want to delete #%s#?",
],
"FileModePops": [
"0: Copied #%s#!",
"1: Failed to copy #%s#!",
"2: Deleted #%s#!",
"3: Failed to delete #%s#!",
"4: Renamed #%s# to #%s#!",
"5: Failed to rename #%s#!",
"6: Created #%s#!",
"7: Failed to create #%s#!"
],
"GeneralPops": [
"0: Unable to exit JKSV while tasks are running!"
],
@ -92,7 +115,8 @@
"5: Enter a new name for the target item.",
"6: Enter a name for the new folder.",
"7: Enter a new output folder name for %s.",
"8: Enter how much to expand (in MB)."
"8: Enter how much to expand (in MB).",
"9: Enter a new name for %s."
],
"MainMenuConfs": [
"0: Are you sure you want to backup the save data for every user on this system? This can take an *extremely* long time!"
@ -258,4 +282,4 @@
"1: No [B]",
"2: OK [A]"
]
}
}

BIN
romfs/Textures/Frame.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 312 B

View File

@ -1,15 +1,16 @@
#include "JKSV.hpp"
#include "StateManager.hpp"
#include "appstates/FileModeState.hpp"
#include "appstates/MainMenuState.hpp"
#include "appstates/TaskState.hpp"
#include "config/config.hpp"
#include "curl/curl.hpp"
#include "data/data.hpp"
#include "error.hpp"
#include "fslib.hpp"
#include "graphics/colors.hpp"
#include "input.hpp"
#include "logging/error.hpp"
#include "logging/logger.hpp"
#include "remote/remote.hpp"
#include "sdl.hpp"
@ -30,9 +31,21 @@ namespace
/// @brief Build month.
constexpr uint8_t BUILD_MON = 8;
/// @brief Build day.
constexpr uint8_t BUILD_DAY = 25;
constexpr uint8_t BUILD_DAY = 29;
/// @brief Year.
constexpr uint16_t BUILD_YEAR = 2025;
/// @brief Config for socket.
constexpr SocketInitConfig SOCKET_INIT_CONFIG = {.tcp_tx_buf_size = 0x20000,
.tcp_rx_buf_size = 0x20000,
.tcp_tx_buf_max_size = 0x80000,
.tcp_rx_buf_max_size = 0x80000,
.udp_tx_buf_size = 0x2400,
.udp_rx_buf_size = 0xA500,
.sb_efficiency = 8,
.num_bsd_sessions = 3,
.bsd_service_type = BsdServiceType_User};
} // namespace
template <typename... Args>
@ -47,12 +60,30 @@ static bool initialize_service(Result (*function)(Args...), const char *serviceN
return true;
}
class BootTimer final
{
public:
BootTimer()
: m_start(std::chrono::high_resolution_clock::now()) {};
~BootTimer()
{
auto end = std::chrono::high_resolution_clock::now();
auto microSeconds = std::chrono::duration_cast<std::chrono::microseconds>(end - m_start);
logger::log("Boot time: %llu microseconds", microSeconds);
}
private:
std::chrono::system_clock::time_point m_start{};
};
// Definition at bottom.
static void finish_initialization();
// This can't really have an initializer list since it sets everything up.
JKSV::JKSV()
{
BootTimer timer{};
appletSetCpuBoostMode(ApmCpuBoostMode_FastLoad);
ABORT_ON_FAILURE(JKSV::initialize_services());
ABORT_ON_FAILURE(JKSV::initialize_filesystem());
@ -86,7 +117,7 @@ JKSV::~JKSV()
JKSV::exit_services();
sdl::text::exit();
sdl::exit();
fslib::exit();
appletSetCpuBoostMode(ApmCpuBoostMode_Normal);
}
@ -133,10 +164,11 @@ void JKSV::render()
bool JKSV::initialize_filesystem()
{
// This needs to be in this specific order
const bool fslib = fslib::initialize();
const bool fslib = fslib::is_initialized();
const bool romfs = initialize_service(romfsInit, "RomFS");
const bool fslibDev = fslib && fslib::dev::initialize_sdmc();
if (!fslib || !romfs || !fslibDev) { return false; }
return true;
}
@ -150,7 +182,7 @@ bool JKSV::initialize_services()
serviceInit = serviceInit && initialize_service(pmshellInitialize, "PMShell");
serviceInit = serviceInit && initialize_service(setInitialize, "Set");
serviceInit = serviceInit && initialize_service(setsysInitialize, "SetSys");
serviceInit = serviceInit && initialize_service(socketInitializeDefault, "Socket");
serviceInit = serviceInit && initialize_service(socketInitialize, "Socket", &SOCKET_INIT_CONFIG);
serviceInit = serviceInit && initialize_service(nifmInitialize, "NIFM", NifmServiceType_User);
return serviceInit;
}

View File

@ -24,6 +24,9 @@ void StateManager::update()
if (stateVector.empty()) { return; }
// Run sub update routines.
for (auto &state : stateVector) { state->sub_update(); }
std::shared_ptr<BaseState> &back = stateVector.back();
if (!back->has_focus()) { back->give_focus(); }
back->update();

View File

@ -5,12 +5,12 @@
#include "appstates/FadeState.hpp"
#include "appstates/ProgressState.hpp"
#include "config/config.hpp"
#include "error.hpp"
#include "fs/fs.hpp"
#include "fslib.hpp"
#include "graphics/colors.hpp"
#include "input.hpp"
#include "keyboard.hpp"
#include "logging/error.hpp"
#include "sdl.hpp"
#include "strings/strings.hpp"
#include "stringutil.hpp"
@ -127,7 +127,7 @@ void BackupMenuState::refresh()
}
int index{};
for (const fslib::DirectoryEntry &entry : m_directoryListing.list())
for (const fslib::DirectoryEntry &entry : m_directoryListing)
{
sm_backupMenu->add_option(entry.get_filename());
m_menuEntries.push_back({MenuEntryType::Local, index++});

View File

@ -1,6 +1,6 @@
#include "appstates/BaseState.hpp"
#include "logging/error.hpp"
#include "error.hpp"
#include <switch.h>

View File

@ -4,8 +4,8 @@
#include "appstates/MainMenuState.hpp"
#include "config/config.hpp"
#include "data/data.hpp"
#include "error.hpp"
#include "input.hpp"
#include "logging/error.hpp"
BlacklistEditState::BlacklistEditState()
: BaseState()

View File

@ -1,7 +1,9 @@
#include "appstates/ExtrasMenuState.hpp"
#include "appstates/FileModeState.hpp"
#include "appstates/MainMenuState.hpp"
#include "data/data.hpp"
#include "error.hpp"
#include "graphics/colors.hpp"
#include "input.hpp"
#include "keyboard.hpp"
@ -50,7 +52,13 @@ void ExtrasMenuState::update()
{
switch (m_extrasMenu.get_selected())
{
case REINIT_DATA: ExtrasMenuState::reinitialize_data(); break;
case REINIT_DATA: ExtrasMenuState::reinitialize_data(); break;
case SD_TO_SD_BROWSER: ExtrasMenuState::sd_to_sd_browser(); break;
case BIS_PRODINFO_F: ExtrasMenuState::prodinfof_to_sd(); break;
case BIS_SAFE: ExtrasMenuState::safe_to_sd(); break;
case BIS_SYSTEM: ExtrasMenuState::system_to_sd(); break;
case BIS_USER: ExtrasMenuState::user_to_sd(); break;
case TERMINATE_PROCESS: ExtrasMenuState::terminate_process(); break;
}
}
else if (bPressed) { BaseState::deactivate(); }
@ -75,6 +83,42 @@ void ExtrasMenuState::initialize_menu()
void ExtrasMenuState::reinitialize_data() { data::launch_initialization(true, finish_reinitialization); }
void ExtrasMenuState::sd_to_sd_browser() { FileModeState::create_and_push("sdmc", "sdmc", false); }
void ExtrasMenuState::prodinfof_to_sd()
{
const bool mountError = error::fslib(fslib::open_bis_filesystem("prodinfo-f", FsBisPartitionId_CalibrationFile));
if (mountError) { return; }
FileModeState::create_and_push("prodinfo-f", "sdmc", false);
}
void ExtrasMenuState::safe_to_sd()
{
const bool mountError = error::fslib(fslib::open_bis_filesystem("safe", FsBisPartitionId_SafeMode));
if (mountError) { return; }
FileModeState::create_and_push("safe", "sdmc", false);
}
void ExtrasMenuState::system_to_sd()
{
const bool mountError = error::fslib(fslib::open_bis_filesystem("system", FsBisPartitionId_System));
if (mountError) { return; }
FileModeState::create_and_push("system", "sdmc", false);
}
void ExtrasMenuState::user_to_sd()
{
const bool mountError = error::fslib(fslib::open_bis_filesystem("user", FsBisPartitionId_User));
if (mountError) { return; }
FileModeState::create_and_push("user", "sdmc", false);
}
void ExtrasMenuState::terminate_process() {}
static void finish_reinitialization()
{
const int popTicks = ui::PopMessageManager::DEFAULT_TICKS;

View File

@ -0,0 +1,261 @@
#include "appstates/FileModeState.hpp"
#include "appstates/FileOptionState.hpp"
#include "config/config.hpp"
#include "graphics/colors.hpp"
#include "input.hpp"
#include "logging/logger.hpp"
#include "mathutil.hpp"
#include "strings/strings.hpp"
#include "stringutil.hpp"
#include <cmath>
FileModeState::FileModeState(std::string_view mountA, std::string_view mountB, int64_t journalSize)
: m_mountA(mountA)
, m_mountB(mountB)
, m_journalSize(journalSize)
, m_y(720.f)
, m_targetY(91.0f)
, m_scaling(config::get_animation_scaling())
{
FileModeState::initialize_static_members();
FileModeState::initialize_paths();
FileModeState::initialize_menus();
}
void FileModeState::update()
{
FileModeState::update_y_coord();
if (!m_inPlace) { return; }
const bool hasFocus = BaseState::has_focus();
ui::Menu &menu = FileModeState::get_source_menu();
fslib::Path &path = FileModeState::get_source_path();
fslib::Directory &directory = FileModeState::get_source_directory();
const bool aPressed = input::button_pressed(HidNpadButton_A);
const bool bPressed = input::button_pressed(HidNpadButton_B);
const bool xPressed = input::button_pressed(HidNpadButton_X);
const bool zlZRPressed = input::button_pressed(HidNpadButton_ZL) || input::button_pressed(HidNpadButton_ZR);
const bool minusPressed = input::button_pressed(HidNpadButton_Minus);
if (aPressed) { FileModeState::enter_selected(path, directory, menu); }
else if (bPressed) { FileModeState::up_one_directory(path, directory, menu); }
else if (xPressed) { FileModeState::open_option_menu(directory, menu); }
else if (zlZRPressed) { FileModeState::change_target(); }
else if (minusPressed) { FileModeState::hide_dialog(); }
else if (FileModeState::is_hidden()) { FileModeState::deactivate_state(); }
menu.update(hasFocus);
}
void FileModeState::render()
{
const bool hasFocus = BaseState::has_focus();
sm_renderTarget->clear(colors::TRANSPARENT);
// This is here so it's rendered underneath the pop-up frame.
if (hasFocus) { FileModeState::render_control_guide(); }
sdl::render_line(sm_renderTarget, 617, 0, 617, 538, colors::WHITE);
sdl::render_line(sm_renderTarget, 618, 0, 618, 538, colors::DIALOG_DARK);
m_dirMenuA->render(sm_renderTarget, hasFocus && m_target == false);
m_dirMenuB->render(sm_renderTarget, hasFocus && m_target);
sm_frame->render(sdl::Texture::Null, true);
sm_renderTarget->render(sdl::Texture::Null, 23, m_y + 12);
}
bool FileModeState::get_target() const noexcept { return m_target; }
fslib::Path FileModeState::get_source()
{
const fslib::Path &sourcePath = FileModeState::get_source_path();
const fslib::Directory &sourceDir = FileModeState::get_source_directory();
const ui::Menu &sourceMenu = FileModeState::get_source_menu();
const int selected = sourceMenu.get_selected();
// If 0, the current path is our target.
if (selected == 0) { return sourcePath; }
const int dirIndex = selected - 2;
const fslib::DirectoryEntry &entry = sourceDir[dirIndex];
return sourcePath / entry;
}
fslib::Path FileModeState::get_destination()
{
const fslib::Path &destPath = FileModeState::get_destination_path();
const fslib::Directory &destDir = FileModeState::get_destination_directory();
const ui::Menu &destMenu = FileModeState::get_destination_menu();
const int selected = destMenu.get_selected();
if (selected == 0) { return destPath; }
const int dirIndex = selected - 1;
const fslib::DirectoryEntry &entry = destDir[dirIndex];
return destPath / entry;
}
bool FileModeState::commit_required() const noexcept { return m_target == false && m_journalSize > 0; }
int64_t FileModeState::get_journal_size() const noexcept { return m_journalSize; }
void FileModeState::render_control_guide() noexcept
{
sdl::text::render(sdl::Texture::Null, sm_controlGuideX, 673, 22, sdl::text::NO_WRAP, colors::WHITE, sm_controlGuide);
}
void FileModeState::initialize_static_members()
{
static constexpr std::string_view RENDER_TARGET_NAME = "FMRenderTarget";
static constexpr int CONTROL_GUIDE_START_X = 1220;
if (sm_frame && sm_renderTarget) { return; }
sm_frame = ui::Frame::create(15, 720, 1250, 555);
sm_renderTarget = sdl::TextureManager::load(RENDER_TARGET_NAME, 1234, 538, SDL_TEXTUREACCESS_TARGET);
sm_controlGuide = strings::get_by_name(strings::names::CONTROL_GUIDES, 4);
sm_controlGuideX = CONTROL_GUIDE_START_X - sdl::text::get_width(22, sm_controlGuide);
}
void FileModeState::initialize_paths()
{
const std::string pathA = m_mountA + ":/";
const std::string pathB = m_mountB + ":/";
m_pathA = pathA;
m_pathB = pathB;
}
void FileModeState::initialize_menus()
{
m_dirMenuA = ui::Menu::create(8, 8, 594, 22, 538);
m_dirMenuB = ui::Menu::create(626, 8, 594, 22, 538);
FileModeState::initialize_directory_menu(m_pathA, m_dirA, *m_dirMenuA.get());
FileModeState::initialize_directory_menu(m_pathB, m_dirB, *m_dirMenuB.get());
}
void FileModeState::initialize_directory_menu(const fslib::Path &path, fslib::Directory &directory, ui::Menu &menu)
{
static constexpr const char *DIR_PREFIX = "[D] ";
static constexpr const char *FILE_PREFIX = "[F] ";
directory.open(path);
if (!directory.is_open()) { return; }
menu.reset();
menu.add_option(".");
menu.add_option("..");
for (const fslib::DirectoryEntry &entry : directory)
{
std::string option{};
if (entry.is_directory()) { option = DIR_PREFIX; }
else { option = FILE_PREFIX; }
option += entry.get_filename();
menu.add_option(option);
}
}
void FileModeState::update_y_coord() noexcept
{
if (m_y == m_targetY) { return; }
const double add = (m_targetY - m_y) / m_scaling;
const double distance = math::Util<double>::absolute_distance(m_targetY, m_y);
m_y += std::round(add);
if (distance <= 4)
{
m_y = m_targetY;
m_inPlace = true;
}
sm_frame->set_y(m_y);
}
void FileModeState::hide_dialog() noexcept
{
if (!m_inPlace) { return; }
m_targetY = 720;
}
bool FileModeState::is_hidden() noexcept { return m_inPlace && m_targetY == 720; }
void FileModeState::enter_selected(fslib::Path &path, fslib::Directory &directory, ui::Menu &menu)
{
const int selected = menu.get_selected();
if (selected == 1) { FileModeState::up_one_directory(path, directory, menu); }
else
{
const int dirIndex = selected - 2;
const fslib::DirectoryEntry &entry = directory[dirIndex];
if (entry.is_directory()) { FileModeState::enter_directory(path, directory, menu, entry); }
}
}
void FileModeState::open_option_menu(fslib::Directory &directory, ui::Menu &menu)
{
const int selected = menu.get_selected();
if (selected == 0 || selected > 1) { FileOptionState::create_and_push(this); }
}
void FileModeState::change_target() { m_target = m_target ? false : true; }
void FileModeState::up_one_directory(fslib::Path &path, fslib::Directory &directory, ui::Menu &menu)
{
if (FileModeState::path_is_root(path)) { return; }
size_t lastSlash = path.find_last_of('/');
if (lastSlash == path.NOT_FOUND) { return; }
else if (lastSlash <= 0) { lastSlash = 1; }
fslib::Path newPath{path.sub_path(lastSlash)};
path = std::move(newPath);
FileModeState::initialize_directory_menu(path, directory, menu);
}
void FileModeState::enter_directory(fslib::Path &path,
fslib::Directory &directory,
ui::Menu &menu,
const fslib::DirectoryEntry &entry)
{
if (!entry.is_directory()) { return; }
path /= entry.get_filename();
FileModeState::initialize_directory_menu(path, directory, menu);
}
ui::Menu &FileModeState::get_source_menu() noexcept { return m_target ? *m_dirMenuB.get() : *m_dirMenuA.get(); }
ui::Menu &FileModeState::get_destination_menu() noexcept { return m_target ? *m_dirMenuA.get() : *m_dirMenuB.get(); }
fslib::Path &FileModeState::get_source_path() noexcept { return m_target ? m_pathB : m_pathA; }
fslib::Path &FileModeState::get_destination_path() noexcept { return m_target ? m_pathA : m_pathB; }
fslib::Directory &FileModeState::get_source_directory() noexcept { return m_target ? m_dirB : m_dirA; }
fslib::Directory &FileModeState::get_destination_directory() noexcept { return m_target ? m_dirA : m_dirB; }
void FileModeState::deactivate_state() noexcept
{
sm_frame->set_y(720);
fslib::close_file_system(m_mountA);
fslib::close_file_system(m_mountB);
BaseState::deactivate();
}

View File

@ -0,0 +1,199 @@
#include "appstates/FileOptionState.hpp"
#include "appstates/ConfirmState.hpp"
#include "appstates/ProgressState.hpp"
#include "appstates/TaskState.hpp"
#include "config/config.hpp"
#include "error.hpp"
#include "fslib.hpp"
#include "input.hpp"
#include "keyboard.hpp"
#include "logging/logger.hpp"
#include "mathutil.hpp"
#include "strings/strings.hpp"
#include "stringutil.hpp"
#include "tasks/fileoptions.hpp"
namespace
{
enum
{
COPY,
DELETE,
RENAME,
CREATE_DIR,
PROPERTIES,
CLOSE
};
// These make things easier to read and type.
using TaskConfirm = ConfirmState<sys::Task, TaskState, FileOptionState::DataStruct>;
using ProgressConfirm = ConfirmState<sys::ProgressTask, ProgressState, FileOptionState::DataStruct>;
}
FileOptionState::FileOptionState(FileModeState *spawningState)
: m_spawningState(spawningState)
, m_commitData(m_spawningState->commit_required())
, m_scaling(config::get_animation_scaling())
, m_dataStruct(std::make_shared<FileOptionState::DataStruct>())
{
FileOptionState::initialize_static_members();
FileOptionState::set_menu_side();
}
void FileOptionState::update()
{
const bool hasFocus = BaseState::has_focus();
FileOptionState::update_x_coord();
if (!m_inPlace) { return; }
sm_copyMenu->update(hasFocus);
const int selected = sm_copyMenu->get_selected();
const bool aPressed = input::button_pressed(HidNpadButton_A);
const bool bPressed = input::button_pressed(HidNpadButton_B);
if (aPressed)
{
switch (selected)
{
case COPY: FileOptionState::copy_target(); break;
case DELETE: FileOptionState::delete_target(); break;
case RENAME: FileOptionState::rename_target(); break;
}
}
else if (bPressed) { FileOptionState::close(); }
else if (FileOptionState::is_closed()) { FileOptionState::deactivate_state(); }
}
void FileOptionState::render()
{
const bool hasFocus = BaseState::has_focus();
sm_dialog->render(sdl::Texture::Null, hasFocus);
sm_copyMenu->render(sdl::Texture::Null, hasFocus);
// I just didn't like this disappearing.
m_spawningState->render_control_guide();
}
void FileOptionState::initialize_static_members()
{
if (sm_copyMenu && sm_dialog) { return; }
sm_copyMenu = ui::Menu::create(0, 236, 234, 20, 720); // High target height is a workaround.
sm_dialog = ui::DialogBox::create(520, 218, 256, 256);
// This never changes, so...
for (int i = 0; const char *menuOption = strings::get_by_name(strings::names::FILEOPTION_MENU, i); i++)
{
sm_copyMenu->add_option(menuOption);
}
}
void FileOptionState::set_menu_side()
{
const bool target = m_spawningState->get_target();
m_x = target ? 1280 : -240;
m_targetX = target ? 840 : 200;
sm_dialog->set_x(m_x);
sm_copyMenu->set_x(m_x + 9);
}
void FileOptionState::update_x_coord()
{
if (m_x == m_targetX) { return; }
const int add = (m_targetX - m_x) / m_scaling;
m_x += add;
const int distance = math::Util<int>::absolute_distance(m_x, m_targetX);
if (distance <= 4)
{
m_x = m_targetX;
m_inPlace = true;
}
sm_dialog->set_x(m_x);
sm_copyMenu->set_x(m_x + 9);
}
void FileOptionState::copy_target()
{
fslib::Path source = m_spawningState->get_source();
fslib::Path dest = m_spawningState->get_destination();
const char *copyFormat = strings::get_by_name(strings::names::FILEOPTION_CONFS, 0);
const std::string query = stringutil::get_formatted_string(copyFormat, source.string().c_str(), dest.string().c_str());
m_dataStruct->sourcePath = std::move(source);
m_dataStruct->destPath = std::move(dest);
m_dataStruct->journalSize = m_journalSize;
ProgressConfirm::create_push_fade(query, false, tasks::fileoptions::copy_source_to_destination, m_dataStruct);
}
void FileOptionState::delete_target()
{
fslib::Path source = m_spawningState->get_source();
const char *deleteFormat = strings::get_by_name(strings::names::FILEOPTION_CONFS, 1);
const std::string query = stringutil::get_formatted_string(deleteFormat, source.string().c_str());
m_dataStruct->sourcePath = std::move(source);
m_dataStruct->journalSize = m_journalSize;
TaskConfirm::create_push_fade(query, true, tasks::fileoptions::delete_target, m_dataStruct);
}
void FileOptionState::rename_target()
{
const int popTicks = ui::PopMessageManager::DEFAULT_TICKS;
const bool target = m_spawningState->get_target();
const fslib::Path targetPath = target ? m_spawningState->get_destination() : m_spawningState->get_source();
char nameBuffer[FS_MAX_PATH] = {0};
const char *filename = targetPath.get_filename();
const char *keyboardFormat = strings::get_by_name(strings::names::KEYBOARD, 9);
const std::string keyboardHeader = stringutil::get_formatted_string(keyboardFormat, filename);
const bool validInput = keyboard::get_input(SwkbdType_QWERTY, filename, keyboardHeader, nameBuffer, FS_MAX_PATH);
if (validInput) { return; }
size_t folderBegin = targetPath.find_last_of('/');
if (folderBegin == targetPath.NOT_FOUND) { return; }
else if (folderBegin < 1) { folderBegin = 1; }
fslib::Path newPath{targetPath.sub_path(folderBegin) / nameBuffer};
const bool isDir = fslib::directory_exists(targetPath);
const bool renameDir = isDir && error::fslib(fslib::rename_directory(targetPath, newPath));
const bool renameFile = !isDir && error::fslib(fslib::rename_file(targetPath, newPath));
if (!renameDir && !renameFile)
{
const char *popFormat = strings::get_by_name(strings::names::FILEMODE_POPS, 5);
const std::string pop = stringutil::get_formatted_string(popFormat, filename);
ui::PopMessageManager::push_message(popTicks, pop);
}
else
{
const char *popFormat = strings::get_by_name(strings::names::FILEMODE_POPS, 4);
const std::string pop = stringutil::get_formatted_string(popFormat, filename, nameBuffer);
ui::PopMessageManager::push_message(popTicks, pop);
}
}
void FileOptionState::close()
{
const bool target = m_spawningState->get_target();
m_close = true;
m_targetX = target ? 1280 : -240;
}
bool FileOptionState::is_closed() { return m_close && m_x == m_targetX; }
void FileOptionState::deactivate_state()
{
sm_copyMenu->set_selected(0);
BaseState::deactivate();
}

View File

@ -3,9 +3,9 @@
#include "StateManager.hpp"
#include "appstates/TaskState.hpp"
#include "data/data.hpp"
#include "error.hpp"
#include "fs/fs.hpp"
#include "input.hpp"
#include "logging/error.hpp"
#include "logging/logger.hpp"
#include "strings/strings.hpp"
#include "stringutil.hpp"

View File

@ -5,11 +5,11 @@
#include "appstates/MessageState.hpp"
#include "config/config.hpp"
#include "data/data.hpp"
#include "error.hpp"
#include "fslib.hpp"
#include "graphics/colors.hpp"
#include "input.hpp"
#include "keyboard.hpp"
#include "logging/error.hpp"
#include "logging/logger.hpp"
#include "strings/strings.hpp"
#include "stringutil.hpp"
@ -157,10 +157,10 @@ void SettingsState::change_working_directory()
const char *popSuccessFormat = strings::get_by_name(strings::names::SETTINGS_POPS, 1);
const char *popFailed = strings::get_by_name(strings::names::SETTINGS_POPS, 2);
const fslib::Path oldPath{config::get_working_directory()};
const std::string oldPath = config::get_working_directory().string();
std::array<char, FS_MAX_PATH> pathBuffer = {0};
const bool input = keyboard::get_input(SwkbdType_Normal, oldPath.full_path(), inputHeader, pathBuffer.data(), FS_MAX_PATH);
const bool input = keyboard::get_input(SwkbdType_Normal, oldPath, inputHeader, pathBuffer.data(), FS_MAX_PATH);
if (!input) { return; }
const fslib::Path newPath{pathBuffer.data()};
@ -179,7 +179,8 @@ void SettingsState::change_working_directory()
return;
}
const std::string popMessage = stringutil::get_formatted_string(popSuccessFormat, newPath.full_path());
const std::string newPathString = newPath.string();
const std::string popMessage = stringutil::get_formatted_string(popSuccessFormat, newPathString);
ui::PopMessageManager::push_message(popTicks, popMessage);
}

View File

@ -1,9 +1,9 @@
#include "appstates/TitleInfoState.hpp"
#include "StateManager.hpp"
#include "error.hpp"
#include "graphics/colors.hpp"
#include "input.hpp"
#include "logging/error.hpp"
#include "sdl.hpp"
#include "strings/strings.hpp"
#include "stringutil.hpp"

View File

@ -2,15 +2,16 @@
#include "StateManager.hpp"
#include "appstates/ConfirmState.hpp"
#include "appstates/FileModeState.hpp"
#include "appstates/MainMenuState.hpp"
#include "appstates/TitleInfoState.hpp"
#include "config/config.hpp"
#include "error.hpp"
#include "fs/fs.hpp"
#include "fslib.hpp"
#include "graphics/colors.hpp"
#include "input.hpp"
#include "keyboard.hpp"
#include "logging/error.hpp"
#include "logging/logger.hpp"
#include "remote/remote.hpp"
#include "strings/strings.hpp"
@ -57,25 +58,20 @@ void TitleOptionState::update()
const bool hasFocus = BaseState::has_focus();
sm_slidePanel->update(hasFocus);
const bool isOpen = sm_slidePanel->is_open();
if (!isOpen) { return; }
const bool isOpen = sm_slidePanel->is_open();
const bool aPressed = input::button_pressed(HidNpadButton_A);
const bool bPressed = input::button_pressed(HidNpadButton_B);
const int selected = sm_titleOptionMenu->get_selected();
// This is kind of tricky to handle, because the blacklist function uses both.
if (m_refreshRequired)
{
// Refresh the views.
MainMenuState::refresh_view_states();
m_refreshRequired = false;
// Return so nothing else happens. Not sure I like this, but w/e.
return;
}
if (m_exitRequired) { sm_slidePanel->close(); }
if (aPressed)
if (aPressed && isOpen)
{
switch (selected)
{
@ -91,10 +87,16 @@ void TitleOptionState::update()
case EXPORT_SVI: TitleOptionState::export_svi_file(); break;
}
}
else if (bPressed) { sm_slidePanel->close(); }
else if (bPressed || m_exitRequired)
{
sm_slidePanel->close();
m_exitRequired = false;
}
else if (sm_slidePanel->is_closed()) { TitleOptionState::deactivate_state(); }
}
void TitleOptionState::sub_update() { sm_slidePanel->sub_update(); }
void TitleOptionState::render()
{
const bool hasFocus = BaseState::has_focus();
@ -131,6 +133,7 @@ void TitleOptionState::initialize_data_struct()
void TitleOptionState::create_push_info_state()
{
sm_slidePanel->hide();
auto titleInfoState = TitleInfoState::create(m_user, m_titleInfo);
StateManager::push_state(titleInfoState);
}
@ -194,7 +197,18 @@ void TitleOptionState::change_output_directory()
ui::PopMessageManager::push_message(popTicks, popSuccess);
}
void TitleOptionState::create_push_file_mode() {}
void TitleOptionState::create_push_file_mode()
{
const uint64_t applicationID = m_titleInfo->get_application_id();
const FsSaveDataInfo *saveInfo = m_user->get_save_info_by_id(applicationID);
if (error::is_null(saveInfo)) { return; }
const bool saveOpened = fslib::open_save_data_with_save_info(fs::DEFAULT_SAVE_MOUNT, *saveInfo);
if (!saveOpened) { return; }
sm_slidePanel->hide();
FileModeState::create_and_push(fs::DEFAULT_SAVE_MOUNT, "sdmc", true);
}
void TitleOptionState::delete_all_local_backups()
{

View File

@ -8,10 +8,10 @@
#include "appstates/TaskState.hpp"
#include "config/config.hpp"
#include "data/data.hpp"
#include "error.hpp"
#include "fs/fs.hpp"
#include "fslib.hpp"
#include "input.hpp"
#include "logging/error.hpp"
#include "logging/logger.hpp"
#include "remote/remote.hpp"
#include "strings/strings.hpp"
@ -78,6 +78,8 @@ void UserOptionState::update()
else if (sm_menuPanel->is_closed()) { UserOptionState::deactivate_state(); }
}
void UserOptionState::sub_update() { sm_menuPanel->sub_update(); }
void UserOptionState::render()
{
// Render target user's title selection screen.
@ -131,7 +133,11 @@ void UserOptionState::backup_all()
else { ProgressConfirm::create_and_push(query, true, tasks::useroptions::backup_all_for_user_local, m_dataStruct); }
}
void UserOptionState::create_save_create() { SaveCreateState::create_and_push(m_user, m_titleSelect); }
void UserOptionState::create_save_create()
{
sm_menuPanel->hide();
SaveCreateState::create_and_push(m_user, m_titleSelect);
}
void UserOptionState::create_all_save_data()
{

View File

@ -3,7 +3,6 @@
#include "JSON.hpp"
#include "config/keys.hpp"
#include "error.hpp"
#include "logging/error.hpp"
#include "logging/logger.hpp"
#include "stringutil.hpp"
@ -181,7 +180,7 @@ bool config::ConfigContext::load_config_file()
const bool exists = fslib::file_exists(configPath);
if (!exists) { return false; }
json::Object configJSON = json::new_object(json_object_from_file, configPath.full_path());
json::Object configJSON = json::new_object(json_object_from_file, PATH_CONFIG_FILE.data());
if (!configJSON) { return false; }
json_object_iterator configIter = json::iter_begin(configJSON);
@ -212,7 +211,8 @@ void config::ConfigContext::save_config_file()
json::Object configJSON = json::new_object(json_object_new_object);
if (!configJSON) { return; }
json_object *workDir = json_object_new_string(m_workingDirectory.full_path());
const std::string workDirString = m_workingDirectory.string();
json_object *workDir = json_object_new_string(workDirString.c_str());
json::add_object(configJSON, config::keys::WORKING_DIRECTORY, workDir);
for (const auto &[key, value] : m_configMap)

View File

@ -1,6 +1,6 @@
#include "curl/curl.hpp"
#include "logging/error.hpp"
#include "error.hpp"
#include "logging/logger.hpp"
#include "stringutil.hpp"

View File

@ -1,8 +1,8 @@
#include "data/DataContext.hpp"
#include "config/config.hpp"
#include "error.hpp"
#include "fs/fs.hpp"
#include "logging/error.hpp"
#include "logging/logger.hpp"
#include "strings/strings.hpp"
#include "stringutil.hpp"
@ -128,7 +128,7 @@ bool data::DataContext::title_is_loaded(uint64_t applicationID)
void data::DataContext::load_title(uint64_t applicationID)
{
std::scoped_lock titleGuard{m_titleMutex, m_iconQueueMutex};
m_titleInfo.emplace(applicationID, applicationID);
m_titleInfo.try_emplace(applicationID, applicationID);
m_iconQueue.push_back(&m_titleInfo.at(applicationID));
}
@ -171,7 +171,8 @@ void data::DataContext::import_svi_files(sys::Task *task)
task->set_status(statusLoadingSvi);
for (const fslib::DirectoryEntry &entry : sviDir.list())
auto controlData = std::make_unique<NsApplicationControlData>();
for (const fslib::DirectoryEntry &entry : sviDir)
{
const fslib::Path target{sviPath / entry};
fslib::File sviFile{target, FsOpenMode_Read};
@ -180,7 +181,6 @@ void data::DataContext::import_svi_files(sys::Task *task)
uint32_t magic{};
uint64_t applicationID{};
auto controlData = std::make_unique<NsApplicationControlData>();
const bool magicRead = sviFile.read(&magic, SIZE_UINT32) == SIZE_UINT32;
const bool idRead = sviFile.read(&applicationID, SIZE_UINT64) == SIZE_UINT64;
const bool exists = DataContext::title_is_loaded(applicationID);
@ -190,8 +190,7 @@ void data::DataContext::import_svi_files(sys::Task *task)
if (!dataRead) { continue; }
std::scoped_lock multiGuard{m_iconQueueMutex, m_titleMutex};
data::TitleInfo newTitle{applicationID, controlData};
m_titleInfo.emplace(applicationID, std::move(newTitle));
m_titleInfo.try_emplace(applicationID, applicationID, *controlData);
m_iconQueue.push_back(&m_titleInfo.at(applicationID));
}
}
@ -214,8 +213,8 @@ bool data::DataContext::read_cache(sys::Task *task)
const char *statusLoadingCache = strings::get_by_name(strings::names::DATA_LOADING_STATUS, 4);
task->set_status(statusLoadingCache);
auto controlData = std::make_unique<NsApplicationControlData>();
do {
auto controlData = std::make_unique<NsApplicationControlData>();
const bool dataRead = cacheZip.read(controlData.get(), SIZE_CTRL_DATA) == SIZE_CTRL_DATA;
if (!dataRead) { continue; }
@ -228,8 +227,7 @@ bool data::DataContext::read_cache(sys::Task *task)
std::scoped_lock multiGuard{m_iconQueueMutex, m_titleMutex};
data::TitleInfo newTitle{applicationID, controlData};
m_titleInfo.emplace(applicationID, std::move(newTitle));
m_titleInfo.try_emplace(applicationID, applicationID, *controlData);
m_iconQueue.push_back(&m_titleInfo.at(applicationID));
} while (cacheZip.next_file());
m_cacheIsValid = true;

View File

@ -1,9 +1,9 @@
#include "data/TitleInfo.hpp"
#include "config/config.hpp"
#include "error.hpp"
#include "graphics/colors.hpp"
#include "graphics/gfxutil.hpp"
#include "logging/error.hpp"
#include "logging/logger.hpp"
#include "stringutil.hpp"
@ -11,28 +11,25 @@
data::TitleInfo::TitleInfo(uint64_t applicationID)
: m_applicationID(applicationID)
, m_data(std::make_unique<NsApplicationControlData>())
{
static constexpr size_t SIZE_CTRL_DATA = sizeof(NsApplicationControlData);
uint64_t controlSize{};
NacpLanguageEntry *entry{};
NsApplicationControlData *data = m_data.get();
// This will filter from even trying to fetch control data for system titles.
const bool isSystem = applicationID & 0x8000000000000000;
const bool getError = !isSystem && error::libnx(nsGetApplicationControlData(NsApplicationControlSource_Storage,
m_applicationID,
data,
&m_data,
SIZE_CTRL_DATA,
&controlSize));
const bool entryError = !getError && error::libnx(nacpGetLanguageEntry(&data->nacp, &entry));
const bool entryError = !getError && error::libnx(nacpGetLanguageEntry(&m_data.nacp, &entry));
if (isSystem || getError)
{
const std::string appIDHex = stringutil::get_formatted_string("%04X", m_applicationID & 0xFFFF);
char *name = data->nacp.lang[SetLanguage_ENUS].name; // I'm hoping this is enough?
char *name = m_data.nacp.lang[SetLanguage_ENUS].name; // I'm hoping this is enough?
std::memset(data, 0x00, SIZE_CTRL_DATA);
std::snprintf(name, TitleInfo::SIZE_PATH_SAFE, "%016lX", m_applicationID);
TitleInfo::get_create_path_safe_title();
}
@ -44,48 +41,29 @@ data::TitleInfo::TitleInfo(uint64_t applicationID)
}
// To do: Make this safer...
data::TitleInfo::TitleInfo(uint64_t applicationID, std::unique_ptr<NsApplicationControlData> &controlData)
data::TitleInfo::TitleInfo(uint64_t applicationID, NsApplicationControlData &controlData)
: m_applicationID(applicationID)
, m_data(std::move(controlData))
{
m_hasData = true;
m_data = controlData;
NacpLanguageEntry *entry{};
const bool entryError = error::libnx(nacpGetLanguageEntry(&m_data->nacp, &entry));
const bool entryError = error::libnx(nacpGetLanguageEntry(&m_data.nacp, &entry));
if (entryError) { std::snprintf(entry->name, TitleInfo::SIZE_PATH_SAFE, "%016lX", m_applicationID); }
TitleInfo::get_create_path_safe_title();
}
data::TitleInfo::TitleInfo(data::TitleInfo &&titleInfo) { *this = std::move(titleInfo); }
data::TitleInfo &data::TitleInfo::operator=(data::TitleInfo &&titleInfo)
{
m_applicationID = titleInfo.m_applicationID;
m_data = std::move(titleInfo.m_data);
m_hasData = titleInfo.m_hasData;
std::memcpy(m_pathSafeTitle, titleInfo.m_pathSafeTitle, TitleInfo::SIZE_PATH_SAFE);
m_icon = titleInfo.m_icon;
titleInfo.m_applicationID = 0;
titleInfo.m_data = nullptr;
titleInfo.m_hasData = false;
std::memset(titleInfo.m_pathSafeTitle, 0x00, TitleInfo::SIZE_PATH_SAFE);
titleInfo.m_icon = nullptr;
return *this;
}
uint64_t data::TitleInfo::get_application_id() const { return m_applicationID; }
NsApplicationControlData *data::TitleInfo::get_control_data() { return m_data.get(); }
NsApplicationControlData *data::TitleInfo::get_control_data() { return &m_data; }
bool data::TitleInfo::has_control_data() const { return m_hasData; }
const char *data::TitleInfo::get_title()
{
NacpLanguageEntry *entry{};
const bool entryError = error::libnx(nacpGetLanguageEntry(&m_data->nacp, &entry));
const bool entryError = error::libnx(nacpGetLanguageEntry(&m_data.nacp, &entry));
if (entryError) { return nullptr; }
return entry->name;
}
@ -95,16 +73,16 @@ const char *data::TitleInfo::get_path_safe_title() const { return m_pathSafeTitl
const char *data::TitleInfo::get_publisher()
{
NacpLanguageEntry *entry{};
const bool entryError = error::libnx(nacpGetLanguageEntry(&m_data->nacp, &entry));
const bool entryError = error::libnx(nacpGetLanguageEntry(&m_data.nacp, &entry));
if (entryError) { return nullptr; }
return entry->author;
}
uint64_t data::TitleInfo::get_save_data_owner_id() const { return m_data->nacp.save_data_owner_id; }
uint64_t data::TitleInfo::get_save_data_owner_id() const { return m_data.nacp.save_data_owner_id; }
int64_t data::TitleInfo::get_save_data_size(uint8_t saveType) const
{
const NacpStruct &nacp = m_data->nacp;
const NacpStruct &nacp = m_data.nacp;
switch (saveType)
{
case FsSaveDataType_Account: return nacp.user_account_save_data_size;
@ -118,7 +96,7 @@ int64_t data::TitleInfo::get_save_data_size(uint8_t saveType) const
int64_t data::TitleInfo::get_save_data_size_max(uint8_t saveType) const
{
const NacpStruct &nacp = m_data->nacp;
const NacpStruct &nacp = m_data.nacp;
switch (saveType)
{
case FsSaveDataType_Account: return std::max(nacp.user_account_save_data_size, nacp.user_account_save_data_size_max);
@ -132,7 +110,7 @@ int64_t data::TitleInfo::get_save_data_size_max(uint8_t saveType) const
int64_t data::TitleInfo::get_journal_size(uint8_t saveType) const
{
const NacpStruct &nacp = m_data->nacp;
const NacpStruct &nacp = m_data.nacp;
switch (saveType)
{
case FsSaveDataType_Account: return nacp.user_account_save_data_journal_size;
@ -146,7 +124,7 @@ int64_t data::TitleInfo::get_journal_size(uint8_t saveType) const
int64_t data::TitleInfo::get_journal_size_max(uint8_t saveType) const
{
const NacpStruct &nacp = m_data->nacp;
const NacpStruct &nacp = m_data.nacp;
switch (saveType)
{
case FsSaveDataType_Account:
@ -162,7 +140,7 @@ int64_t data::TitleInfo::get_journal_size_max(uint8_t saveType) const
bool data::TitleInfo::has_save_data_type(uint8_t saveType) const
{
const NacpStruct &nacp = m_data->nacp;
const NacpStruct &nacp = m_data.nacp;
switch (saveType)
{
case FsSaveDataType_Account: return nacp.user_account_save_data_size > 0 || nacp.user_account_save_data_size_max > 0;
@ -197,7 +175,7 @@ void data::TitleInfo::get_create_path_safe_title()
}
const bool useTitleId = config::get_by_key(config::keys::USE_TITLE_IDS);
const bool entryError = !useTitleId && error::libnx(nacpGetLanguageEntry(&m_data->nacp, &entry));
const bool entryError = !useTitleId && error::libnx(nacpGetLanguageEntry(&m_data.nacp, &entry));
const bool sanitized =
!useTitleId && !entryError && stringutil::sanitize_string_for_path(entry->name, m_pathSafeTitle, SIZE_PATH_SAFE);
if (useTitleId || entryError || !sanitized)
@ -214,7 +192,7 @@ void data::TitleInfo::load_icon()
if (m_hasData)
{
const std::string textureName = stringutil::get_formatted_string("%016llX", m_applicationID);
m_icon = sdl::TextureManager::load(textureName, m_data->icon, SIZE_ICON);
m_icon = sdl::TextureManager::load(textureName, m_data.icon, SIZE_ICON);
}
else
{

View File

@ -2,10 +2,10 @@
#include "config/config.hpp"
#include "data/data.hpp"
#include "error.hpp"
#include "fs/fs.hpp"
#include "graphics/colors.hpp"
#include "graphics/gfxutil.hpp"
#include "logging/error.hpp"
#include "logging/logger.hpp"
#include "sdl.hpp"
#include "stringutil.hpp"
@ -164,15 +164,12 @@ void data::User::load_user_data()
while (infoReader.read())
{
const int64_t readCount = infoReader.get_read_count();
for (int64_t i = 0; i < readCount; i++)
for (const FsSaveDataInfo &saveInfo : infoReader)
{
const FsSaveDataInfo &saveInfo = infoReader[i];
const uint64_t saveInfoAppID = saveInfo.application_id;
const uint64_t saveInfoSysID = saveInfo.system_save_data_id;
const uint64_t applicationID = saveInfoAppID != 0 ? saveInfoAppID : saveInfoSysID;
const uint8_t saveDataType = saveInfo.save_data_type;
const uint64_t saveInfoAppID = saveInfo.application_id;
const uint64_t saveInfoSysID = saveInfo.system_save_data_id;
const uint64_t applicationID = saveInfoAppID != 0 ? saveInfoAppID : saveInfoSysID;
const uint8_t saveDataType = saveInfo.save_data_type;
const bool isSystemSave = saveDataType == FsSaveDataType_System || saveDataType == FsSaveDataType_SystemBcat;
const bool isBlacklisted = config::is_blacklisted(applicationID);

View File

@ -2,7 +2,8 @@
#include "appstates/DataLoadingState.hpp"
#include "data/DataContext.hpp"
#include "logging/error.hpp"
#include "error.hpp"
#include "logging/logger.hpp"
#include "strings/strings.hpp"
#include <switch.h>

View File

@ -1,4 +1,4 @@
#include "logging/error.hpp"
#include "error.hpp"
#include "fslib.hpp"
#include "logging/logger.hpp"

View File

@ -1,6 +1,6 @@
#include "fs/MiniUnzip.hpp"
#include "logging/error.hpp"
#include "error.hpp"
#include "logging/logger.hpp"
fs::MiniUnzip::MiniUnzip(const fslib::Path &path) { MiniUnzip::open(path); }
@ -12,7 +12,9 @@ bool fs::MiniUnzip::is_open() const { return m_isOpen; }
bool fs::MiniUnzip::open(const fslib::Path &path)
{
MiniUnzip::close();
m_unz = unzOpen64(path.full_path());
const std::string pathString = path.string();
m_unz = unzOpen64(pathString.c_str());
if (error::is_null(m_unz) || !MiniUnzip::reset()) { return false; }
m_isOpen = true;
return true;

View File

@ -1,14 +1,19 @@
#include "fs/MiniZip.hpp"
#include "config/config.hpp"
#include "logging/error.hpp"
#include "error.hpp"
#include "logging/logger.hpp"
#include <ctime>
// Definition at bottom.
static zip_fileinfo create_zip_file_info();
fs::MiniZip::MiniZip(const fslib::Path &path) { MiniZip::open(path); }
fs::MiniZip::MiniZip(const fslib::Path &path)
: m_level(config::get_by_key(config::keys::ZIP_COMPRESSION_LEVEL))
{
MiniZip::open(path);
}
fs::MiniZip::~MiniZip() { MiniZip::close(); }
@ -17,7 +22,9 @@ bool fs::MiniZip::is_open() const { return m_isOpen; }
bool fs::MiniZip::open(const fslib::Path &path)
{
MiniZip::close();
m_zip = zipOpen64(path.full_path(), APPEND_STATUS_CREATE);
const std::string pathString = path.string();
m_zip = zipOpen64(pathString.c_str(), APPEND_STATUS_CREATE);
if (error::is_null(m_zip)) { return false; }
m_isOpen = true;
return true;
@ -32,19 +39,21 @@ void fs::MiniZip::close()
bool fs::MiniZip::open_new_file(std::string_view filename, bool trimPath, size_t trimPlaces)
{
const uint8_t zipLevel = config::get_by_key(config::keys::ZIP_COMPRESSION_LEVEL);
const size_t pathBegin = filename.find_first_of('/');
if (pathBegin != filename.npos) { filename = filename.substr(pathBegin + 1); }
const zip_fileinfo fileInfo = create_zip_file_info();
return zipOpenNewFileInZip64(m_zip, filename.data(), &fileInfo, nullptr, 0, nullptr, 0, nullptr, Z_DEFLATED, zipLevel, 0) ==
return zipOpenNewFileInZip64(m_zip, filename.data(), &fileInfo, nullptr, 0, nullptr, 0, nullptr, Z_DEFLATED, m_level, 0) ==
ZIP_OK;
}
bool fs::MiniZip::close_current_file() { return zipCloseFileInZip(m_zip) == ZIP_OK; }
bool fs::MiniZip::write(const void *buffer, size_t dataSize) { return zipWriteInFileInZip(m_zip, buffer, dataSize) == ZIP_OK; }
bool fs::MiniZip::write(const void *buffer, size_t dataSize)
{
if (!m_isOpen) { return false; }
return zipWriteInFileInZip(m_zip, buffer, dataSize) == ZIP_OK;
}
static zip_fileinfo create_zip_file_info()
{

View File

@ -4,7 +4,8 @@
fs::PathFilter::PathFilter(const fslib::Path &filePath)
{
json::Object filterJSON = json::new_object(json_object_from_file, filePath.full_path());
const std::string pathString = filePath.string();
json::Object filterJSON = json::new_object(json_object_from_file, pathString.c_str());
if (!filterJSON) { return; }
json_object *filter = json::get_object(filterJSON, "filters");
@ -25,5 +26,5 @@ bool fs::PathFilter::has_paths() const { return !m_paths.empty(); }
bool fs::PathFilter::is_filtered(const fslib::Path &path)
{
return std::find(m_paths.begin(), m_paths.end(), path.full_path()) != m_paths.end();
return std::find(m_paths.begin(), m_paths.end(), path) != m_paths.end();
}

View File

@ -1,31 +1,20 @@
#include "fs/SaveMetaData.hpp"
#include "error.hpp"
#include "fs/directory_functions.hpp"
#include "fs/save_data_functions.hpp"
#include "fs/save_mount.hpp"
#include "fslib.hpp"
#include "logging/error.hpp"
namespace
{
constexpr size_t SIZE_EXTRA_DATA = sizeof(FsSaveDataExtraData);
}
bool fs::read_save_data_extra_info(const FsSaveDataInfo *saveInfo, FsSaveDataExtraData &dataOut)
{
const FsSaveDataSpaceId spaceID = static_cast<FsSaveDataSpaceId>(saveInfo->save_data_space_id);
const uint64_t saveDataID = saveInfo->save_data_id;
const bool readError =
error::libnx(fsReadSaveDataFileSystemExtraDataBySaveDataSpaceId(&dataOut, SIZE_EXTRA_DATA, spaceID, saveDataID));
if (readError) { return false; }
return true;
}
bool fs::fill_save_meta_data(const FsSaveDataInfo *saveInfo, fs::SaveMetaData &meta)
{
FsSaveDataExtraData extraData{};
const bool extraRead = fs::read_save_data_extra_info(saveInfo, extraData);
const bool extraRead = fs::read_save_extra_data(saveInfo, extraData);
if (!extraRead) { return false; }
meta = {.magic = fs::SAVE_META_MAGIC,
@ -49,7 +38,7 @@ bool fs::fill_save_meta_data(const FsSaveDataInfo *saveInfo, fs::SaveMetaData &m
bool fs::process_save_meta_data(const FsSaveDataInfo *saveInfo, const SaveMetaData &meta)
{
FsSaveDataExtraData extraData{};
const bool extraRead = fs::read_save_data_extra_info(saveInfo, extraData);
const bool extraRead = fs::read_save_extra_data(saveInfo, extraData);
if (!extraRead) { return false; }
const bool needsExtend = extraData.data_size < meta.saveDataSize;

View File

@ -1,7 +1,7 @@
#include "fs/ScopedSaveMount.hpp"
#include "error.hpp"
#include "fslib.hpp"
#include "logging/error.hpp"
fs::ScopedSaveMount::ScopedSaveMount(std::string_view mount, const FsSaveDataInfo *saveInfo, bool log)
: m_mountPoint(mount)

View File

@ -8,7 +8,7 @@ uint64_t fs::get_directory_total_size(const fslib::Path &targetPath)
if (!targetDir.is_open()) { return 0; }
uint64_t directorySize = 0;
for (const fslib::DirectoryEntry &entry : targetDir.list())
for (const fslib::DirectoryEntry &entry : targetDir)
{
if (entry.is_directory())
{
@ -26,7 +26,7 @@ bool fs::directory_has_contents(const fslib::Path &directoryPath)
fslib::Directory testDir{directoryPath};
if (!testDir.is_open()) { return false; }
for (const fslib::DirectoryEntry &entry : testDir.list())
for (const fslib::DirectoryEntry &entry : testDir)
{
if (entry.get_filename() != fs::NAME_SAVE_META) { return true; }
}

View File

@ -1,8 +1,8 @@
#include "fs/io.hpp"
#include "error.hpp"
#include "fs/SaveMetaData.hpp"
#include "fslib.hpp"
#include "logging/error.hpp"
#include "strings/strings.hpp"
#include "stringutil.hpp"
#include "sys/sys.hpp"
@ -70,7 +70,8 @@ void fs::copy_file(const fslib::Path &source, const fslib::Path &destination, sy
const int64_t sourceSize = sourceFile.get_size();
if (task)
{
const std::string status = stringutil::get_formatted_string(statusTemplate, source.full_path());
const std::string sourceString = source.string();
const std::string status = stringutil::get_formatted_string(statusTemplate, sourceString.c_str());
task->set_status(status);
task->reset(static_cast<double>(sourceSize));
}
@ -111,7 +112,6 @@ void fs::copy_file(const fslib::Path &source, const fslib::Path &destination, sy
void fs::copy_file_commit(const fslib::Path &source,
const fslib::Path &destination,
std::string_view device,
int64_t journalSize,
sys::ProgressTask *task)
{
@ -126,7 +126,8 @@ void fs::copy_file_commit(const fslib::Path &source,
const int64_t sourceSize = sourceFile.get_size();
if (task)
{
const std::string status = stringutil::get_formatted_string(copyingStatus, source.full_path());
const std::string sourceString = source.string();
const std::string status = stringutil::get_formatted_string(copyingStatus, sourceString.c_str());
task->set_status(status);
task->reset(static_cast<double>(sourceSize));
}
@ -163,7 +164,7 @@ void fs::copy_file_commit(const fslib::Path &source,
if (needsCommit)
{
destFile.close();
const bool commitError = error::fslib(fslib::commit_data_to_file_system(device));
const bool commitError = error::fslib(fslib::commit_data_to_file_system(destination.get_device_name()));
// To do: Handle this better. Threads current have no way of communicating errors.
if (commitError) { ui::PopMessageManager::push_message(popTicks, popCommitFailed); }
@ -180,7 +181,7 @@ void fs::copy_file_commit(const fslib::Path &source,
readThread.join();
destFile.close();
const bool commitError = error::fslib(fslib::commit_data_to_file_system(device));
const bool commitError = error::fslib(fslib::commit_data_to_file_system(destination.get_device_name()));
if (commitError) { ui::PopMessageManager::push_message(popTicks, popCommitFailed); }
}
@ -189,7 +190,7 @@ void fs::copy_directory(const fslib::Path &source, const fslib::Path &destinatio
fslib::Directory sourceDir{source};
if (error::fslib(sourceDir.is_open())) { return; }
for (const fslib::DirectoryEntry &entry : sourceDir.list())
for (const fslib::DirectoryEntry &entry : sourceDir)
{
const char *filename = entry.get_filename();
if (filename == fs::NAME_SAVE_META) { continue; }
@ -210,14 +211,13 @@ void fs::copy_directory(const fslib::Path &source, const fslib::Path &destinatio
void fs::copy_directory_commit(const fslib::Path &source,
const fslib::Path &destination,
std::string_view device,
int64_t journalSize,
sys::ProgressTask *task)
{
fslib::Directory sourceDir{source};
if (error::fslib(sourceDir.is_open())) { return; }
for (const fslib::DirectoryEntry &entry : sourceDir.list())
for (const fslib::DirectoryEntry &entry : sourceDir)
{
const char *filename = entry.get_filename();
if (filename == fs::NAME_SAVE_META) { continue; }
@ -230,8 +230,8 @@ void fs::copy_directory_commit(const fslib::Path &source,
const bool createError = !destExists && error::fslib(fslib::create_directory(fullDest));
if (!destExists && createError) { continue; }
fs::copy_directory_commit(fullSource, fullDest, device, journalSize, task);
fs::copy_directory_commit(fullSource, fullDest, journalSize, task);
}
else { fs::copy_file_commit(fullSource, fullDest, device, journalSize, task); }
else { fs::copy_file_commit(fullSource, fullDest, journalSize, task); }
}
}

View File

@ -1,6 +1,6 @@
#include "fs/save_data_functions.hpp"
#include "logging/error.hpp"
#include "error.hpp"
#include "logging/logger.hpp"
bool fs::create_save_data_for(data::User *targetUser, data::TitleInfo *titleInfo)
@ -29,8 +29,7 @@ bool fs::create_save_data_for(data::User *targetUser, data::TitleInfo *titleInfo
.save_data_space_id = FsSaveDataSpaceId_User};
// I want this recorded.
const bool createError = error::libnx(fsCreateSaveDataFileSystem(&saveAttributes, &saveCreation, &saveMeta));
return createError == false;
return error::libnx(fsCreateSaveDataFileSystem(&saveAttributes, &saveCreation, &saveMeta)) == false;
}
bool fs::delete_save_data(const FsSaveDataInfo *saveInfo)
@ -47,8 +46,7 @@ bool fs::delete_save_data(const FsSaveDataInfo *saveInfo)
.save_data_rank = saveInfo->save_data_rank,
.save_data_index = saveInfo->save_data_index};
const bool deleteError = error::libnx(fsDeleteSaveDataFileSystemBySaveDataAttribute(spaceID, &saveAttributes));
return deleteError == false;
return error::libnx(fsDeleteSaveDataFileSystemBySaveDataAttribute(spaceID, &saveAttributes)) == false;
}
bool fs::extend_save_data(const FsSaveDataInfo *saveInfo, int64_t size, int64_t journalSize)
@ -56,11 +54,22 @@ bool fs::extend_save_data(const FsSaveDataInfo *saveInfo, int64_t size, int64_t
const FsSaveDataSpaceId spaceID = static_cast<FsSaveDataSpaceId>(saveInfo->save_data_space_id);
const uint64_t saveID = saveInfo->save_data_id;
const bool extendError = error::libnx(fsExtendSaveDataFileSystem(spaceID, saveID, size, journalSize));
return extendError == false;
return error::libnx(fsExtendSaveDataFileSystem(spaceID, saveID, size, journalSize)) == false;
}
bool fs::is_system_save_data(const FsSaveDataInfo *saveInfo)
{
return saveInfo->save_data_type == FsSaveDataType_System || saveInfo->save_data_type == FsSaveDataType_SystemBcat;
}
bool fs::read_save_extra_data(const FsSaveDataInfo *saveInfo, FsSaveDataExtraData &extraOut)
{
static constexpr size_t EXTRA_SIZE = sizeof(FsSaveDataExtraData);
const FsSaveDataSpaceId spaceID = static_cast<FsSaveDataSpaceId>(saveInfo->save_data_space_id);
const bool readError = error::libnx(
fsReadSaveDataFileSystemExtraDataBySaveDataSpaceId(&extraOut, EXTRA_SIZE, spaceID, saveInfo->save_data_id));
return !readError;
}

View File

@ -1,8 +1,8 @@
#include "fs/zip.hpp"
#include "config/config.hpp"
#include "error.hpp"
#include "fs/SaveMetaData.hpp"
#include "logging/error.hpp"
#include "logging/logger.hpp"
#include "strings/strings.hpp"
#include "stringutil.hpp"
@ -100,14 +100,15 @@ void fs::copy_directory_to_zip(const fslib::Path &source, fs::MiniZip &dest, sys
fslib::Directory sourceDir{source};
if (error::fslib(sourceDir.is_open())) { return; }
for (const fslib::DirectoryEntry &entry : sourceDir.list())
for (const fslib::DirectoryEntry &entry : sourceDir)
{
const fslib::Path fullSource{source / entry};
if (entry.is_directory()) { fs::copy_directory_to_zip(fullSource, dest, task); }
else
{
fslib::File sourceFile{fullSource, FsOpenMode_Read};
const bool newZipFile = dest.open_new_file(fullSource.full_path());
const std::string sourceString = fullSource.string();
const bool newZipFile = dest.open_new_file(sourceString);
if (error::fslib(sourceFile.is_open()) || !newZipFile) { continue; }
const int64_t fileSize = sourceFile.get_size();
@ -117,7 +118,7 @@ void fs::copy_directory_to_zip(const fslib::Path &source, fs::MiniZip &dest, sys
if (task)
{
const std::string status = stringutil::get_formatted_string(ioStatus, fullSource.full_path());
const std::string status = stringutil::get_formatted_string(ioStatus, sourceString.c_str());
task->set_status(status);
task->reset(static_cast<double>(fileSize));
}
@ -158,17 +159,13 @@ void fs::copy_directory_to_zip(const fslib::Path &source, fs::MiniZip &dest, sys
}
}
void fs::copy_zip_to_directory(fs::MiniUnzip &unzip,
const fslib::Path &dest,
int64_t journalSize,
std::string_view commitDevice,
sys::ProgressTask *task)
void fs::copy_zip_to_directory(fs::MiniUnzip &unzip, const fslib::Path &dest, int64_t journalSize, sys::ProgressTask *task)
{
if (!unzip.reset()) { return; }
const int popTicks = ui::PopMessageManager::DEFAULT_TICKS;
const char *popCommitFailed = strings::get_by_name(strings::names::IO_POPS, 0);
const char *statusTemplate = strings::get_by_name(strings::names::IO_STATUSES, 2);
const bool needCommits = journalSize > 0 && !commitDevice.empty();
const bool needCommits = journalSize > 0;
do {
if (unzip.get_filename() == fs::NAME_SAVE_META) { continue; }
@ -178,9 +175,10 @@ void fs::copy_zip_to_directory(fs::MiniUnzip &unzip,
if (lastDir == fullDest.NOT_FOUND) { continue; }
const fslib::Path dirPath{fullDest.sub_path(lastDir)};
const bool exists = dirPath.is_valid() && fslib::directory_exists(dirPath);
const bool createError = dirPath.is_valid() && !exists && error::fslib(fslib::create_directories_recursively(dirPath));
if (dirPath.is_valid() && !exists && createError) { continue; }
const bool isValid = dirPath.is_valid();
const bool exists = isValid && fslib::directory_exists(dirPath);
const bool createError = isValid && !exists && error::fslib(fslib::create_directories_recursively(dirPath));
if (isValid && !exists && createError) { continue; }
const int64_t fileSize = unzip.get_uncompressed_size();
fslib::File destFile{fullDest, FsOpenMode_Create | FsOpenMode_Write, fileSize};
@ -203,8 +201,8 @@ void fs::copy_zip_to_directory(fs::MiniUnzip &unzip,
bool &bufferReady = sharedData->bufferReady;
std::unique_ptr<sys::byte[]> &sharedBuffer = sharedData->sharedBuffer;
std::thread readThread(unzipReadThreadFunction, std::ref(unzip), sharedData);
int64_t journalCount{};
std::thread readThread(unzipReadThreadFunction, std::ref(unzip), sharedData);
for (int64_t i = 0; i < fileSize;)
{
ssize_t localRead{};
@ -223,7 +221,7 @@ void fs::copy_zip_to_directory(fs::MiniUnzip &unzip,
if (commitNeeded)
{
destFile.close();
const bool commitError = error::fslib(fslib::commit_data_to_file_system(commitDevice));
const bool commitError = error::fslib(fslib::commit_data_to_file_system(dest.get_device_name()));
if (commitError) { ui::PopMessageManager::push_message(popTicks, popCommitFailed); } // To do: How to recover?
destFile.open(fullDest, FsOpenMode_Write);
@ -240,7 +238,7 @@ void fs::copy_zip_to_directory(fs::MiniUnzip &unzip,
readThread.join();
destFile.close();
const bool commitError = needCommits && error::fslib(fslib::commit_data_to_file_system(commitDevice));
const bool commitError = needCommits && error::fslib(fslib::commit_data_to_file_system(dest.get_device_name()));
if (commitError) { ui::PopMessageManager::push_message(popTicks, popCommitFailed); }
} while (unzip.next_file());
}

View File

@ -1,6 +1,6 @@
#include "keyboard.hpp"
#include "logging/error.hpp"
#include "error.hpp"
#include <string>

View File

@ -2,7 +2,7 @@
#include "JSON.hpp"
#include "curl/curl.hpp"
#include "logging/error.hpp"
#include "error.hpp"
#include "logging/logger.hpp"
#include "remote/remote.hpp"
#include "strings/strings.hpp"

View File

@ -2,7 +2,7 @@
#include "StateManager.hpp"
#include "appstates/TaskState.hpp"
#include "logging/error.hpp"
#include "error.hpp"
#include "logging/logger.hpp"
#include "remote/GoogleDrive.hpp"
#include "remote/WebDav.hpp"

View File

@ -2,8 +2,8 @@
#include "JSON.hpp"
#include "config/config.hpp"
#include "error.hpp"
#include "fslib.hpp"
#include "logging/error.hpp"
#include "logging/logger.hpp"
#include "stringutil.hpp"
@ -15,6 +15,7 @@ namespace
{
// This is the actual map where the strings are.
std::map<std::pair<std::string, int>, std::string> s_stringMap;
// This map is for matching files to the language value
std::unordered_map<SetLanguage, std::string_view> s_fileMap = {{SetLanguage_JA, "JA.json"},
{SetLanguage_ENUS, "ENUS.json"},
@ -42,8 +43,8 @@ static void replace_buttons_in_string(std::string &target);
bool strings::initialize()
{
const fslib::Path filePath = get_file_path();
json::Object stringJSON = json::new_object(json_object_from_file, filePath.full_path());
const std::string stringPath = get_file_path().string();
json::Object stringJSON = json::new_object(json_object_from_file, stringPath.c_str());
if (!stringJSON) { return false; }
json_object_iterator stringIterator = json::iter_begin(stringJSON);

View File

@ -59,12 +59,12 @@ bool stringutil::sanitize_string_for_path(const char *stringIn, char *stringOut,
const bool countCheck = count <= 0 || i + count >= stringOutSize;
const bool codeCheck = codepoint < 0x20 || codepoint > 0x7E;
if (countCheck) { break; }
else if (codepoint == L'é') { stringOut[offset++] = 'e'; }
else if (codeCheck) { return false; }
const bool forbidden = std::find(FORBIDDEN_PATH_CHARACTERS.begin(), FORBIDDEN_PATH_CHARACTERS.end(), codepoint) !=
FORBIDDEN_PATH_CHARACTERS.end();
if (forbidden) { stringOut[offset++] = 0x20; }
else if (codepoint == L'é') { stringOut[offset++] = 'e'; }
else
{
std::memcpy(&stringOut[offset], &stringIn[i], static_cast<size_t>(count));

View File

@ -1,8 +1,8 @@
#include "tasks/backup.hpp"
#include "config/config.hpp"
#include "error.hpp"
#include "fs/fs.hpp"
#include "logging/error.hpp"
#include "logging/logger.hpp"
#include "remote/remote.hpp"
#include "strings/strings.hpp"
@ -36,7 +36,8 @@ void tasks::backup::create_new_backup_local(sys::ProgressTask *task,
if (error::is_null(task)) { return; }
if (error::is_null(user) || error::is_null(titleInfo)) { TASK_FINISH_RETURN(task); }
const bool hasZipExt = std::strstr(target.full_path(), STRING_ZIP_EXT);
const std::string targetString = target.string();
const bool hasZipExt = std::strstr(targetString.c_str(), STRING_ZIP_EXT);
const uint64_t applicationID = titleInfo->get_application_id();
const FsSaveDataInfo *saveInfo = user->get_save_info_by_id(applicationID);
if (error::is_null(saveInfo)) { TASK_FINISH_RETURN(task); }
@ -198,12 +199,16 @@ void tasks::backup::restore_backup_local(sys::ProgressTask *task, BackupMenuStat
const uint64_t applicationID = titleInfo->get_application_id();
const FsSaveDataInfo *saveInfo = user->get_save_info_by_id(applicationID);
const uint8_t saveType = user->get_account_save_type();
const uint64_t journalSize = titleInfo->get_journal_size(saveType);
if (error::is_null(saveInfo)) { TASK_FINISH_RETURN(task); }
const bool autoBackup = config::get_by_key(config::keys::AUTO_BACKUP_ON_RESTORE);
const bool isDir = fslib::directory_exists(target);
const bool hasZipExt = std::strstr(target.full_path(), STRING_ZIP_EXT);
FsSaveDataExtraData extraData{};
const bool readExtra = fs::read_save_extra_data(saveInfo, extraData);
const int64_t journalSize = readExtra ? extraData.journal_size : titleInfo->get_journal_size(saveType);
const std::string targetString = target.string();
const bool autoBackup = config::get_by_key(config::keys::AUTO_BACKUP_ON_RESTORE);
const bool isDir = fslib::directory_exists(target);
const bool hasZipExt = std::strstr(targetString.c_str(), STRING_ZIP_EXT);
const int popTicks = ui::PopMessageManager::DEFAULT_TICKS;
if (autoBackup) { auto_backup(task, taskData); }
@ -226,18 +231,18 @@ void tasks::backup::restore_backup_local(sys::ProgressTask *task, BackupMenuStat
read_and_process_meta(unzip, taskData, task);
auto scopedMount = create_scoped_mount(saveInfo);
fs::copy_zip_to_directory(unzip, fs::DEFAULT_SAVE_ROOT, journalSize, fs::DEFAULT_SAVE_MOUNT, task);
fs::copy_zip_to_directory(unzip, fs::DEFAULT_SAVE_ROOT, journalSize, task);
}
else if (isDir)
{
read_and_process_meta(target, taskData, task);
auto scopedMount = create_scoped_mount(saveInfo);
fs::copy_directory_commit(target, fs::DEFAULT_SAVE_ROOT, fs::DEFAULT_SAVE_MOUNT, journalSize, task);
fs::copy_directory_commit(target, fs::DEFAULT_SAVE_ROOT, journalSize, task);
}
else
{
auto scopedMount = create_scoped_mount(saveInfo);
fs::copy_file_commit(target, fs::DEFAULT_SAVE_ROOT, fs::DEFAULT_SAVE_MOUNT, journalSize, task);
fs::copy_file_commit(target, fs::DEFAULT_SAVE_ROOT, journalSize, task);
}
spawningState->refresh();
@ -306,10 +311,12 @@ void tasks::backup::restore_backup_remote(sys::ProgressTask *task, BackupMenuSta
read_and_process_meta(backup, taskData, task);
{
const uint8_t saveType = user->get_account_save_type();
const uint64_t journalSize = titleInfo->get_journal_size(saveType);
FsSaveDataExtraData extraData{};
const bool readExtra = fs::read_save_extra_data(saveInfo, extraData);
const uint8_t saveType = user->get_account_save_type();
const int64_t journalSize = readExtra ? extraData.journal_size : titleInfo->get_journal_size(saveType);
fs::ScopedSaveMount saveMount{fs::DEFAULT_SAVE_MOUNT, saveInfo};
fs::copy_zip_to_directory(backup, fs::DEFAULT_SAVE_ROOT, journalSize, fs::DEFAULT_SAVE_MOUNT, task);
fs::copy_zip_to_directory(backup, fs::DEFAULT_SAVE_ROOT, journalSize, task);
}
backup.close();
@ -333,8 +340,9 @@ void tasks::backup::delete_backup_local(sys::Task *task, BackupMenuState::TaskDa
const int popTicks = ui::PopMessageManager::DEFAULT_TICKS;
{
const char *statusFormat = strings::get_by_name(strings::names::IO_STATUSES, 3);
const std::string status = stringutil::get_formatted_string(statusFormat, path.full_path());
const std::string pathString = path.string();
const char *statusFormat = strings::get_by_name(strings::names::IO_STATUSES, 3);
const std::string status = stringutil::get_formatted_string(statusFormat, pathString.c_str());
task->set_status(status);
}

View File

@ -0,0 +1,42 @@
#include "tasks/fileoptions.hpp"
#include "error.hpp"
#include "fs/fs.hpp"
#include "fslib.hpp"
void tasks::fileoptions::copy_source_to_destination(sys::ProgressTask *task, FileOptionState::TaskData taskData)
{
if (error::is_null(task)) { return; }
fslib::Path &source = taskData->sourcePath;
fslib::Path &dest = taskData->destPath;
int64_t journalSpace = taskData->journalSize;
const bool isDir = fslib::directory_exists(source);
const bool needsCommit = journalSpace > 0;
if (isDir && needsCommit) { fs::copy_directory_commit(source, dest, journalSpace, task); }
else if (!isDir && needsCommit) { fs::copy_file_commit(source, dest, journalSpace, task); }
else if (isDir && !needsCommit) { fs::copy_directory(source, dest, task); }
else if (!isDir && !needsCommit) { fs::copy_file(source, dest, task); }
task->complete();
}
void tasks::fileoptions::delete_target(sys::Task *task, FileOptionState::TaskData taskData)
{
if (error::is_null(task)) { return; }
fslib::Path target = taskData->sourcePath;
int64_t journalSpace = taskData->journalSize;
const bool isDir = fslib::directory_exists(target);
bool needsCommit = journalSpace > 0;
if (isDir) { fslib::delete_directory_recursively(target); }
else { fslib::delete_file(target); }
if (needsCommit) { fslib::commit_data_to_file_system(target.get_device_name()); }
task->complete();
}

View File

@ -2,8 +2,8 @@
#include "config/config.hpp"
#include "data/data.hpp"
#include "error.hpp"
#include "fs/fs.hpp"
#include "logging/error.hpp"
#include "remote/remote.hpp"
#include "stringutil.hpp"
#include "tasks/backup.hpp"

View File

@ -1,7 +1,7 @@
#include "tasks/savecreate.hpp"
#include "error.hpp"
#include "fs/fs.hpp"
#include "logging/error.hpp"
#include "strings/strings.hpp"
#include "stringutil.hpp"
#include "ui/PopMessageManager.hpp"

View File

@ -2,9 +2,9 @@
#include "config/config.hpp"
#include "data/data.hpp"
#include "error.hpp"
#include "fs/fs.hpp"
#include "keyboard.hpp"
#include "logging/error.hpp"
#include "logging/logger.hpp"
#include "remote/remote.hpp"
#include "strings/strings.hpp"
@ -208,7 +208,7 @@ void tasks::titleoptions::extend_save_data(sys::Task *task, TitleOptionState::Ta
std::array<char, 5> sizeBuffer = {0};
FsSaveDataExtraData extraData{};
const bool readExtra = fs::read_save_data_extra_info(saveInfo, extraData);
const bool readExtra = fs::read_save_extra_data(saveInfo, extraData);
const int sizeMB = extraData.data_size / SIZE_MB;
const char *keyboardHeader = strings::get_by_name(strings::names::KEYBOARD, 8);

View File

@ -1,8 +1,8 @@
#include "tasks/useroptions.hpp"
#include "config/config.hpp"
#include "error.hpp"
#include "fs/fs.hpp"
#include "logging/error.hpp"
#include "remote/remote.hpp"
#include "strings/strings.hpp"
#include "stringutil.hpp"

View File

@ -49,17 +49,13 @@ void ui::DialogBox::render(sdl::SharedTexture &target, bool hasFocus)
CORNER_HEIGHT);
}
void ui::DialogBox::set_xy(int x, int y)
{
if (x != DialogBox::NO_SET) { m_x = x; }
if (y != DialogBox::NO_SET) { m_y = y; }
}
void ui::DialogBox::set_x(int x) { m_x = x; }
void ui::DialogBox::set_width_height(int width, int height)
{
if (width != DialogBox::NO_SET) { m_width = width; }
if (height != DialogBox::NO_SET) { m_height = height; }
}
void ui::DialogBox::set_y(int y) { m_y = y; }
void ui::DialogBox::set_width(int width) { m_width = width; }
void ui::DialogBox::set_height(int height) { m_height = height; }
void ui::DialogBox::initialize_static_members()
{

56
source/ui/Frame.cpp Normal file
View File

@ -0,0 +1,56 @@
#include "ui/Frame.hpp"
#include "graphics/colors.hpp"
ui::Frame::Frame(int x, int y, int width, int height)
: m_x(x)
, m_y(y)
, m_width(width)
, m_height(height)
{
Frame::initialize_static_members();
}
void ui::Frame::render(sdl::SharedTexture &target, bool hasFocus)
{
// This is the size of one of the "tiles" of the frame.
static constexpr int TILE = 16;
const int midWidth = m_width - (TILE * 2);
const int midHeight = m_height - (TILE * 2);
const int rightEdge = (m_x + m_width) - TILE;
const int bottomEdge = (m_y + m_height) - TILE;
const int textureCorner = TILE * 2;
// Top.
sm_frameCorners->render_part(target, m_x, m_y, 0, 0, TILE, TILE);
sm_frameCorners->render_part_stretched(target, TILE, 0, TILE, TILE, m_x + TILE, m_y, midWidth, TILE);
sm_frameCorners->render_part(target, rightEdge, m_y, 32, 0, TILE, TILE);
// Middle
sm_frameCorners->render_part_stretched(target, 0, TILE, TILE, TILE, m_x, m_y + TILE, TILE, midHeight);
sdl::render_rect_fill(target, m_x + TILE, m_y + TILE, midWidth, midHeight, colors::SLIDE_PANEL_CLEAR);
sm_frameCorners->render_part_stretched(target, textureCorner, TILE, TILE, TILE, rightEdge, m_y + TILE, TILE, midHeight);
// Bottom
sm_frameCorners->render_part(target, m_x, bottomEdge, 0, textureCorner, TILE, TILE);
sm_frameCorners->render_part_stretched(target, TILE, textureCorner, TILE, TILE, m_x + TILE, bottomEdge, midWidth, TILE);
sm_frameCorners->render_part(target, rightEdge, bottomEdge, textureCorner, textureCorner, TILE, TILE);
}
void ui::Frame::set_x(int x) { m_x = x; }
void ui::Frame::set_y(int y) { m_y = y; }
void ui::Frame::set_width(int width) { m_width = width; }
void ui::Frame::set_height(int height) { m_height = height; }
void ui::Frame::initialize_static_members()
{
static constexpr std::string_view FRAME_NAME = "FrameCorners";
static constexpr const char *FRAME_PATH = "romfs:/Textures/Frame.png";
if (sm_frameCorners) { return; }
sm_frameCorners = sdl::TextureManager::load(FRAME_NAME, FRAME_PATH);
}

View File

@ -99,6 +99,10 @@ void ui::Menu::set_selected(int selected)
void ui::Menu::set_width(int width) { m_width = width; }
void ui::Menu::set_x(int x) { m_x = x; }
void ui::Menu::set_y(int y) { m_y = y; }
void ui::Menu::reset()
{
m_selected = 0;
@ -106,6 +110,8 @@ void ui::Menu::reset()
m_options.clear();
}
bool ui::Menu::is_empty() const { return m_options.empty(); }
void ui::Menu::update_scroll_text()
{
const std::string_view text = m_optionScroll->get_text();

View File

@ -51,7 +51,7 @@ void ui::PopMessage::update_y(double targetY)
m_yMet = true;
m_typeTimer.start(5);
}
m_dialog->set_xy(m_dialog->NO_SET, m_y - 6);
m_dialog->set_y(m_y - 6);
}
void ui::PopMessage::update_text_offset()
@ -76,7 +76,7 @@ void ui::PopMessage::update_text_offset()
const int dialogX = m_textX - 16;
const int dialogWidth = stringWidth + 32;
m_dialog->set_xy(dialogX, m_dialog->NO_SET);
m_dialog->set_width_height(dialogWidth, m_dialog->NO_SET);
m_dialog->set_x(dialogX);
m_dialog->set_width(dialogWidth);
if (m_substrOffset >= messageLength) { m_displayTimer.start(m_ticks); }
}

View File

@ -25,11 +25,9 @@ ui::SlideOutPanel::SlideOutPanel(int width, Side side)
void ui::SlideOutPanel::update(bool hasFocus)
{
const bool openingFromLeft = !m_isOpen && m_side == Side::Left && m_x < m_targetX;
const bool openingFromRight = !m_isOpen && m_side == Side::Right && m_x > m_targetX;
if (hasFocus && SlideOutPanel::is_hidden()) { SlideOutPanel::unhide(); }
if (openingFromLeft) { SlideOutPanel::slide_out_left(); }
else if (openingFromRight) { SlideOutPanel::slide_out_right(); }
slide_out();
if (m_isOpen)
{
@ -37,6 +35,8 @@ void ui::SlideOutPanel::update(bool hasFocus)
}
}
void ui::SlideOutPanel::sub_update() { SlideOutPanel::close_hide_panel(); }
void ui::SlideOutPanel::render(sdl::SharedTexture &target, bool hasFocus)
{
for (auto &currentElement : m_elements) { currentElement->render(m_renderTarget, hasFocus); }
@ -55,27 +55,39 @@ void ui::SlideOutPanel::reset()
void ui::SlideOutPanel::close() { m_closePanel = true; }
void ui::SlideOutPanel::hide() { m_hidePanel = true; }
void ui::SlideOutPanel::unhide() { m_hidePanel = false; }
bool ui::SlideOutPanel::is_open() const { return m_isOpen; }
bool ui::SlideOutPanel::is_closed()
{
// I'm assuming this is going to be called, waiting for the panel to close so.
const double scaling = config::get_animation_scaling();
const bool closeToLeft = m_closePanel && m_side == Side::Left && m_x > -m_width;
const bool closeToRight = m_closePanel && m_side == Side::Right && m_x < SCREEN_WIDTH;
if (closeToLeft) { m_x += -(m_width - m_x) / scaling; }
else if (closeToRight) { m_x += m_x / scaling; }
close_hide_panel();
const bool closed = m_side == Side::Left ? m_x <= -m_width : m_x >= SCREEN_WIDTH;
return m_closePanel && closed;
}
bool ui::SlideOutPanel::is_hidden() const { return m_hidePanel; }
void ui::SlideOutPanel::push_new_element(std::shared_ptr<ui::Element> newElement) { m_elements.push_back(newElement); }
void ui::SlideOutPanel::clear_elements() { m_elements.clear(); }
sdl::SharedTexture &ui::SlideOutPanel::get_target() { return m_renderTarget; }
void ui::SlideOutPanel::slide_out()
{
const bool needsSlideOut = !m_isOpen && !m_closePanel && !m_hidePanel;
if (!needsSlideOut) { return; }
const bool slideRight = m_side == SlideOutPanel::Side::Left && m_x < 0;
const bool slideLeft = m_side == SlideOutPanel::Side::Right && m_x > SCREEN_WIDTH - m_width;
if (slideRight) { SlideOutPanel::slide_out_left(); }
else if (slideLeft) { SlideOutPanel::slide_out_right(); }
}
void ui::SlideOutPanel::slide_out_left()
{
const double scaling = config::get_animation_scaling();
@ -106,3 +118,16 @@ void ui::SlideOutPanel::slide_out_right()
m_isOpen = true;
}
}
void ui::SlideOutPanel::close_hide_panel()
{
const double scaling = config::get_animation_scaling();
const bool closeHide = m_closePanel || m_hidePanel;
const bool closeToLeft = closeHide && m_side == Side::Left && m_x > -m_width;
const bool closeToRight = closeHide && m_side == Side::Right && m_x < SCREEN_WIDTH;
if (closeToLeft) { m_x += -((m_width - m_x) / scaling); }
else if (closeToRight) { m_x += m_x / scaling; }
const bool closedOrHidden = (m_x <= -m_width) || (m_x >= SCREEN_WIDTH);
if (closedOrHidden) { m_isOpen = false; }
}

View File

@ -1,9 +1,9 @@
#include "ui/TitleView.hpp"
#include "config/config.hpp"
#include "error.hpp"
#include "graphics/colors.hpp"
#include "input.hpp"
#include "logging/error.hpp"
#include "logging/logger.hpp"
#include <cmath>