diff --git a/README.md b/README.md index d2c4f61..d47dfb5 100644 --- a/README.md +++ b/README.md @@ -4,19 +4,22 @@ This module is supposed to be loaded with the [EnvironmentLoader](https://github other modules of the environment are loading. ## Usage -Place the `01_splashscreen.rpx` in the `[ENVIRONMENT]/modules/setup` folder and run the -EnvironmentLoader. The module will attempt to load the splash image, in this order: - 1. `[ENVIRONMENT]/splash.png` - 2. `[ENVIRONMENT]/splash.jpg` or `[ENVIRONMENT]/splash.jpeg` - 3. `[ENVIRONMENT]/splash.tga` - 4. A random image (PNG, JPEG or TGA) from the directory `[ENVIRONMENT]/splashes/`. - -If no splash image is found on the sd card, this module will effectively do nothing. + 1. Place the `01_splashscreen.rpx` in the `[ENVIRONMENT]/modules/setup` folder. + 2. Place your splash images (PNG, JPEG, TGA or WEBP) in the folder `SD:/wiiu/splashes/`. **Notes:** - - `[ENVIRONMENT]` is the directory of the environment, for Aroma with would be `sd:/wiiu/enviroments/aroma/splash.png` - - When using a `tga` make sure its 24 bit and uncompressed - - In theory any (reasonable) resolution is supported, something like 1280x720 is recommended + - `[ENVIRONMENT]` is the directory of the environment, for Aroma with would be `SD:/wiiu/enviroments/aroma`. + - When using a TGA image, make sure its 24 bit and uncompressed, + - In theory any (reasonable) resolution is supported, **1280x720** is recommended for best quality on both gamepad and TV screens. + +## Path priority +The module will attempt to load a splash image from multiple places, in this order: + 1. `[ENVIRONMENT]/`: an image named `splash.EXT` + 2. `[ENVIRONMENT]/splashes/`: a **random** image in that folder. + 3. `SD:/wiiu/`: an image named `splash.EXT` + 4. `SD:/wiiu/splashes/`: a **random** image in that folder. + +Where `EXT` can be `png`, `jpg`, `jpeg`, `tga` or `webp`. ## Buildflags @@ -32,8 +35,9 @@ If the [LoggingModule](https://github.com/wiiu-env/LoggingModule) is not present ## Building For building, you need to install (via devkitPro's `pacman`): - [wut](https://github.com/devkitPro/wut/) - - ppc-libpng - ppc-libjpeg-turbo + - ppc-libpng + - ppc-libwebp Then use the `make` command. diff --git a/source/gfx/SplashScreenDrawer.cpp b/source/gfx/SplashScreenDrawer.cpp index 45e4aff..f237aad 100644 --- a/source/gfx/SplashScreenDrawer.cpp +++ b/source/gfx/SplashScreenDrawer.cpp @@ -7,13 +7,15 @@ #include "gfx.h" #include "utils/logger.h" #include "utils/utils.h" +#include +#include #include -#include #include +#include #include #include #include -#include +#include #include /* @@ -120,14 +122,6 @@ uint8_t empty_png[119] = { 0x0C, 0x0C, 0x00, 0x00, 0x0E, 0x00, 0x01, 0x7A, 0xB1, 0xB9, 0x30, 0x00, 0x00, 0x00, 0x00, 0x49, 0x45, 0x4E, 0x44, 0xAE, 0x42, 0x60, 0x82}; -static std::filesystem::path ToLower(const std::filesystem::path &p) { - std::string result; - for (auto c : p.string()) { - result.push_back(std::tolower(static_cast(c))); - } - return result; -} - static GX2Texture *LoadImageAsTexture(const std::filesystem::path &filename) { std::vector buffer; if (LoadFileIntoBuffer(filename, buffer)) { @@ -145,55 +139,20 @@ static GX2Texture *LoadImageAsTexture(const std::filesystem::path &filename) { return nullptr; } -SplashScreenDrawer::SplashScreenDrawer(const std::filesystem::path &splash_base_path) { - mTexture = LoadImageAsTexture(splash_base_path / "splash.png"); - if (!mTexture) { - mTexture = LoadImageAsTexture(splash_base_path / "splash.jpg"); - } - if (!mTexture) { - mTexture = LoadImageAsTexture(splash_base_path / "splash.jpeg"); - } - if (!mTexture) { - mTexture = LoadImageAsTexture(splash_base_path / "splash.tga"); - } - if (!mTexture) { - mTexture = LoadImageAsTexture(splash_base_path / "splash.webp"); - } - if (!mTexture) { - // try to load a random one from "splashes/*" - try { - std::vector candidates; - for (const auto &entry : std::filesystem::directory_iterator{splash_base_path / "splashes"}) { - if (!entry.is_regular_file()) { - continue; - } - auto ext = ToLower(entry.path().extension()); - if (ext == ".png" || - ext == ".tga" || - ext == ".jpg" || - ext == ".jpeg" || - ext == ".webp") { - candidates.push_back(entry.path()); - } - } - if (!candidates.empty()) { - auto t = static_cast(OSGetTime()); - std::seed_seq seed{static_cast(t), - static_cast(t >> 32)}; - std::minstd_rand eng{seed}; - std::uniform_int_distribution dist{0, candidates.size() - 1}; - auto selected = dist(eng); - mTexture = LoadImageAsTexture(candidates[selected]); - } - } catch (std::exception &) {} - } - if (!mTexture) { - mTexture = PNG_LoadTexture(empty_png); - } - if (!mTexture) { - return; +SplashScreenDrawer::SplashScreenDrawer(const std::filesystem::path &envDir) { + // 1: Use env dir. + if (!LoadTextureFrom(envDir)) { + // 2: Use general dir. + if (!LoadTextureFrom("fs:/vol/external01/wiiu")) { + // 3: Use fallback empty texture. + mTexture = PNG_LoadTexture(empty_png); + } } + InitResources(); +} + +void SplashScreenDrawer::InitResources() { // create shader group mVertexShaderWrapper = DeserializeVertexShader(s_textureVertexShaderCompiled); mPixelShaderWrapper = DeserializePixelShader(s_texturePixelShaderCompiled); @@ -232,6 +191,52 @@ SplashScreenDrawer::SplashScreenDrawer(const std::filesystem::path &splash_base_ GX2InitSampler(&mSampler, GX2_TEX_CLAMP_MODE_CLAMP, GX2_TEX_XY_FILTER_MODE_LINEAR); } +bool SplashScreenDrawer::LoadTextureFrom(const std::filesystem::path &dir) { + if (dir.empty()) { + return false; + } + + const std::array extensions = { + ".png", + ".jpg", + ".jpeg", + ".tga", + ".webp"}; + + // First try the splash.* image. + for (const auto &ext : extensions) { + auto fname = std::string{"splash"} + ext; + mTexture = LoadImageAsTexture(dir / fname); + if (mTexture) { + return true; + } + } + + try { + // Make a list of all candidates in splashes/* to select one at random. + std::vector candidates; + for (const auto &entry : std::filesystem::directory_iterator{dir / "splashes"}) { + if (!entry.is_regular_file()) { + continue; + } + auto ext = ToLower(entry.path().extension()); + if (std::ranges::contains(extensions, ext)) { + candidates.push_back(entry.path()); + } + } + if (!candidates.empty()) { + auto selected = GetRandomIndex(candidates.size()); + mTexture = LoadImageAsTexture(candidates[selected]); + if (mTexture) { + return true; + } + } + } catch (std::exception &e) { + DEBUG_FUNCTION_LINE_INFO("Loading texture failed: %s", e.what()); + } + return false; +} + void SplashScreenDrawer::Draw() { if (!mTexture) { DEBUG_FUNCTION_LINE_INFO("Texture is missing"); diff --git a/source/gfx/SplashScreenDrawer.h b/source/gfx/SplashScreenDrawer.h index 2576fa3..5776ab8 100644 --- a/source/gfx/SplashScreenDrawer.h +++ b/source/gfx/SplashScreenDrawer.h @@ -11,7 +11,7 @@ class SplashScreenDrawer { public: - explicit SplashScreenDrawer(const std::filesystem::path &meta_dir); + explicit SplashScreenDrawer(const std::filesystem::path &envDir); void Draw(); @@ -47,4 +47,8 @@ class SplashScreenDrawer { GX2RBuffer mTexCoordBuffer = {}; GX2Texture *mTexture = nullptr; GX2Sampler mSampler = {}; + + void InitResources(); + + bool LoadTextureFrom(const std::filesystem::path &dir); }; diff --git a/source/main.cpp b/source/main.cpp index 64fef0d..86d90b8 100644 --- a/source/main.cpp +++ b/source/main.cpp @@ -7,18 +7,18 @@ #define MODULE_VERSION "v0.3" #define MODULE_VERSION_FULL MODULE_VERSION SPLASHSCREEN_MODULE_VERSION_EXTRA -int32_t main(int32_t argc, char **argv) { +int main(int argc, char **argv) { initLogging(); DEBUG_FUNCTION_LINE_INFO("Running SplashScreen Module " MODULE_VERSION_FULL ""); - std::filesystem::path basePath = "fs:/vol/external01/wiiu"; + std::filesystem::path envDir; if (argc >= 1) { - basePath = argv[0]; + envDir = argv[0]; } GfxInit(); { - SplashScreenDrawer splashScreenDrawer(basePath); + SplashScreenDrawer splashScreenDrawer(envDir); splashScreenDrawer.Draw(); } GfxShutdown(); diff --git a/source/utils/utils.cpp b/source/utils/utils.cpp index b657912..d8eba44 100644 --- a/source/utils/utils.cpp +++ b/source/utils/utils.cpp @@ -1,7 +1,11 @@ #include "utils.h" #include "logger.h" +#include #include +#include +#include #include +#include #include #include @@ -30,3 +34,26 @@ bool LoadFileIntoBuffer(const std::filesystem::path &filename, std::vector sRandomEngine; +} // namespace + +std::size_t GetRandomIndex(std::size_t size) { + if (!sRandomEngine) { + auto t = static_cast(OSGetTime()); + std::seed_seq seeder{static_cast(t), + static_cast(t >> 32)}; + sRandomEngine.emplace(seeder); + } + std::uniform_int_distribution dist{0, size - 1}; + return dist(*sRandomEngine); +} + +std::filesystem::path ToLower(const std::filesystem::path &p) { + std::string result; + for (auto c : p.string()) { + result.push_back(std::tolower(static_cast(c))); + } + return result; +} diff --git a/source/utils/utils.h b/source/utils/utils.h index ff36367..3af0438 100644 --- a/source/utils/utils.h +++ b/source/utils/utils.h @@ -1,7 +1,12 @@ #pragma once +#include #include #include #include bool LoadFileIntoBuffer(const std::filesystem::path &filename, std::vector &buffer); + +std::size_t GetRandomIndex(std::size_t size); + +std::filesystem::path ToLower(const std::filesystem::path &p);