AppInfo.cpp 22 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682
  1. #include <cpp3ds/System/Err.hpp>
  2. #include <cpp3ds/Window/Window.hpp>
  3. #include <iostream>
  4. #include <cpp3ds/System/I18n.hpp>
  5. #include "AssetManager.hpp"
  6. #include "AppInfo.hpp"
  7. #include "Download.hpp"
  8. #include "Util.hpp"
  9. #include "DownloadQueue.hpp"
  10. #include "Notification.hpp"
  11. #include "InstalledList.hpp"
  12. #include "FreeShop.hpp"
  13. #include "States/DialogState.hpp"
  14. #include <sstream>
  15. #include <TweenEngine/Tween.h>
  16. #include <cpp3ds/System/FileInputStream.hpp>
  17. #include <cpp3ds/System/FileSystem.hpp>
  18. #include <sys/stat.h>
  19. #include <cmath>
  20. namespace FreeShop
  21. {
  22. AppInfo::AppInfo()
  23. : m_appItem(nullptr)
  24. , m_currentScreenshot(0)
  25. , m_descriptionVelocity(0.f)
  26. , m_isDemoInstalled(false)
  27. {
  28. m_textDownload.setFillColor(cpp3ds::Color::White);
  29. m_textDownload.setOutlineColor(cpp3ds::Color(0, 0, 0, 200));
  30. m_textDownload.setOutlineThickness(2.f);
  31. m_textDownload.setFont(AssetManager<cpp3ds::Font>::get("fonts/fontawesome.ttf"));
  32. m_textDownload.setString("\uf019");
  33. m_textDownload.setCharacterSize(30);
  34. m_textDownload.setPosition(67.f, 93.f);
  35. m_textDelete = m_textDownload;
  36. m_textDelete.setString("\uf1f8");
  37. m_textDelete.setPosition(70.f, 90.f);
  38. m_textExecute = m_textDownload;
  39. m_textExecute.setString("\uf01d");
  40. m_textExecute.setPosition(5.f, 90.f);
  41. m_arrowLeft.setFont(AssetManager<cpp3ds::Font>::get("fonts/fontawesome.ttf"));
  42. m_arrowLeft.setCharacterSize(24);
  43. m_arrowLeft.setFillColor(cpp3ds::Color(255,255,255,150));
  44. m_arrowLeft.setOutlineColor(cpp3ds::Color(0,0,0,100));
  45. m_arrowLeft.setOutlineThickness(1.f);
  46. m_arrowLeft.setPosition(4.f, 100.f);
  47. m_arrowLeft.setString("\uf053");
  48. m_arrowRight = m_arrowLeft;
  49. m_arrowRight.setPosition(298.f, 100.f);
  50. m_arrowRight.setString("\uf054");
  51. m_close = m_arrowLeft;
  52. m_close.setCharacterSize(30);
  53. m_close.setPosition(285.f, 4.f);
  54. m_close.setString("\uf00d");
  55. m_screenshotsBackground.setFillColor(cpp3ds::Color(190, 190, 190, 255));
  56. m_screenshotsBackground.setSize(cpp3ds::Vector2f(320.f, 74.f));
  57. m_screenshotsBackground.setPosition(0.f, 166.f);
  58. m_textScreenshotsEmpty.setCharacterSize(12);
  59. m_textScreenshotsEmpty.setFillColor(cpp3ds::Color(255, 255, 255, 220));
  60. m_textScreenshotsEmpty.useSystemFont();
  61. m_textScreenshotsEmpty.setString(_("No Screenshots"));
  62. cpp3ds::FloatRect textRect = m_textScreenshotsEmpty.getLocalBounds();
  63. m_textScreenshotsEmpty.setOrigin(textRect.left + textRect.width / 2.f, textRect.top + textRect.height / 2.f);
  64. m_textScreenshotsEmpty.setPosition(160.f, 202.f);
  65. m_textNothingSelected.setString(_("No game selected"));
  66. m_textNothingSelected.setCharacterSize(16);
  67. m_textNothingSelected.setFillColor(cpp3ds::Color(0, 0, 0, 120));
  68. m_textNothingSelected.useSystemFont();
  69. m_textNothingSelected.setPosition(160.f, 105.f);
  70. m_textNothingSelected.setOrigin(m_textNothingSelected.getLocalBounds().width / 2, m_textNothingSelected.getLocalBounds().height / 2);
  71. TweenEngine::Tween::to(m_textNothingSelected, m_textNothingSelected.SCALE_XY, 4.f)
  72. .target(0.9f, 0.9f).repeatYoyo(-1, 0.f).start(m_tweenManager);
  73. m_textTitle.setCharacterSize(15);
  74. m_textTitle.setFillColor(cpp3ds::Color::Black);
  75. m_textTitle.setStyle(cpp3ds::Text::Bold);
  76. m_textTitle.setPosition(207.f, 28.f);
  77. m_textTitle.useSystemFont();
  78. m_textDescription.setCharacterSize(12);
  79. m_textDescription.setFillColor(cpp3ds::Color(100, 100, 100, 255));
  80. m_textDescription.useSystemFont();
  81. m_textTitleId.setCharacterSize(10);
  82. m_textTitleId.setFillColor(cpp3ds::Color(80, 80, 80, 255));
  83. m_textTitleId.setPosition(2.f, 127.f);
  84. m_textDemo.setString(_("Demo"));
  85. m_textDemo.setFillColor(cpp3ds::Color(100, 100, 100));
  86. m_textDemo.setCharacterSize(10);
  87. m_textDemo.setPosition(25.f, 140.f);
  88. m_textDemo.useSystemFont();
  89. m_textIconDemo.setFont(AssetManager<cpp3ds::Font>::get("fonts/fontawesome.ttf"));
  90. m_textIconDemo.setFillColor(cpp3ds::Color(50, 100, 50));
  91. m_textIconDemo.setCharacterSize(18);
  92. m_textIconDemo.setPosition(5.f, 135.f);
  93. m_fadeTextRect.setTexture(&AssetManager<cpp3ds::Texture>::get("images/fade.png"));
  94. m_fadeTextRect.setSize(cpp3ds::Vector2f(250.f, 8.f));
  95. m_fadeTextRect.setOrigin(m_fadeTextRect.getSize());
  96. m_fadeTextRect.setRotation(180.f);
  97. m_fadeTextRect.setPosition(102.f, 46.f);
  98. m_icon.setPosition(2.f, 30.f);
  99. m_icon.setScale(2.f, 2.f);
  100. m_fadeRect.setPosition(0.f, 30.f);
  101. m_fadeRect.setSize(cpp3ds::Vector2f(320.f, 210.f));
  102. m_fadeRect.setFillColor(cpp3ds::Color::White);
  103. }
  104. AppInfo::~AppInfo()
  105. {
  106. }
  107. void AppInfo::drawTop(cpp3ds::Window &window)
  108. {
  109. if (m_currentScreenshot)
  110. window.draw(m_screenshotTopTop);
  111. }
  112. void AppInfo::draw(cpp3ds::RenderTarget &target, cpp3ds::RenderStates states) const
  113. {
  114. states.transform *= getTransform();
  115. if (m_appItem)
  116. {
  117. target.draw(m_screenshotsBackground, states);
  118. if (m_screenshotTops.empty())
  119. target.draw(m_textScreenshotsEmpty, states);
  120. target.draw(m_icon, states);
  121. target.draw(m_textTitle, states);
  122. states.scissor = cpp3ds::UintRect(0, 46, 320, 120);
  123. target.draw(m_textDescription, states);
  124. states.scissor = cpp3ds::UintRect();
  125. target.draw(m_fadeTextRect, states);
  126. if (m_appItem->isInstalled())
  127. {
  128. target.draw(m_textExecute, states);
  129. target.draw(m_textDelete, states);
  130. }
  131. else if (!DownloadQueue::getInstance().isDownloading(m_appItem))
  132. {
  133. target.draw(m_textDownload, states);
  134. }
  135. if (!m_appItem->getDemos().empty() && !DownloadQueue::getInstance().isDownloading(m_appItem->getDemos()[0]))
  136. {
  137. target.draw(m_textDemo, states);
  138. target.draw(m_textIconDemo, states);
  139. }
  140. for (auto& screenshot : m_screenshotTops)
  141. target.draw(*screenshot, states);
  142. for (auto& screenshot : m_screenshotBottoms)
  143. target.draw(*screenshot, states);
  144. target.draw(m_fadeRect, states);
  145. }
  146. else
  147. {
  148. target.draw(m_textNothingSelected, states);
  149. }
  150. if (m_currentScreenshot)
  151. {
  152. target.draw(m_screenshotTopBottom);
  153. target.draw(m_screenshotBottom);
  154. if (m_currentScreenshot > 1)
  155. target.draw(m_arrowLeft);
  156. if (m_currentScreenshot < m_screenshotTops.size())
  157. target.draw(m_arrowRight);
  158. target.draw(m_close);
  159. }
  160. }
  161. void AppInfo::setValues(int tweenType, float *newValues)
  162. {
  163. switch (tweenType) {
  164. case ALPHA: {
  165. cpp3ds::Color color = m_fadeRect.getFillColor();
  166. color.a = 255.f - std::max(std::min(newValues[0], 255.f), 0.f);
  167. m_fadeRect.setFillColor(color);
  168. break;
  169. }
  170. default:
  171. TweenTransformable::setValues(tweenType, newValues);
  172. }
  173. }
  174. int AppInfo::getValues(int tweenType, float *returnValues)
  175. {
  176. switch (tweenType) {
  177. case ALPHA:
  178. returnValues[0] = 255.f - m_fadeRect.getFillColor().a;
  179. return 1;
  180. default:
  181. return TweenTransformable::getValues(tweenType, returnValues);
  182. }
  183. }
  184. void AppInfo::loadApp(std::shared_ptr<AppItem> appItem)
  185. {
  186. if (m_appItem == appItem)
  187. return;
  188. m_appItem = appItem;
  189. if (appItem)
  190. {
  191. std::string countryCode = getCountryCode(appItem->getRegions());
  192. std::string jsonFilename = appItem->getJsonFilename();
  193. std::string urlTitleData = "https://samurai.ctr.shop.nintendo.net/samurai/ws/" + countryCode + "/title/" + appItem->getContentId() + "/?shop_id=1";
  194. std::string dir = cpp3ds::FileSystem::getFilePath(FREESHOP_DIR "/tmp/" + appItem->getTitleIdStr());
  195. if (!pathExists(dir.c_str(), false))
  196. mkdir(dir.c_str(), 0777);
  197. if (!appItem->isCached())
  198. {
  199. Download download(urlTitleData, jsonFilename);
  200. download.setField("Accept", "application/json");
  201. download.run();
  202. auto status = download.getLastResponse().getStatus();
  203. if (status != cpp3ds::Http::Response::Ok && status != cpp3ds::Http::Response::PartialContent)
  204. {
  205. urlTitleData = "https://samurai.ctr.shop.nintendo.net/samurai/ws/" + appItem->getUriRegion() + "/title/" + appItem->getContentId() + "/?shop_id=1";
  206. download.setUrl(urlTitleData);
  207. download.run();
  208. }
  209. }
  210. updateInfo();
  211. m_textTitle.setString("");
  212. m_textDescription.setString("");
  213. m_textTitleId.setString(appItem->getTitleIdStr());
  214. cpp3ds::IntRect textureRect;
  215. m_icon.setTexture(*appItem->getIcon(textureRect), true);
  216. m_icon.setTextureRect(textureRect);
  217. m_screenshotTops.clear();
  218. m_screenshotTopTextures.clear();
  219. m_screenshotBottoms.clear();
  220. m_screenshotBottomTextures.clear();
  221. rapidjson::Document doc;
  222. std::string json;
  223. cpp3ds::FileInputStream file;
  224. if (file.open(jsonFilename))
  225. {
  226. json.resize(file.getSize());
  227. file.read(&json[0], json.size());
  228. doc.Parse(json.c_str());
  229. if (!doc.HasParseError())
  230. {
  231. if (doc["title"].HasMember("screenshots"))
  232. setScreenshots(doc["title"]["screenshots"]["screenshot"]);
  233. if (doc["title"].HasMember("description"))
  234. setDescription(doc["title"]["description"]);
  235. m_textDescription.setPosition(102.f, 49.f);
  236. m_textTitle.setString(appItem->getTitle());
  237. m_textTitle.setOrigin(std::min(m_textTitle.getLocalBounds().width, 205.f) / 2.f, 0.f);
  238. m_textDownload.setFillColor(cpp3ds::Color::White);
  239. }
  240. }
  241. }
  242. }
  243. const std::shared_ptr<AppItem> AppInfo::getAppItem() const
  244. {
  245. return m_appItem;
  246. }
  247. #define SCREENSHOT_BUTTON_FADEIN(object) \
  248. object.setFillColor(cpp3ds::Color(255,255,255,190)); \
  249. object.setOutlineColor(cpp3ds::Color(0,0,0,100)); \
  250. TweenEngine::Tween::from(object, util3ds::TweenText::FILL_COLOR_ALPHA, 4.f) \
  251. .target(0).start(m_tweenManager); \
  252. TweenEngine::Tween::from(object, util3ds::TweenText::OUTLINE_COLOR_ALPHA, 4.f) \
  253. .target(0).start(m_tweenManager);
  254. #define SCREENSHOT_BUTTON_FADEOUT(object) \
  255. TweenEngine::Tween::to(object, util3ds::TweenText::FILL_COLOR_ALPHA, 0.5f) \
  256. .target(0).start(m_tweenManager); \
  257. TweenEngine::Tween::to(object, util3ds::TweenText::OUTLINE_COLOR_ALPHA, 0.5f) \
  258. .target(0).start(m_tweenManager);
  259. void AppInfo::setCurrentScreenshot(int screenshotIndex)
  260. {
  261. if (screenshotIndex != 0)
  262. {
  263. cpp3ds::Sprite* screenshotTop = m_screenshotTops[screenshotIndex-1].get();
  264. cpp3ds::Sprite* screenshotBottom = m_screenshotBottoms[screenshotIndex-1].get();
  265. m_screenshotTopTop.setTexture(*screenshotTop->getTexture(), true);
  266. m_screenshotTopBottom.setTexture(*screenshotTop->getTexture(), true);
  267. m_screenshotBottom.setTexture(*screenshotBottom->getTexture(), true);
  268. // If another screenshot not already showing
  269. if (m_currentScreenshot == 0)
  270. {
  271. SCREENSHOT_BUTTON_FADEIN(m_close);
  272. SCREENSHOT_BUTTON_FADEIN(m_arrowLeft);
  273. SCREENSHOT_BUTTON_FADEIN(m_arrowRight);
  274. m_screenshotTopTop.setPosition(screenshotTop->getPosition() + cpp3ds::Vector2f(0.f, 270.f));
  275. m_screenshotTopTop.setScale(screenshotTop->getScale());
  276. m_screenshotTopBottom.setPosition(screenshotTop->getPosition());
  277. m_screenshotTopBottom.setScale(screenshotTop->getScale());
  278. m_screenshotBottom.setPosition(screenshotBottom->getPosition());
  279. m_screenshotBottom.setScale(screenshotBottom->getScale());
  280. TweenEngine::Tween::to(m_screenshotTopBottom, util3ds::TweenSprite::SCALE_XY, 0.5f)
  281. .target(0.18f, 0.18f)
  282. .ease(TweenEngine::TweenEquations::easeOutElastic)
  283. .start(m_tweenManager);
  284. TweenEngine::Tween::to(m_screenshotTopBottom, util3ds::TweenSprite::POSITION_XY, 0.5f)
  285. .targetRelative(-6.f, -6.f)
  286. .ease(TweenEngine::TweenEquations::easeOutElastic)
  287. .start(m_tweenManager);
  288. TweenEngine::Tween::to(m_screenshotBottom, util3ds::TweenSprite::SCALE_XY, 0.5f)
  289. .target(0.18f, 0.18f)
  290. .ease(TweenEngine::TweenEquations::easeOutElastic)
  291. .start(m_tweenManager);
  292. TweenEngine::Tween::to(m_screenshotBottom, util3ds::TweenSprite::POSITION_XY, 0.5f)
  293. .targetRelative(-6.f, 0.f)
  294. .ease(TweenEngine::TweenEquations::easeOutElastic)
  295. .start(m_tweenManager);
  296. TweenEngine::Tween::to(m_screenshotTopTop, util3ds::TweenSprite::SCALE_XY, 0.7f)
  297. .target(1.f, 1.f)
  298. .delay(0.5f)
  299. .start(m_tweenManager);
  300. TweenEngine::Tween::to(m_screenshotTopTop, util3ds::TweenSprite::POSITION_XY, 0.7f)
  301. .target(0.f, 0.f)
  302. .delay(0.5f)
  303. .start(m_tweenManager);
  304. TweenEngine::Tween::to(m_screenshotTopBottom, util3ds::TweenSprite::SCALE_XY, 0.7f)
  305. .target(1.f, 1.f)
  306. .delay(0.5f)
  307. .start(m_tweenManager);
  308. TweenEngine::Tween::to(m_screenshotTopBottom, util3ds::TweenSprite::POSITION_XY, 0.7f)
  309. .target(-40.f, -240.f)
  310. .delay(0.5f)
  311. .start(m_tweenManager);
  312. TweenEngine::Tween::to(m_screenshotBottom, util3ds::TweenSprite::SCALE_XY, 0.7f)
  313. .target(1.f, 1.f)
  314. .delay(0.5f)
  315. .start(m_tweenManager);
  316. TweenEngine::Tween::to(m_screenshotBottom, util3ds::TweenSprite::POSITION_XY, 0.7f)
  317. .target(0.f, 0.f)
  318. .delay(0.5f)
  319. .start(m_tweenManager);
  320. }
  321. }
  322. else if (m_currentScreenshot != 0)
  323. {
  324. // Close screenshot
  325. cpp3ds::Sprite* screenshotTop = m_screenshotTops[m_currentScreenshot-1].get();
  326. cpp3ds::Sprite* screenshotBottom = m_screenshotBottoms[m_currentScreenshot-1].get();
  327. SCREENSHOT_BUTTON_FADEOUT(m_close);
  328. SCREENSHOT_BUTTON_FADEOUT(m_arrowLeft);
  329. SCREENSHOT_BUTTON_FADEOUT(m_arrowRight);
  330. TweenEngine::Tween::to(m_screenshotTopTop, util3ds::TweenSprite::SCALE_XY, 0.7f)
  331. .target(0.15f, 0.15f)
  332. .start(m_tweenManager);
  333. TweenEngine::Tween::to(m_screenshotTopTop, util3ds::TweenSprite::POSITION_XY, 0.7f)
  334. .target(screenshotTop->getPosition().x, screenshotTop->getPosition().y + 270.f)
  335. .start(m_tweenManager);
  336. TweenEngine::Tween::to(m_screenshotTopBottom, util3ds::TweenSprite::SCALE_XY, 0.7f)
  337. .target(0.15f, 0.15f)
  338. .start(m_tweenManager);
  339. TweenEngine::Tween::to(m_screenshotTopBottom, util3ds::TweenSprite::POSITION_XY, 0.7f)
  340. .target(screenshotTop->getPosition().x, screenshotTop->getPosition().y)
  341. .start(m_tweenManager);
  342. TweenEngine::Tween::to(m_screenshotBottom, util3ds::TweenSprite::SCALE_XY, 0.7f)
  343. .target(0.15f, 0.15f)
  344. .start(m_tweenManager);
  345. TweenEngine::Tween::to(m_screenshotBottom, util3ds::TweenSprite::POSITION_XY, 0.7f)
  346. .target(screenshotBottom->getPosition().x, screenshotBottom->getPosition().y)
  347. .setCallback(TweenEngine::TweenCallback::COMPLETE, [=](TweenEngine::BaseTween* source) {
  348. m_currentScreenshot = screenshotIndex;
  349. })
  350. .start(m_tweenManager);
  351. return;
  352. }
  353. m_currentScreenshot = screenshotIndex;
  354. }
  355. bool AppInfo::processEvent(const cpp3ds::Event &event)
  356. {
  357. if (!m_appItem)
  358. return true;
  359. if (m_currentScreenshot)
  360. {
  361. if (event.type == cpp3ds::Event::KeyPressed)
  362. {
  363. switch (event.key.code)
  364. {
  365. case cpp3ds::Keyboard::B:
  366. setCurrentScreenshot(0);
  367. break;
  368. case cpp3ds::Keyboard::DPadRight:
  369. if (m_currentScreenshot < m_screenshotTops.size())
  370. setCurrentScreenshot(m_currentScreenshot + 1);
  371. break;
  372. case cpp3ds::Keyboard::DPadLeft:
  373. if (m_currentScreenshot > 1)
  374. setCurrentScreenshot(m_currentScreenshot - 1);
  375. break;
  376. default:
  377. break;
  378. }
  379. }
  380. else if (event.type == cpp3ds::Event::TouchBegan)
  381. {
  382. if (m_close.getGlobalBounds().contains(event.touch.x, event.touch.y))
  383. {
  384. setCurrentScreenshot(0);
  385. }
  386. else if (m_currentScreenshot > 1 && event.touch.x < 140)
  387. {
  388. setCurrentScreenshot(m_currentScreenshot - 1);
  389. }
  390. else if (m_currentScreenshot < m_screenshotTops.size() && event.touch.x > 180)
  391. {
  392. setCurrentScreenshot(m_currentScreenshot + 1);
  393. }
  394. }
  395. return false;
  396. }
  397. if (event.type == cpp3ds::Event::TouchBegan)
  398. {
  399. if (m_textIconDemo.getGlobalBounds().contains(event.touch.x, event.touch.y))
  400. {
  401. if (!m_appItem->getDemos().empty())
  402. {
  403. cpp3ds::Uint64 demoTitleId = m_appItem->getDemos()[0];
  404. cpp3ds::String appTitle = m_appItem->getTitle();
  405. if (InstalledList::isInstalled(demoTitleId))
  406. {
  407. g_browseState->requestStackPush(States::Dialog, false, [=](void *data) mutable {
  408. auto event = reinterpret_cast<DialogState::Event*>(data);
  409. if (event->type == DialogState::GetText)
  410. {
  411. auto str = reinterpret_cast<cpp3ds::String*>(event->data);
  412. *str = _("You are deleting the demo for\nthis title:\n\n%s", appTitle.toAnsiString().c_str());
  413. return true;
  414. }
  415. else if (event->type == DialogState::Response)
  416. {
  417. bool *accepted = reinterpret_cast<bool*>(event->data);
  418. if (*accepted)
  419. {
  420. #ifdef _3DS
  421. AM_DeleteTitle(MEDIATYPE_SD, demoTitleId);
  422. #endif
  423. Notification::spawn(_("Deleted demo: %s", appTitle.toAnsiString().c_str()));
  424. InstalledList::getInstance().refresh();
  425. updateInfo();
  426. }
  427. return true;
  428. }
  429. return false;
  430. });
  431. }
  432. else
  433. {
  434. DownloadQueue::getInstance().addDownload(m_appItem, demoTitleId, [this](bool succeeded){
  435. updateInfo();
  436. });
  437. Notification::spawn(_("Queued demo: %s", appTitle.toAnsiString().c_str()));
  438. }
  439. }
  440. }
  441. else if (m_appItem->isInstalled())
  442. {
  443. if (m_textExecute.getGlobalBounds().contains(event.touch.x, event.touch.y))
  444. {
  445. g_requestJump = m_appItem->getTitleId();
  446. }
  447. else if (m_textDelete.getGlobalBounds().contains(event.touch.x, event.touch.y))
  448. {
  449. cpp3ds::String appTitle = m_appItem->getTitle();
  450. g_browseState->requestStackPush(States::Dialog, false, [=](void *data) mutable {
  451. auto event = reinterpret_cast<DialogState::Event*>(data);
  452. if (event->type == DialogState::GetText)
  453. {
  454. auto str = reinterpret_cast<cpp3ds::String*>(event->data);
  455. *str = _("You are deleting this game,\nincluding all save data:\n\n%s", appTitle.toAnsiString().c_str());
  456. return true;
  457. }
  458. else if (event->type == DialogState::Response)
  459. {
  460. bool *accepted = reinterpret_cast<bool*>(event->data);
  461. if (*accepted)
  462. {
  463. #ifdef _3DS
  464. FS_MediaType mediaType = ((m_appItem->getTitleId() >> 32) == TitleKeys::DSiWare) ? MEDIATYPE_NAND : MEDIATYPE_SD;
  465. AM_DeleteTitle(mediaType, m_appItem->getTitleId());
  466. #endif
  467. m_appItem->setInstalled(false);
  468. Notification::spawn(_("Deleted: %s", appTitle.toAnsiString().c_str()));
  469. InstalledList::getInstance().refresh();
  470. updateInfo();
  471. }
  472. return true;
  473. }
  474. return false;
  475. });
  476. }
  477. }
  478. else if (m_textDownload.getGlobalBounds().contains(event.touch.x, event.touch.y))
  479. {
  480. if (!DownloadQueue::getInstance().isDownloading(m_appItem))
  481. {
  482. DownloadQueue::getInstance().addDownload(m_appItem);
  483. TweenEngine::Tween::to(m_textDownload, util3ds::TweenText::FILL_COLOR_RGB, 0.5f)
  484. .target(230, 20, 20)
  485. .start(m_tweenManager);
  486. Notification::spawn(_("Queued install: %s", m_appItem->getTitle().toAnsiString().c_str()));
  487. }
  488. }
  489. }
  490. for (int i = 0; i < m_screenshotTops.size(); ++i)
  491. {
  492. if (m_screenshotTops[i]->getGlobalBounds().contains(event.touch.x, event.touch.y))
  493. setCurrentScreenshot(i+1);
  494. else if (m_screenshotBottoms[i]->getGlobalBounds().contains(event.touch.x, event.touch.y))
  495. setCurrentScreenshot(i+1);
  496. }
  497. if (event.type == cpp3ds::Event::JoystickMoved)
  498. {
  499. m_descriptionVelocity = event.joystickMove.y * 2.f;
  500. }
  501. return true;
  502. }
  503. void AppInfo::update(float delta)
  504. {
  505. // if (!m_downloads.isDownloading(m_currentApp) && !m_downloads.isInstalling(m_currentApp))
  506. // {
  507. // m_textDownload.setFillColor(cpp3ds::Color::White);
  508. // }
  509. m_textDescription.move(0.f, m_descriptionVelocity * delta);
  510. if (m_textDescription.getPosition().y < 49.f - m_textDescription.getLocalBounds().height + 110.f)
  511. m_textDescription.setPosition(102.f, 49.f - m_textDescription.getLocalBounds().height + 110.f);
  512. if (m_textDescription.getPosition().y > 49.f)
  513. m_textDescription.setPosition(102.f, 49.f);
  514. m_tweenManager.update(delta);
  515. }
  516. void AppInfo::setDescription(const rapidjson::Value &jsonDescription)
  517. {
  518. // Decode description, strip newlines
  519. const char *dd = jsonDescription.GetString();
  520. cpp3ds::String description = cpp3ds::String::fromUtf8(dd, dd + jsonDescription.GetStringLength());
  521. description.replace("\n", " ");
  522. description.replace("<br>", "\n");
  523. description.replace("<BR>", "\n");
  524. description.replace("<br/>", "\n");
  525. // Calculate word-wrapping for description
  526. int startPos = 0;
  527. int lastSpace = 0;
  528. auto s = description.toUtf8();
  529. cpp3ds::Text tmpText = m_textDescription;
  530. for (int i = 0; i < s.size(); ++i)
  531. {
  532. if (s[i] == ' ')
  533. lastSpace = i;
  534. tmpText.setString(cpp3ds::String::fromUtf8(s.begin() + startPos, s.begin() + i));
  535. if (tmpText.getLocalBounds().width > 206)
  536. {
  537. if (lastSpace != 0)
  538. {
  539. s[lastSpace] = '\n';
  540. i = startPos = lastSpace + 1;
  541. lastSpace = 0;
  542. }
  543. else
  544. {
  545. s.insert(s.begin() + i, '\n');
  546. startPos = ++i;
  547. }
  548. }
  549. }
  550. m_textDescription.setString(cpp3ds::String::fromUtf8(s.begin(), s.end()));
  551. }
  552. void AppInfo::setScreenshots(const rapidjson::Value &jsonScreenshots)
  553. {
  554. if (jsonScreenshots.IsArray())
  555. for (int i = 0; i < jsonScreenshots.Size(); ++i)
  556. {
  557. addScreenshot(i, jsonScreenshots[i]["image_url"][0]);
  558. addScreenshot(i, jsonScreenshots[i]["image_url"][1]);
  559. }
  560. float startX = std::round((320.f - 61.f * m_screenshotTops.size()) / 2.f);
  561. for (int i = 0; i < m_screenshotTops.size(); ++i)
  562. {
  563. m_screenshotTops[i]->setPosition(startX + i * 61.f, 167.f);
  564. m_screenshotBottoms[i]->setPosition(startX + 6.f + i * 61.f, 203.f);
  565. }
  566. }
  567. void AppInfo::addScreenshot(int index, const rapidjson::Value &jsonScreenshot)
  568. {
  569. std::string url = jsonScreenshot["value"].GetString();
  570. std::string type = jsonScreenshot["type"].GetString();
  571. std::string filename = _(FREESHOP_DIR "/tmp/%s/screen%d%s.jpg", m_appItem->getTitleIdStr().c_str(), index, type.c_str());
  572. bool isUpper = type == "upper";
  573. if (!pathExists(filename.c_str()))
  574. {
  575. Download download(url, filename);
  576. download.run();
  577. }
  578. std::unique_ptr<cpp3ds::Texture> texture(new cpp3ds::Texture());
  579. std::unique_ptr<cpp3ds::Sprite> sprite(new cpp3ds::Sprite());
  580. texture->loadFromFile(filename);
  581. texture->setSmooth(true);
  582. sprite->setTexture(*texture, true);
  583. sprite->setScale(0.15f, 0.15f);
  584. sprite->setPosition(400.f, 0.f); // Keep offscreen
  585. if (isUpper) {
  586. m_screenshotTops.emplace_back(std::move(sprite));
  587. m_screenshotTopTextures.emplace_back(std::move(texture));
  588. } else {
  589. m_screenshotBottoms.emplace_back(std::move(sprite));
  590. m_screenshotBottomTextures.emplace_back(std::move(texture));
  591. }
  592. }
  593. void AppInfo::updateInfo()
  594. {
  595. if (m_appItem->getDemos().size() > 0)
  596. {
  597. if (InstalledList::isInstalled(m_appItem->getDemos()[0])) {
  598. m_textIconDemo.setString(L"\uf1f8");
  599. } else {
  600. m_textIconDemo.setString(L"\uf019");
  601. }
  602. }
  603. }
  604. } // namespace FreeShop