Checkpoint/switch/source/title.cpp

323 lines
8.6 KiB
C++
Raw Normal View History

2018-05-09 09:25:01 +00:00
/*
* This file is part of Checkpoint
* Copyright (C) 2017-2018 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/>.
*
* 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.
*/
#include "title.hpp"
2018-06-10 09:11:01 +00:00
static std::unordered_map<u128, std::vector<Title>> titles;
2018-06-12 19:53:05 +00:00
static std::unordered_map<u64, std::pair<u8*, u8*>> icons;
2018-05-09 09:25:01 +00:00
2018-06-12 19:53:05 +00:00
void freeIcons(void)
2018-05-09 09:25:01 +00:00
{
2018-06-12 20:10:30 +00:00
for (auto& i : icons)
2018-05-09 09:25:01 +00:00
{
2018-06-12 20:10:30 +00:00
free(i.second.first);
free(i.second.second);
2018-05-09 09:25:01 +00:00
}
2018-06-12 19:53:05 +00:00
}
2018-05-09 09:25:01 +00:00
2018-06-12 19:53:05 +00:00
static void loadIcon(u64 id, NsApplicationControlData* nsacd, size_t iconsize)
{
auto it = icons.find(id);
if (it == icons.end())
2018-05-09 09:25:01 +00:00
{
2018-06-12 19:53:05 +00:00
uint8_t* imageptr = NULL;
size_t imagesize = 256*256*3;
njInit();
if (njDecode(nsacd->icon, iconsize) != NJ_OK)
{
njDone();
return;
}
2018-05-09 09:25:01 +00:00
2018-06-12 19:53:05 +00:00
if (njGetWidth() != 256 || njGetHeight() != 256 || (size_t)njGetImageSize() != imagesize || njIsColor() != 1)
{
njDone();
return;
}
imageptr = njGetImage();
if (imageptr == NULL)
{
njDone();
return;
}
u8* mIcon = (u8*)malloc(imagesize);
std::copy(imageptr, imageptr + imagesize, mIcon);
u8* mSmallIcon = (u8*)malloc(128*128*3);
downscaleRGBImg(mIcon, mSmallIcon, 256, 256, 128, 128);
icons.insert({id, std::make_pair(mIcon, mSmallIcon)});
imageptr = NULL;
2018-05-09 09:25:01 +00:00
njDone();
}
}
2018-07-17 16:25:07 +00:00
void Title::init(u8 saveDataType, u64 id, u128 userID, const std::string& name, const std::string& author)
2018-05-09 09:25:01 +00:00
{
mId = id;
mUserId = userID;
2018-07-17 16:25:07 +00:00
mSaveDataType = saveDataType;
2018-06-10 20:24:37 +00:00
mUserName = Account::username(userID);
mAuthor = author;
2018-05-17 07:54:28 +00:00
mDisplayName = name;
2018-07-23 05:24:28 +00:00
mSafeName = StringUtils::containsInvalidChar(name) ? StringUtils::format("0x%016llX", mId) : StringUtils::removeForbiddenCharacters(name);
2018-05-17 07:54:28 +00:00
mPath = "sdmc:/switch/Checkpoint/saves/" + StringUtils::format("0x%016llX", mId) + " " + mSafeName;
2018-05-09 09:25:01 +00:00
if (!io::directoryExists(mPath))
{
io::createDirectory(mPath);
}
refreshDirectories();
}
2018-07-17 16:25:07 +00:00
bool Title::systemSave(void)
{
return mSaveDataType != FsSaveDataType_SaveData;
}
u8 Title::saveDataType(void)
{
return mSaveDataType;
}
2018-05-09 09:25:01 +00:00
u64 Title::id(void)
{
return mId;
}
u128 Title::userId(void)
{
return mUserId;
}
2018-06-10 20:24:37 +00:00
std::string Title::userName(void)
{
return mUserName;
}
std::string Title::author(void)
{
return mAuthor;
}
2018-05-09 09:25:01 +00:00
std::string Title::name(void)
{
2018-05-17 07:54:28 +00:00
return mDisplayName;
2018-05-09 09:25:01 +00:00
}
std::string Title::path(void)
{
return mPath;
}
2018-07-17 16:25:07 +00:00
std::string Title::fullPath(size_t index)
{
return mFullSavePaths.at(index);
}
2018-05-09 09:25:01 +00:00
std::vector<std::string> Title::saves()
{
return mSaves;
}
u8* Title::icon(void)
{
2018-06-12 19:53:05 +00:00
auto it = icons.find(mId);
return it != icons.end() ? it->second.first : NULL;
2018-05-09 09:25:01 +00:00
}
2018-06-10 20:24:37 +00:00
u8* Title::smallIcon(void)
{
2018-06-12 19:53:05 +00:00
auto it = icons.find(mId);
return it != icons.end() ? it->second.second : NULL;
2018-06-10 20:24:37 +00:00
}
2018-05-09 09:25:01 +00:00
void Title::refreshDirectories(void)
{
mSaves.clear();
2018-07-17 16:25:07 +00:00
mFullSavePaths.clear();
2018-05-09 09:25:01 +00:00
Directory savelist(mPath);
if (savelist.good())
{
for (size_t i = 0, sz = savelist.size(); i < sz; i++)
{
if (savelist.folder(i))
{
mSaves.push_back(savelist.entry(i));
2018-07-17 16:25:07 +00:00
mFullSavePaths.push_back(mPath + "/" + savelist.entry(i));
2018-05-09 09:25:01 +00:00
}
}
std::sort(mSaves.rbegin(), mSaves.rend());
2018-07-17 16:25:07 +00:00
std::sort(mFullSavePaths.rbegin(), mFullSavePaths.rend());
2018-05-09 09:25:01 +00:00
mSaves.insert(mSaves.begin(), "New...");
2018-07-17 16:25:07 +00:00
mFullSavePaths.insert(mFullSavePaths.begin(), "New...");
2018-05-09 09:25:01 +00:00
}
else
{
Gui::createError(savelist.error(), "Couldn't retrieve the directory list for the title " + name() + ".");
}
2018-07-17 16:25:07 +00:00
// save backups from configuration
std::vector<std::string> additionalFolders = Configuration::getInstance().additionalSaveFolders(mId);
for (std::vector<std::string>::const_iterator it = additionalFolders.begin(); it != additionalFolders.end(); it++)
{
// we have other folders to parse
Directory list(*it);
if (list.good())
{
for (size_t i = 0, sz = list.size(); i < sz; i++)
{
if (list.folder(i))
{
mSaves.push_back(list.entry(i));
mFullSavePaths.push_back(*it + "/" + list.entry(i));
}
}
}
}
2018-05-09 09:25:01 +00:00
}
void loadTitles(void)
{
titles.clear();
FsSaveDataIterator iterator;
FsSaveDataInfo info;
size_t total_entries = 0;
size_t outsize = 0;
NacpLanguageEntry* nle = NULL;
NsApplicationControlData* nsacd = (NsApplicationControlData*)malloc(sizeof(NsApplicationControlData));
if (nsacd == NULL)
{
return;
}
memset(nsacd, 0, sizeof(NsApplicationControlData));
Result res = fsOpenSaveDataIterator(&iterator, FsSaveDataSpaceId_NandUser);
if (R_FAILED(res))
{
return;
}
while(1)
{
res = fsSaveDataIteratorRead(&iterator, &info, 1, &total_entries);
if (R_FAILED(res) || total_entries == 0)
{
break;
}
if (info.SaveDataType == FsSaveDataType_SaveData)
{
u64 tid = info.titleID;
u128 uid = info.userID;
2018-07-14 15:05:30 +00:00
if (!Configuration::getInstance().filter(tid))
2018-05-09 09:25:01 +00:00
{
2018-07-14 15:05:30 +00:00
res = nsGetApplicationControlData(1, tid, nsacd, sizeof(NsApplicationControlData), &outsize);
if (R_SUCCEEDED(res) && !(outsize < sizeof(nsacd->nacp)))
2018-05-09 09:25:01 +00:00
{
2018-07-14 15:05:30 +00:00
res = nacpGetLanguageEntry(&nsacd->nacp, &nle);
if (R_SUCCEEDED(res) && nle != NULL)
2018-06-10 09:11:01 +00:00
{
2018-07-14 15:05:30 +00:00
Title title;
2018-07-17 16:25:07 +00:00
title.init(info.SaveDataType, tid, uid, std::string(nle->name), std::string(nle->author));
2018-07-14 15:05:30 +00:00
loadIcon(tid, nsacd, outsize - sizeof(nsacd->nacp));
// check if the vector is already created
std::unordered_map<u128, std::vector<Title>>::iterator it = titles.find(uid);
if (it != titles.end())
{
// found
it->second.push_back(title);
}
else
{
// not found, insert into map
std::vector<Title> v;
v.push_back(title);
titles.emplace(uid, v);
}
2018-06-10 09:11:01 +00:00
}
2018-05-09 09:25:01 +00:00
}
2018-07-14 15:05:30 +00:00
nle = NULL;
2018-05-09 09:25:01 +00:00
}
2018-07-14 15:05:30 +00:00
2018-05-09 09:25:01 +00:00
}
}
free(nsacd);
fsSaveDataIteratorClose(&iterator);
2018-06-10 09:11:01 +00:00
for (auto& vect : titles)
{
std::sort(vect.second.begin(), vect.second.end(), [](Title& l, Title& r) {
return l.name() < r.name();
});
}
2018-05-09 09:25:01 +00:00
}
2018-06-10 09:11:01 +00:00
void getTitle(Title &dst, u128 uid, size_t i)
2018-05-09 09:25:01 +00:00
{
2018-06-10 09:11:01 +00:00
std::unordered_map<u128, std::vector<Title>>::iterator it = titles.find(uid);
if (it != titles.end() && i < getTitleCount(uid))
2018-05-09 09:25:01 +00:00
{
2018-06-10 09:11:01 +00:00
dst = it->second.at(i);
2018-05-09 09:25:01 +00:00
}
}
2018-06-10 09:11:01 +00:00
size_t getTitleCount(u128 uid)
2018-05-09 09:25:01 +00:00
{
2018-06-10 09:11:01 +00:00
std::unordered_map<u128, std::vector<Title>>::iterator it = titles.find(uid);
return it != titles.end() ? it->second.size() : 0;
2018-05-09 09:25:01 +00:00
}
void refreshDirectories(u64 id)
{
2018-06-10 09:11:01 +00:00
for (auto& pair : titles)
2018-05-09 09:25:01 +00:00
{
2018-06-10 09:11:01 +00:00
for (size_t i = 0; i < pair.second.size(); i++)
2018-05-09 09:25:01 +00:00
{
2018-06-10 09:11:01 +00:00
if (pair.second.at(i).id() == id)
{
pair.second.at(i).refreshDirectories();
}
2018-05-09 09:25:01 +00:00
}
}
2018-06-10 20:24:37 +00:00
}
u8* smallIcon(u128 uid, size_t i)
{
std::unordered_map<u128, std::vector<Title>>::iterator it = titles.find(uid);
return it != titles.end() ? it->second.at(i).smallIcon() : NULL;
2018-06-12 19:54:01 +00:00
}