Broken UI and Loading screens

This commit is contained in:
Jan Racek
2026-03-26 19:14:37 +01:00
parent 37ba4c85fe
commit 19bb2254f3
8 changed files with 630 additions and 6 deletions

125
src/loadingscreen.hh Normal file
View File

@@ -0,0 +1,125 @@
#pragma once
#include <stdint.h>
#include <psyqo/font.hh>
#include <psyqo/gpu.hh>
#include <psyqo/primitives/common.hh>
#include "uisystem.hh"
namespace psxsplash {
/// Loader pack header — matches the binary written by PSXLoaderPackWriter.cs.
struct LoaderPackHeader {
char magic[2]; // "LP"
uint16_t version; // 2
uint8_t fontCount;
uint8_t canvasCount; // always 1
uint16_t resW;
uint16_t resH;
uint8_t atlasCount; // texture atlases for UI images
uint8_t clutCount; // CLUTs for indexed-color images
uint32_t tableOffset;
};
static_assert(sizeof(LoaderPackHeader) == 16, "LoaderPackHeader must be 16 bytes");
/// Atlas entry in the loader pack (matches SPLASHPACKTextureAtlas layout).
struct LoaderPackAtlas {
uint32_t pixelDataOffset; // absolute offset in file
uint16_t width, height;
uint16_t x, y; // VRAM position
};
static_assert(sizeof(LoaderPackAtlas) == 12, "LoaderPackAtlas must be 12 bytes");
/// CLUT entry in the loader pack (matches SPLASHPACKClut layout).
struct LoaderPackClut {
uint32_t clutDataOffset; // absolute offset in file
uint16_t clutX; // VRAM X (in 16-pixel units × 16)
uint16_t clutY; // VRAM Y
uint16_t length; // number of palette entries
uint16_t pad;
};
static_assert(sizeof(LoaderPackClut) == 12, "LoaderPackClut must be 12 bytes");
/// Loading screen controller.
///
/// Loads a .loading file (tiny UI-only binary), renders the screen,
/// and provides progress updates during the main splashpack load.
///
/// Strategy:
/// 1. Blank the screen immediately (user sees black, not frozen frame).
/// 2. Load the .loading file (small, fast).
/// 3. Upload texture atlases, CLUTs, and custom font textures to VRAM.
/// 4. Render ALL elements to BOTH framebuffers using direct GPU commands:
/// - Images via sendPrimitive(GouraudTexturedTriangle)
/// - Custom font text via sendPrimitive(TPage) + sendPrimitive(Sprite)
/// - System font text via font.print()
/// - Boxes and progress bars via sendPrimitive(Rectangle)
/// 5. FREE all loaded data (loader pack) so the splashpack has room.
/// 6. During splashpack loading, call updateProgress() to redraw ONLY the
/// progress bar (simple rectangles in both framebuffers — no VRAM needed).
///
/// The progress bar is found by looking for a PSXUIProgressBar element named "loading".
class LoadingScreen {
public:
/// Try to load a loader pack from a file.
/// Returns true if a loading screen was successfully loaded.
/// @param gpu GPU reference for rendering.
/// @param systemFont System font used if no custom font for text.
/// @param sceneIndex Scene index to derive the filename (scene_N.loading).
bool load(psyqo::GPU& gpu, psyqo::Font<>& systemFont, int sceneIndex);
/// Render all loading screen elements to BOTH framebuffers,
/// then FREE all loaded data. After this, only updateProgress works.
void renderInitialAndFree(psyqo::GPU& gpu);
/// Update the progress bar to the given percentage (0-100).
/// Redraws the progress bar rectangles in both framebuffers.
/// Safe after data is freed — uses only cached layout values.
void updateProgress(psyqo::GPU& gpu, uint8_t percent);
/// Returns true if a loading screen was loaded (even after data freed).
bool isActive() const { return m_active; }
private:
/// Render a filled rectangle at an absolute VRAM position.
void drawRect(psyqo::GPU& gpu, int16_t x, int16_t y, int16_t w, int16_t h,
uint8_t r, uint8_t g, uint8_t b);
/// Render an image element (two textured triangles).
/// Assumes DrawingOffset is already configured for the target buffer.
void drawImage(psyqo::GPU& gpu, int handle, int16_t x, int16_t y,
int16_t w, int16_t h, uint8_t r, uint8_t g, uint8_t b);
/// Render custom-font text via sendPrimitive (TPage + Sprite per glyph).
/// Assumes DrawingOffset is already configured for the target buffer.
void drawCustomText(psyqo::GPU& gpu, int handle, int16_t x, int16_t y,
uint8_t r, uint8_t g, uint8_t b);
/// Render ALL elements to a single framebuffer at the given VRAM Y offset.
void renderToBuffer(psyqo::GPU& gpu, int16_t yOffset);
/// Upload atlas/CLUT data to VRAM.
void uploadTextures(psyqo::GPU& gpu);
/// Find the "loading" progress bar element and cache its layout.
void findProgressBar();
uint8_t* m_data = nullptr;
int m_dataSize = 0;
psyqo::Font<>* m_font = nullptr;
bool m_active = false;
// Temporary UISystem to parse the loader pack's canvas/element data
UISystem m_ui;
// Cached layout for the "loading" progress bar (resolved at load time)
bool m_hasProgressBar = false;
int16_t m_barX = 0, m_barY = 0, m_barW = 0, m_barH = 0;
uint8_t m_barFillR = 255, m_barFillG = 255, m_barFillB = 255;
uint8_t m_barBgR = 0, m_barBgG = 0, m_barBgB = 0;
// Resolution from the loader pack
int16_t m_resW = 320, m_resH = 240;
};
} // namespace psxsplash