AppItem.cpp 11 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468
  1. #include <cpp3ds/System/Err.hpp>
  2. #include <iostream>
  3. #include <cpp3ds/System/I18n.hpp>
  4. #include "AssetManager.hpp"
  5. #include "AppItem.hpp"
  6. #include "Util.hpp"
  7. #include "TitleKeys.hpp"
  8. #include "Theme.hpp"
  9. #include "DownloadQueue.hpp"
  10. #include <sstream>
  11. #include "Notification.hpp"
  12. #include "Installer.hpp"
  13. #include "States/BrowseState.hpp"
  14. #include "States/DialogState.hpp"
  15. #ifndef EMULATION
  16. #include <3ds.h>
  17. #endif
  18. namespace {
  19. std::vector<std::unique_ptr<cpp3ds::Texture>> textures;
  20. #ifdef _3DS
  21. typedef struct {
  22. u16 shortDescription[0x40];
  23. u16 longDescription[0x80];
  24. u16 publisher[0x40];
  25. } SMDH_title;
  26. typedef struct {
  27. char magic[0x04];
  28. u16 version;
  29. u16 reserved1;
  30. SMDH_title titles[0x10];
  31. u8 ratings[0x10];
  32. u32 region;
  33. u32 matchMakerId;
  34. u64 matchMakerBitId;
  35. u32 flags;
  36. u16 eulaVersion;
  37. u16 reserved;
  38. u32 optimalBannerFrame;
  39. u32 streetpassId;
  40. u64 reserved2;
  41. u8 smallIcon[0x480];
  42. u8 largeIcon[0x1200];
  43. } SMDH;
  44. #endif
  45. }
  46. namespace FreeShop
  47. {
  48. AppItem::AppItem()
  49. : m_installed(false)
  50. , m_regions(0)
  51. , m_languages(0)
  52. , m_iconIndex(-1)
  53. , m_iconRect(cpp3ds::IntRect(0, 0, 48, 48))
  54. , m_systemIconTexture(nullptr)
  55. , m_threadSleepInstall(&AppItem::queueForSleepInstallThread, this)
  56. {
  57. if (Theme::isMissingIconThemed) {
  58. cpp3ds::Texture &texture = AssetManager<cpp3ds::Texture>::get(FREESHOP_DIR "/theme/images/missing-icon.png");
  59. texture.setSmooth(true);
  60. m_iconTexture = &texture;
  61. } else {
  62. cpp3ds::Texture &texture = AssetManager<cpp3ds::Texture>::get("images/missing-icon.png");
  63. texture.setSmooth(true);
  64. m_iconTexture = &texture;
  65. }
  66. }
  67. AppItem::~AppItem()
  68. {
  69. m_threadSleepInstall.wait();
  70. delete m_systemIconTexture;
  71. }
  72. const cpp3ds::String &AppItem::getTitle() const
  73. {
  74. return m_title;
  75. }
  76. void AppItem::loadFromJSON(const char* titleId, const rapidjson::Value &json)
  77. {
  78. m_genres.clear();
  79. m_features.clear();
  80. m_seed.clear();
  81. m_languages = 0;
  82. m_titleIdStr = titleId;
  83. m_titleId = strtoull(titleId, 0, 16);
  84. m_updates = TitleKeys::getRelated(m_titleId, TitleKeys::Update);
  85. m_demos = TitleKeys::getRelated(m_titleId, TitleKeys::Demo);
  86. m_dlc = TitleKeys::getRelated(m_titleId, TitleKeys::DLC);
  87. m_isSleepBusy = false;
  88. const char *title = json[0].GetString();
  89. m_title = cpp3ds::String::fromUtf8(title, title + json[0].GetStringLength());
  90. m_normalizedTitle = json[1].GetString();
  91. m_contentId = json[2].GetString();
  92. m_regions = json[3].GetInt();
  93. m_uriRegion = json[4].GetString();
  94. m_filesize = json[5].GetUint64();
  95. int iconIndex = json[6].GetInt();
  96. if (iconIndex >= 0)
  97. setIconIndex(iconIndex);
  98. // Crypto seed
  99. std::string seed = json[7].GetString();
  100. if (!seed.empty())
  101. {
  102. m_seed.clear();
  103. for (int i = 0; i < 16; ++i)
  104. {
  105. std::string byteStr = seed.substr(i*2, 2);
  106. char byte = strtol(byteStr.c_str(), NULL, 16);
  107. m_seed.push_back(byte);
  108. }
  109. }
  110. // Genres
  111. auto genres = json[8].GetArray();
  112. for (int i = 0; i < genres.Size(); ++i)
  113. m_genres.push_back(genres[i].GetInt());
  114. // Languages
  115. auto languages = json[9].GetArray();
  116. for (int i = 0; i < languages.Size(); ++i)
  117. {
  118. std::string lang(languages[i].GetString());
  119. if ("ja" == lang) m_languages |= Japanese;
  120. else if ("en" == lang) m_languages |= English;
  121. else if ("es" == lang) m_languages |= Spanish;
  122. else if ("fr" == lang) m_languages |= French;
  123. else if ("de" == lang) m_languages |= German;
  124. else if ("it" == lang) m_languages |= Italian;
  125. else if ("nl" == lang) m_languages |= Dutch;
  126. else if ("pt" == lang) m_languages |= Portuguese;
  127. else if ("ru" == lang) m_languages |= Russian;
  128. }
  129. // Features
  130. auto features = json[10].GetArray();
  131. for (int i = 0; i < features.Size(); ++i)
  132. m_features.push_back(features[i].GetInt());
  133. if (!json[11].IsNull())
  134. m_voteScore = json[11].GetFloat();
  135. m_voteCount = json[12].GetInt();
  136. m_releaseDate = json[13].GetInt();
  137. m_productCode = json[14].GetString();
  138. m_platform = json[15].GetInt();
  139. m_publisher = json[16].GetInt();
  140. }
  141. void AppItem::loadFromSystemTitleId(cpp3ds::Uint64 titleId)
  142. {
  143. m_titleId = titleId;
  144. m_titleIdStr = _("%016llX", titleId);
  145. m_updates = TitleKeys::getRelated(m_titleId, TitleKeys::Update);
  146. m_demos = TitleKeys::getRelated(m_titleId, TitleKeys::Demo);
  147. m_dlc = TitleKeys::getRelated(m_titleId, TitleKeys::DLC);
  148. #ifdef _3DS
  149. Result res = 0;
  150. Handle fileHandle;
  151. char productCode[12] = {'\0'};
  152. AM_TitleEntry titleInfo;
  153. AM_GetTitleInfo(MEDIATYPE_NAND, 1, &titleId, &titleInfo);
  154. AM_GetTitleProductCode(MEDIATYPE_NAND, titleId, productCode);
  155. m_title = productCode;
  156. if (m_title.toAnsiString().compare(0, 9, "CTR-N-HMM") == 0)
  157. m_title = _("Home Menu Themes");
  158. static const u32 filePath[5] = {0x0, 0x0, 0x2, 0x6E6F6369, 0x0};
  159. u32 archivePath[4] = {(u32) (titleId & 0xFFFFFFFF), (u32) ((titleId >> 32) & 0xFFFFFFFF), MEDIATYPE_NAND, 0x0};
  160. FS_Path binArchPath = {PATH_BINARY, 0x10, archivePath};
  161. FS_Path binFilePath = {PATH_BINARY, 0x14, filePath};
  162. if(R_SUCCEEDED(res = FSUSER_OpenFileDirectly(&fileHandle, ARCHIVE_SAVEDATA_AND_CONTENT, binArchPath, binFilePath, FS_OPEN_READ, 0)))
  163. {
  164. auto smdh = new SMDH();
  165. if (smdh)
  166. {
  167. FSFILE_Read(fileHandle, nullptr, 0, smdh, sizeof(SMDH));
  168. if (smdh->magic[0] == 'S' && smdh->magic[1] == 'M' && smdh->magic[2] == 'D' && smdh->magic[3] == 'H')
  169. {
  170. u8 systemLanguage = CFG_LANGUAGE_EN;
  171. CFGU_GetSystemLanguage(&systemLanguage);
  172. u16 *title = smdh->titles[systemLanguage].shortDescription;
  173. m_title = cpp3ds::String::fromUtf16(title, title + 0x40);
  174. delete m_systemIconTexture;
  175. m_systemIconTexture = new cpp3ds::Texture();
  176. m_systemIconTexture->loadFromPreprocessedMemory(smdh->largeIcon, sizeof(smdh->largeIcon), 48, 48, GPU_RGB565);
  177. }
  178. delete smdh;
  179. }
  180. FSFILE_Close(fileHandle);
  181. }
  182. #else
  183. m_title = m_titleIdStr;
  184. #endif
  185. m_normalizedTitle = m_title;
  186. for (auto &c : m_normalizedTitle)
  187. c = std::tolower(c);
  188. }
  189. void AppItem::setIconIndex(size_t iconIndex)
  190. {
  191. size_t textureIndex = iconIndex / 441;
  192. iconIndex %= 441;
  193. // Load texture from file if not done already
  194. if (textures.size() < textureIndex + 1)
  195. {
  196. for (int i = textures.size(); i <= textureIndex; ++i)
  197. {
  198. std::unique_ptr<cpp3ds::Texture> texture(new cpp3ds::Texture());
  199. #ifdef EMULATION
  200. texture->loadFromFile(_(FREESHOP_DIR "/cache/images/icons%d.jpg", i));
  201. #else
  202. texture->loadFromPreprocessedFile(_(FREESHOP_DIR "/cache/images/icons%d.bin", i), 1024, 1024, GPU_ETC1);
  203. #endif
  204. texture->setSmooth(true);
  205. textures.push_back(std::move(texture));
  206. }
  207. }
  208. m_iconIndex = iconIndex;
  209. m_iconTexture = textures[textureIndex].get();
  210. int x = (iconIndex / 21) * 48;
  211. int y = (iconIndex % 21) * 48;
  212. m_iconRect = cpp3ds::IntRect(x, y, 48, 48);
  213. }
  214. bool AppItem::isCached() const
  215. {
  216. return pathExists(getJsonFilename().c_str());
  217. }
  218. const std::string AppItem::getJsonFilename() const
  219. {
  220. std::string filename = FREESHOP_DIR "/tmp/" + getTitleIdStr() + "/data.json";
  221. return filename;
  222. }
  223. void AppItem::setInstalled(bool installed)
  224. {
  225. m_installed = installed;
  226. }
  227. bool AppItem::isInstalled() const
  228. {
  229. return m_installed;
  230. }
  231. cpp3ds::Uint64 AppItem::getFilesize() const
  232. {
  233. return m_filesize;
  234. }
  235. const std::string &AppItem::getNormalizedTitle() const
  236. {
  237. return m_normalizedTitle;
  238. }
  239. const std::string &AppItem::getTitleIdStr() const
  240. {
  241. return m_titleIdStr;
  242. }
  243. cpp3ds::Uint64 AppItem::getTitleId() const
  244. {
  245. return m_titleId;
  246. }
  247. const std::string &AppItem::getContentId() const
  248. {
  249. return m_contentId;
  250. }
  251. const std::string &AppItem::getUriRegion() const
  252. {
  253. return m_uriRegion;
  254. }
  255. int AppItem::getRegions() const
  256. {
  257. return m_regions;
  258. }
  259. int AppItem::getLanguages() const
  260. {
  261. return m_languages;
  262. }
  263. const std::vector<char> &AppItem::getSeed() const
  264. {
  265. return m_seed;
  266. }
  267. const std::vector<int> &AppItem::getGenres() const
  268. {
  269. return m_genres;
  270. }
  271. int AppItem::getPlatform() const
  272. {
  273. return m_platform;
  274. }
  275. int AppItem::getPublisher() const
  276. {
  277. return m_publisher;
  278. }
  279. int AppItem::getIconIndex() const
  280. {
  281. return m_iconIndex;
  282. }
  283. const cpp3ds::Texture *AppItem::getIcon(cpp3ds::IntRect &outRect) const
  284. {
  285. outRect = m_iconRect;
  286. return m_systemIconTexture ? : m_iconTexture;
  287. }
  288. void AppItem::queueForInstall()
  289. {
  290. DownloadQueue::getInstance().addDownload(shared_from_this());
  291. for (auto &id : m_updates)
  292. DownloadQueue::getInstance().addDownload(shared_from_this(), id);
  293. for (auto &id : m_dlc)
  294. DownloadQueue::getInstance().addDownload(shared_from_this(), id);
  295. }
  296. void AppItem::queueForSleepInstall(bool installRelated)
  297. {
  298. #ifndef EMULATION
  299. if (g_isLatestFirmwareVersion) {
  300. if (m_isSleepBusy) {
  301. Notification::spawn(_("Already queued for sleep installation: \n%s", m_title.toAnsiString().c_str()));
  302. } else {
  303. m_sleepInstallRelated = installRelated;
  304. m_threadSleepInstall.launch();
  305. }
  306. } else {
  307. Notification::spawn(_("A system update is required to download in sleep mode."));
  308. }
  309. #endif
  310. }
  311. void AppItem::queueForSleepInstallThread()
  312. {
  313. #ifndef EMULATION
  314. m_isSleepBusy = true;
  315. Notification::spawn(_("Preparing for sleep installation: \n%s", m_title.toAnsiString().c_str()));
  316. DownloadQueue::getInstance().addSleepDownload(shared_from_this(), m_titleId, m_title);
  317. if (m_sleepInstallRelated) {
  318. for (auto &id : m_updates)
  319. DownloadQueue::getInstance().addSleepDownload(shared_from_this(), id, m_title);
  320. for (auto &id : m_dlc)
  321. DownloadQueue::getInstance().addSleepDownload(shared_from_this(), id, m_title);
  322. }
  323. m_isSleepBusy = false;
  324. #endif
  325. }
  326. void AppItem::removeSleepInstall(bool removeRelated)
  327. {
  328. #ifndef EMULATION
  329. m_removeSleepRelated = removeRelated;
  330. askUserToRemove(m_titleId, m_title);
  331. if (m_removeSleepRelated) {
  332. for (auto &id : m_updates)
  333. askUserToRemove(id, _("[Update] %s", m_title.toAnsiString().c_str()));
  334. for (auto &id : m_dlc)
  335. askUserToRemove(id, _("[DLC] %s", m_title.toAnsiString().c_str()));
  336. }
  337. #endif
  338. }
  339. void AppItem::askUserToRemove(cpp3ds::Uint64 titleID, cpp3ds::String titleName)
  340. {
  341. #ifndef EMULATION
  342. g_browseState->requestStackPush(States::Dialog, false, [=](void *data) mutable {
  343. auto event = reinterpret_cast<DialogState::Event*>(data);
  344. if (event->type == DialogState::GetText)
  345. {
  346. auto str = reinterpret_cast<cpp3ds::String*>(event->data);
  347. *str = _("%s\n\nAre you sure you want to cancel\nthis installation and lose all progress?", titleName.toAnsiString().c_str());
  348. return true;
  349. }
  350. else if (event->type == DialogState::Response)
  351. {
  352. bool *accepted = reinterpret_cast<bool*>(event->data);
  353. if (*accepted)
  354. {
  355. NIMS_UnregisterTask(titleID);
  356. Notification::spawn(_("Sleep download canceled: \n%s", titleName.toAnsiString().c_str()));
  357. }
  358. return true;
  359. }
  360. return false;
  361. });
  362. #endif
  363. }
  364. const std::vector<cpp3ds::Uint64> &AppItem::getUpdates() const
  365. {
  366. return m_updates;
  367. }
  368. const std::vector<cpp3ds::Uint64> &AppItem::getDemos() const
  369. {
  370. return m_demos;
  371. }
  372. const std::vector<cpp3ds::Uint64> &AppItem::getDLC() const
  373. {
  374. return m_dlc;
  375. }
  376. float AppItem::getVoteScore() const
  377. {
  378. return m_voteScore;
  379. }
  380. int AppItem::getVoteCount() const
  381. {
  382. return m_voteCount;
  383. }
  384. time_t AppItem::getReleaseDate() const
  385. {
  386. return m_releaseDate;
  387. }
  388. const std::string &AppItem::getProductCode() const
  389. {
  390. return m_productCode;
  391. }
  392. bool AppItem::isSleepBusy() const
  393. {
  394. return m_isSleepBusy;
  395. }
  396. void AppItem::setSleepBusy(bool newState)
  397. {
  398. m_isSleepBusy = newState;
  399. }
  400. } // namespace FreeShop