BrowseState.cpp 26 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914915916917918919920921922923924925926927928929930
  1. #include "BrowseState.hpp"
  2. #include "SyncState.hpp"
  3. #include "../Notification.hpp"
  4. #include "../AssetManager.hpp"
  5. #include "../Util.hpp"
  6. #include "../Installer.hpp"
  7. #include "SleepState.hpp"
  8. #include "../InstalledList.hpp"
  9. #include "../Config.hpp"
  10. #include "../TitleKeys.hpp"
  11. #include "../FreeShop.hpp"
  12. #include "../Theme.hpp"
  13. #include "../LoadInformations.hpp"
  14. #ifndef EMULATION
  15. #include "../KeyboardApplet.hpp"
  16. #endif
  17. #include <TweenEngine/Tween.h>
  18. #include <cpp3ds/Window/Window.hpp>
  19. #include <sstream>
  20. #include <cpp3ds/System/I18n.hpp>
  21. #include <cpp3ds/System/FileSystem.hpp>
  22. #include <time.h>
  23. namespace FreeShop {
  24. BrowseState *g_browseState = nullptr;
  25. bool g_isLatestFirmwareVersion = true;
  26. cpp3ds::Clock BrowseState::clockDownloadInactivity;
  27. BrowseState::BrowseState(StateStack& stack, Context& context, StateCallback callback)
  28. : State(stack, context, callback)
  29. , m_appListPositionX(0.f)
  30. , m_threadInitialize(&BrowseState::initialize, this)
  31. , m_threadLoadApp(&BrowseState::loadApp, this)
  32. , m_threadBusy(false)
  33. , m_activeDownloadCount(0)
  34. , m_mode(Info)
  35. , m_gwenRenderer(nullptr)
  36. , m_gwenSkin(nullptr)
  37. , m_settingsGUI(nullptr)
  38. , m_isTransitioning(false)
  39. , m_isJapKeyboard(false)
  40. , m_isTIDKeyboard(false)
  41. , m_musicMode(0)
  42. , m_isSliderOff(false)
  43. , m_counter(1)
  44. , m_isControlsBlocked(false)
  45. {
  46. g_browseState = this;
  47. m_musicLoop.setLoop(true);
  48. #ifdef EMULATION
  49. g_syncComplete = true;
  50. initialize();
  51. #else
  52. m_threadInitialize.setRelativePriority(1);
  53. m_threadInitialize.launch();
  54. #endif
  55. }
  56. BrowseState::~BrowseState()
  57. {
  58. settingsSaveToConfig();
  59. Config::saveToFile();
  60. if (m_settingsGUI)
  61. delete m_settingsGUI;
  62. if (m_gwenSkin)
  63. delete m_gwenSkin;
  64. if (m_gwenRenderer)
  65. delete m_gwenRenderer;
  66. }
  67. void BrowseState::initialize()
  68. {
  69. // Initialize AppList singleton first
  70. LoadInformations::getInstance().setStatus(_("Loading game list..."));
  71. AppList::getInstance().refresh();
  72. LoadInformations::getInstance().setStatus(_("Loading installed game list..."));
  73. LoadInformations::getInstance().updateLoadingPercentage(-1);
  74. InstalledList::getInstance().refresh();
  75. LoadInformations::getInstance().setStatus(_("Welcome, %s", getUsername().toAnsiString().c_str()));
  76. //Var init
  77. m_ctrSdPath = "";
  78. m_keyHistory = {};
  79. m_musicFileName = "";
  80. m_iconSet.addIcon(L"\uf0ae");
  81. m_iconSet.addIcon(L"\uf290");
  82. m_iconSet.addIcon(L"\uf019");
  83. m_iconSet.addIcon(L"\uf11b");
  84. m_iconSet.addIcon(L"\uf013");
  85. m_iconSet.addIcon(L"\uf002");
  86. m_iconSet.setPosition(60.f, 13.f);
  87. m_iconSet.setSelectedIndex(m_mode);
  88. m_textActiveDownloads.setCharacterSize(8);
  89. m_textActiveDownloads.setFillColor(cpp3ds::Color::Black);
  90. m_textActiveDownloads.setOutlineColor(cpp3ds::Color::White);
  91. m_textActiveDownloads.setOutlineThickness(1.f);
  92. m_textActiveDownloads.setPosition(128.f, 3.f);
  93. m_textInstalledCount = m_textActiveDownloads;
  94. m_textInstalledCount.setPosition(162.f, 3.f);
  95. //If there's no title key available, or no cache, one of these messages will appear
  96. if (TitleKeys::getIds().empty())
  97. m_textListEmpty.setString(_("No title keys found.\nMake sure you have keys in\n%s\n\nManually copy keys to the directory\nor check settings to enter a URL\nfor downloading title keys.", FREESHOP_DIR "/keys/"));
  98. else
  99. m_textListEmpty.setString(_("No cache entries found\nfor your title keys.\n\nTry refreshing cache in settings.\nIf that doesn't work, then your\ntitles simply won't work with\nfreeShop currently."));
  100. m_textListEmpty.useSystemFont();
  101. m_textListEmpty.setCharacterSize(16);
  102. m_textListEmpty.setFillColor(cpp3ds::Color(80, 80, 80, 255));
  103. m_textListEmpty.setPosition(200.f, 140.f);
  104. m_textListEmpty.setOrigin(m_textListEmpty.getLocalBounds().width / 2, m_textListEmpty.getLocalBounds().height / 2);
  105. //Load keyboard file
  106. reloadKeyboard();
  107. m_textMatches.resize(4);
  108. for (auto& text : m_textMatches)
  109. {
  110. text.setCharacterSize(13);
  111. text.useSystemFont();
  112. }
  113. m_scrollbarInstalledList.setPosition(314.f, 50.f);
  114. m_scrollbarInstalledList.setDragRect(cpp3ds::IntRect(0, 50, 320, 190));
  115. m_scrollbarInstalledList.setScrollAreaSize(cpp3ds::Vector2u(320, 210));
  116. m_scrollbarInstalledList.setSize(cpp3ds::Vector2u(8, 190));
  117. m_scrollbarInstalledList.setColor(cpp3ds::Color(150, 150, 150, 150));
  118. m_scrollbarDownloadQueue = m_scrollbarInstalledList;
  119. m_scrollbarDownloadQueue.setDragRect(cpp3ds::IntRect(0, 30, 320, 210));
  120. m_scrollbarDownloadQueue.setScrollAreaSize(cpp3ds::Vector2u(320, 210));
  121. m_scrollbarDownloadQueue.setSize(cpp3ds::Vector2u(8, 210));
  122. m_scrollbarInstalledList.attachObject(&InstalledList::getInstance());
  123. m_scrollbarDownloadQueue.attachObject(&DownloadQueue::getInstance());
  124. m_textSearchInstalledList.setString(_("Search..."));
  125. m_textSearchInstalledList.setPosition(160.f, 32.f);
  126. if (Theme::isTextThemed)
  127. m_textSearchInstalledList.setFillColor(Theme::primaryTextColor);
  128. else
  129. m_textSearchInstalledList.setFillColor(cpp3ds::Color::Black);
  130. m_textSearchInstalledList.setCharacterSize(12.f);
  131. m_textSearchInstalledList.setStyle(cpp3ds::Text::Italic);
  132. m_textSearchInstalledList.setOrigin(m_textSearchInstalledList.getGlobalBounds().width / 2, 0);
  133. if (Theme::isTextThemed)
  134. m_textBoxInstalledList.setOutlineColor(Theme::boxOutlineColor);
  135. else
  136. m_textBoxInstalledList.setOutlineColor(cpp3ds::Color(158, 158, 158, 255));
  137. m_textBoxInstalledList.setOutlineThickness(1);
  138. if (Theme::isTextThemed)
  139. m_textBoxInstalledList.setFillColor(Theme::boxColor);
  140. else
  141. m_textBoxInstalledList.setFillColor(cpp3ds::Color(245, 245, 245));
  142. m_textBoxInstalledList.setSize(cpp3ds::Vector2f(320.f, 16.f));
  143. m_textBoxInstalledList.setPosition(0.f, 32.f);
  144. setMode(Info);
  145. m_bottomView.setCenter(cpp3ds::Vector2f(160.f, 120.f));
  146. m_bottomView.setSize(cpp3ds::Vector2f(320.f, 240.f));
  147. #ifdef _3DS
  148. while (!m_gwenRenderer)
  149. cpp3ds::sleep(cpp3ds::milliseconds(10));
  150. m_gwenSkin = new Gwen::Skin::TexturedBase(m_gwenRenderer);
  151. if (pathExists(cpp3ds::FileSystem::getFilePath(FREESHOP_DIR "/theme/images/DefaultSkin.png").c_str(), true))
  152. m_gwenSkin->Init(cpp3ds::FileSystem::getFilePath(FREESHOP_DIR "/theme/images/DefaultSkin.png"));
  153. else
  154. m_gwenSkin->Init("images/DefaultSkin.png");
  155. m_gwenSkin->SetDefaultFont(L"", 11);
  156. //Check if the system firmware is the latest for sleep download
  157. NIMS_WantUpdate(&g_isLatestFirmwareVersion);
  158. g_isLatestFirmwareVersion = !g_isLatestFirmwareVersion;
  159. // Need to wait until title screen is done to prevent music from
  160. // settings starting prematurely.
  161. while(!g_syncComplete)
  162. cpp3ds::sleep(cpp3ds::milliseconds(10));
  163. //White screen used for transitions
  164. m_whiteScreen.setPosition(0.f, 30.f);
  165. m_whiteScreen.setSize(cpp3ds::Vector2f(320.f, 210.f));
  166. if (Theme::isTextThemed)
  167. m_whiteScreen.setFillColor(Theme::transitionScreenColor);
  168. else
  169. m_whiteScreen.setFillColor(cpp3ds::Color::White);
  170. m_whiteScreen.setFillColor(cpp3ds::Color(m_whiteScreen.getFillColor().r, m_whiteScreen.getFillColor().g, m_whiteScreen.getFillColor().b, 0));
  171. m_settingsGUI = new GUI::Settings(m_gwenSkin, this);
  172. #endif
  173. #ifndef EMULATION
  174. //Get the /Nintendo 3DS/<id0>/<id1> path
  175. fsInit();
  176. u8 * outdata = static_cast<u8 *>(malloc(1024));
  177. FSUSER_GetSdmcCtrRootPath(outdata, 1024);
  178. char* charOut;
  179. std::string ctrPath;
  180. for (size_t i = 0; i < 158; ++i) {
  181. if (i % 2 == 0) {
  182. charOut = (char*)outdata + i;
  183. ctrPath += charOut;
  184. }
  185. }
  186. fsExit();
  187. m_ctrSdPath = ctrPath;
  188. free(outdata);
  189. #endif
  190. g_browserLoaded = true;
  191. m_topInfos.resetModeTimer();
  192. m_topInfos.setModeChangeEnabled(true);
  193. SleepState::clock.restart();
  194. clockDownloadInactivity.restart();
  195. requestStackClearUnder();
  196. }
  197. void BrowseState::renderTopScreen(cpp3ds::Window& window)
  198. {
  199. if (!g_syncComplete || !g_browserLoaded)
  200. return;
  201. if (AppList::getInstance().getList().size() == 0) {
  202. window.draw(m_textListEmpty);
  203. } else {
  204. window.draw(AppList::getInstance());
  205. }
  206. // Special draw method to draw top screenshot if selected
  207. m_appInfo.drawTop(window);
  208. }
  209. void BrowseState::renderBottomScreen(cpp3ds::Window& window)
  210. {
  211. if (!m_gwenRenderer)
  212. {
  213. m_gwenRenderer = new Gwen::Renderer::cpp3dsRenderer(window);
  214. #ifdef EMULATION
  215. m_gwenSkin = new Gwen::Skin::TexturedBase(m_gwenRenderer);
  216. std::cout << cpp3ds::FileSystem::getFilePath(FREESHOP_DIR "/theme/images/DefaultSkin.png").c_str() << std::endl;
  217. if (pathExists(cpp3ds::FileSystem::getFilePath(FREESHOP_DIR "/theme/images/DefaultSkin.png").c_str(), true))
  218. m_gwenSkin->Init(cpp3ds::FileSystem::getFilePath(FREESHOP_DIR "/theme/images/DefaultSkin.png"));
  219. else
  220. m_gwenSkin->Init("images/DefaultSkin.png");
  221. m_gwenSkin->SetDefaultFont(L"", 11);
  222. m_settingsGUI = new GUI::Settings(m_gwenSkin, this);
  223. #endif
  224. }
  225. if (!g_syncComplete || !g_browserLoaded)
  226. return;
  227. window.draw(m_topInfos);
  228. window.draw(m_iconSet);
  229. if (m_activeDownloadCount > 0)
  230. window.draw(m_textActiveDownloads);
  231. if (InstalledList::getInstance().getGameCount() > 0 && Config::get(Config::ShowGameCounter).GetBool())
  232. window.draw(m_textInstalledCount);
  233. window.setView(m_bottomView);
  234. if (m_mode == App)
  235. window.draw(m_appInfo);
  236. if (m_mode == Downloads)
  237. {
  238. window.draw(DownloadQueue::getInstance());
  239. window.draw(m_scrollbarDownloadQueue);
  240. }
  241. if (m_mode == Installed)
  242. {
  243. window.draw(InstalledList::getInstance());
  244. window.draw(m_scrollbarInstalledList);
  245. window.draw(m_textBoxInstalledList);
  246. window.draw(m_textSearchInstalledList);
  247. }
  248. if (m_mode == Search)
  249. {
  250. window.draw(m_keyboard);
  251. for (auto& textMatch : m_textMatches)
  252. window.draw(textMatch);
  253. }
  254. if (m_mode == Info)
  255. window.draw(m_botInfos);
  256. if (m_mode == Settings)
  257. {
  258. m_settingsGUI->render();
  259. }
  260. window.setView(window.getDefaultView());
  261. if (m_isTransitioning)
  262. window.draw(m_whiteScreen);
  263. }
  264. bool BrowseState::update(float delta)
  265. {
  266. if (!g_syncComplete || !g_browserLoaded)
  267. return true;
  268. if (m_threadBusy)
  269. {
  270. clockDownloadInactivity.restart();
  271. SleepState::clock.restart();
  272. }
  273. // Show latest news if requested
  274. if (Config::get(Config::ShowNews).GetBool())
  275. {
  276. Config::set(Config::ShowNews, false);
  277. requestStackPush(States::News);
  278. }
  279. // Go into sleep state after inactivity
  280. if (!SleepState::isSleeping && (Config::get(Config::SleepMode).GetBool() || Config::get(Config::SleepModeBottom).GetBool() || Config::get(Config::DimLEDs).GetBool()) && SleepState::clock.getElapsedTime() > cpp3ds::seconds(Config::get(Config::InactivitySeconds).GetFloat()))
  281. {
  282. if (!Config::get(Config::MusicOnInactivity).GetBool())
  283. stopBGM();
  284. requestStackPush(States::Sleep);
  285. return false;
  286. }
  287. // Power off after sufficient download inactivity
  288. if (m_activeDownloadCount == 0
  289. && Config::get(Config::PowerOffAfterDownload).GetBool()
  290. && clockDownloadInactivity.getElapsedTime() > cpp3ds::seconds(Config::get(Config::PowerOffTime).GetInt()))
  291. {
  292. g_requestShutdown = true;
  293. return false;
  294. }
  295. // If selected icon changed, change mode accordingly
  296. // If the selected mode is Search and the "Use system keyboard" option is enabled, show the System keyboard
  297. int iconIndex = m_iconSet.getSelectedIndex();
  298. if (iconIndex == Search && Config::get(Config::SystemKeyboard).GetBool()) {
  299. m_iconSet.setSelectedIndex(m_mode);
  300. #ifndef EMULATION
  301. //Check if the keyboard mode is Title ID
  302. if (m_isTIDKeyboard) {
  303. KeyboardApplet kb(KeyboardApplet::TitleID);
  304. swkbdSetHintText(kb, _("Type a game Title ID...").toAnsiString().c_str());
  305. cpp3ds::String input = kb.getInput();
  306. if (!input.isEmpty())
  307. AppList::getInstance().filterBySearch(input.toAnsiString(), m_textMatches);
  308. } else {
  309. KeyboardApplet kb(KeyboardApplet::Text);
  310. swkbdSetHintText(kb, _("Type a game name...").toAnsiString().c_str());
  311. cpp3ds::String input = kb.getInput();
  312. if (!input.isEmpty())
  313. AppList::getInstance().filterBySearch(input.toAnsiString(), m_textMatches);
  314. }
  315. #else
  316. std::cout << "System keyboard." << std::endl;
  317. #endif
  318. }
  319. else if (m_mode != iconIndex && iconIndex >= 0)
  320. setMode(static_cast<Mode>(iconIndex));
  321. // Update the active mode
  322. if (m_mode == App)
  323. {
  324. m_appInfo.update(delta);
  325. }
  326. else if (m_mode == Downloads)
  327. {
  328. DownloadQueue::getInstance().update(delta);
  329. m_scrollbarDownloadQueue.update(delta);
  330. }
  331. else if (m_mode == Installed)
  332. {
  333. InstalledList::getInstance().update(delta);
  334. m_scrollbarInstalledList.update(delta);
  335. }
  336. else if (m_mode == Search)
  337. {
  338. m_keyboard.update(delta);
  339. }
  340. else if (m_mode == Info)
  341. {
  342. m_botInfos.update(delta);
  343. }
  344. if (m_activeDownloadCount != DownloadQueue::getInstance().getActiveCount())
  345. {
  346. clockDownloadInactivity.restart();
  347. m_activeDownloadCount = DownloadQueue::getInstance().getActiveCount();
  348. m_textActiveDownloads.setString(_("%u", m_activeDownloadCount));
  349. }
  350. m_textInstalledCount.setString(_("%u", InstalledList::getInstance().getGameCount()));
  351. m_iconSet.update(delta);
  352. m_topInfos.update(delta);
  353. AppList::getInstance().update(delta);
  354. m_tweenManager.update(delta);
  355. return true;
  356. }
  357. bool BrowseState::processEvent(const cpp3ds::Event& event)
  358. {
  359. if (SleepState::isSleeping)
  360. {
  361. if (!Config::get(Config::MusicOnInactivity).GetBool())
  362. m_settingsGUI->playMusic();
  363. return true;
  364. }
  365. SleepState::clock.restart();
  366. clockDownloadInactivity.restart();
  367. if (m_threadBusy || !g_syncComplete || !g_browserLoaded || m_isControlsBlocked)
  368. return false;
  369. if (m_mode == App) {
  370. if (!m_appInfo.processEvent(event))
  371. return false;
  372. }
  373. else if (m_mode == Downloads) {
  374. if (!m_scrollbarDownloadQueue.processEvent(event))
  375. DownloadQueue::getInstance().processEvent(event);
  376. } else if (m_mode == Installed) {
  377. if (!m_scrollbarInstalledList.processEvent(event))
  378. InstalledList::getInstance().processEvent(event);
  379. } else if (m_mode == Settings) {
  380. m_settingsGUI->processEvent(event);
  381. }
  382. m_iconSet.processEvent(event);
  383. if (m_mode == Search)
  384. {
  385. if (!m_keyboard.processEvents(event))
  386. return true;
  387. cpp3ds::String currentInput;
  388. if (m_keyboard.popString(currentInput))
  389. {
  390. // Enter was pressed, so close keyboard
  391. m_iconSet.setSelectedIndex(App);
  392. m_lastKeyboardInput.clear();
  393. }
  394. else
  395. {
  396. currentInput = m_keyboard.getCurrentInput();
  397. if (m_lastKeyboardInput != currentInput)
  398. {
  399. m_lastKeyboardInput = currentInput;
  400. AppList::getInstance().filterBySearch(currentInput, m_textMatches);
  401. TweenEngine::Tween::to(AppList::getInstance(), AppList::POSITION_XY, 0.3f)
  402. .target(0.f, 0.f)
  403. .start(m_tweenManager);
  404. }
  405. }
  406. }
  407. else
  408. {
  409. // Events for all modes except Search
  410. AppList::getInstance().processEvent(event);
  411. }
  412. if (event.type == cpp3ds::Event::KeyPressed)
  413. {
  414. int index = AppList::getInstance().getSelectedIndex();
  415. //Dat secret block of code
  416. m_keyHistory.push_back(event.key.code);
  417. if (m_keyHistory.size() > 10) {
  418. m_keyHistory.erase(m_keyHistory.begin());
  419. }
  420. if (m_keyHistory.size() >= 10) {
  421. if (m_keyHistory[0] == cpp3ds::Keyboard::DPadUp && m_keyHistory[1] == cpp3ds::Keyboard::DPadUp && m_keyHistory[2] == cpp3ds::Keyboard::DPadDown && m_keyHistory[3] == cpp3ds::Keyboard::DPadDown && m_keyHistory[4] == cpp3ds::Keyboard::DPadLeft && m_keyHistory[5] == cpp3ds::Keyboard::DPadRight && m_keyHistory[6] == cpp3ds::Keyboard::DPadLeft && m_keyHistory[7] == cpp3ds::Keyboard::DPadRight && m_keyHistory[8] == cpp3ds::Keyboard::B && m_keyHistory[9] == cpp3ds::Keyboard::A) {
  422. switch (m_counter) {
  423. case 1: Notification::spawn(_("There are no Easter Eggs in this program.")); break;
  424. case 2: Notification::spawn(_("There really are no Easter Eggs in this program.")); break;
  425. case 3: Notification::spawn(_("Didn't I already tell you that\nthere are no Easter Eggs in this program?")); break;
  426. case 4: Notification::spawn(_("All right, you win.\n\n /----\\\n -------/ \\\n / \\\n / |\n -----------------/ --------\\\n ----------------------------------------------")); break;
  427. default: Notification::spawn(_("What is it? It's an elephant being eaten by a snake, of course.")); break;
  428. }
  429. m_counter++;
  430. }
  431. }
  432. switch (event.key.code)
  433. {
  434. case cpp3ds::Keyboard::Start:
  435. #ifndef EMULATION
  436. // If the user launched the freeShop with the 3dsx version, don't allow it to close via Start to prevent crash
  437. if (!envIsHomebrew()) {
  438. FreeShop::prepareToCloseApp();
  439. requestStackClear();
  440. return true;
  441. }
  442. #endif
  443. case cpp3ds::Keyboard::A:
  444. {
  445. // Don't load if game info is already loaded or nothing is selected
  446. if (!AppList::getInstance().getSelected())
  447. break;
  448. if (m_appInfo.getAppItem() == AppList::getInstance().getSelected()->getAppItem())
  449. break;
  450. m_threadBusy = true;
  451. // Only fade out if a game is loaded already
  452. if (!m_appInfo.getAppItem())
  453. m_threadLoadApp.launch();
  454. else
  455. TweenEngine::Tween::to(m_appInfo, AppInfo::ALPHA, 0.3f)
  456. .target(0.f)
  457. .setCallback(TweenEngine::TweenCallback::COMPLETE, [this](TweenEngine::BaseTween* source) {
  458. m_threadLoadApp.launch();
  459. })
  460. .start(m_tweenManager);
  461. break;
  462. }
  463. case cpp3ds::Keyboard::B:
  464. AppList::getInstance().filterBySearch("", m_textMatches);
  465. break;
  466. case cpp3ds::Keyboard::X: {
  467. if (!AppList::getInstance().getSelected())
  468. break;
  469. auto app = AppList::getInstance().getSelected()->getAppItem();
  470. if (app && !DownloadQueue::getInstance().isDownloading(app))
  471. {
  472. if (!app->isInstalled())
  473. {
  474. app->queueForInstall();
  475. Notification::spawn(_("Queued install: %s", app->getTitle().toAnsiString().c_str()));
  476. }
  477. else
  478. Notification::spawn(_("Already installed: %s", app->getTitle().toAnsiString().c_str()));
  479. } else {
  480. Notification::spawn(_("Already in downloading: \n%s", app->getTitle().toAnsiString().c_str()));
  481. }
  482. break;
  483. }
  484. case cpp3ds::Keyboard::Y: {
  485. if (!AppList::getInstance().getSelected())
  486. break;
  487. auto app = AppList::getInstance().getSelected()->getAppItem();
  488. bool isSleepDownloading = false;
  489. #ifndef EMULATION
  490. NIMS_IsTaskRegistered(app->getTitleId(), &isSleepDownloading);
  491. #endif
  492. if (app && !DownloadQueue::getInstance().isDownloading(app) && !isSleepDownloading)
  493. {
  494. if (!app->isInstalled())
  495. {
  496. app->queueForSleepInstall();
  497. }
  498. else
  499. Notification::spawn(_("Already installed: %s", app->getTitle().toAnsiString().c_str()));
  500. } else if (isSleepDownloading) {
  501. app->removeSleepInstall();
  502. } else {
  503. Notification::spawn(_("Already in downloading: \n%s", app->getTitle().toAnsiString().c_str()));
  504. }
  505. break;
  506. }
  507. case cpp3ds::Keyboard::CStickRight: {
  508. if (getMode() < 5) {
  509. int newMode = getMode() + 1;
  510. setMode(static_cast<Mode>(newMode));
  511. m_iconSet.setSelectedIndex(newMode);
  512. }
  513. break;
  514. }
  515. case cpp3ds::Keyboard::CStickLeft: {
  516. if (getMode() > 0) {
  517. int newMode = getMode() - 1;
  518. setMode(static_cast<Mode>(newMode));
  519. m_iconSet.setSelectedIndex(newMode);
  520. }
  521. break;
  522. }
  523. default:
  524. break;
  525. }
  526. } else if (event.type == cpp3ds::Event::SliderVolumeChanged) {
  527. if (event.slider.value < 0.1 && !m_isSliderOff && Config::get(Config::MusicTurnOffSlider).GetBool()) {
  528. // The volume slider is under 10%, stop the music
  529. m_isSliderOff = true;
  530. stopBGM();
  531. } else if (event.slider.value >= 0.1 && m_isSliderOff && Config::get(Config::MusicTurnOffSlider).GetBool()) {
  532. // The volume slider is above or equals 10%, re-launch the music
  533. m_isSliderOff = false;
  534. m_settingsGUI->playMusic();
  535. }
  536. }
  537. return true;
  538. }
  539. void BrowseState::loadApp()
  540. {
  541. auto item = AppList::getInstance().getSelected()->getAppItem();
  542. if (!item)
  543. return;
  544. // TODO: Don't show loading when game is cached?
  545. bool showLoading = g_browserLoaded && m_appInfo.getAppItem() != item;
  546. m_iconSet.setSelectedIndex(App);
  547. if (m_appInfo.getAppItem() != item)
  548. {
  549. setMode(App);
  550. if (showLoading)
  551. requestStackPush(States::Loading);
  552. m_appInfo.loadApp(item);
  553. }
  554. TweenEngine::Tween::to(m_appInfo, AppInfo::ALPHA, 0.5f)
  555. .target(255.f)
  556. .start(m_tweenManager);
  557. if (showLoading)
  558. requestStackPop();
  559. m_threadBusy = false;
  560. }
  561. void BrowseState::setMode(BrowseState::Mode mode)
  562. {
  563. if (m_mode == mode || m_isTransitioning)
  564. return;
  565. // Transition / end current mode
  566. if (m_mode == Search)
  567. {
  568. float delay = 0.f;
  569. for (auto& text : m_textMatches)
  570. {
  571. TweenEngine::Tween::to(text, util3ds::RichText::POSITION_X, 0.2f)
  572. .target(-text.getLocalBounds().width)
  573. .delay(delay)
  574. .start(m_tweenManager);
  575. delay += 0.05f;
  576. }
  577. AppList::getInstance().setCollapsed(false);
  578. m_topInfos.setCollapsed(false);
  579. TweenEngine::Tween::to(m_iconSet, IconSet::POSITION_X, 0.3f)
  580. .target(60.f)
  581. .start(m_tweenManager);
  582. TweenEngine::Tween::to(m_textActiveDownloads, util3ds::TweenText::POSITION_X, 0.3f)
  583. .target(128.f)
  584. .start(m_tweenManager);
  585. TweenEngine::Tween::to(m_textInstalledCount, util3ds::TweenText::POSITION_X, 0.3f)
  586. .target(162.f)
  587. .start(m_tweenManager);
  588. }
  589. else if (m_mode == Settings)
  590. {
  591. m_settingsGUI->saveToConfig();
  592. }
  593. // Transition / start new mode
  594. if (mode == Search)
  595. {
  596. float posY = 1.f;
  597. for (auto& text : m_textMatches)
  598. {
  599. text.clear();
  600. text.setPosition(5.f, posY);
  601. posY += 13.f;
  602. }
  603. AppList::getInstance().setCollapsed(true);
  604. AppList::getInstance().setIndexDelta(0);
  605. m_topInfos.setCollapsed(true);
  606. TweenEngine::Tween::to(m_iconSet, IconSet::POSITION_X, 0.3f)
  607. .target(155.f)
  608. .start(m_tweenManager);
  609. TweenEngine::Tween::to(m_textActiveDownloads, util3ds::TweenText::POSITION_X, 0.3f)
  610. .target(223.f)
  611. .start(m_tweenManager);
  612. TweenEngine::Tween::to(m_textInstalledCount, util3ds::TweenText::POSITION_X, 0.3f)
  613. .target(257.f)
  614. .start(m_tweenManager);
  615. m_lastKeyboardInput = "";
  616. m_keyboard.setCurrentInput(m_lastKeyboardInput);
  617. }
  618. TweenEngine::Tween::to(m_whiteScreen, m_whiteScreen.FILL_COLOR_ALPHA, 0.4f)
  619. .target(255.f)
  620. .setCallback(TweenEngine::TweenCallback::COMPLETE, [=](TweenEngine::BaseTween* source) {
  621. m_mode = mode;
  622. })
  623. .start(m_tweenManager);
  624. TweenEngine::Tween::to(m_whiteScreen, m_whiteScreen.FILL_COLOR_ALPHA, 0.4f)
  625. .target(0.f)
  626. .setCallback(TweenEngine::TweenCallback::COMPLETE, [=](TweenEngine::BaseTween* source) {
  627. m_isTransitioning = false;
  628. })
  629. .delay(0.4f)
  630. .start(m_tweenManager);
  631. if (mode > m_mode) {
  632. TweenEngine::Tween::to(m_bottomView, m_bottomView.CENTER_XY, 0.4f)
  633. .target(180.f, 120.f)
  634. .setCallback(TweenEngine::TweenCallback::COMPLETE, [=](TweenEngine::BaseTween* source) {
  635. m_bottomView.setCenter(cpp3ds::Vector2f(140.f, 120.f));
  636. })
  637. .start(m_tweenManager);
  638. if (mode == Settings || m_mode == Settings) {
  639. TweenEngine::Tween::to(*m_settingsGUI, m_settingsGUI->POSITION_XY, 0.4f)
  640. .target(-20.f, 0.f)
  641. .setCallback(TweenEngine::TweenCallback::COMPLETE, [=](TweenEngine::BaseTween* source) {
  642. m_settingsGUI->setPosition(cpp3ds::Vector2f(20.f, 0.f));
  643. })
  644. .start(m_tweenManager);
  645. TweenEngine::Tween::to(*m_settingsGUI, m_settingsGUI->POSITION_XY, 0.4f)
  646. .target(0.f, 0.f)
  647. .delay(0.4f)
  648. .start(m_tweenManager);
  649. }
  650. } else {
  651. TweenEngine::Tween::to(m_bottomView, m_bottomView.CENTER_XY, 0.4f)
  652. .target(140.f, 120.f)
  653. .setCallback(TweenEngine::TweenCallback::COMPLETE, [=](TweenEngine::BaseTween* source) {
  654. m_bottomView.setCenter(cpp3ds::Vector2f(180.f, 120.f));
  655. })
  656. .start(m_tweenManager);
  657. if (mode == Settings || m_mode == Settings) {
  658. TweenEngine::Tween::to(*m_settingsGUI, m_settingsGUI->POSITION_XY, 0.4f)
  659. .target(20.f, 0.f)
  660. .setCallback(TweenEngine::TweenCallback::COMPLETE, [=](TweenEngine::BaseTween* source) {
  661. m_settingsGUI->setPosition(cpp3ds::Vector2f(-20.f, 0.f));
  662. })
  663. .start(m_tweenManager);
  664. TweenEngine::Tween::to(*m_settingsGUI, m_settingsGUI->POSITION_XY, 0.4f)
  665. .target(0.f, 0.f)
  666. .delay(0.4f)
  667. .start(m_tweenManager);
  668. }
  669. }
  670. TweenEngine::Tween::to(m_bottomView, m_bottomView.CENTER_XY, 0.4f)
  671. .target(160.f, 120.f)
  672. .delay(0.4f)
  673. .start(m_tweenManager);
  674. if (mode == Settings || m_mode == Settings) {
  675. TweenEngine::Tween::to(*m_settingsGUI, m_settingsGUI->POSITION_XY, 0.4f)
  676. .target(0.f, 0.f)
  677. .delay(0.4f)
  678. .start(m_tweenManager);
  679. }
  680. m_isTransitioning = true;
  681. }
  682. bool BrowseState::playBGMeShop()
  683. {
  684. stopBGM();
  685. int bgmCount = 2;
  686. int randIndex = (std::rand() % bgmCount) + 1;
  687. // In case it doesn't find it, loop down until it hopefully does
  688. for (int i = randIndex; i > 0; --i)
  689. {
  690. std::string filePath = fmt::sprintf(FREESHOP_DIR "/music/eshop/boss_bgm%d", i);
  691. if (m_musicLoop.openFromFile(filePath))
  692. {
  693. m_musicLoop.play();
  694. break;
  695. }
  696. }
  697. }
  698. bool BrowseState::playBGM(const std::string &filename)
  699. {
  700. stopBGM();
  701. if (m_musicLoopBCSTM.openFromFile(filename)) {
  702. m_musicLoopBCSTM.play();
  703. m_musicMode = 1;
  704. } else if (m_musicLoopMP3.openFromFile(filename)) {
  705. m_musicLoopMP3.play();
  706. m_musicMode = 2;
  707. } else if (m_musicLoop.openFromFile(filename)) {
  708. m_musicLoop.play();
  709. m_musicMode = 3;
  710. } else {
  711. Notification::spawn(_("This song file type isn't supported."));
  712. return false;
  713. }
  714. m_musicFileName = filename;
  715. return true;
  716. }
  717. void BrowseState::stopBGM()
  718. {
  719. m_musicLoopBCSTM.stop();
  720. m_musicLoopMP3.stop();
  721. m_musicLoop.stop();
  722. m_musicMode = 0;
  723. }
  724. void BrowseState::reloadKeyboard()
  725. {
  726. m_isJapKeyboard = false;
  727. m_isTIDKeyboard = false;
  728. //Loading the keyboard locale file
  729. if (std::string(Config::get(Config::Keyboard).GetString()) == "azerty")
  730. m_keyboard.loadFromFile("kb/keyboard.azerty.xml");
  731. else if (std::string(Config::get(Config::Keyboard).GetString()) == "qwertz")
  732. m_keyboard.loadFromFile("kb/keyboard.qwertz.xml");
  733. else if (std::string(Config::get(Config::Keyboard).GetString()) == "jap") {
  734. m_keyboard.loadFromFile("kb/keyboard.jap.xml");
  735. m_isJapKeyboard = true;
  736. }
  737. else if (std::string(Config::get(Config::Keyboard).GetString()) == "tid") {
  738. m_keyboard.loadFromFile("kb/keyboard.titleid.xml");
  739. m_isTIDKeyboard = true;
  740. }
  741. else
  742. m_keyboard.loadFromFile("kb/keyboard.qwerty.xml");
  743. }
  744. int BrowseState::getMode()
  745. {
  746. return m_mode;
  747. }
  748. bool BrowseState::isAppInfoLoaded()
  749. {
  750. if (!m_appInfo.getAppItem())
  751. return false;
  752. else
  753. return true;
  754. }
  755. bool BrowseState::getJapKeyboard()
  756. {
  757. return m_isJapKeyboard;
  758. }
  759. bool BrowseState::getTIDKeyboard()
  760. {
  761. return m_isTIDKeyboard;
  762. }
  763. std::string BrowseState::getCtrSdPath()
  764. {
  765. return m_ctrSdPath;
  766. }
  767. void BrowseState::settingsSaveToConfig()
  768. {
  769. if (m_settingsGUI)
  770. m_settingsGUI->saveToConfig();
  771. }
  772. void BrowseState::setInstalledListSearchText(std::string text)
  773. {
  774. if (text.empty())
  775. m_textSearchInstalledList.setString(_("Search..."));
  776. else
  777. m_textSearchInstalledList.setString(text);
  778. m_textSearchInstalledList.setOrigin(m_textSearchInstalledList.getGlobalBounds().width / 2, 0);
  779. }
  780. void BrowseState::wokeUp()
  781. {
  782. m_topInfos.wokeUp();
  783. }
  784. void BrowseState::blockControls(bool isControlsBlocked)
  785. {
  786. m_isControlsBlocked = isControlsBlocked;
  787. }
  788. bool BrowseState::isControlsBlocked()
  789. {
  790. return m_isControlsBlocked;
  791. }
  792. } // namespace FreeShop