BrowseState.cpp 28 KB

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