123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485 |
- #include <cpp3ds/System/Err.hpp>
- #include <iostream>
- #include <cpp3ds/System/I18n.hpp>
- #include "AssetManager.hpp"
- #include "AppItem.hpp"
- #include "Util.hpp"
- #include "TitleKeys.hpp"
- #include "Theme.hpp"
- #include "DownloadQueue.hpp"
- #include <sstream>
- #include <unistd.h>
- #include "Notification.hpp"
- #include "Installer.hpp"
- #include "States/BrowseState.hpp"
- #include "States/DialogState.hpp"
- #ifndef EMULATION
- #include <3ds.h>
- #endif
- namespace {
- std::vector<std::unique_ptr<cpp3ds::Texture>> textures;
- #ifdef _3DS
- typedef struct {
- u16 shortDescription[0x40];
- u16 longDescription[0x80];
- u16 publisher[0x40];
- } SMDH_title;
- typedef struct {
- char magic[0x04];
- u16 version;
- u16 reserved1;
- SMDH_title titles[0x10];
- u8 ratings[0x10];
- u32 region;
- u32 matchMakerId;
- u64 matchMakerBitId;
- u32 flags;
- u16 eulaVersion;
- u16 reserved;
- u32 optimalBannerFrame;
- u32 streetpassId;
- u64 reserved2;
- u8 smallIcon[0x480];
- u8 largeIcon[0x1200];
- } SMDH;
- #endif
- }
- namespace FreeShop
- {
- AppItem::AppItem()
- : m_installed(false)
- , m_regions(0)
- , m_languages(0)
- , m_iconIndex(-1)
- , m_iconRect(cpp3ds::IntRect(0, 0, 48, 48))
- , m_systemIconTexture(nullptr)
- , m_threadSleepInstall(&AppItem::queueForSleepInstallThread, this)
- , m_sleepInstallDemo(false)
- , m_sleepInstallRelated(false)
- {
- if (Theme::isMissingIconThemed) {
- cpp3ds::Texture &texture = AssetManager<cpp3ds::Texture>::get(FREESHOP_DIR "/theme/images/missing-icon.png");
- texture.setSmooth(true);
- m_iconTexture = &texture;
- } else {
- cpp3ds::Texture &texture = AssetManager<cpp3ds::Texture>::get("images/missing-icon.png");
- texture.setSmooth(true);
- m_iconTexture = &texture;
- }
- }
- AppItem::~AppItem()
- {
- m_threadSleepInstall.wait();
- delete m_systemIconTexture;
- }
- const cpp3ds::String &AppItem::getTitle() const
- {
- return m_title;
- }
- void AppItem::loadFromJSON(const char* titleId, const rapidjson::Value &json)
- {
- m_genres.clear();
- m_features.clear();
- m_seed.clear();
- m_languages = 0;
- m_titleIdStr = titleId;
- m_titleId = strtoull(titleId, 0, 16);
- m_updates = TitleKeys::getRelated(m_titleId, TitleKeys::Update);
- m_demos = TitleKeys::getRelated(m_titleId, TitleKeys::Demo);
- m_dlc = TitleKeys::getRelated(m_titleId, TitleKeys::DLC);
- m_isSleepBusy = false;
- const char *title = json[0].GetString();
- m_title = cpp3ds::String::fromUtf8(title, title + json[0].GetStringLength());
- m_normalizedTitle = json[1].GetString();
- m_contentId = json[2].GetString();
- m_regions = json[3].GetInt();
- m_uriRegion = json[4].GetString();
- m_filesize = json[5].GetUint64();
- int iconIndex = json[6].GetInt();
- if (iconIndex >= 0)
- setIconIndex(iconIndex);
- // Crypto seed
- std::string seed = json[7].GetString();
- if (!seed.empty())
- {
- m_seed.clear();
- for (int i = 0; i < 16; ++i)
- {
- std::string byteStr = seed.substr(i*2, 2);
- char byte = strtol(byteStr.c_str(), NULL, 16);
- m_seed.push_back(byte);
- }
- }
- // Genres
- auto genres = json[8].GetArray();
- for (int i = 0; i < genres.Size(); ++i)
- m_genres.push_back(genres[i].GetInt());
- // Languages
- auto languages = json[9].GetArray();
- for (int i = 0; i < languages.Size(); ++i)
- {
- std::string lang(languages[i].GetString());
- if ("ja" == lang) m_languages |= Japanese;
- else if ("en" == lang) m_languages |= English;
- else if ("es" == lang) m_languages |= Spanish;
- else if ("fr" == lang) m_languages |= French;
- else if ("de" == lang) m_languages |= German;
- else if ("it" == lang) m_languages |= Italian;
- else if ("nl" == lang) m_languages |= Dutch;
- else if ("pt" == lang) m_languages |= Portuguese;
- else if ("ru" == lang) m_languages |= Russian;
- }
- // Features
- auto features = json[10].GetArray();
- for (int i = 0; i < features.Size(); ++i)
- m_features.push_back(features[i].GetInt());
- if (!json[11].IsNull())
- m_voteScore = json[11].GetFloat();
- m_voteCount = json[12].GetInt();
- m_releaseDate = json[13].GetInt();
- m_productCode = json[14].GetString();
- m_platform = json[15].GetInt();
- m_publisher = json[16].GetInt();
- }
- void AppItem::loadFromSystemTitleId(cpp3ds::Uint64 titleId)
- {
- m_titleId = titleId;
- m_titleIdStr = _("%016llX", titleId);
- m_updates = TitleKeys::getRelated(m_titleId, TitleKeys::Update);
- m_demos = TitleKeys::getRelated(m_titleId, TitleKeys::Demo);
- m_dlc = TitleKeys::getRelated(m_titleId, TitleKeys::DLC);
- #ifdef _3DS
- Result res = 0;
- Handle fileHandle;
- char productCode[12] = {'\0'};
- AM_TitleEntry titleInfo;
- AM_GetTitleInfo(MEDIATYPE_NAND, 1, &titleId, &titleInfo);
- AM_GetTitleProductCode(MEDIATYPE_NAND, titleId, productCode);
- m_title = productCode;
- if (m_title.toAnsiString().compare(0, 9, "CTR-N-HMM") == 0)
- m_title = _("Home Menu Themes");
- static const u32 filePath[5] = {0x0, 0x0, 0x2, 0x6E6F6369, 0x0};
- u32 archivePath[4] = {(u32) (titleId & 0xFFFFFFFF), (u32) ((titleId >> 32) & 0xFFFFFFFF), MEDIATYPE_NAND, 0x0};
- FS_Path binArchPath = {PATH_BINARY, 0x10, archivePath};
- FS_Path binFilePath = {PATH_BINARY, 0x14, filePath};
- if(R_SUCCEEDED(res = FSUSER_OpenFileDirectly(&fileHandle, ARCHIVE_SAVEDATA_AND_CONTENT, binArchPath, binFilePath, FS_OPEN_READ, 0)))
- {
- auto smdh = new SMDH();
- if (smdh)
- {
- FSFILE_Read(fileHandle, nullptr, 0, smdh, sizeof(SMDH));
- if (smdh->magic[0] == 'S' && smdh->magic[1] == 'M' && smdh->magic[2] == 'D' && smdh->magic[3] == 'H')
- {
- u8 systemLanguage = CFG_LANGUAGE_EN;
- CFGU_GetSystemLanguage(&systemLanguage);
- u16 *title = smdh->titles[systemLanguage].shortDescription;
- m_title = cpp3ds::String::fromUtf16(title, title + 0x40);
- delete m_systemIconTexture;
- m_systemIconTexture = new cpp3ds::Texture();
- m_systemIconTexture->loadFromPreprocessedMemory(smdh->largeIcon, sizeof(smdh->largeIcon), 48, 48, GPU_RGB565);
- }
- delete smdh;
- }
- FSFILE_Close(fileHandle);
- }
- #else
- m_title = m_titleIdStr;
- #endif
- m_normalizedTitle = m_title;
- for (auto &c : m_normalizedTitle)
- c = std::tolower(c);
- }
- void AppItem::setIconIndex(size_t iconIndex)
- {
- size_t textureIndex = iconIndex / 441;
- iconIndex %= 441;
- // Load texture from file if not done already
- if (textures.size() < textureIndex + 1)
- {
- for (int i = textures.size(); i <= textureIndex; ++i)
- {
- std::unique_ptr<cpp3ds::Texture> texture(new cpp3ds::Texture());
- #ifdef EMULATION
- texture->loadFromFile(_(FREESHOP_DIR "/cache/images/icons%d.jpg", i));
- #else
- texture->loadFromPreprocessedFile(_(FREESHOP_DIR "/cache/images/icons%d.bin", i), 1024, 1024, GPU_ETC1);
- #endif
- texture->setSmooth(true);
- textures.push_back(std::move(texture));
- }
- }
- m_iconIndex = iconIndex;
- m_iconTexture = textures[textureIndex].get();
- int x = (iconIndex / 21) * 48;
- int y = (iconIndex % 21) * 48;
- m_iconRect = cpp3ds::IntRect(x, y, 48, 48);
- }
- bool AppItem::isCached() const
- {
- return pathExists(getJsonFilename().c_str());
- }
- const std::string AppItem::getJsonFilename() const
- {
- std::string filename = FREESHOP_DIR "/tmp/" + getTitleIdStr() + "/data.json";
- return filename;
- }
- void AppItem::setInstalled(bool installed)
- {
- m_installed = installed;
- }
- bool AppItem::isInstalled() const
- {
- return m_installed;
- }
- cpp3ds::Uint64 AppItem::getFilesize() const
- {
- return m_filesize;
- }
- const std::string &AppItem::getNormalizedTitle() const
- {
- return m_normalizedTitle;
- }
- const std::string &AppItem::getTitleIdStr() const
- {
- return m_titleIdStr;
- }
- cpp3ds::Uint64 AppItem::getTitleId() const
- {
- return m_titleId;
- }
- const std::string &AppItem::getContentId() const
- {
- return m_contentId;
- }
- const std::string &AppItem::getUriRegion() const
- {
- return m_uriRegion;
- }
- int AppItem::getRegions() const
- {
- return m_regions;
- }
- int AppItem::getLanguages() const
- {
- return m_languages;
- }
- const std::vector<char> &AppItem::getSeed() const
- {
- return m_seed;
- }
- const std::vector<int> &AppItem::getGenres() const
- {
- return m_genres;
- }
- const std::vector<int> &AppItem::getFeatures() const
- {
- return m_features;
- }
- int AppItem::getPlatform() const
- {
- return m_platform;
- }
- int AppItem::getPublisher() const
- {
- return m_publisher;
- }
- int AppItem::getIconIndex() const
- {
- return m_iconIndex;
- }
- const cpp3ds::Texture *AppItem::getIcon(cpp3ds::IntRect &outRect) const
- {
- outRect = m_iconRect;
- return m_systemIconTexture ? : m_iconTexture;
- }
- void AppItem::queueForInstall()
- {
- DownloadQueue::getInstance().addDownload(shared_from_this());
- for (auto &id : m_updates)
- DownloadQueue::getInstance().addDownload(shared_from_this(), id);
- for (auto &id : m_dlc)
- DownloadQueue::getInstance().addDownload(shared_from_this(), id);
- }
- void AppItem::queueForSleepInstall(bool installRelated, bool isDemo)
- {
- #ifndef EMULATION
- if (g_isLatestFirmwareVersion) {
- if (m_isSleepBusy) {
- Notification::spawn(_("Already queued for sleep installation: \n%s", m_title.toAnsiString().c_str()));
- } else {
- m_sleepInstallRelated = installRelated;
- m_sleepInstallDemo = isDemo;
- m_threadSleepInstall.launch();
- }
- } else {
- Notification::spawn(_("A system update is required to download in sleep mode."));
- }
- #endif
- }
- void AppItem::queueForSleepInstallThread()
- {
- #ifndef EMULATION
- m_isSleepBusy = true;
- Notification::spawn(_("Preparing for sleep installation: \n%s", m_title.toAnsiString().c_str()));
- if (!m_sleepInstallDemo)
- DownloadQueue::getInstance().addSleepDownload(shared_from_this(), m_titleId, m_title);
- else
- DownloadQueue::getInstance().addSleepDownload(shared_from_this(), getDemos()[0], m_title);
- if (m_sleepInstallRelated) {
- for (auto &id : m_updates)
- DownloadQueue::getInstance().addSleepDownload(shared_from_this(), id, m_title);
- for (auto &id : m_dlc)
- DownloadQueue::getInstance().addSleepDownload(shared_from_this(), id, m_title);
- }
- m_isSleepBusy = false;
- #endif
- }
- void AppItem::removeSleepInstall(bool removeRelated)
- {
- #ifndef EMULATION
- m_removeSleepRelated = removeRelated;
- askUserToRemove(m_titleId, m_title);
- if (m_removeSleepRelated) {
- for (auto &id : m_updates)
- askUserToRemove(id, _("[Update] %s", m_title.toAnsiString().c_str()));
- for (auto &id : m_dlc)
- askUserToRemove(id, _("[DLC] %s", m_title.toAnsiString().c_str()));
- }
- #endif
- }
- void AppItem::askUserToRemove(cpp3ds::Uint64 titleID, cpp3ds::String titleName)
- {
- #ifndef EMULATION
- g_browseState->requestStackPush(States::Dialog, false, [=](void *data) mutable {
- auto event = reinterpret_cast<DialogState::Event*>(data);
- if (event->type == DialogState::GetText)
- {
- auto str = reinterpret_cast<cpp3ds::String*>(event->data);
- *str = _("%s\n\nAre you sure you want to cancel this installation and lose all progress?", titleName.toAnsiString().c_str());
- return true;
- }
- else if (event->type == DialogState::GetTitle) {
- auto str = reinterpret_cast<cpp3ds::String *>(event->data);
- *str = _("Cancel a download");
- return true;
- }
- else if (event->type == DialogState::Response)
- {
- bool *accepted = reinterpret_cast<bool*>(event->data);
- if (*accepted)
- {
- NIMS_UnregisterTask(titleID);
- Notification::spawn(_("Sleep download canceled: \n%s", titleName.toAnsiString().c_str()));
- }
- return true;
- }
- return false;
- });
- #endif
- }
- const std::vector<cpp3ds::Uint64> &AppItem::getUpdates() const
- {
- return m_updates;
- }
- const std::vector<cpp3ds::Uint64> &AppItem::getDemos() const
- {
- return m_demos;
- }
- const std::vector<cpp3ds::Uint64> &AppItem::getDLC() const
- {
- return m_dlc;
- }
- float AppItem::getVoteScore() const
- {
- return m_voteScore;
- }
- int AppItem::getVoteCount() const
- {
- return m_voteCount;
- }
- time_t AppItem::getReleaseDate() const
- {
- return m_releaseDate;
- }
- const std::string &AppItem::getProductCode() const
- {
- return m_productCode;
- }
- bool AppItem::isSleepBusy() const
- {
- return m_isSleepBusy;
- }
- void AppItem::setSleepBusy(bool newState)
- {
- m_isSleepBusy = newState;
- }
- } // namespace FreeShop
|