InstalledList.cpp 9.4 KB

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