From a1ac060ce389e3cd5100487d64423f95bb1b7f6d Mon Sep 17 00:00:00 2001 From: Pk11 Date: Thu, 26 Aug 2021 11:12:27 -0500 Subject: [PATCH] Add spritesheet and change image format --- include/font.hpp | 4 +- include/gui.hpp | 16 +-- include/image.hpp | 64 +++++------ include/spritesheet.hpp | 66 +++++++++++ source/gui.cpp | 13 +-- source/image.cpp | 235 ++++++++++++++++++++++------------------ source/sprite.cpp | 8 +- source/spritesheet.cpp | 92 ++++++++++++++++ 8 files changed, 336 insertions(+), 162 deletions(-) create mode 100644 include/spritesheet.hpp create mode 100644 source/spritesheet.cpp diff --git a/include/font.hpp b/include/font.hpp index e23bb6b..4975ec7 100644 --- a/include/font.hpp +++ b/include/font.hpp @@ -60,8 +60,8 @@ private: static u8 textBuf[2][256 * 192]; #endif - u8 tileWidth, tileHeight; - u16 tileSize; + u8 tileWidth = 0, tileHeight = 0; + u16 tileSize = 0; u16 questionMark = 0; std::vector fontTiles; std::vector fontWidths; diff --git a/include/gui.hpp b/include/gui.hpp index 2722c04..32f8e78 100644 --- a/include/gui.hpp +++ b/include/gui.hpp @@ -28,9 +28,9 @@ #define _UNIVERSAL_CORE_GUI_HPP #include "font.hpp" -#include "image.hpp" #include "screen.hpp" #include "sprite.hpp" +#include "spritesheet.hpp" #include @@ -66,23 +66,23 @@ namespace Gui { void clearScreen(bool top); /* - Load an image (TODO: spritesheets?). + Load an a spritesheet - Path: Path to the image file. (GFX) - sheet: Reference to the Image declaration. + Path: Path to the spritesheet file. (TDX) + sheet: Reference to the Spritesheet declaration. */ - bool loadSheet(const char *Path, Image &image); + bool loadSheet(const char *Path, Spritesheet &image); /* Draw an image. - image: The image which should be used. - imgIndex: For compatibility with 3DS, unused. + image: The spritesheet which should be used. + imgIndex: Index within the spritesheet. x: The X Position where the sprite should be drawn. y: The Y Position where the sprite should be drawn. ScaleX: The X-Scale for the sprite. (Optional!) ScaleY: The Y-Scale for the sprite. (Optional!) */ - void DrawSprite(Image image, size_t imgindex, int x, int y, float ScaleX = 1.0f, float ScaleY = 1.0f); + void DrawSprite(Spritesheet &sheet, size_t imgindex, int x, int y, float ScaleX = 1.0f, float ScaleY = 1.0f); /* Initialize the GUI. diff --git a/include/image.hpp b/include/image.hpp index b8a28ae..c86218d 100644 --- a/include/image.hpp +++ b/include/image.hpp @@ -33,46 +33,52 @@ class Image { private: - u16 _width; - u16 _height; - std::vector _bitmap; - std::vector _palette; - u16 _palOfs; + u32 _width = 0; + u32 _height = 0; + u8 _paletteStart = 0; + std::vector _bitmap = {}; + std::vector _palette = {}; + + static void decompressGrf(void *dst, const void *src); + + void load(const u8 *grf, u8 paletteStart); public: /** - * Image drawing class + * @brief Image drawing class * @param paths The paths to try load the image from, from highest to lowest priority + * @param paletteStart Where to start the palette in VRAM */ - Image(const std::vector &paths); + Image(const std::vector &paths, u8 paletteStart); /** - * @brief Text printing class + * @brief Image drawing class * @param path The path to load the image from + * @param paletteStart Where to start the palette in VRAM */ - Image(const std::string &path) : Image(std::vector({path})) {}; + Image(const std::string &path, u8 paletteStart) : Image(std::vector({path}), paletteStart) {}; /** * @brief Image drawing class - * @param file The file to load the image from, seeked to the '.GFX' magic + * @param grf Pointer to a GRF in memory + * @param paletteStart Where to start the palette in VRAM */ - Image(FILE *file); + Image(const u8 *grf, u8 paletteStart); - Image() {}; ~Image(void) {}; u16 width(void) const { return _width; } u16 height(void) const { return _height; } + u8 paletteStart(void) const { return _paletteStart; } std::vector bitmap(void) const { return _bitmap; } std::vector palette(void) const { return _palette; } - u16 palOfs(void) const { return _palOfs; } + + void changePaletteStart(u8 paletteStart); /** - * @brief Draws the image to a background layer, faster but without alpha, scaling, or palette offsetting - * @param x The X position to draw at - * @param y The Y position to draw at - * @param copyPal (Optional) Whether to copy the image's palette into palette VRAM + * @brief Copies the palette into VRAM + * @param paletteStart Where to start the palette in VRAM */ - void draw(int x, int y, bool copyPal = true); + void copyPalette(int paletteStart); /** * @brief Draws the image to a background layer, slower but can skip alpha, scale, and offset the palette @@ -80,22 +86,9 @@ public: * @param y The Y position to draw at * @param scaleX (Optional) The scale for the X axis * @param scaleY (Optional) The scale for the Y axis - * @param paletteOffset (Optional) How much to offset the palette by - * @param copyPal (Optional) Whether to copy the image's palette into palette VRAM + * @param skipAlpha (Optional) Whether to skip transparent pixels, draws faster when disabled at 1.0 scale */ - void drawSpecial(int x, int y, float scaleX = 1.0f, float scaleY = 1.0f, int paletteOffset = 0, bool copyPal = true); - - /** - * @brief Draws a segment of an image to a background layer, faster but overwrites alpha and no scaling or palette offsetting - * @param x The X position to draw at - * @param y The Y position to draw at - * @param imageX The X position in the image to draw from - * @param imageY The Y position in the image to draw from - * @param w The width to draw - * @param h The height to draw - * @param copyPal (Optional) Whether to copy the image's palette into palette VRAM - */ - void drawSegment(int x, int y, int imageX, int imageY, int w, int h, bool copyPal = true); + void draw(int x, int y, float scaleX = 1.0f, float scaleY = 1.0f, bool skipAlpha = true); /** * @brief Draws a segment of an image to a background layer, slower but can skip alpha, scale, and offset the palette @@ -107,10 +100,9 @@ public: * @param h The height to draw * @param scaleX (Optional) The scale for the X axis * @param scaleY (Optional) The scale for the Y axis - * @param paletteOffset (Optional) How much to offset the palette by - * @param copyPal (Optional) Whether to copy the image's palette into palette VRAM + * @param skipAlpha (Optional) Whether to skip transparent pixels, draws faster when disabled at 1.0 scale */ - void drawSegmentSpecial(int x, int y, int imageX, int imageY, int w, int h, float scaleX = 1.0f, float scaleY = 1.0f, int paletteOffset = 0, bool copyPal = true); + void drawSegment(int x, int y, int imageX, int imageY, int w, int h, float scaleX = 1.0f, float scaleY = 1.0f, bool skipAlpha = true); }; diff --git a/include/spritesheet.hpp b/include/spritesheet.hpp new file mode 100644 index 0000000..b5d7d56 --- /dev/null +++ b/include/spritesheet.hpp @@ -0,0 +1,66 @@ +/* +* This file is part of Universal-Core +* Copyright (C) 2021 Universal-Team +* +* 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 . +* +* Additional Terms 7.b and 7.c of GPLv3 apply to this file: +* * Requiring preservation of specified reasonable legal notices or +* author attributions in that material or in the Appropriate Legal +* Notices displayed by works containing it. +* * Prohibiting misrepresentation of the origin of that material, +* or requiring that modified versions of such material be marked in +* reasonable ways as different from the original version. +*/ + +#ifndef _UNIVERSAL_CORE_SPRITESHEET_HPP +#define _UNIVERSAL_CORE_SPRITESHEET_HPP + +#include "image.hpp" + +class Spritesheet { +private: + static Image _blank; + + std::vector _images; + +public: + /** + * @brief Image group class + * @param paths The paths to try load the spritesheet from, from highest to lowest priority + * @param indexes The image indexes to load, if blank all images will be loaded + */ + Spritesheet(const std::vector &paths, const std::vector indexes = {}); + /** + * @brief Image group class + * @param path The path to load the spritesheet from + * @param indexes The image indexes to load, if blank all images will be loaded + */ + Spritesheet(const std::string &path, const std::vector indexes = {}) : Spritesheet(std::vector({path}), indexes) {}; + + ~Spritesheet(void); + + /** + * @brief Returns the number of images in the spritesheet + */ + size_t size(void) { return _images.size(); } + + /** + * @brief Returns the Image at index + * @param index The Image to get + */ + Image &operator[](size_t index); +}; + +#endif diff --git a/source/gui.cpp b/source/gui.cpp index b3b8525..ad1df15 100644 --- a/source/gui.cpp +++ b/source/gui.cpp @@ -87,8 +87,8 @@ void Gui::clearTextBufs(void) { DefaultFont->clear(); } -void Gui::DrawSprite(Image image, size_t imgindex, int x, int y, float ScaleX, float ScaleY) { - image.drawSpecial(x, y, ScaleX, ScaleY); +void Gui::DrawSprite(Spritesheet &sheet, size_t imgindex, int x, int y, float ScaleX, float ScaleY) { + sheet[imgindex].draw(x, y, 0x20, ScaleX, ScaleY); } bool Gui::loadFont(Font &fnt, const char *Path) { @@ -98,18 +98,13 @@ bool Gui::loadFont(Font &fnt, const char *Path) { return true; } -bool Gui::loadSheet(const char *Path, Image &image) { +bool Gui::loadSheet(const char *Path, Spritesheet &sheet) { if(Path && access(Path, F_OK) == 0) // Only load if found. - image = Image(Path); + sheet = Spritesheet(Path); return true; } -/* - Reinitialize the GUI. - - fontRegion: The region to use for the system font. -*/ bool Gui::reinit(const char *FontPath) { return Gui::init(FontPath); } diff --git a/source/image.cpp b/source/image.cpp index 03565a0..1dec8d3 100644 --- a/source/image.cpp +++ b/source/image.cpp @@ -27,10 +27,37 @@ #include "image.hpp" #include "screenCommon.hpp" - #include "tonccpy.h" -Image::Image(const std::vector &paths) { +#include + +void Image::decompressGrf(void *dst, const void *src) { + if(!src || !dst) + return; + + u32 header = *(u32*)src; + u32 size = header >> 8; + + switch(header & 0xF0) { + case 0x00: // No compression + tonccpy(dst, (u8*)src + 4, size); + break; + case 0x10: // LZ77 + decompress(src, dst, LZ77); + break; + case 0x20: // Huffman + decompress(src, dst, HUFF); + break; + case 0x30: // RLE + decompress(src, dst, RLE); + break; + default: + break; + } +} + + +Image::Image(const std::vector &paths, u8 paletteStart) { // Attempt to load from the given paths FILE *file = nullptr; for(const auto &path : paths) { @@ -43,136 +70,138 @@ Image::Image(const std::vector &paths) { if(!file) return; - // Return if not in the correct format - char magic[5] = {0}; - fread(magic, 1, 4, file); - if(strcmp(magic, ".GFX")) - return; + fseek(file, 0, SEEK_END); + long size = ftell(file); + fseek(file, 0, SEEK_SET); - // Read image - fread(&_width, 1, 2, file); - fread(&_height, 1, 2, file); - _bitmap = std::vector(_width * _height); - fread(_bitmap.data(), 1, _bitmap.size(), file); - u16 palCount; - fread(&palCount, 1, 2, file); - fread(&_palOfs, 1, 2, file); - _palette = std::vector(palCount); - fread(_palette.data(), 2, palCount, file); - fclose(file); -} + u8 *grf = new u8[size]; + if(grf) { + fread(grf, 1, size, file); -Image::Image(FILE *file) { - // Return if no file - if(!file) - return; + load(grf, paletteStart); - // Return if not in the correct format - char magic[5]; - fread(magic, 1, 4, file); - if(strcmp(magic, ".GFX")) - return; - - // Read image - fread(&_width, 1, 2, file); - fread(&_height, 1, 2, file); - _bitmap = std::vector(_width * _height); - fread(_bitmap.data(), 1, _bitmap.size(), file); - u16 palCount; - fread(&palCount, 1, 2, file); - fread(&_palOfs, 1, 2, file); - _palette = std::vector(palCount); - fread(_palette.data(), 2, palCount, file); - fclose(file); -} - -void Image::draw(int x, int y, bool copyPal) { - SCALE_3DS(x); - SCALE_3DS(y); - - if(copyPal) - tonccpy((currentScreen ? BG_PALETTE : BG_PALETTE_SUB) + _palOfs, _palette.data(), _palette.size() * 2); - - u8 *dst = (u8 *)bgGetGfxPtr(currentScreen ? 3 : 7) + y * 256 + x; - - // If full width and X is 0, copy it all in one go - if(_width == 256 && x == 0) { - tonccpy(dst, _bitmap.data(), _width * _height); - } else { - for(int i = 0; i < _height; i++) { - tonccpy(dst + i * 256, _bitmap.data() + i * _width, _width); - } + delete[] grf; } } -void Image::drawSpecial(int x, int y, float scaleX, float scaleY, int paletteOffset, - bool copyPal) { +Image::Image(const u8 *grf, u8 paletteStart) { + load(grf, paletteStart); +} + +void Image::load(const u8 *grf, u8 paletteStart) { + const u32 *ptr = (u32 *)grf; + if(!ptr || ptr[0] != 0x46464952 || ptr[2] != 0x20465247) { + return; + } + + u32 size = ptr[1]; // RIFF size + ptr += 3; // Skip to first section + + while((u8 *)ptr < grf + size) { + switch(*ptr) { + case 0x20524448: { // 'HDR ' + memcpy(&_width, ptr + 4, 4); + memcpy(&_height, ptr + 5, 4); + break; + } case 0x20584647: { // 'GFX ' + _bitmap = std::vector(ptr[2] >> 8); + decompressGrf(_bitmap.data(), ptr + 2); + changePaletteStart(paletteStart); + break; + } case 0x204C4150: { // 'PAL ' + _palette = std::vector((ptr[2] >> 8) / 2); + decompressGrf(_palette.data(), ptr + 2); + break; + } default: { + // Not supported yet + break; + } + } + + ptr += 2 + ptr[1] / 4; + } +} + +void Image::changePaletteStart(u8 paletteStart) { + int moveBy = paletteStart - _paletteStart; + + if(moveBy != 0) { + for(u8 &px : _bitmap) { + px += moveBy; + } + + _paletteStart = paletteStart; + } +} + +void Image::copyPalette(int paletteStart) { + tonccpy((currentScreen ? BG_PALETTE : BG_PALETTE_SUB) + paletteStart, _palette.data(), _palette.size() * 2); +} + +void Image::draw(int x, int y, float scaleX, float scaleY, bool skipAlpha) { SCALE_3DS(x); SCALE_3DS(y); - - if(copyPal) - tonccpy((currentScreen ? BG_PALETTE : BG_PALETTE_SUB) + _palOfs + paletteOffset, _palette.data(), _palette.size() * 2); - - u8 *dst = (u8 *)bgGetGfxPtr(currentScreen ? 3 : 7) + y * 256 + x; + char s[64]; + // _bitmap[0] = 39; + snprintf(s, sizeof(s), "%lu, %lu, %u, %u, 0x%X", _width, _height, _bitmap.size(), _bitmap[0], 0); // If the scale is 1 use faster integer math if(scaleX == 1.0f && scaleY == 1.0f) { - for(int i = 0; i < _height; i++) { - for(int j = 0; j < _width; j++) { - u8 px = _bitmap[i * _width + j]; - if(_palette[px - _palOfs] & 0x8000) - toncset(dst + i * 256 + j, px + paletteOffset, 1); + if(skipAlpha) { + for(u32 i = 0; i < _height; i++) { + u8 *src = _bitmap.data() + i * _width; + u8 *dst = (u8 *)bgGetGfxPtr(currentScreen ? 3 : 7) + (y + i) * 256 + x; + for(u32 j = 0; j < _width; j++) { + if(_palette[src[j] - _paletteStart] != 0x7C1F) + toncset(dst + j, src[j], 1); + } + } + } else { + u8 *dst = (u8 *)bgGetGfxPtr(currentScreen ? 3 : 7) + y * 256 + x; + for(u32 i = 0; i < _height; i++) { + tonccpy(dst + i * 256, _bitmap.data() + i * _width, _width); } } } else { - for(int i = 0; i < _height * scaleY; i++) { - for(int j = 0; j < _width * scaleX; j++) { + for(u32 i = 0; i < _height * scaleY; i++) { + u8 *dst = (u8 *)bgGetGfxPtr(currentScreen ? 3 : 7) + (y + i) * 256 + x; + for(u32 j = 0; j < _width * scaleX; j++) { u8 px = _bitmap[int(i / scaleY) * _width + int(j / scaleX)]; - if(_palette[px - _palOfs] & 0x8000) - toncset(dst + i * 256 + j, px + paletteOffset, 1); + if(_palette[px - _paletteStart] != 0x7C1F || !skipAlpha) + toncset(dst + j, px, 1); } } } } -void Image::drawSegment(int x, int y, int imageX, int imageY, int w, int h, bool copyPal) { +void Image::drawSegment(int x, int y, int imageX, int imageY, int w, int h, float scaleX, float scaleY, bool skipAlpha) { SCALE_3DS(x); SCALE_3DS(y); - if(copyPal) - tonccpy((currentScreen ? BG_PALETTE : BG_PALETTE_SUB) + _palOfs, _palette.data(), _palette.size() * 2); - - for(int i = 0; i < h; i++) { - tonccpy((u8 *)bgGetGfxPtr(currentScreen ? 3 : 7) + (y + i) * 256 + x, - _bitmap.data() + (imageY + i) * _width + imageX, w); - } -} - -void Image::drawSegmentSpecial(int x, int y, int imageX, int imageY, int w, int h, float scaleX, - float scaleY, int paletteOffset, bool copyPal) { - SCALE_3DS(x); - SCALE_3DS(y); - - if(copyPal) - tonccpy((currentScreen ? BG_PALETTE : BG_PALETTE_SUB) + _palOfs + paletteOffset, _palette.data(), _palette.size() * 2); - - u8 *dst = (u8 *)bgGetGfxPtr(currentScreen ? 3 : 7) + y * 256 + x; - // If the scale is 1 use faster integer math if(scaleX == 1.0f && scaleY == 1.0f) { - for(int i = 0; i < h; i++) { - for(int j = 0; j < w; j++) { - u8 px = _bitmap[i * _width + j]; - if(_palette[px - _palOfs] & 0x8000) - toncset(dst + i * 256 + j, px + paletteOffset, 1); + if(skipAlpha) { + for(int i = 0; i < h; i++) { + u8 *src = _bitmap.data() + i * _width; + u8 *dst = (u8 *)bgGetGfxPtr(currentScreen ? 3 : 7) + (y + i) * 256 + x; + for(int j = 0; j < w; j++) { + if(_palette[src[j] - _paletteStart] != 0x7C1F) + toncset(dst + j, src[j], 1); + } + } + } else { + for(int i = 0; i < h; i++) { + tonccpy((u8 *)bgGetGfxPtr(currentScreen ? 3 : 7) + (y + i) * 256 + x, + _bitmap.data() + (imageY + i) * _width + imageX, w); } } } else { - for(int i = 0; i < h * scaleY; i++) { - for(int j = 0; j < w * scaleX; j++) { + for(u32 i = 0; i < h * scaleY; i++) { + u8 *dst = (u8 *)bgGetGfxPtr(currentScreen ? 3 : 7) + (y + i) * 256 + x; + for(u32 j = 0; j < w * scaleX; j++) { u8 px = _bitmap[(imageY + int(i / scaleY)) * _width + imageX + int(j / scaleX)]; - if(_palette[px - _palOfs] & 0x8000) - toncset(dst + i * 256 + j, px + paletteOffset, 1); + if(_palette[px - _paletteStart] != 0x7C1F || !skipAlpha) + toncset(dst + j, px, 1); } } } diff --git a/source/sprite.cpp b/source/sprite.cpp index 04d1266..930e4bd 100644 --- a/source/sprite.cpp +++ b/source/sprite.cpp @@ -194,7 +194,7 @@ void Sprite::drawImage(int x, int y, const Image &image, float scaleX, float sca if(scaleX == 1.0f && scaleY == 1.0f) { for(int i = 0; i < image.height(); i++) { for(int j = 0; j < image.width(); j++) { - u16 px = image.palette()[image.bitmap()[i * image.width() + j] - image.palOfs()]; + u16 px = image.palette()[image.bitmap()[i * image.width() + j] - image.paletteStart()]; if(px & 0x8000) _gfx[(y + i) * _height + x + j] = px; } @@ -203,7 +203,7 @@ void Sprite::drawImage(int x, int y, const Image &image, float scaleX, float sca for(int i = 0; i < image.height() * scaleY; i++) { for(int j = 0; j < image.width() * scaleX; j++) { u16 px = - image.palette()[image.bitmap()[int(i / scaleY) * image.width() + int(j / scaleX)] - image.palOfs()]; + image.palette()[image.bitmap()[int(i / scaleY) * image.width() + int(j / scaleX)] - image.paletteStart()]; if(px & 0x8000) _gfx[(y + i) * _height + j + x] = px; } @@ -217,7 +217,7 @@ void Sprite::drawImageSegment(int x, int y, int imageX, int imageY, int w, int h if(scaleX == 1.0f && scaleY == 1.0f) { for(int i = 0; i < h; i++) { for(int j = 0; j < w; j++) { - u16 px = image.palette()[image.bitmap()[(imageY + i) * image.width() + imageX + j] - image.palOfs()]; + u16 px = image.palette()[image.bitmap()[(imageY + i) * image.width() + imageX + j] - image.paletteStart()]; if(px & 0x8000) _gfx[(y + i) * _height + x + j] = px; } @@ -227,7 +227,7 @@ void Sprite::drawImageSegment(int x, int y, int imageX, int imageY, int w, int h for(int j = 0; j < w * scaleX; j++) { u16 px = image.palette()[image.bitmap()[(imageY + int(i / scaleY)) * image.width() + imageX + int(j / scaleX)] - - image.palOfs()]; + image.paletteStart()]; if(px & 0x8000) _gfx[(y + i) * _height + x + j] = px; } diff --git a/source/spritesheet.cpp b/source/spritesheet.cpp new file mode 100644 index 0000000..f17c87e --- /dev/null +++ b/source/spritesheet.cpp @@ -0,0 +1,92 @@ +/* + * This file is part of Universal-Core + * Copyright (C) 2021 Universal-Team + * + * 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 . + * + * Additional Terms 7.b and 7.c of GPLv3 apply to this file: + * * Requiring preservation of specified reasonable legal notices or + * author attributions in that material or in the Appropriate Legal + * Notices displayed by works containing it. + * * Prohibiting misrepresentation of the origin of that material, + * or requiring that modified versions of such material be marked in + * reasonable ways as different from the original version. + */ + +#include "spritesheet.hpp" + +#include + +Image Spritesheet::_blank = Image(nullptr, 0); + +Spritesheet::Spritesheet(const std::vector &paths, const std::vector indexes) { + // Attempt to load from the given paths + FILE *file = nullptr; + for(const auto &path : paths) { + file = fopen(path.c_str(), "rb"); + if(file) + break; + } + + // Return if no file found + if(!file) + return; + + u32 magic; + fread(&magic, 4, 1, file); + if(magic != 0x5844542E) // '.TDX' + return; + + u32 imageCount; + fread(&imageCount, 4, 1, file); + + _images.resize(imageCount); + + for(u32 i = 0; i < imageCount; i++) { + nocashMessage(std::to_string(std::find(indexes.begin(), indexes.end(), i) - indexes.begin()).c_str()); + if(indexes.size() == 0 || std::find(indexes.begin(), indexes.end(), i) != indexes.end()) { + fseek(file, 0x10 + i * 8, SEEK_SET); + + u32 offset, size; + fread(&offset, 4, 1, file); + fread(&size, 4, 1, file); + + u8 *grf = new u8[size]; + fseek(file, offset, SEEK_SET); + fread(grf, 1, size, file); + + _images[i] = new Image(grf, 0); + + delete[] grf; + } else { + _images[i] = nullptr; + } + } + + fclose(file); +} + +Spritesheet::~Spritesheet(void) { + for(Image *image : _images) { + if(image) + delete image; + } +} + +Image &Spritesheet::operator[](size_t index) { + if(_images[index] && index < _images.size()) + return *_images[index]; + else + return _blank; +} \ No newline at end of file