#pragma once #include #include #include #include #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