Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
28 changes: 16 additions & 12 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -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

Expand All @@ -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.

Expand Down
119 changes: 62 additions & 57 deletions source/gfx/SplashScreenDrawer.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -7,13 +7,15 @@
#include "gfx.h"
#include "utils/logger.h"
#include "utils/utils.h"
#include <algorithm>
#include <array>
#include <cctype>
#include <coreinit/time.h>
#include <cstdlib>
#include <exception>
#include <gx2/draw.h>
#include <gx2/mem.h>
#include <gx2r/draw.h>
#include <random>
#include <string>
#include <whb/log.h>

/*
Expand Down Expand Up @@ -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<unsigned char>(c)));
}
return result;
}

static GX2Texture *LoadImageAsTexture(const std::filesystem::path &filename) {
std::vector<uint8_t> buffer;
if (LoadFileIntoBuffer(filename, buffer)) {
Expand All @@ -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<std::filesystem::path> 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<std::uint64_t>(OSGetTime());
std::seed_seq seed{static_cast<std::uint32_t>(t),
static_cast<std::uint32_t>(t >> 32)};
std::minstd_rand eng{seed};
std::uniform_int_distribution<std::size_t> 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);
Expand Down Expand Up @@ -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<std::filesystem::path> 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");
Expand Down
6 changes: 5 additions & 1 deletion source/gfx/SplashScreenDrawer.h
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,7 @@

class SplashScreenDrawer {
public:
explicit SplashScreenDrawer(const std::filesystem::path &meta_dir);
explicit SplashScreenDrawer(const std::filesystem::path &envDir);

void Draw();

Expand Down Expand Up @@ -47,4 +47,8 @@ class SplashScreenDrawer {
GX2RBuffer mTexCoordBuffer = {};
GX2Texture *mTexture = nullptr;
GX2Sampler mSampler = {};

void InitResources();

bool LoadTextureFrom(const std::filesystem::path &dir);
};
8 changes: 4 additions & 4 deletions source/main.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -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();
Expand Down
27 changes: 27 additions & 0 deletions source/utils/utils.cpp
Original file line number Diff line number Diff line change
@@ -1,7 +1,11 @@
#include "utils.h"
#include "logger.h"
#include <coreinit/time.h>
#include <cstdio>
#include <optional>
#include <random>
#include <stdexcept>
#include <string>
#include <sys/stat.h>
#include <vector>

Expand Down Expand Up @@ -30,3 +34,26 @@ bool LoadFileIntoBuffer(const std::filesystem::path &filename, std::vector<uint8
fclose(f);
return true;
}

namespace {
std::optional<std::minstd_rand> sRandomEngine;
} // namespace

std::size_t GetRandomIndex(std::size_t size) {
if (!sRandomEngine) {
auto t = static_cast<std::uint64_t>(OSGetTime());
std::seed_seq seeder{static_cast<std::uint32_t>(t),
static_cast<std::uint32_t>(t >> 32)};
sRandomEngine.emplace(seeder);
}
std::uniform_int_distribution<std::size_t> 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<unsigned char>(c)));
}
return result;
}
5 changes: 5 additions & 0 deletions source/utils/utils.h
Original file line number Diff line number Diff line change
@@ -1,7 +1,12 @@
#pragma once

#include <cstddef>
#include <cstdint>
#include <filesystem>
#include <vector>

bool LoadFileIntoBuffer(const std::filesystem::path &filename, std::vector<uint8_t> &buffer);

std::size_t GetRandomIndex(std::size_t size);

std::filesystem::path ToLower(const std::filesystem::path &p);