2017-09-29 07:45:56 +00:00
|
|
|
/* This file is part of Checkpoint
|
|
|
|
> Copyright (C) 2017 Bernardo Giordano
|
|
|
|
>
|
|
|
|
> 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/>.
|
|
|
|
> See LICENSE for information.
|
|
|
|
*/
|
|
|
|
|
|
|
|
#include "fsstream.h"
|
|
|
|
|
|
|
|
FSStream::FSStream(FS_Archive archive, std::u16string path, u32 flags)
|
|
|
|
{
|
|
|
|
loaded = false;
|
|
|
|
size = 0;
|
2017-10-10 17:17:04 +00:00
|
|
|
offset = 0;
|
2017-09-29 07:45:56 +00:00
|
|
|
|
|
|
|
res = FSUSER_OpenFile(&handle, archive, fsMakePath(PATH_UTF16, path.data()), flags, 0);
|
|
|
|
if (R_SUCCEEDED(res))
|
|
|
|
{
|
2017-10-10 17:17:04 +00:00
|
|
|
FSFILE_GetSize(handle, (u64*)&size);
|
2017-09-29 07:45:56 +00:00
|
|
|
loaded = true;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2017-10-10 17:17:04 +00:00
|
|
|
FSStream::FSStream(FS_Archive archive, std::u16string path, u32 flags, u32 _size)
|
2017-09-29 07:45:56 +00:00
|
|
|
{
|
|
|
|
loaded = false;
|
|
|
|
size = _size;
|
2017-10-10 17:17:04 +00:00
|
|
|
offset = 0;
|
2017-09-29 07:45:56 +00:00
|
|
|
|
|
|
|
res = FSUSER_OpenFile(&handle, archive, fsMakePath(PATH_UTF16, path.data()), flags, 0);
|
|
|
|
if (R_FAILED(res))
|
|
|
|
{
|
|
|
|
res = FSUSER_CreateFile(archive, fsMakePath(PATH_UTF16, path.data()), flags, size);
|
|
|
|
if (R_SUCCEEDED(res))
|
|
|
|
{
|
|
|
|
res = FSUSER_OpenFile(&handle, archive, fsMakePath(PATH_UTF16, path.data()), flags, 0);
|
|
|
|
if (R_SUCCEEDED(res))
|
|
|
|
{
|
|
|
|
loaded = true;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
Result FSStream::close(void)
|
|
|
|
{
|
|
|
|
res = FSFILE_Close(handle);
|
|
|
|
return res;
|
|
|
|
}
|
|
|
|
|
|
|
|
bool FSStream::getLoaded(void)
|
|
|
|
{
|
|
|
|
return loaded;
|
|
|
|
}
|
|
|
|
|
|
|
|
Result FSStream::getResult(void)
|
|
|
|
{
|
|
|
|
return res;
|
|
|
|
}
|
|
|
|
|
|
|
|
u32 FSStream::getSize(void)
|
|
|
|
{
|
|
|
|
return size;
|
|
|
|
}
|
|
|
|
|
2017-10-10 17:17:04 +00:00
|
|
|
u32 FSStream::read(void *buf, u32 sz)
|
2017-09-29 07:45:56 +00:00
|
|
|
{
|
2017-10-10 17:17:04 +00:00
|
|
|
u32 rd = 0;
|
|
|
|
res = FSFILE_Read(handle, &rd, offset, buf, sz);
|
|
|
|
offset += rd;
|
|
|
|
return rd;
|
2017-09-29 07:45:56 +00:00
|
|
|
}
|
|
|
|
|
2017-10-10 17:17:04 +00:00
|
|
|
u32 FSStream::write(void *buf, u32 sz)
|
2017-09-29 07:45:56 +00:00
|
|
|
{
|
2017-10-10 17:17:04 +00:00
|
|
|
u32 wt = 0;
|
|
|
|
res = FSFILE_Write(handle, &wt, offset, buf, sz, FS_WRITE_FLUSH);
|
|
|
|
offset += wt;
|
|
|
|
return wt;
|
|
|
|
}
|
|
|
|
|
|
|
|
bool FSStream::isEndOfFile(void)
|
|
|
|
{
|
|
|
|
return size > offset;
|
2017-09-29 07:45:56 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
bool fileExist(FS_Archive archive, std::u16string path)
|
|
|
|
{
|
|
|
|
FSStream stream(archive, path, FS_OPEN_READ);
|
|
|
|
bool exist = stream.getLoaded();
|
|
|
|
stream.close();
|
|
|
|
return exist;
|
|
|
|
}
|
|
|
|
|
2017-10-02 12:12:00 +00:00
|
|
|
void copyFile(FS_Archive srcArch, FS_Archive dstArch, std::u16string srcPath, std::u16string dstPath)
|
2017-09-29 07:45:56 +00:00
|
|
|
{
|
2017-10-02 12:12:00 +00:00
|
|
|
FSStream input(srcArch, srcPath, FS_OPEN_READ);
|
2017-10-10 17:17:04 +00:00
|
|
|
if (!input.getLoaded())
|
2017-10-02 12:17:56 +00:00
|
|
|
{
|
|
|
|
return;
|
|
|
|
}
|
2017-09-29 07:45:56 +00:00
|
|
|
|
2017-10-10 17:17:04 +00:00
|
|
|
FSStream output(dstArch, dstPath, FS_OPEN_WRITE, input.getSize());
|
2017-10-02 12:12:00 +00:00
|
|
|
if (output.getLoaded())
|
2017-09-29 07:45:56 +00:00
|
|
|
{
|
2017-10-10 17:17:04 +00:00
|
|
|
u8* buf = new u8[BUFFER_SIZE];
|
|
|
|
do {
|
|
|
|
u32 rd = input.read(buf, BUFFER_SIZE);
|
|
|
|
output.write(buf, rd);
|
|
|
|
} while(input.isEndOfFile());
|
|
|
|
|
2017-10-02 12:12:00 +00:00
|
|
|
delete[] buf;
|
2017-09-29 07:45:56 +00:00
|
|
|
}
|
2017-10-02 12:12:00 +00:00
|
|
|
|
|
|
|
input.close();
|
|
|
|
output.close();
|
2017-09-29 07:45:56 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
Result copyDirectory(FS_Archive srcArch, FS_Archive dstArch, std::u16string srcPath, std::u16string dstPath)
|
|
|
|
{
|
|
|
|
Result res = 0;
|
|
|
|
bool quit = false;
|
|
|
|
Directory items(srcArch, srcPath);
|
|
|
|
|
|
|
|
if (!items.getLoaded())
|
|
|
|
{
|
|
|
|
return items.getError();
|
|
|
|
}
|
|
|
|
|
|
|
|
for (size_t i = 0, sz = items.getCount(); i < sz && !quit; i++)
|
|
|
|
{
|
|
|
|
std::u16string newsrc = srcPath + items.getItem(i);
|
|
|
|
std::u16string newdst = dstPath + items.getItem(i);
|
|
|
|
|
|
|
|
if (items.isFolder(i))
|
|
|
|
{
|
|
|
|
res = createDirectory(dstArch, newdst);
|
|
|
|
|
|
|
|
if (R_FAILED(res))
|
|
|
|
{
|
|
|
|
quit = true;
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
newsrc += u8tou16("/");
|
|
|
|
newdst += u8tou16("/");
|
|
|
|
res = copyDirectory(srcArch, dstArch, newsrc, newdst);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
2017-10-07 12:19:53 +00:00
|
|
|
drawCopy(items.getItem(i));
|
2017-10-02 12:12:00 +00:00
|
|
|
copyFile(srcArch, dstArch, newsrc, newdst);
|
2017-09-29 07:45:56 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
return res;
|
|
|
|
}
|
|
|
|
|
|
|
|
Result createDirectory(FS_Archive archive, std::u16string path)
|
|
|
|
{
|
|
|
|
return FSUSER_CreateDirectory(archive, fsMakePath(PATH_UTF16, path.data()), 0);
|
|
|
|
}
|
|
|
|
|
|
|
|
bool directoryExist(FS_Archive archive, std::u16string path)
|
|
|
|
{
|
|
|
|
Handle handle;
|
|
|
|
|
|
|
|
if (R_FAILED(FSUSER_OpenDirectory(&handle, archive, fsMakePath(PATH_UTF16, path.data()))))
|
|
|
|
{
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (R_FAILED(FSDIR_Close(handle)))
|
|
|
|
{
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
|
|
|
|
void backup(size_t index)
|
|
|
|
{
|
2017-09-29 20:58:55 +00:00
|
|
|
const Mode_t mode = getMode();
|
2017-09-29 07:45:56 +00:00
|
|
|
const size_t cellIndex = getScrollableIndex();
|
|
|
|
const bool isNewFolder = cellIndex == 0;
|
|
|
|
Result res = 0;
|
|
|
|
|
|
|
|
Title title;
|
|
|
|
getTitle(title, index);
|
|
|
|
|
2017-10-04 11:18:41 +00:00
|
|
|
if (title.getCardType() == CARD_CTR)
|
2017-09-29 20:58:55 +00:00
|
|
|
{
|
2017-10-04 11:18:41 +00:00
|
|
|
FS_Archive archive;
|
|
|
|
if (mode == MODE_SAVE)
|
|
|
|
{
|
|
|
|
res = getArchiveSave(&archive, title.getMediaType(), title.getLowId(), title.getHighId());
|
|
|
|
}
|
|
|
|
else if (mode == MODE_EXTDATA)
|
|
|
|
{
|
|
|
|
res = getArchiveExtdata(&archive, title.getExtdataId());
|
|
|
|
}
|
|
|
|
|
|
|
|
if (R_SUCCEEDED(res))
|
|
|
|
{
|
2017-10-06 18:15:37 +00:00
|
|
|
std::string suggestion = multipleSelectionEnabled() ? "Autobackup " + getCleanDateTime() : getPathDateTime();
|
2017-10-04 11:18:41 +00:00
|
|
|
|
|
|
|
std::u16string customPath;
|
|
|
|
if (multipleSelectionEnabled())
|
|
|
|
{
|
|
|
|
customPath = isNewFolder ? u8tou16(suggestion.c_str()) : u8tou16(getPathFromCell(cellIndex).c_str());
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
customPath = isNewFolder ? getPath(suggestion) : u8tou16(getPathFromCell(cellIndex).c_str());
|
|
|
|
}
|
|
|
|
|
|
|
|
if (!customPath.compare(u8tou16(" ")))
|
|
|
|
{
|
|
|
|
FSUSER_CloseArchive(archive);
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
std::u16string dstPath = mode == MODE_SAVE ? title.getBackupPath() : title.getExtdataPath();
|
|
|
|
dstPath += u8tou16("/") + customPath;
|
|
|
|
|
|
|
|
if (!isNewFolder || directoryExist(getArchiveSDMC(), dstPath))
|
|
|
|
{
|
|
|
|
res = FSUSER_DeleteDirectoryRecursively(getArchiveSDMC(), fsMakePath(PATH_UTF16, dstPath.data()));
|
|
|
|
if (R_FAILED(res))
|
|
|
|
{
|
|
|
|
FSUSER_CloseArchive(archive);
|
|
|
|
createError(res, "Failed to delete the existing backup directory recursively.");
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
res = createDirectory(getArchiveSDMC(), dstPath);
|
|
|
|
if (R_FAILED(res))
|
|
|
|
{
|
|
|
|
FSUSER_CloseArchive(archive);
|
|
|
|
createError(res, "Failed to create destination directory.");
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
std::u16string copyPath = dstPath + u8tou16("/");
|
|
|
|
|
|
|
|
res = copyDirectory(archive, getArchiveSDMC(), u8tou16("/"), copyPath);
|
|
|
|
if (R_FAILED(res))
|
|
|
|
{
|
|
|
|
std::string message = mode == MODE_SAVE ? "Failed to backup save." : "Failed to backup extdata.";
|
|
|
|
FSUSER_CloseArchive(archive);
|
|
|
|
createError(res, message);
|
|
|
|
|
|
|
|
FSUSER_DeleteDirectoryRecursively(getArchiveSDMC(), fsMakePath(PATH_UTF16, dstPath.data()));
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
refreshDirectories(index);
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
createError(res, "Failed to open save archive.");
|
|
|
|
}
|
|
|
|
|
|
|
|
FSUSER_CloseArchive(archive);
|
2017-09-29 20:58:55 +00:00
|
|
|
}
|
2017-10-04 11:18:41 +00:00
|
|
|
else
|
2017-09-29 07:45:56 +00:00
|
|
|
{
|
2017-10-04 11:18:41 +00:00
|
|
|
CardType cardType = title.getSPICardType();
|
|
|
|
u32 saveSize = SPIGetCapacity(cardType);
|
|
|
|
u32 sectorSize = (saveSize < 0x10000) ? saveSize : 0x10000;
|
|
|
|
|
2017-10-03 12:16:10 +00:00
|
|
|
std::string suggestion = multipleSelectionEnabled() ? title.getShortDescription() + " - Autobackup" : getPathDateTime();
|
2017-10-04 11:18:41 +00:00
|
|
|
|
|
|
|
std::u16string customPath;
|
|
|
|
if (multipleSelectionEnabled())
|
|
|
|
{
|
|
|
|
customPath = isNewFolder ? u8tou16(suggestion.c_str()) : u8tou16(getPathFromCell(cellIndex).c_str());
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
customPath = isNewFolder ? getPath(suggestion) : u8tou16(getPathFromCell(cellIndex).c_str());
|
|
|
|
}
|
|
|
|
|
2017-09-29 07:45:56 +00:00
|
|
|
if (!customPath.compare(u8tou16(" ")))
|
|
|
|
{
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
2017-09-29 20:58:55 +00:00
|
|
|
std::u16string dstPath = mode == MODE_SAVE ? title.getBackupPath() : title.getExtdataPath();
|
|
|
|
dstPath += u8tou16("/") + customPath;
|
2017-09-29 07:45:56 +00:00
|
|
|
|
|
|
|
if (!isNewFolder || directoryExist(getArchiveSDMC(), dstPath))
|
|
|
|
{
|
|
|
|
res = FSUSER_DeleteDirectoryRecursively(getArchiveSDMC(), fsMakePath(PATH_UTF16, dstPath.data()));
|
|
|
|
if (R_FAILED(res))
|
|
|
|
{
|
|
|
|
createError(res, "Failed to delete the existing backup directory recursively.");
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
res = createDirectory(getArchiveSDMC(), dstPath);
|
|
|
|
if (R_FAILED(res))
|
|
|
|
{
|
|
|
|
createError(res, "Failed to create destination directory.");
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
2017-10-04 11:18:41 +00:00
|
|
|
std::u16string copyPath = dstPath + u8tou16("/") + u8tou16(title.getShortDescription().c_str()) + u8tou16(".sav");
|
|
|
|
|
|
|
|
u8* saveFile = new u8[saveSize];
|
|
|
|
for (u32 i = 0; i < saveSize/sectorSize; ++i)
|
|
|
|
{
|
|
|
|
res = SPIReadSaveData(cardType, sectorSize*i, saveFile + sectorSize*i, sectorSize);
|
|
|
|
if (R_FAILED(res))
|
|
|
|
{
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
}
|
2017-09-29 07:45:56 +00:00
|
|
|
|
|
|
|
if (R_FAILED(res))
|
|
|
|
{
|
2017-10-04 11:18:41 +00:00
|
|
|
delete[] saveFile;
|
|
|
|
createError(res, "Failed to backup save.");
|
2017-10-01 20:43:42 +00:00
|
|
|
FSUSER_DeleteDirectoryRecursively(getArchiveSDMC(), fsMakePath(PATH_UTF16, dstPath.data()));
|
2017-09-29 07:45:56 +00:00
|
|
|
return;
|
|
|
|
}
|
2017-10-01 20:43:42 +00:00
|
|
|
|
2017-10-04 11:18:41 +00:00
|
|
|
FSStream stream(getArchiveSDMC(), copyPath, FS_OPEN_WRITE, saveSize);
|
|
|
|
if (stream.getLoaded())
|
|
|
|
{
|
2017-10-10 17:17:04 +00:00
|
|
|
stream.write(saveFile, saveSize);
|
2017-10-04 11:18:41 +00:00
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
delete[] saveFile;
|
|
|
|
stream.close();
|
|
|
|
createError(res, "Failed to backup save.");
|
|
|
|
FSUSER_DeleteDirectoryRecursively(getArchiveSDMC(), fsMakePath(PATH_UTF16, dstPath.data()));
|
|
|
|
return;
|
|
|
|
}
|
2017-09-29 20:58:55 +00:00
|
|
|
|
2017-10-04 11:18:41 +00:00
|
|
|
delete[] saveFile;
|
|
|
|
stream.close();
|
|
|
|
refreshDirectories(index);
|
2017-09-29 07:45:56 +00:00
|
|
|
}
|
2017-10-07 12:19:53 +00:00
|
|
|
|
|
|
|
createInfo("Success!", "Progress correctly saved to disk.");
|
2017-09-29 07:45:56 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
void restore(size_t index)
|
|
|
|
{
|
2017-09-29 20:58:55 +00:00
|
|
|
const Mode_t mode = getMode();
|
2017-09-29 07:45:56 +00:00
|
|
|
const size_t cellIndex = getScrollableIndex();
|
|
|
|
if (cellIndex == 0)
|
|
|
|
{
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
Result res = 0;
|
|
|
|
|
|
|
|
Title title;
|
|
|
|
getTitle(title, index);
|
|
|
|
|
2017-10-04 11:18:41 +00:00
|
|
|
if (title.getCardType() == CARD_CTR)
|
2017-09-29 20:58:55 +00:00
|
|
|
{
|
2017-10-04 11:18:41 +00:00
|
|
|
FS_Archive archive;
|
|
|
|
if (mode == MODE_SAVE)
|
2017-09-29 07:45:56 +00:00
|
|
|
{
|
2017-10-04 11:18:41 +00:00
|
|
|
res = getArchiveSave(&archive, title.getMediaType(), title.getLowId(), title.getHighId());
|
|
|
|
}
|
|
|
|
else if (mode == MODE_EXTDATA)
|
|
|
|
{
|
|
|
|
res = getArchiveExtdata(&archive, title.getExtdataId());
|
2017-09-29 07:45:56 +00:00
|
|
|
}
|
|
|
|
|
2017-10-04 11:18:41 +00:00
|
|
|
if (R_SUCCEEDED(res))
|
2017-09-29 07:45:56 +00:00
|
|
|
{
|
2017-10-04 11:18:41 +00:00
|
|
|
std::u16string srcPath = mode == MODE_SAVE ? title.getBackupPath() : title.getExtdataPath();
|
|
|
|
srcPath += u8tou16("/") + u8tou16(getPathFromCell(cellIndex).c_str()) + u8tou16("/");
|
|
|
|
std::u16string dstPath = u8tou16("/");
|
|
|
|
|
|
|
|
res = FSUSER_DeleteDirectoryRecursively(archive, fsMakePath(PATH_UTF16, dstPath.data()));
|
|
|
|
res = copyDirectory(getArchiveSDMC(), archive, srcPath, dstPath);
|
2017-09-29 20:58:55 +00:00
|
|
|
if (R_FAILED(res))
|
|
|
|
{
|
2017-10-04 11:18:41 +00:00
|
|
|
std::string message = mode == MODE_SAVE ? "Failed to restore save." : "Failed to restore extdata.";
|
2017-09-29 20:58:55 +00:00
|
|
|
FSUSER_CloseArchive(archive);
|
2017-10-04 11:18:41 +00:00
|
|
|
createError(res, message);
|
|
|
|
return;
|
2017-09-29 20:58:55 +00:00
|
|
|
}
|
|
|
|
|
2017-10-04 11:18:41 +00:00
|
|
|
if (mode == MODE_SAVE)
|
2017-09-29 20:58:55 +00:00
|
|
|
{
|
2017-10-04 11:18:41 +00:00
|
|
|
res = FSUSER_ControlArchive(archive, ARCHIVE_ACTION_COMMIT_SAVE_DATA, NULL, 0, NULL, 0);
|
|
|
|
if (R_FAILED(res))
|
|
|
|
{
|
|
|
|
FSUSER_CloseArchive(archive);
|
|
|
|
createError(res, "Failed to commit save data.");
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
u8 out;
|
|
|
|
u64 secureValue = ((u64)SECUREVALUE_SLOT_SD << 32) | (title.getUniqueId() << 8);
|
|
|
|
res = FSUSER_ControlSecureSave(SECURESAVE_ACTION_DELETE, &secureValue, 8, &out, 1);
|
|
|
|
if (R_FAILED(res))
|
|
|
|
{
|
|
|
|
FSUSER_CloseArchive(archive);
|
|
|
|
createError(res, "Failed to fix secure value.");
|
|
|
|
return;
|
|
|
|
}
|
2017-09-29 20:58:55 +00:00
|
|
|
}
|
2017-09-29 07:45:56 +00:00
|
|
|
}
|
2017-10-04 11:18:41 +00:00
|
|
|
else
|
|
|
|
{
|
|
|
|
createError(res, "Failed to open save archive.");
|
|
|
|
}
|
2017-09-29 07:45:56 +00:00
|
|
|
|
2017-10-04 11:18:41 +00:00
|
|
|
FSUSER_CloseArchive(archive);
|
2017-09-29 07:45:56 +00:00
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
2017-10-04 11:18:41 +00:00
|
|
|
CardType cardType = title.getSPICardType();
|
|
|
|
u32 saveSize = SPIGetCapacity(cardType);
|
|
|
|
u32 pageSize = SPIGetPageSize(cardType);
|
|
|
|
|
|
|
|
std::u16string srcPath = mode == MODE_SAVE ? title.getBackupPath() : title.getExtdataPath();
|
|
|
|
srcPath += u8tou16("/") + u8tou16(getPathFromCell(cellIndex).c_str()) + u8tou16("/") + u8tou16(title.getShortDescription().c_str()) + u8tou16(".sav");
|
|
|
|
|
|
|
|
u8* saveFile = new u8[saveSize];
|
|
|
|
FSStream stream(getArchiveSDMC(), srcPath, FS_OPEN_READ);
|
|
|
|
|
|
|
|
if (stream.getLoaded())
|
|
|
|
{
|
2017-10-10 17:17:04 +00:00
|
|
|
stream.read(saveFile, saveSize);
|
2017-10-04 11:18:41 +00:00
|
|
|
}
|
2017-10-10 17:17:04 +00:00
|
|
|
res = stream.getResult();
|
2017-10-04 11:18:41 +00:00
|
|
|
stream.close();
|
|
|
|
|
|
|
|
if (R_FAILED(res))
|
|
|
|
{
|
|
|
|
delete[] saveFile;
|
|
|
|
createError(res, "Failed to read save file backup.");
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
for (u32 i = 0; i < saveSize/pageSize; ++i)
|
|
|
|
{
|
|
|
|
res = SPIWriteSaveData(cardType, pageSize*i, saveFile + pageSize*i, pageSize);
|
|
|
|
if (R_FAILED(res))
|
|
|
|
{
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
if (R_FAILED(res))
|
|
|
|
{
|
|
|
|
delete[] saveFile;
|
|
|
|
createError(res, "Failed to restore save.");
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
delete[] saveFile;
|
2017-09-29 07:45:56 +00:00
|
|
|
}
|
|
|
|
|
2017-10-04 11:18:41 +00:00
|
|
|
createInfo("Success!", getPathFromCell(cellIndex) + " has been restored successfully.");
|
2017-09-29 07:45:56 +00:00
|
|
|
}
|