InstalledList.cpp 10 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361
  1. #include <cmath>
  2. #include <TweenEngine/Tween.h>
  3. #include <cpp3ds/System/Lock.hpp>
  4. #include "InstalledList.hpp"
  5. #include "AppList.hpp"
  6. #include "TitleKeys.hpp"
  7. namespace FreeShop {
  8. InstalledList::InstalledList()
  9. : m_scrollPos(0.f)
  10. , m_size(320.f, 0.f)
  11. , m_expandedItem(nullptr)
  12. , m_isUpdatingList(false)
  13. , m_gameCount(0)
  14. {
  15. // Make install options initially transparent for fade in
  16. TweenEngine::Tween::set(m_options, InstalledOptions::ALPHA)
  17. .target(0.f).start(m_tweenManager);
  18. }
  19. InstalledList &InstalledList::getInstance()
  20. {
  21. static InstalledList installedList;
  22. return installedList;
  23. }
  24. void InstalledList::refresh()
  25. {
  26. cpp3ds::Lock lock(m_mutexRefresh);
  27. cpp3ds::Uint64 relatedTitleId;
  28. std::vector<cpp3ds::Uint64> installedTitleIds;
  29. m_isUpdatingList = true;
  30. m_installedTitleIds.clear();
  31. m_installedItems.clear();
  32. m_expandedItem = nullptr;
  33. TweenEngine::Tween::set(m_options, InstalledOptions::ALPHA).target(0.f).start(m_tweenManager);
  34. #ifdef EMULATION
  35. // some hardcoded title IDs for testing
  36. installedTitleIds.emplace_back(0x00040000000edf00); // [US] Super Smash Bros.
  37. installedTitleIds.emplace_back(0x0004000e000edf00); // [US] Super Smash Bros. [UPDATE]
  38. installedTitleIds.emplace_back(0x0004008c000edf00); // [US] Super Smash Bros. [DLC]
  39. installedTitleIds.emplace_back(0x00040002000edf01); // [US] Super Smash Bros. [DEMO]
  40. installedTitleIds.emplace_back(0x0004001000021800); // [US] StreetPass Mii Plaza
  41. installedTitleIds.emplace_back(0x0004000000030800); // [US] Mario Kart 7
  42. installedTitleIds.emplace_back(0x0004000000041700); // [US] Kirby's Dream Land
  43. installedTitleIds.emplace_back(0x0004008000008f00); // [US] Home Menu
  44. installedTitleIds.emplace_back(0x0004000000162000); // [EU] Project X Zone 2
  45. installedTitleIds.emplace_back(0x000400000017a400); // [EU] Fire Emblem Fates
  46. installedTitleIds.emplace_back(0x000480044b513945); // [US] The Legend of Zelda™: Four Swords Anniversary Edition
  47. #else
  48. u32 titleCount;
  49. AM_GetTitleCount(MEDIATYPE_SD, &titleCount);
  50. installedTitleIds.resize(titleCount);
  51. AM_GetTitleList(nullptr, MEDIATYPE_SD, titleCount, &installedTitleIds[0]);
  52. AM_GetTitleCount(MEDIATYPE_NAND, &titleCount);
  53. installedTitleIds.resize(titleCount + installedTitleIds.size());
  54. AM_GetTitleList(nullptr, MEDIATYPE_NAND, titleCount, &installedTitleIds[installedTitleIds.size() - titleCount]);
  55. FS_CardType type;
  56. bool cardInserted;
  57. m_cardInserted = (R_SUCCEEDED(FSUSER_CardSlotIsInserted(&cardInserted)) && cardInserted && R_SUCCEEDED(FSUSER_GetCardType(&type)) && type == CARD_CTR);
  58. if (m_cardInserted)
  59. {
  60. // Retry a bunch of times. When the card is newly inserted,
  61. // it sometimes takes a short while before title can be read.
  62. int retryCount = 100;
  63. u64 cardTitleId;
  64. while (retryCount-- > 0)
  65. if (R_SUCCEEDED(AM_GetTitleList(nullptr, MEDIATYPE_GAME_CARD, 1, &cardTitleId)))
  66. {
  67. try
  68. {
  69. std::unique_ptr<InstalledItem> item(new InstalledItem(cardTitleId));
  70. m_installedItems.emplace_back(std::move(item));
  71. }
  72. catch (int e)
  73. {
  74. //
  75. }
  76. break;
  77. }
  78. else
  79. cpp3ds::sleep(cpp3ds::milliseconds(5));
  80. }
  81. #endif
  82. for (auto& titleId : installedTitleIds)
  83. {
  84. size_t titleType = titleId >> 32;
  85. if (titleType == TitleKeys::Game || titleType == TitleKeys::Update ||
  86. titleType == TitleKeys::DLC || titleType == TitleKeys::Demo || titleType == TitleKeys::DSiWare ||
  87. titleType == TitleKeys::SystemApplet || titleType == TitleKeys::SystemApplication)
  88. m_installedTitleIds.push_back(titleId);
  89. }
  90. // Add all primary game titles first
  91. for (auto& titleId : installedTitleIds)
  92. {
  93. TitleKeys::TitleType titleType = static_cast<TitleKeys::TitleType>(titleId >> 32);
  94. if (titleType == TitleKeys::Game || titleType == TitleKeys::DSiWare || titleType == TitleKeys::SystemApplication || titleType == TitleKeys::SystemApplet)
  95. try
  96. {
  97. std::unique_ptr<InstalledItem> item(new InstalledItem(titleId));
  98. m_installedItems.emplace_back(std::move(item));
  99. }
  100. catch (int e)
  101. {
  102. //std::cout << "Error while adding: " << titleId << std::endl;
  103. }
  104. }
  105. // Add updates that have not yet been installed for which we have a titlekey
  106. for (auto& titleId : TitleKeys::getIds())
  107. {
  108. size_t titleType = titleId >> 32;
  109. if (titleType == TitleKeys::Update || titleType == TitleKeys::DLC)
  110. {
  111. size_t titleLower = (titleId & 0xFFFFFFFF) >> 8;
  112. for (auto& installedItem : m_installedItems)
  113. {
  114. if (titleLower == (installedItem->getTitleId() & 0xFFFFFFFF) >> 8)
  115. {
  116. if (titleType == TitleKeys::Update)
  117. installedItem->setUpdateStatus(titleId, false);
  118. else
  119. installedItem->setDLCStatus(titleId, false);
  120. }
  121. }
  122. }
  123. }
  124. // Add all installed updates/DLC to the above titles added.
  125. // If not found, attempt to fetch parent title info from system.
  126. for (auto& titleId : installedTitleIds)
  127. {
  128. TitleKeys::TitleType titleType = static_cast<TitleKeys::TitleType>(titleId >> 32);
  129. if (titleType == TitleKeys::Update || titleType == TitleKeys::DLC)
  130. {
  131. cpp3ds::Uint32 titleLower = (titleId & 0xFFFFFFFF) >> 8;
  132. for (auto& installedItem : m_installedItems)
  133. {
  134. if (titleLower == (installedItem->getTitleId() & 0xFFFFFFFF) >> 8)
  135. {
  136. if (titleType == TitleKeys::Update)
  137. installedItem->setUpdateStatus(titleId, true);
  138. else
  139. installedItem->setDLCStatus(titleId, true);
  140. break;
  141. }
  142. }
  143. }
  144. }
  145. // Remove all system titles that have no Update or DLC
  146. for (auto it = m_installedItems.begin(); it != m_installedItems.end();)
  147. {
  148. size_t titleType = (*it)->getTitleId() >> 32;
  149. if ((titleType != TitleKeys::SystemApplication && titleType != TitleKeys::SystemApplet) ||
  150. ((*it)->hasDLC() || (*it)->hasDLC()))
  151. it++;
  152. else
  153. it = m_installedItems.erase(it);
  154. }
  155. // For too long title name, shorten them
  156. for (auto& installedItem : m_installedItems)
  157. {
  158. installedItem->updateGameTitle();
  159. }
  160. // Sort alphabetically
  161. std::sort(m_installedItems.begin(), m_installedItems.end(), [=](const std::unique_ptr<InstalledItem>& a, const std::unique_ptr<InstalledItem>& b)
  162. {
  163. return a->getAppItem()->getNormalizedTitle() < b->getAppItem()->getNormalizedTitle();
  164. });
  165. repositionItems();
  166. m_isUpdatingList = false;
  167. }
  168. void InstalledList::draw(cpp3ds::RenderTarget &target, cpp3ds::RenderStates states) const
  169. {
  170. states.transform *= getTransform();
  171. states.scissor = cpp3ds::UintRect(0, 30, 320, 210);
  172. for (auto& item : m_installedItems)
  173. {
  174. float posY = item->getPosition().y;
  175. if (posY > -10.f && posY < 240.f)
  176. target.draw(*item, states);
  177. }
  178. if (m_expandedItem)
  179. target.draw(m_options, states);
  180. }
  181. void InstalledList::update(float delta)
  182. {
  183. #ifdef _3DS
  184. FS_CardType type;
  185. bool cardInserted;
  186. if (m_cardInserted != (R_SUCCEEDED(FSUSER_CardSlotIsInserted(&cardInserted)) && cardInserted && R_SUCCEEDED(FSUSER_GetCardType(&type)) && type == CARD_CTR))
  187. refresh();
  188. #endif
  189. m_tweenManager.update(delta);
  190. }
  191. bool InstalledList::processEvent(const cpp3ds::Event &event)
  192. {
  193. if (m_tweenManager.getRunningTweensCount() > 0)
  194. return false;
  195. if (event.type == cpp3ds::Event::TouchEnded)
  196. {
  197. if (event.touch.y < 30)
  198. return false;
  199. for (auto &item : m_installedItems)
  200. {
  201. float posY = getPosition().y + item->getPosition().y;
  202. if (event.touch.y > posY && event.touch.y < posY + item->getHeight())
  203. {
  204. if (item.get() == m_expandedItem)
  205. {
  206. if (event.touch.y < posY + 16.f)
  207. expandItem(nullptr);
  208. else
  209. m_options.processTouchEvent(event);
  210. }
  211. else
  212. expandItem(item.get());
  213. break;
  214. }
  215. }
  216. }
  217. return false;
  218. }
  219. void InstalledList::setScroll(float position)
  220. {
  221. m_scrollPos = std::round(position);
  222. repositionItems();
  223. }
  224. float InstalledList::getScroll()
  225. {
  226. return m_scrollPos;
  227. }
  228. void InstalledList::repositionItems()
  229. {
  230. float posY = 30.f + m_scrollPos;
  231. for (auto& item : m_installedItems)
  232. {
  233. if (item.get() == m_expandedItem)
  234. m_options.setPosition(0.f, posY + 20.f);
  235. item->setPosition(0.f, posY);
  236. posY += item->getHeight();
  237. }
  238. m_size.y = posY - 6.f - m_scrollPos;
  239. if (m_expandedItem)
  240. m_size.y -= 24.f;
  241. updateScrollSize();
  242. }
  243. const cpp3ds::Vector2f &InstalledList::getScrollSize()
  244. {
  245. return m_size;
  246. }
  247. void InstalledList::expandItem(InstalledItem *item)
  248. {
  249. if (item == m_expandedItem)
  250. return;
  251. const float optionsFadeDelay = 0.15f;
  252. const float expandDuration = 0.2f;
  253. // Expand animation
  254. if (m_expandedItem)
  255. {
  256. TweenEngine::Tween::to(*m_expandedItem, InstalledItem::HEIGHT, expandDuration)
  257. .target(16.f)
  258. .setCallback(TweenEngine::TweenCallback::COMPLETE, [=](TweenEngine::BaseTween* source) {
  259. m_expandedItem = item;
  260. repositionItems();
  261. })
  262. .delay(optionsFadeDelay)
  263. .start(m_tweenManager);
  264. TweenEngine::Tween::to(m_options, InstalledOptions::ALPHA, optionsFadeDelay + 0.05f)
  265. .target(0.f)
  266. .start(m_tweenManager);
  267. }
  268. if (item)
  269. {
  270. TweenEngine::Tween::to(*item, InstalledItem::HEIGHT, expandDuration)
  271. .target(40.f)
  272. .setCallback(TweenEngine::TweenCallback::COMPLETE, [=](TweenEngine::BaseTween* source) {
  273. m_expandedItem = item;
  274. m_options.setInstalledItem(item);
  275. repositionItems();
  276. })
  277. .delay(m_expandedItem ? optionsFadeDelay : 0.f)
  278. .start(m_tweenManager);
  279. TweenEngine::Tween::to(m_options, InstalledOptions::ALPHA, expandDuration)
  280. .target(255.f)
  281. .delay(m_expandedItem ? expandDuration + optionsFadeDelay : optionsFadeDelay)
  282. .start(m_tweenManager);
  283. }
  284. // Move animation for items in between expanded items
  285. bool foundItem = false;
  286. bool foundExpanded = false;
  287. for (auto &itemToMove : m_installedItems)
  288. {
  289. if (foundItem && !foundExpanded)
  290. {
  291. TweenEngine::Tween::to(*itemToMove, InstalledItem::POSITION_Y, expandDuration)
  292. .targetRelative(24.f)
  293. .delay(m_expandedItem ? optionsFadeDelay : 0.f)
  294. .start(m_tweenManager);
  295. }
  296. else if (foundExpanded && !foundItem)
  297. {
  298. TweenEngine::Tween::to(*itemToMove, InstalledItem::POSITION_Y, expandDuration)
  299. .targetRelative(-24.f)
  300. .delay(m_expandedItem ? optionsFadeDelay : 0.f)
  301. .start(m_tweenManager);
  302. }
  303. if (itemToMove.get() == m_expandedItem)
  304. foundExpanded = true;
  305. else if (itemToMove.get() == item)
  306. {
  307. foundItem = true;
  308. }
  309. if (foundExpanded && foundItem)
  310. break;
  311. }
  312. }
  313. bool InstalledList::isInstalled(cpp3ds::Uint64 titleId)
  314. {
  315. auto &v = getInstance().m_installedTitleIds;
  316. return std::find(v.begin(), v.end(), titleId) != v.end();
  317. }
  318. int InstalledList::getGameCount()
  319. {
  320. if (!m_isUpdatingList)
  321. m_gameCount = m_installedItems.size();
  322. return m_gameCount;
  323. }
  324. } // namespace FreeShop