InstalledList.cpp 9.9 KB

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