AppInfo.cpp 24 KB

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