diff --git a/Dockerfile b/Dockerfile index 312a02e..47768ed 100644 --- a/Dockerfile +++ b/Dockerfile @@ -1,5 +1,5 @@ -FROM wiiuenv/devkitppc:20211229 +FROM ghcr.io/wiiu-env/devkitppc:20240704 COPY --from=wiiuenv/libgui:20220109 /artifacts $DEVKITPRO -WORKDIR project \ No newline at end of file +WORKDIR project diff --git a/README.md b/README.md index a172b22..7c271e3 100644 --- a/README.md +++ b/README.md @@ -11,7 +11,7 @@ A simple Wii U Menu replacement, still in early development and not ready for a ## Known Issues - Random crashes - The Keyboard input is implemented, but result is ignored. -- nn::spm is not initalized and no quick start menu support. For the it's relying on the [AutobootModule](https://github.com/wiiu-env/AutobootModule) doing this. +- `nn::spm` is not initalized and no quick start menu support. For the it's relying on the [AutobootModule](https://github.com/wiiu-env/AutobootModule) doing this. - No sound on splash screen. - Probably a lot more @@ -23,7 +23,7 @@ A simple Wii U Menu replacement, still in early development and not ready for a - Display applets like the original Wii U Menu - Implement Account selection when no default account is set. - Implement update check/no way to update games -- Properly implement nn::spm and nn:sl (external storage and quick start menu) +- Properly implement `nn::spm` and `nn::sl` (external storage and quick start menu) - Fix search - Implement all the other stuff the Wii U Menu offers (Account creationg, switching between Accounts, set default account etc.) - Implement ways to launch the original Wii U Menu. diff --git a/src/Application.cpp b/src/Application.cpp index 907df52..bc7b1fc 100644 --- a/src/Application.cpp +++ b/src/Application.cpp @@ -1,42 +1,165 @@ -/**************************************************************************** - * Copyright (C) 2015 Dimok - * - * This program is free software: you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation, either version 3 of the License, or - * (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program. If not, see . - ****************************************************************************/ #include "Application.h" #include "common/common.h" #include "resources/Resources.h" #include "utils/AsyncExecutor.h" #include "utils/logger.h" +#include "utils/SplashSoundPlayer.h" #include #include #include -#include -#include -#include -#include -#include +#include +#include "gui/FreeTypeGX.h" +#include "gui/VPadController.h" +#include "gui/WPadController.h" +#include "gui/memory.h" +#include "gui/sounds/SoundHandler.hpp" +#include +#include #include +#include #include +#include +#include #include +#include +#include +#include -Application *Application::applicationInstance = nullptr; -bool Application::exitApplication = false; -bool Application::quitRequest = false; +static void InitEmptyExternalStorage() { + DEBUG_FUNCTION_LINE("Fallback to empty ExtendedStorage"); + nn::spm::VolumeId empty{}; + nn::spm::SetDefaultExtendedStorageVolumeId(empty); -Application::Application() - : CThread(CThread::eAttributeAffCore1 | CThread::eAttributePinnedAff, 0, 0x800000), bgMusic(nullptr), video(nullptr), mainWindow(nullptr), fontSystem(nullptr), exitCode(0) { + nn::spm::StorageIndex storageIndex = 0; + nn::spm::SetExtendedStorage(&storageIndex); +} + +static int numberUSBStorageDevicesConnected() { + DEBUG_FUNCTION_LINE("Check if USB Storage is connected"); + auto *handle = (UhsHandle *) memalign(0x40, sizeof(UhsHandle)); + if (!handle) { + return -1; + } + memset(handle, 0, sizeof(UhsHandle)); + auto *config = (UhsConfig *) memalign(0x40, sizeof(UhsConfig)); + if (!config) { + free(handle); + return -2; + } + memset(config, 0, sizeof(UhsConfig)); + + config->controller_num = 0; + uint32_t size = 5120; + void *buffer = memalign(0x40, size); + if (!buffer) { + free(handle); + free(config); + return -3; + } + memset(buffer, 0, size); + + config->buffer = buffer; + config->buffer_size = size; + + if (UhsClientOpen(handle, config) != UHS_STATUS_OK) { + DEBUG_FUNCTION_LINE("UhsClient failed"); + free(handle); + free(config); + free(buffer); + return -4; + } + + UhsInterfaceProfile profiles[10]; + UhsInterfaceFilter filter = { + .match_params = MATCH_ANY}; + + UHSStatus result; + if ((result = UhsQueryInterfaces(handle, &filter, profiles, 10)) <= UHS_STATUS_OK) { + DEBUG_FUNCTION_LINE("UhsQueryInterfaces failed"); + UhsClientClose(handle); + free(handle); + free(config); + free(buffer); + return -5; + } + + auto found = 0; + for (int i = 0; i < (int) result; i++) { + if (profiles[i].if_desc.bInterfaceClass == USBCLASS_STORAGE) { + DEBUG_FUNCTION_LINE("Found USBCLASS_STORAGE"); + found++; + } + } + + UhsClientClose(handle); + free(handle); + free(config); + free(buffer); + return found; +} + +void initExternalStorage() { + if (OSGetTitleID() == _SYSGetSystemApplicationTitleId(SYSTEM_APP_ID_MII_MAKER)) { + // nn::spm functions always call OSFatal when they fail, so we make sure have permission to use + // the lib before actually using it. + return; + } + int numConnectedStorage; + int maxTries = 1200; // Wait up to 20 seconds, like the Wii U Menu + if ((numConnectedStorage = numberUSBStorageDevicesConnected()) <= 0) { + maxTries = 1; // Only try once if no USBStorageDrive is connected + } else { + DEBUG_FUNCTION_LINE("Connected StorageDevices = %d", numConnectedStorage); + } + + nn::spm::Initialize(); + + nn::spm::StorageListItem items[0x20]; + int tries = 0; + bool found = false; + + while (tries < maxTries) { + int32_t numItems = nn::spm::GetStorageList(items, 0x20); + + DEBUG_FUNCTION_LINE("Number of items: %d", numItems); + + for (int32_t i = 0; i < numItems; i++) { + if (items[i].type == nn::spm::STORAGE_TYPE_WFS) { + nn::spm::StorageInfo info{}; + if (nn::spm::GetStorageInfo(&info, &items[i].index) == 0) { + DEBUG_FUNCTION_LINE("Using %s for extended storage", info.path); + + nn::spm::SetExtendedStorage(&items[i].index); + ACPMountExternalStorage(); + + nn::spm::SetDefaultExtendedStorageVolumeId(info.volumeId); + + found = true; + break; + } + } + } + if (found || (numConnectedStorage == numItems)) { + DEBUG_FUNCTION_LINE("Found all expected items, breaking."); + break; + } + OSSleepTicks(OSMillisecondsToTicks(16)); + tries++; + } + if (!found) { + if (numConnectedStorage > 0) { + DEBUG_FUNCTION_LINE("USB Storage is connected but either it doesn't have a WFS partition or we ran into a timeout."); + } + InitEmptyExternalStorage(); + } + + nn::spm::Finalize(); +} +Application * Application::applicationInstance = nullptr; +bool Application::exitApplication = false; +bool Application::quitRequest = false; + +Application::Application(): CThread(CThread::eAttributeAffCore1 | CThread::eAttributePinnedAff, 0, 0x800000), bgMusic(nullptr), video(nullptr), mainWindow(nullptr), fontSystem(nullptr), exitCode(0) { controller[0] = new VPadController(GuiTrigger::CHANNEL_1); controller[1] = new WPadController(GuiTrigger::CHANNEL_2); controller[2] = new WPadController(GuiTrigger::CHANNEL_3); @@ -45,11 +168,13 @@ Application::Application() //! create bgMusic bgMusic = new GuiSound(Resources::GetFile("bgMusic.ogg"), Resources::GetFileSize("bgMusic.ogg")); - bgMusic->SetLoop(true); - bgMusic->Play(); - bgMusic->SetVolume(50); + bgMusic -> SetLoop(true); + bgMusic -> Play(); + bgMusic -> SetVolume(50); - AsyncExecutor::execute([] { DEBUG_FUNCTION_LINE("Hello"); }); + AsyncExecutor::execute([] { + DEBUG_FUNCTION_LINE("Hello"); + }); exitApplication = false; @@ -62,7 +187,7 @@ Application::~Application() { DEBUG_FUNCTION_LINE("Destroy controller"); - for (auto &i : controller) { + for (auto & i: controller) { delete i; } @@ -88,13 +213,20 @@ int32_t Application::exec() { } void Application::quit(int32_t code) { - exitCode = code; + exitCode = code; exitApplication = true; - quitRequest = true; + quitRequest = true; } void Application::fadeOut() { - GuiImage fadeOut(video->getTvWidth(), video->getTvHeight(), (GX2Color){0, 0, 0, 255}); + GuiImage fadeOut(video->getTvWidth(), video->getTvHeight(), (GX2Color) { + 0, + 0, + 0, + 255 + }); + +glm::mat4 identityMatrix(1.0f); for (int32_t i = 0; i < 255; i += 10) { if (i > 255) @@ -103,30 +235,30 @@ void Application::fadeOut() { fadeOut.setAlpha(i / 255.0f); //! start rendering DRC - video->prepareDrcRendering(); - mainWindow->drawDrc(video); + video -> prepareDrcRendering(); + mainWindow -> drawDrc(video); GX2SetDepthOnlyControl(GX2_DISABLE, GX2_DISABLE, GX2_COMPARE_FUNC_ALWAYS); - fadeOut.draw(video); + fadeOut.draw(video, identityMatrix); GX2SetDepthOnlyControl(GX2_ENABLE, GX2_ENABLE, GX2_COMPARE_FUNC_LEQUAL); - video->drcDrawDone(); + video -> drcDrawDone(); //! start rendering TV - video->prepareTvRendering(); + video -> prepareTvRendering(); - mainWindow->drawTv(video); + mainWindow -> drawTv(video); GX2SetDepthOnlyControl(GX2_DISABLE, GX2_DISABLE, GX2_COMPARE_FUNC_ALWAYS); - fadeOut.draw(video); + fadeOut.draw(video, identityMatrix); GX2SetDepthOnlyControl(GX2_ENABLE, GX2_ENABLE, GX2_COMPARE_FUNC_LEQUAL); - video->tvDrawDone(); + video -> tvDrawDone(); //! as last point update the effects as it can drop elements - mainWindow->updateEffects(); + mainWindow -> updateEffects(); - video->waitForVSync(); + video -> waitForVSync(); } } @@ -134,63 +266,63 @@ bool Application::procUI() { bool executeProcess = false; switch (ProcUIProcessMessages(true)) { - case PROCUI_STATUS_EXITING: { - DEBUG_FUNCTION_LINE("PROCUI_STATUS_EXITING"); - exitCode = EXIT_SUCCESS; - exitApplication = true; - break; - } - case PROCUI_STATUS_RELEASE_FOREGROUND: { - DEBUG_FUNCTION_LINE("PROCUI_STATUS_RELEASE_FOREGROUND"); - if (video != nullptr) { - // we can turn ofF the screen but we don't need to and it will display the last image - video->tvEnable(true); - video->drcEnable(true); - - DEBUG_FUNCTION_LINE("delete fontSystem"); - delete fontSystem; - fontSystem = nullptr; - - DEBUG_FUNCTION_LINE("delete video"); - delete video; - video = nullptr; - - DEBUG_FUNCTION_LINE("deinitialze memory"); - libgui_memoryRelease(); - ProcUIDrawDoneRelease(); - } else { - ProcUIDrawDoneRelease(); - } - break; + case PROCUI_STATUS_EXITING: { + DEBUG_FUNCTION_LINE("PROCUI_STATUS_EXITING"); + exitCode = EXIT_SUCCESS; + exitApplication = true; + break; + } + case PROCUI_STATUS_RELEASE_FOREGROUND: { + DEBUG_FUNCTION_LINE("PROCUI_STATUS_RELEASE_FOREGROUND"); + if (video != nullptr) { + // we can turn ofF the screen but we don't need to and it will display the last image + video -> tvEnable(true); + video -> drcEnable(true); + + DEBUG_FUNCTION_LINE("delete fontSystem"); + delete fontSystem; + fontSystem = nullptr; + + DEBUG_FUNCTION_LINE("delete video"); + delete video; + video = nullptr; + + DEBUG_FUNCTION_LINE("deinitialze memory"); + libgui_memoryRelease(); + ProcUIDrawDoneRelease(); + } else { + ProcUIDrawDoneRelease(); } - case PROCUI_STATUS_IN_FOREGROUND: { - if (!quitRequest) { - if (video == nullptr) { - DEBUG_FUNCTION_LINE("PROCUI_STATUS_IN_FOREGROUND"); - DEBUG_FUNCTION_LINE("initialze memory"); - libgui_memoryInitialize(); - - DEBUG_FUNCTION_LINE("Initialize video"); - video = new CVideo(GX2_TV_SCAN_MODE_720P, GX2_DRC_RENDER_MODE_SINGLE); - DEBUG_FUNCTION_LINE("Video size %i x %i", video->getTvWidth(), video->getTvHeight()); - - //! setup default Font - DEBUG_FUNCTION_LINE("Initialize main font system"); - auto *fontSystem = new FreeTypeGX(Resources::GetFile("font.ttf"), Resources::GetFileSize("font.ttf"), true); - GuiText::setPresetFont(fontSystem); - - if (mainWindow == nullptr) { - DEBUG_FUNCTION_LINE("Initialize main window"); - mainWindow = new MainWindow(video->getTvWidth(), video->getTvHeight()); - } + break; + } + case PROCUI_STATUS_IN_FOREGROUND: { + if (!quitRequest) { + if (video == nullptr) { + DEBUG_FUNCTION_LINE("PROCUI_STATUS_IN_FOREGROUND"); + DEBUG_FUNCTION_LINE("initialze memory"); + libgui_memoryInitialize(); + + DEBUG_FUNCTION_LINE("Initialize video"); + video = new CVideo(GX2_TV_SCAN_MODE_720P, GX2_DRC_RENDER_MODE_SINGLE); + DEBUG_FUNCTION_LINE("Video size %i x %i", video -> getTvWidth(), video -> getTvHeight()); + + //! setup default Font + DEBUG_FUNCTION_LINE("Initialize main font system"); + auto * fontSystem = new FreeTypeGX(Resources::GetFile("font.ttf"), Resources::GetFileSize("font.ttf"), true); + GuiText::setPresetFont(fontSystem); + + if (mainWindow == nullptr) { + DEBUG_FUNCTION_LINE("Initialize main window"); + mainWindow = new MainWindow(video -> getTvWidth(), video -> getTvHeight()); } - executeProcess = true; } - break; + executeProcess = true; } - case PROCUI_STATUS_IN_BACKGROUND: - default: - break; + break; + } + case PROCUI_STATUS_IN_BACKGROUND: + default: + break; } return executeProcess; @@ -205,43 +337,43 @@ void Application::executeThread() { continue; } - mainWindow->lockGUI(); - mainWindow->process(); + mainWindow -> lockGUI(); + mainWindow -> process(); //! Read out inputs - for (auto &i : controller) { - if (!i->update(video->getTvWidth(), video->getTvHeight())) + for (auto & i: controller) { + if (!i -> update(video -> getTvWidth(), video -> getTvHeight())) continue; //! update controller states - mainWindow->update(i); + mainWindow -> update(i); } //! start rendering DRC - video->prepareDrcRendering(); - mainWindow->drawDrc(video); - video->drcDrawDone(); + video -> prepareDrcRendering(); + mainWindow -> drawDrc(video); + video -> drcDrawDone(); //! start rendering TV - video->prepareTvRendering(); - mainWindow->drawTv(video); - video->tvDrawDone(); + video -> prepareTvRendering(); + mainWindow -> drawTv(video); + video -> tvDrawDone(); //! enable screen after first frame render - if (video->getFrameCount() == 0) { - video->tvEnable(true); - video->drcEnable(true); + if (video -> getFrameCount() == 0) { + video -> tvEnable(true); + video -> drcEnable(true); } //! as last point update the effects as it can drop elements - mainWindow->updateEffects(); - mainWindow->unlockGUI(); + mainWindow -> updateEffects(); + mainWindow -> unlockGUI(); - video->waitForVSync(); + video -> waitForVSync(); } if (bgMusic) { - bgMusic->SetVolume(0); + bgMusic -> SetVolume(0); } DEBUG_FUNCTION_LINE("delete mainWindow"); @@ -258,4 +390,5 @@ void Application::executeThread() { DEBUG_FUNCTION_LINE("deinitialize memory"); libgui_memoryRelease(); + }