Checkpoint/switch/source/io.cpp

292 lines
11 KiB
C++
Raw Normal View History

2018-05-09 09:25:01 +00:00
/*
2019-05-06 20:51:09 +00:00
* This file is part of Checkpoint
* Copyright (C) 2017-2019 Bernardo Giordano, FlagBrew
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*
* Additional Terms 7.b and 7.c of GPLv3 apply to this file:
* * Requiring preservation of specified reasonable legal notices or
* author attributions in that material or in the Appropriate Legal
* Notices displayed by works containing it.
* * Prohibiting misrepresentation of the origin of that material,
* or requiring that modified versions of such material be marked in
* reasonable ways as different from the original version.
*/
2018-05-09 09:25:01 +00:00
#include "io.hpp"
bool io::fileExists(const std::string& path)
{
2019-05-06 20:51:09 +00:00
struct stat buffer;
return (stat(path.c_str(), &buffer) == 0);
2018-05-09 09:25:01 +00:00
}
void io::copyFile(const std::string& srcPath, const std::string& dstPath)
{
FILE* src = fopen(srcPath.c_str(), "rb");
2019-05-18 20:51:58 +00:00
if (src == NULL) {
Logger::getInstance().log(Logger::ERROR, "Failed to open source file " + srcPath + " during copy. Skipping...");
2019-05-18 20:51:58 +00:00
return;
}
FILE* dst = fopen(dstPath.c_str(), "wb");
2019-05-18 20:51:58 +00:00
if (dst == NULL) {
Logger::getInstance().log(Logger::ERROR, "Failed to open destination file " + dstPath + " during copy. Skipping...");
2019-05-18 20:51:58 +00:00
fclose(src);
2018-05-09 09:25:01 +00:00
return;
}
fseek(src, 0, SEEK_END);
u64 sz = ftell(src);
rewind(src);
2018-05-09 09:25:01 +00:00
2019-07-02 21:42:48 +00:00
u8* buf = new u8[BUFFER_SIZE];
u64 offset = 0;
while (offset < sz) {
u32 count = fread((char*)buf, 1, BUFFER_SIZE, src);
fwrite((char*)buf, 1, count, dst);
offset += count;
2018-05-09 09:25:01 +00:00
}
delete[] buf;
fclose(src);
fclose(dst);
2018-05-11 20:20:54 +00:00
// commit each file to the save
2019-05-06 20:51:09 +00:00
if (dstPath.rfind("save:/", 0) == 0) {
Logger::getInstance().log(Logger::ERROR, "Committing file " + dstPath + " to the save archive.");
fsdevCommitDevice("save");
}
2018-05-09 09:25:01 +00:00
}
Result io::copyDirectory(const std::string& srcPath, const std::string& dstPath)
{
Result res = 0;
2019-05-06 20:51:09 +00:00
bool quit = false;
2018-05-09 09:25:01 +00:00
Directory items(srcPath);
2019-05-06 20:51:09 +00:00
if (!items.good()) {
2018-05-09 09:25:01 +00:00
return items.error();
}
2019-05-06 20:51:09 +00:00
for (size_t i = 0, sz = items.size(); i < sz && !quit; i++) {
2018-05-09 09:25:01 +00:00
std::string newsrc = srcPath + items.entry(i);
std::string newdst = dstPath + items.entry(i);
2019-05-06 20:51:09 +00:00
if (items.folder(i)) {
2018-05-09 09:25:01 +00:00
res = io::createDirectory(newdst);
2019-05-06 20:51:09 +00:00
if (R_SUCCEEDED(res)) {
2018-05-09 09:25:01 +00:00
newsrc += "/";
newdst += "/";
res = io::copyDirectory(newsrc, newdst);
}
2019-05-06 20:51:09 +00:00
else {
2018-05-09 09:25:01 +00:00
quit = true;
}
}
2019-05-06 20:51:09 +00:00
else {
2018-05-09 09:25:01 +00:00
io::copyFile(newsrc, newdst);
}
}
2019-05-06 20:51:09 +00:00
2018-05-09 09:25:01 +00:00
return 0;
}
Result io::createDirectory(const std::string& path)
{
2019-05-03 20:49:01 +00:00
mkdir(path.c_str(), 777);
2018-05-09 09:25:01 +00:00
return 0;
}
bool io::directoryExists(const std::string& path)
{
struct stat sb;
return (stat(path.c_str(), &sb) == 0 && S_ISDIR(sb.st_mode));
}
Result io::deleteFolderRecursively(const std::string& path)
2018-05-09 09:25:01 +00:00
{
Directory dir(path);
2019-05-06 20:51:09 +00:00
if (!dir.good()) {
return dir.error();
2018-05-09 09:25:01 +00:00
}
2019-05-06 20:51:09 +00:00
for (size_t i = 0, sz = dir.size(); i < sz; i++) {
if (dir.folder(i)) {
std::string newpath = path + "/" + dir.entry(i) + "/";
deleteFolderRecursively(newpath);
newpath = path + dir.entry(i);
rmdir(newpath.c_str());
}
2019-05-06 20:51:09 +00:00
else {
std::string newpath = path + dir.entry(i);
std::remove(newpath.c_str());
}
2018-05-09 09:25:01 +00:00
}
rmdir(path.c_str());
2018-05-09 09:25:01 +00:00
return 0;
}
2019-07-02 21:42:48 +00:00
std::tuple<bool, Result, std::string> io::backup(size_t index, u128 uid, size_t cellIndex)
2018-05-09 09:25:01 +00:00
{
2019-07-02 21:42:48 +00:00
const bool isNewFolder = cellIndex == 0;
Result res = 0;
std::tuple<bool, Result, std::string> ret = std::make_tuple(false, -1, "");
2018-05-09 09:25:01 +00:00
Title title;
2018-06-10 09:11:07 +00:00
getTitle(title, uid, index);
2019-05-06 20:51:09 +00:00
Logger::getInstance().log(Logger::INFO, "Started backup of %s. Title id: 0x%016lX; User id: 0x%lX%lX.", title.name().c_str(), title.id(), (u64)(title.userId() >> 8), (u64)(title.userId()));
2018-05-09 09:25:01 +00:00
FsFileSystem fileSystem;
res = FileSystem::mount(&fileSystem, title.id(), title.userId());
2019-05-06 20:51:09 +00:00
if (R_SUCCEEDED(res)) {
2019-07-09 21:13:23 +00:00
int rc = FileSystem::mount(fileSystem);
if (rc == -1) {
2018-05-09 09:25:01 +00:00
FileSystem::unmount();
Logger::getInstance().log(Logger::ERROR, "Failed to mount filesystem during backup. Title id: 0x%016lX; User id: 0x%lX%lX.", title.id(), (u64)(title.userId() >> 8), (u64)(title.userId()));
2019-07-02 21:42:48 +00:00
return std::make_tuple(false, -2, "Failed to mount save.");
2018-05-09 09:25:01 +00:00
}
}
2019-05-06 20:51:09 +00:00
else {
Logger::getInstance().log(Logger::ERROR, "Failed to mount filesystem during backup with result %X. Title id: 0x%016lX; User id: 0x%lX%lX.", res, title.id(), (u64)(title.userId() >> 8), (u64)(title.userId()));
2019-07-02 21:42:48 +00:00
return std::make_tuple(false, res, "Failed to mount save.");
2018-05-09 09:25:01 +00:00
}
2019-05-06 20:51:09 +00:00
std::string suggestion = DateTime::dateTimeStr() + " " +
(StringUtils::containsInvalidChar(Account::username(title.userId()))
? ""
: StringUtils::removeNotAscii(StringUtils::removeAccents(Account::username(title.userId()))));
2018-05-09 09:25:01 +00:00
std::string customPath;
2019-05-06 20:51:09 +00:00
if (MS::multipleSelectionEnabled()) {
2018-07-17 16:25:07 +00:00
customPath = isNewFolder ? suggestion : "";
2018-05-09 09:25:01 +00:00
}
2019-05-06 20:51:09 +00:00
else {
if (isNewFolder) {
2018-08-05 14:44:55 +00:00
std::pair<bool, std::string> keyboardResponse = KeyboardManager::get().keyboard(suggestion);
2019-05-06 20:51:09 +00:00
if (keyboardResponse.first) {
2018-08-05 14:44:55 +00:00
customPath = StringUtils::removeForbiddenCharacters(keyboardResponse.second);
}
2019-05-06 20:51:09 +00:00
else {
2018-08-05 14:44:55 +00:00
FileSystem::unmount();
Logger::getInstance().log(Logger::INFO, "Copy operation aborted by the user through the system keyboard.");
2019-07-02 21:42:48 +00:00
return std::make_tuple(false, 0, "Operation aborted by the user.");
2018-08-05 14:44:55 +00:00
}
}
2019-05-06 20:51:09 +00:00
else {
2018-08-05 14:44:55 +00:00
customPath = "";
}
2018-05-09 09:25:01 +00:00
}
2019-05-06 20:51:09 +00:00
2018-07-17 16:25:07 +00:00
std::string dstPath;
2019-05-06 20:51:09 +00:00
if (!isNewFolder) {
2018-07-17 16:25:07 +00:00
// we're overriding an existing folder
dstPath = title.fullPath(cellIndex);
}
2019-05-06 20:51:09 +00:00
else {
2018-07-17 16:25:07 +00:00
dstPath = title.path() + "/" + customPath;
2019-05-06 20:51:09 +00:00
}
2018-07-17 16:25:07 +00:00
2019-05-06 20:51:09 +00:00
if (!isNewFolder || io::directoryExists(dstPath)) {
2019-07-09 21:13:23 +00:00
int rc = io::deleteFolderRecursively((dstPath + "/").c_str());
if (rc != 0) {
2018-05-09 09:25:01 +00:00
FileSystem::unmount();
Logger::getInstance().log(Logger::ERROR, "Failed to recursively delete directory " + dstPath);
2019-07-09 21:13:23 +00:00
return std::make_tuple(false, (Result)rc, "Failed to delete the existing backup\ndirectory recursively.");
2018-05-09 09:25:01 +00:00
}
}
2019-05-06 20:51:09 +00:00
2019-05-18 20:51:58 +00:00
io::createDirectory(dstPath);
2018-05-09 09:25:01 +00:00
res = io::copyDirectory("save:/", dstPath + "/");
2019-05-06 20:51:09 +00:00
if (R_FAILED(res)) {
2018-05-09 09:25:01 +00:00
FileSystem::unmount();
io::deleteFolderRecursively((dstPath + "/").c_str());
Logger::getInstance().log(Logger::ERROR, "Failed to copy directory " + dstPath + ". Skipping...");
2019-07-02 21:42:48 +00:00
return std::make_tuple(false, res, "Failed to backup save.");
2018-05-09 09:25:01 +00:00
}
2019-05-06 20:51:09 +00:00
2018-05-09 09:25:01 +00:00
refreshDirectories(title.id());
2019-05-06 20:51:09 +00:00
2018-05-09 09:25:01 +00:00
FileSystem::unmount();
2019-05-06 20:51:09 +00:00
if (!MS::multipleSelectionEnabled()) {
2019-05-04 12:35:16 +00:00
blinkLed(4);
2019-07-02 21:42:48 +00:00
ret = std::make_tuple(true, 0, "Progress correctly saved to disk.");
2019-04-12 21:39:55 +00:00
}
// TODO: figure out if this code can be accessed at all
2019-04-12 21:39:55 +00:00
auto systemKeyboardAvailable = KeyboardManager::get().isSystemKeyboardAvailable();
2019-05-06 20:51:09 +00:00
if (!systemKeyboardAvailable.first) {
2019-07-02 21:42:48 +00:00
return std::make_tuple(
false, systemKeyboardAvailable.second, "System keyboard applet not accessible.\nThe suggested destination folder was used\ninstead.");
2019-04-12 21:39:55 +00:00
}
Logger::getInstance().log(Logger::INFO, "Backup succeeded.");
2019-07-02 21:42:48 +00:00
return ret;
2018-05-09 09:25:01 +00:00
}
2019-07-02 21:42:48 +00:00
std::tuple<bool, Result, std::string> io::restore(size_t index, u128 uid, size_t cellIndex, const std::string& nameFromCell)
2018-05-09 09:25:01 +00:00
{
2019-07-02 21:42:48 +00:00
Result res = 0;
std::tuple<bool, Result, std::string> ret = std::make_tuple(false, -1, "");
2018-05-09 09:25:01 +00:00
Title title;
2018-06-10 09:11:07 +00:00
getTitle(title, uid, index);
2019-05-06 20:51:09 +00:00
Logger::getInstance().log(Logger::INFO, "Started restore of %s. Title id: 0x%016lX; User id: 0x%lX%lX.", title.name().c_str(), title.id(), (u64)(title.userId() >> 8), (u64)(title.userId()));
2018-05-09 09:25:01 +00:00
FsFileSystem fileSystem;
2018-07-17 16:25:07 +00:00
res = title.systemSave() ? FileSystem::mount(&fileSystem, title.id()) : FileSystem::mount(&fileSystem, title.id(), title.userId());
2019-05-06 20:51:09 +00:00
if (R_SUCCEEDED(res)) {
2019-07-09 21:13:23 +00:00
int rc = FileSystem::mount(fileSystem);
if (rc == -1) {
2018-05-09 09:25:01 +00:00
FileSystem::unmount();
Logger::getInstance().log(Logger::ERROR, "Failed to mount filesystem during restore. Title id: 0x%016lX; User id: 0x%lX%lX.", title.id(), (u64)(title.userId() >> 8), (u64)(title.userId()));
2019-07-02 21:42:48 +00:00
return std::make_tuple(false, -2, "Failed to mount save.");
2018-05-09 09:25:01 +00:00
}
}
2019-05-06 20:51:09 +00:00
else {
Logger::getInstance().log(Logger::ERROR, "Failed to mount filesystem during restore with result %X. Title id: 0x%016lX; User id: 0x%lX%lX.", res, title.id(), (u64)(title.userId() >> 8), (u64)(title.userId()));
2019-07-02 21:42:48 +00:00
return std::make_tuple(false, res, "Failed to mount save.");
2018-05-09 09:25:01 +00:00
}
2018-07-17 16:25:07 +00:00
std::string srcPath = title.fullPath(cellIndex) + "/";
2018-05-09 09:25:01 +00:00
std::string dstPath = "save:/";
2019-05-06 20:51:09 +00:00
2018-05-09 09:25:01 +00:00
res = io::deleteFolderRecursively(dstPath.c_str());
2019-05-06 20:51:09 +00:00
if (R_FAILED(res)) {
2018-05-09 09:25:01 +00:00
FileSystem::unmount();
Logger::getInstance().log(Logger::ERROR, "Failed to recursively delete directory " + dstPath);
2019-07-02 21:42:48 +00:00
return std::make_tuple(false, res, "Failed to delete save.");
2018-05-09 09:25:01 +00:00
}
2019-05-06 20:51:09 +00:00
2018-05-09 09:25:01 +00:00
res = io::copyDirectory(srcPath, dstPath);
2019-05-06 20:51:09 +00:00
if (R_FAILED(res)) {
2018-05-09 09:25:01 +00:00
FileSystem::unmount();
Logger::getInstance().log(Logger::ERROR, "Failed to copy directory " + srcPath + " to " + dstPath + ". Skipping...");
2019-07-02 21:42:48 +00:00
return std::make_tuple(false, res, "Failed to restore save.");
2018-05-09 09:25:01 +00:00
}
2019-05-06 20:51:09 +00:00
2018-05-09 09:25:01 +00:00
res = fsdevCommitDevice("save");
2019-05-06 20:51:09 +00:00
if (R_FAILED(res)) {
Logger::getInstance().log(Logger::ERROR, "Failed to commit save with result %X.", res);
2019-07-02 21:42:48 +00:00
return std::make_tuple(false, res, "Failed to commit to save device.");
2018-05-09 09:25:01 +00:00
}
2019-05-06 20:51:09 +00:00
else {
2019-05-06 18:55:04 +00:00
blinkLed(4);
2019-07-02 21:42:48 +00:00
ret = std::make_tuple(true, 0, nameFromCell + "\nhas been restored successfully.");
2018-05-09 09:25:01 +00:00
}
2019-05-06 20:51:09 +00:00
2018-05-09 09:25:01 +00:00
FileSystem::unmount();
Logger::getInstance().log(Logger::INFO, "Restore succeeded.");
2019-07-02 21:42:48 +00:00
return ret;
2018-05-09 09:25:01 +00:00
}