AppInfo.cpp 21 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672
  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 jsonFilename = appItem->getJsonFilename();
  192. std::string urlTitleData = "https://samurai.ctr.shop.nintendo.net/samurai/ws/" + appItem->getUriRegion() + "/title/" + appItem->getContentId() + "/?shop_id=1";
  193. std::string dir = cpp3ds::FileSystem::getFilePath(FREESHOP_DIR "/tmp/" + appItem->getTitleIdStr());
  194. if (!pathExists(dir.c_str(), false))
  195. mkdir(dir.c_str(), 0777);
  196. if (!appItem->isCached())
  197. {
  198. Download download(urlTitleData, jsonFilename);
  199. download.setField("Accept", "application/json");
  200. download.run();
  201. }
  202. updateInfo();
  203. m_textTitle.setString("");
  204. m_textDescription.setString("");
  205. m_textTitleId.setString(appItem->getTitleIdStr());
  206. cpp3ds::IntRect textureRect;
  207. m_icon.setTexture(*appItem->getIcon(textureRect), true);
  208. m_icon.setTextureRect(textureRect);
  209. m_screenshotTops.clear();
  210. m_screenshotTopTextures.clear();
  211. m_screenshotBottoms.clear();
  212. m_screenshotBottomTextures.clear();
  213. rapidjson::Document doc;
  214. std::string json;
  215. cpp3ds::FileInputStream file;
  216. if (file.open(jsonFilename))
  217. {
  218. json.resize(file.getSize());
  219. file.read(&json[0], json.size());
  220. doc.Parse(json.c_str());
  221. if (!doc.HasParseError())
  222. {
  223. if (doc["title"].HasMember("screenshots"))
  224. setScreenshots(doc["title"]["screenshots"]["screenshot"]);
  225. setDescription(doc["title"]["description"]);
  226. m_textDescription.setPosition(102.f, 49.f);
  227. m_textTitle.setString(appItem->getTitle());
  228. m_textTitle.setOrigin(std::min(m_textTitle.getLocalBounds().width, 205.f) / 2.f, 0.f);
  229. m_textDownload.setFillColor(cpp3ds::Color::White);
  230. }
  231. }
  232. }
  233. }
  234. const std::shared_ptr<AppItem> AppInfo::getAppItem() const
  235. {
  236. return m_appItem;
  237. }
  238. #define SCREENSHOT_BUTTON_FADEIN(object) \
  239. object.setFillColor(cpp3ds::Color(255,255,255,190)); \
  240. object.setOutlineColor(cpp3ds::Color(0,0,0,100)); \
  241. TweenEngine::Tween::from(object, util3ds::TweenText::FILL_COLOR_ALPHA, 4.f) \
  242. .target(0).start(m_tweenManager); \
  243. TweenEngine::Tween::from(object, util3ds::TweenText::OUTLINE_COLOR_ALPHA, 4.f) \
  244. .target(0).start(m_tweenManager);
  245. #define SCREENSHOT_BUTTON_FADEOUT(object) \
  246. TweenEngine::Tween::to(object, util3ds::TweenText::FILL_COLOR_ALPHA, 0.5f) \
  247. .target(0).start(m_tweenManager); \
  248. TweenEngine::Tween::to(object, util3ds::TweenText::OUTLINE_COLOR_ALPHA, 0.5f) \
  249. .target(0).start(m_tweenManager);
  250. void AppInfo::setCurrentScreenshot(int screenshotIndex)
  251. {
  252. if (screenshotIndex != 0)
  253. {
  254. cpp3ds::Sprite* screenshotTop = m_screenshotTops[screenshotIndex-1].get();
  255. cpp3ds::Sprite* screenshotBottom = m_screenshotBottoms[screenshotIndex-1].get();
  256. m_screenshotTopTop.setTexture(*screenshotTop->getTexture(), true);
  257. m_screenshotTopBottom.setTexture(*screenshotTop->getTexture(), true);
  258. m_screenshotBottom.setTexture(*screenshotBottom->getTexture(), true);
  259. // If another screenshot not already showing
  260. if (m_currentScreenshot == 0)
  261. {
  262. SCREENSHOT_BUTTON_FADEIN(m_close);
  263. SCREENSHOT_BUTTON_FADEIN(m_arrowLeft);
  264. SCREENSHOT_BUTTON_FADEIN(m_arrowRight);
  265. m_screenshotTopTop.setPosition(screenshotTop->getPosition() + cpp3ds::Vector2f(0.f, 270.f));
  266. m_screenshotTopTop.setScale(screenshotTop->getScale());
  267. m_screenshotTopBottom.setPosition(screenshotTop->getPosition());
  268. m_screenshotTopBottom.setScale(screenshotTop->getScale());
  269. m_screenshotBottom.setPosition(screenshotBottom->getPosition());
  270. m_screenshotBottom.setScale(screenshotBottom->getScale());
  271. TweenEngine::Tween::to(m_screenshotTopBottom, util3ds::TweenSprite::SCALE_XY, 0.5f)
  272. .target(0.18f, 0.18f)
  273. .ease(TweenEngine::TweenEquations::easeOutElastic)
  274. .start(m_tweenManager);
  275. TweenEngine::Tween::to(m_screenshotTopBottom, util3ds::TweenSprite::POSITION_XY, 0.5f)
  276. .targetRelative(-6.f, -6.f)
  277. .ease(TweenEngine::TweenEquations::easeOutElastic)
  278. .start(m_tweenManager);
  279. TweenEngine::Tween::to(m_screenshotBottom, util3ds::TweenSprite::SCALE_XY, 0.5f)
  280. .target(0.18f, 0.18f)
  281. .ease(TweenEngine::TweenEquations::easeOutElastic)
  282. .start(m_tweenManager);
  283. TweenEngine::Tween::to(m_screenshotBottom, util3ds::TweenSprite::POSITION_XY, 0.5f)
  284. .targetRelative(-6.f, 0.f)
  285. .ease(TweenEngine::TweenEquations::easeOutElastic)
  286. .start(m_tweenManager);
  287. TweenEngine::Tween::to(m_screenshotTopTop, util3ds::TweenSprite::SCALE_XY, 0.7f)
  288. .target(1.f, 1.f)
  289. .delay(0.5f)
  290. .start(m_tweenManager);
  291. TweenEngine::Tween::to(m_screenshotTopTop, util3ds::TweenSprite::POSITION_XY, 0.7f)
  292. .target(0.f, 0.f)
  293. .delay(0.5f)
  294. .start(m_tweenManager);
  295. TweenEngine::Tween::to(m_screenshotTopBottom, util3ds::TweenSprite::SCALE_XY, 0.7f)
  296. .target(1.f, 1.f)
  297. .delay(0.5f)
  298. .start(m_tweenManager);
  299. TweenEngine::Tween::to(m_screenshotTopBottom, util3ds::TweenSprite::POSITION_XY, 0.7f)
  300. .target(-40.f, -240.f)
  301. .delay(0.5f)
  302. .start(m_tweenManager);
  303. TweenEngine::Tween::to(m_screenshotBottom, util3ds::TweenSprite::SCALE_XY, 0.7f)
  304. .target(1.f, 1.f)
  305. .delay(0.5f)
  306. .start(m_tweenManager);
  307. TweenEngine::Tween::to(m_screenshotBottom, util3ds::TweenSprite::POSITION_XY, 0.7f)
  308. .target(0.f, 0.f)
  309. .delay(0.5f)
  310. .start(m_tweenManager);
  311. }
  312. }
  313. else if (m_currentScreenshot != 0)
  314. {
  315. // Close screenshot
  316. cpp3ds::Sprite* screenshotTop = m_screenshotTops[m_currentScreenshot-1].get();
  317. cpp3ds::Sprite* screenshotBottom = m_screenshotBottoms[m_currentScreenshot-1].get();
  318. SCREENSHOT_BUTTON_FADEOUT(m_close);
  319. SCREENSHOT_BUTTON_FADEOUT(m_arrowLeft);
  320. SCREENSHOT_BUTTON_FADEOUT(m_arrowRight);
  321. TweenEngine::Tween::to(m_screenshotTopTop, util3ds::TweenSprite::SCALE_XY, 0.7f)
  322. .target(0.15f, 0.15f)
  323. .start(m_tweenManager);
  324. TweenEngine::Tween::to(m_screenshotTopTop, util3ds::TweenSprite::POSITION_XY, 0.7f)
  325. .target(screenshotTop->getPosition().x, screenshotTop->getPosition().y + 270.f)
  326. .start(m_tweenManager);
  327. TweenEngine::Tween::to(m_screenshotTopBottom, util3ds::TweenSprite::SCALE_XY, 0.7f)
  328. .target(0.15f, 0.15f)
  329. .start(m_tweenManager);
  330. TweenEngine::Tween::to(m_screenshotTopBottom, util3ds::TweenSprite::POSITION_XY, 0.7f)
  331. .target(screenshotTop->getPosition().x, screenshotTop->getPosition().y)
  332. .start(m_tweenManager);
  333. TweenEngine::Tween::to(m_screenshotBottom, util3ds::TweenSprite::SCALE_XY, 0.7f)
  334. .target(0.15f, 0.15f)
  335. .start(m_tweenManager);
  336. TweenEngine::Tween::to(m_screenshotBottom, util3ds::TweenSprite::POSITION_XY, 0.7f)
  337. .target(screenshotBottom->getPosition().x, screenshotBottom->getPosition().y)
  338. .setCallback(TweenEngine::TweenCallback::COMPLETE, [=](TweenEngine::BaseTween* source) {
  339. m_currentScreenshot = screenshotIndex;
  340. })
  341. .start(m_tweenManager);
  342. return;
  343. }
  344. m_currentScreenshot = screenshotIndex;
  345. }
  346. bool AppInfo::processEvent(const cpp3ds::Event &event)
  347. {
  348. if (!m_appItem)
  349. return true;
  350. if (m_currentScreenshot)
  351. {
  352. if (event.type == cpp3ds::Event::KeyPressed)
  353. {
  354. switch (event.key.code)
  355. {
  356. case cpp3ds::Keyboard::B:
  357. setCurrentScreenshot(0);
  358. break;
  359. case cpp3ds::Keyboard::DPadRight:
  360. if (m_currentScreenshot < m_screenshotTops.size())
  361. setCurrentScreenshot(m_currentScreenshot + 1);
  362. break;
  363. case cpp3ds::Keyboard::DPadLeft:
  364. if (m_currentScreenshot > 1)
  365. setCurrentScreenshot(m_currentScreenshot - 1);
  366. break;
  367. default:
  368. break;
  369. }
  370. }
  371. else if (event.type == cpp3ds::Event::TouchBegan)
  372. {
  373. if (m_close.getGlobalBounds().contains(event.touch.x, event.touch.y))
  374. {
  375. setCurrentScreenshot(0);
  376. }
  377. else if (m_currentScreenshot > 1 && event.touch.x < 140)
  378. {
  379. setCurrentScreenshot(m_currentScreenshot - 1);
  380. }
  381. else if (m_currentScreenshot < m_screenshotTops.size() && event.touch.x > 180)
  382. {
  383. setCurrentScreenshot(m_currentScreenshot + 1);
  384. }
  385. }
  386. return false;
  387. }
  388. if (event.type == cpp3ds::Event::TouchBegan)
  389. {
  390. if (m_textIconDemo.getGlobalBounds().contains(event.touch.x, event.touch.y))
  391. {
  392. if (!m_appItem->getDemos().empty())
  393. {
  394. cpp3ds::Uint64 demoTitleId = m_appItem->getDemos()[0];
  395. cpp3ds::String appTitle = m_appItem->getTitle();
  396. if (InstalledList::isInstalled(demoTitleId))
  397. {
  398. g_browseState->requestStackPush(States::Dialog, false, [=](void *data) mutable {
  399. auto event = reinterpret_cast<DialogState::Event*>(data);
  400. if (event->type == DialogState::GetText)
  401. {
  402. auto str = reinterpret_cast<cpp3ds::String*>(event->data);
  403. *str = _("You are deleting the demo for\nthis title:\n\n%s", appTitle.toAnsiString().c_str());
  404. return true;
  405. }
  406. else if (event->type == DialogState::Response)
  407. {
  408. bool *accepted = reinterpret_cast<bool*>(event->data);
  409. if (*accepted)
  410. {
  411. #ifdef _3DS
  412. AM_DeleteTitle(MEDIATYPE_SD, demoTitleId);
  413. #endif
  414. Notification::spawn(_("Deleted demo: %s", appTitle.toAnsiString().c_str()));
  415. InstalledList::getInstance().refresh();
  416. updateInfo();
  417. }
  418. return true;
  419. }
  420. return false;
  421. });
  422. }
  423. else
  424. {
  425. DownloadQueue::getInstance().addDownload(m_appItem, demoTitleId, [this](bool succeeded){
  426. updateInfo();
  427. });
  428. Notification::spawn(_("Queued demo: %s", appTitle.toAnsiString().c_str()));
  429. }
  430. }
  431. }
  432. else if (m_appItem->isInstalled())
  433. {
  434. if (m_textExecute.getGlobalBounds().contains(event.touch.x, event.touch.y))
  435. {
  436. g_requestJump = m_appItem->getTitleId();
  437. }
  438. else if (m_textDelete.getGlobalBounds().contains(event.touch.x, event.touch.y))
  439. {
  440. cpp3ds::String appTitle = m_appItem->getTitle();
  441. g_browseState->requestStackPush(States::Dialog, false, [=](void *data) mutable {
  442. auto event = reinterpret_cast<DialogState::Event*>(data);
  443. if (event->type == DialogState::GetText)
  444. {
  445. auto str = reinterpret_cast<cpp3ds::String*>(event->data);
  446. *str = _("You are deleting this game,\nincluding all save data:\n\n%s", appTitle.toAnsiString().c_str());
  447. return true;
  448. }
  449. else if (event->type == DialogState::Response)
  450. {
  451. bool *accepted = reinterpret_cast<bool*>(event->data);
  452. if (*accepted)
  453. {
  454. #ifdef _3DS
  455. FS_MediaType mediaType = ((m_appItem->getTitleId() >> 32) & 0x8010) != 0 ? MEDIATYPE_NAND : MEDIATYPE_SD;
  456. AM_DeleteTitle(mediaType, m_appItem->getTitleId());
  457. #endif
  458. m_appItem->setInstalled(false);
  459. Notification::spawn(_("Deleted: %s", appTitle.toAnsiString().c_str()));
  460. InstalledList::getInstance().refresh();
  461. updateInfo();
  462. }
  463. return true;
  464. }
  465. return false;
  466. });
  467. }
  468. }
  469. else if (m_textDownload.getGlobalBounds().contains(event.touch.x, event.touch.y))
  470. {
  471. if (!DownloadQueue::getInstance().isDownloading(m_appItem))
  472. {
  473. DownloadQueue::getInstance().addDownload(m_appItem);
  474. TweenEngine::Tween::to(m_textDownload, util3ds::TweenText::FILL_COLOR_RGB, 0.5f)
  475. .target(230, 20, 20)
  476. .start(m_tweenManager);
  477. Notification::spawn(_("Queued install: %s", m_appItem->getTitle().toAnsiString().c_str()));
  478. }
  479. }
  480. }
  481. for (int i = 0; i < m_screenshotTops.size(); ++i)
  482. {
  483. if (m_screenshotTops[i]->getGlobalBounds().contains(event.touch.x, event.touch.y))
  484. setCurrentScreenshot(i+1);
  485. else if (m_screenshotBottoms[i]->getGlobalBounds().contains(event.touch.x, event.touch.y))
  486. setCurrentScreenshot(i+1);
  487. }
  488. if (event.type == cpp3ds::Event::JoystickMoved)
  489. {
  490. m_descriptionVelocity = event.joystickMove.y * 2.f;
  491. }
  492. return true;
  493. }
  494. void AppInfo::update(float delta)
  495. {
  496. // if (!m_downloads.isDownloading(m_currentApp) && !m_downloads.isInstalling(m_currentApp))
  497. // {
  498. // m_textDownload.setFillColor(cpp3ds::Color::White);
  499. // }
  500. m_textDescription.move(0.f, m_descriptionVelocity * delta);
  501. if (m_textDescription.getPosition().y < 49.f - m_textDescription.getLocalBounds().height + 110.f)
  502. m_textDescription.setPosition(102.f, 49.f - m_textDescription.getLocalBounds().height + 110.f);
  503. if (m_textDescription.getPosition().y > 49.f)
  504. m_textDescription.setPosition(102.f, 49.f);
  505. m_tweenManager.update(delta);
  506. }
  507. void AppInfo::setDescription(const rapidjson::Value &jsonDescription)
  508. {
  509. // Decode description, strip newlines
  510. const char *dd = jsonDescription.GetString();
  511. cpp3ds::String description = cpp3ds::String::fromUtf8(dd, dd + jsonDescription.GetStringLength());
  512. description.replace("\n", " ");
  513. description.replace("<br>", "\n");
  514. description.replace("<BR>", "\n");
  515. description.replace("<br/>", "\n");
  516. // Calculate word-wrapping for description
  517. int startPos = 0;
  518. int lastSpace = 0;
  519. auto s = description.toUtf8();
  520. cpp3ds::Text tmpText = m_textDescription;
  521. for (int i = 0; i < s.size(); ++i)
  522. {
  523. if (s[i] == ' ')
  524. lastSpace = i;
  525. tmpText.setString(cpp3ds::String::fromUtf8(s.begin() + startPos, s.begin() + i));
  526. if (tmpText.getLocalBounds().width > 206)
  527. {
  528. if (lastSpace != 0)
  529. {
  530. s[lastSpace] = '\n';
  531. i = startPos = lastSpace + 1;
  532. lastSpace = 0;
  533. }
  534. else
  535. {
  536. s.insert(s.begin() + i, '\n');
  537. startPos = ++i;
  538. }
  539. }
  540. }
  541. m_textDescription.setString(cpp3ds::String::fromUtf8(s.begin(), s.end()));
  542. }
  543. void AppInfo::setScreenshots(const rapidjson::Value &jsonScreenshots)
  544. {
  545. if (jsonScreenshots.IsArray())
  546. for (int i = 0; i < jsonScreenshots.Size(); ++i)
  547. {
  548. addScreenshot(i, jsonScreenshots[i]["image_url"][0]);
  549. addScreenshot(i, jsonScreenshots[i]["image_url"][1]);
  550. }
  551. float startX = std::round((320.f - 61.f * m_screenshotTops.size()) / 2.f);
  552. for (int i = 0; i < m_screenshotTops.size(); ++i)
  553. {
  554. m_screenshotTops[i]->setPosition(startX + i * 61.f, 167.f);
  555. m_screenshotBottoms[i]->setPosition(startX + 6.f + i * 61.f, 203.f);
  556. }
  557. }
  558. void AppInfo::addScreenshot(int index, const rapidjson::Value &jsonScreenshot)
  559. {
  560. std::string url = jsonScreenshot["value"].GetString();
  561. std::string type = jsonScreenshot["type"].GetString();
  562. std::string filename = _(FREESHOP_DIR "/tmp/%s/screen%d%s.jpg", m_appItem->getTitleIdStr().c_str(), index, type.c_str());
  563. bool isUpper = type == "upper";
  564. if (!pathExists(filename.c_str()))
  565. {
  566. Download download(url, filename);
  567. download.run();
  568. }
  569. std::unique_ptr<cpp3ds::Texture> texture(new cpp3ds::Texture());
  570. std::unique_ptr<cpp3ds::Sprite> sprite(new cpp3ds::Sprite());
  571. texture->loadFromFile(filename);
  572. texture->setSmooth(true);
  573. sprite->setTexture(*texture, true);
  574. sprite->setScale(0.15f, 0.15f);
  575. sprite->setPosition(400.f, 0.f); // Keep offscreen
  576. if (isUpper) {
  577. m_screenshotTops.emplace_back(std::move(sprite));
  578. m_screenshotTopTextures.emplace_back(std::move(texture));
  579. } else {
  580. m_screenshotBottoms.emplace_back(std::move(sprite));
  581. m_screenshotBottomTextures.emplace_back(std::move(texture));
  582. }
  583. }
  584. void AppInfo::updateInfo()
  585. {
  586. if (m_appItem->getDemos().size() > 0)
  587. {
  588. if (InstalledList::isInstalled(m_appItem->getDemos()[0])) {
  589. m_textIconDemo.setString(L"\uf1f8");
  590. } else {
  591. m_textIconDemo.setString(L"\uf019");
  592. }
  593. }
  594. }
  595. } // namespace FreeShop