/* 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 . > See LICENSE for information. */ #include "title.h" static bool checkHigh(u64 id); static void loadTextureIcon(smdh_s *smdh, size_t i); static std::vector titles; static std::vector<u32> filter; bool Title::load(u64 _id, FS_MediaType _media) { static size_t index = 1; id = _id; media = _media; smdh_s *smdh = loadSMDH(getLowId(), getHighId(), media); if (smdh == NULL) { return false; } shortDescription = removeForbiddenCharacters((char16_t*)smdh->applicationTitles[1].shortDescription); longDescription = (char16_t*)smdh->applicationTitles[1].longDescription; backupPath = u8tou16("/Checkpoint/saves/") + shortDescription; extdataPath = u8tou16("/Checkpoint/extdata/") + shortDescription; loadTextureIcon(smdh, index); textureId = index; index++; if (!directoryExist(getArchiveSDMC(), backupPath)) { Result res = createDirectory(getArchiveSDMC(), backupPath); if (R_FAILED(res)) { createError(res, "Failed to create backup directory."); } } if (!directoryExist(getArchiveSDMC(), extdataPath)) { Result res = createDirectory(getArchiveSDMC(), extdataPath); if (R_FAILED(res)) { createError(res, "Failed to create backup directory."); } } refreshDirectories(); delete smdh; return true; } std::string Title::getMediatypeString(void) { switch(media) { case MEDIATYPE_SD: return "SD Card"; case MEDIATYPE_GAME_CARD: return "Cartridge"; case MEDIATYPE_NAND: return "NAND"; default: return " "; } return " "; } std::string Title::getShortDescription(void) { return u16tou8(shortDescription); } std::string Title::getLongDescription(void) { return u16tou8(longDescription); } std::u16string Title::getBackupPath(void) { return backupPath; } std::u16string Title::getExtdataPath(void) { return extdataPath; } std::vector<std::u16string> Title::getDirectories(void) { return directories; } std::vector<std::u16string> Title::getExtdatas(void) { return extdatas; } void Title::refreshDirectories(void) { directories.clear(); extdatas.clear(); // save backups Directory savelist(getArchiveSDMC(), backupPath); if (savelist.getLoaded()) { for (size_t i = 0, sz = savelist.getCount(); i < sz; i++) { if (savelist.isFolder(i)) { directories.push_back(savelist.getItem(i)); } } std::sort(directories.begin(), directories.end()); directories.insert(directories.begin(), u8tou16("New...")); } else { createError(savelist.getError(), "Couldn't retrieve the directory list for the title " + getShortDescription()); } // extdata backups Directory extlist(getArchiveSDMC(), extdataPath); if (extlist.getLoaded()) { for (size_t i = 0, sz = extlist.getCount(); i < sz; i++) { if (extlist.isFolder(i)) { extdatas.push_back(extlist.getItem(i)); } } std::sort(extdatas.begin(), extdatas.end()); extdatas.insert(extdatas.begin(), u8tou16("New...")); } else { createError(extlist.getError(), "Couldn't retrieve the extdata list for the title " + getShortDescription()); } } u32 Title::getHighId(void) { return (u32)(id >> 32); } u32 Title::getLowId(void) { return (u32)id; } u32 Title::getUniqueId(void) { return (getLowId() >> 8); } u64 Title::getId(void) { return id; } u32 Title::getExtdataId(void) { u32 low = getLowId(); switch(low) { case 0x00055E00: return 0x0000055D; // Pokémon Y case 0x0011C400: return 0x000011C5; // Pokémon Omega Ruby case 0x00175E00: return 0x00001648; // Pokémon Moon } return low >> 8; } FS_MediaType Title::getMediaType(void) { return media; } size_t Title::getTextureId(void) { return textureId; } static bool checkHigh(u64 id) { u32 high = id >> 32; return (high == 0x00040000 || high == 0x00040002); } void loadFilter(void) { Result res; u64 size = getFileSize(getArchiveSDMC(), u8tou16(PATH_FILTER)); if (size == 0 || size % 16 != 0) { return; } FSStream stream(getArchiveSDMC(), u8tou16(PATH_FILTER), FS_OPEN_READ); if (stream.getLoaded()) { u8 buf[size]; res = stream.read(buf); if (R_SUCCEEDED(res)) { filter.reserve(size/16); for (u32 i = 0, sz = size/16; i < sz; i++) { char tmp[9]; memcpy(tmp, buf + i*16 + 8, 8); tmp[8] = '\0'; std::string titleId(tmp); u32 id = std::stoul(titleId, nullptr, 16); filter.push_back(id); } } } stream.close(); } static bool checkFilter(u32 low) { for (u32 i = 0; i < filter.size(); i++) { if (low == filter.at(i)) { return true; } } return false; } void loadTitles(void) { u32 count; AM_GetTitleCount(MEDIATYPE_SD, &count); titles.reserve(count); u64 ids[count]; AM_GetTitleList(NULL, MEDIATYPE_SD, count, ids); for (u32 i = 0; i < count; i++) { if (checkHigh(ids[i]) && !checkFilter((u32)ids[i])) { Title title; if (title.load(ids[i], MEDIATYPE_SD)) { titles.push_back(title); std::sort(titles.begin(), titles.end(), [](Title l, Title r) { return l.getShortDescription() < r.getShortDescription(); }); } } } AM_GetTitleCount(MEDIATYPE_GAME_CARD, &count); if (count > 0) { AM_GetTitleList(NULL, MEDIATYPE_GAME_CARD, count, ids); if (checkHigh(ids[0])) { Title title; if (title.load(ids[0], MEDIATYPE_GAME_CARD)) { titles.insert(titles.begin(), title); } } } } void getTitle(Title &dst, int i) { if (i < getTitlesCount()) dst = titles.at(i); } int getTitlesCount(void) { return titles.size(); } size_t getTextureId(int i) { return titles.at(i).getTextureId(); } static void loadTextureIcon(smdh_s *smdh, size_t i) { static const u32 width = 48, height = 48; u32 *image = (u32*)malloc(width*height*sizeof(u32)); for (u32 x = 0; x < width; x++) { for (u32 y = 0; y < height; y++) { unsigned int dest_pixel = (x + y*width); unsigned int source_pixel = (((y >> 3) * (width >> 3) + (x >> 3)) << 6) + ((x & 1) | ((y & 1) << 1) | ((x & 2) << 1) | ((y & 2) << 2) | ((x & 4) << 2) | ((y & 4) << 3)); u8 r = ((smdh->bigIconData[source_pixel] >> 11) & 0b11111) << 3; u8 g = ((smdh->bigIconData[source_pixel] >> 5) & 0b111111) << 2; u8 b = (smdh->bigIconData[source_pixel] & 0b11111) << 3; u8 a = 0xFF; image[dest_pixel] = (r << 24) | (g << 16) | (b << 8) | a; } } pp2d_load_texture_memory(i, (u8*)image, (u32)width, (u32)height); free(image); } void refreshDirectories(size_t i) { titles.at(i).refreshDirectories(); }