Path: blob/main/RSDKv4/Sprite.cpp
817 views
#include "RetroEngine.hpp"12struct GifDecoder {3int depth;4int clearCode;5int eofCode;6int runningCode;7int runningBits;8int prevCode;9int currentCode;10int maxCodePlusOne;11int stackPtr;12int shiftState;13int fileState;14int position;15int bufferSize;16uint shiftData;17uint pixelCount;18byte buffer[256];19byte stack[4096];20byte suffix[4096];21uint prefix[4096];22};2324const int LOADING_IMAGE = 0;25const int LOAD_COMPLETE = 1;26const int LZ_MAX_CODE = 4095;27const int LZ_BITS = 12;28const int FIRST_CODE = 4097;29const int NO_SUCH_CODE = 4098;3031GifDecoder gifDecoder;32int codeMasks[] = { 0, 1, 3, 7, 15, 31, 63, 127, 255, 511, 1023, 2047, 4095 };3334int ReadGifCode(void);35byte ReadGifByte(void);36byte TraceGifPrefix(uint *prefix, int code, int clearCode);3738void InitGifDecoder()39{40byte code = 0;41FileRead(&code, 1);42gifDecoder.fileState = LOADING_IMAGE;43gifDecoder.position = 0;44gifDecoder.bufferSize = 0;45gifDecoder.buffer[0] = 0;46gifDecoder.depth = code;47gifDecoder.clearCode = 1 << code;48gifDecoder.eofCode = gifDecoder.clearCode + 1;49gifDecoder.runningCode = gifDecoder.eofCode + 1;50gifDecoder.runningBits = code + 1;51gifDecoder.maxCodePlusOne = 1 << gifDecoder.runningBits;52gifDecoder.stackPtr = 0;53gifDecoder.prevCode = NO_SUCH_CODE;54gifDecoder.shiftState = 0;55gifDecoder.shiftData = 0;56for (int i = 0; i <= LZ_MAX_CODE; ++i) gifDecoder.prefix[i] = (byte)NO_SUCH_CODE;57}58void ReadGifLine(byte *line, int length, int offset)59{60int i = 0;61int stackPtr = gifDecoder.stackPtr;62int eofCode = gifDecoder.eofCode;63int clearCode = gifDecoder.clearCode;64int prevCode = gifDecoder.prevCode;65if (stackPtr != 0) {66while (stackPtr != 0) {67if (i >= length)68break;69line[offset++] = gifDecoder.stack[--stackPtr];70i++;71}72}73while (i < length) {74int gifCode = ReadGifCode();75if (gifCode == eofCode) {76if (i != length - 1 || gifDecoder.pixelCount != 0)77return;78i++;79}80else {81if (gifCode == clearCode) {82for (int p = 0; p <= LZ_MAX_CODE; p++) gifDecoder.prefix[p] = NO_SUCH_CODE;83gifDecoder.runningCode = gifDecoder.eofCode + 1;84gifDecoder.runningBits = gifDecoder.depth + 1;85gifDecoder.maxCodePlusOne = 1 << gifDecoder.runningBits;86prevCode = (gifDecoder.prevCode = NO_SUCH_CODE);87}88else {89if (gifCode < clearCode) {90line[offset] = (byte)gifCode;91offset++;92i++;93}94else {95if (gifCode < 0 || gifCode > LZ_MAX_CODE)96return;9798int code = 0;99if (gifDecoder.prefix[gifCode] == NO_SUCH_CODE) {100if (gifCode != gifDecoder.runningCode - 2)101return;102103code = prevCode;104gifDecoder.suffix[gifDecoder.runningCode - 2] = gifDecoder.stack[stackPtr++] =105TraceGifPrefix(gifDecoder.prefix, prevCode, clearCode);106}107else {108code = gifCode;109}110int c = 0;111while (c++ <= LZ_MAX_CODE && code > clearCode && code <= LZ_MAX_CODE) {112gifDecoder.stack[stackPtr++] = gifDecoder.suffix[code];113code = gifDecoder.prefix[code];114}115if (c >= LZ_MAX_CODE || code > LZ_MAX_CODE)116return;117118gifDecoder.stack[stackPtr++] = (byte)code;119while (stackPtr != 0 && i++ < length) line[offset++] = gifDecoder.stack[--stackPtr];120}121if (prevCode != NO_SUCH_CODE) {122if (gifDecoder.runningCode < 2 || gifDecoder.runningCode > FIRST_CODE)123return;124125gifDecoder.prefix[gifDecoder.runningCode - 2] = prevCode;126if (gifCode == gifDecoder.runningCode - 2)127gifDecoder.suffix[gifDecoder.runningCode - 2] = TraceGifPrefix(gifDecoder.prefix, prevCode, clearCode);128else129gifDecoder.suffix[gifDecoder.runningCode - 2] = TraceGifPrefix(gifDecoder.prefix, gifCode, clearCode);130}131prevCode = gifCode;132}133}134}135gifDecoder.prevCode = prevCode;136gifDecoder.stackPtr = stackPtr;137}138139int ReadGifCode()140{141while (gifDecoder.shiftState < gifDecoder.runningBits) {142byte b = ReadGifByte();143gifDecoder.shiftData |= (uint)((uint)b << gifDecoder.shiftState);144gifDecoder.shiftState += 8;145}146int code = (gifDecoder.shiftData & codeMasks[gifDecoder.runningBits]);147gifDecoder.shiftData >>= gifDecoder.runningBits;148gifDecoder.shiftState -= gifDecoder.runningBits;149if (++gifDecoder.runningCode > gifDecoder.maxCodePlusOne && gifDecoder.runningBits < LZ_BITS) {150gifDecoder.maxCodePlusOne <<= 1;151gifDecoder.runningBits++;152}153return code;154}155156byte ReadGifByte()157{158byte c = 0;159if (gifDecoder.fileState == LOAD_COMPLETE)160return c;161162byte b;163if (gifDecoder.position == gifDecoder.bufferSize) {164FileRead(&b, 1);165gifDecoder.bufferSize = (int)b;166if (gifDecoder.bufferSize == 0) {167gifDecoder.fileState = LOAD_COMPLETE;168return c;169}170FileRead(gifDecoder.buffer, gifDecoder.bufferSize);171b = gifDecoder.buffer[0];172gifDecoder.position = 1;173}174else {175b = gifDecoder.buffer[gifDecoder.position++];176}177return b;178}179180byte TraceGifPrefix(uint *prefix, int code, int clearCode)181{182int i = 0;183while (code > clearCode && i++ <= LZ_MAX_CODE) code = prefix[code];184185return code;186}187void ReadGifPictureData(int width, int height, bool interlaced, byte *gfxData, int offset)188{189int initialRow[] = { 0, 4, 2, 1 };190int rowInc[] = { 8, 8, 4, 2 };191InitGifDecoder();192if (interlaced) {193for (int p = 0; p < 4; ++p) {194for (int y = initialRow[p]; y < height; y += rowInc[p]) ReadGifLine(gfxData, width, y * width + offset);195}196}197else {198for (int y = 0; y < height; ++y) ReadGifLine(gfxData, width, y * width + offset);199}200}201202int AddGraphicsFile(const char *filePath)203{204char sheetPath[0x100];205206StrCopy(sheetPath, "Data/Sprites/");207StrAdd(sheetPath, filePath);208int sheetID = 0;209while (StrLength(gfxSurface[sheetID].fileName) > 0) {210if (StrComp(gfxSurface[sheetID].fileName, sheetPath))211return sheetID;212if (++sheetID == SURFACE_COUNT) // Max Sheet cnt213return 0;214}215byte fileExtension = (byte)sheetPath[(StrLength(sheetPath) - 1) & 0xFF];216switch (fileExtension) {217case 'f': LoadGIFFile(sheetPath, sheetID); break;218case 'p': LoadBMPFile(sheetPath, sheetID); break;219case 'r': LoadPVRFile(sheetPath, sheetID); break;220}221222return sheetID;223}224void RemoveGraphicsFile(const char *filePath, int sheetID)225{226if (sheetID < 0) {227for (int i = 0; i < SURFACE_COUNT; ++i) {228if (StrLength(gfxSurface[i].fileName) > 0 && StrComp(gfxSurface[i].fileName, filePath))229sheetID = i;230}231}232233if (sheetID >= 0 && StrLength(gfxSurface[sheetID].fileName)) {234StrCopy(gfxSurface[sheetID].fileName, "");235int dataPosStart = gfxSurface[sheetID].dataPosition;236int dataPosEnd = gfxSurface[sheetID].dataPosition + gfxSurface[sheetID].height * gfxSurface[sheetID].width;237for (int i = GFXDATA_SIZE - dataPosEnd; i > 0; --i) graphicData[dataPosStart++] = graphicData[dataPosEnd++];238gfxDataPosition -= gfxSurface[sheetID].height * gfxSurface[sheetID].width;239for (int i = 0; i < SURFACE_COUNT; ++i) {240if (gfxSurface[i].dataPosition > gfxSurface[sheetID].dataPosition)241gfxSurface[i].dataPosition -= gfxSurface[sheetID].height * gfxSurface[sheetID].width;242}243}244}245246int LoadBMPFile(const char *filePath, byte sheetID)247{248FileInfo info;249if (LoadFile(filePath, &info)) {250GFXSurface *surface = &gfxSurface[sheetID];251StrCopy(surface->fileName, filePath);252253byte fileBuffer = 0;254255SetFilePosition(18);256FileRead(&fileBuffer, 1);257surface->width = fileBuffer;258FileRead(&fileBuffer, 1);259surface->width |= fileBuffer << 8;260FileRead(&fileBuffer, 1);261surface->width |= fileBuffer << 16;262FileRead(&fileBuffer, 1);263surface->width |= fileBuffer << 24;264265FileRead(&fileBuffer, 1);266surface->height = fileBuffer;267FileRead(&fileBuffer, 1);268surface->height |= fileBuffer << 8;269FileRead(&fileBuffer, 1);270surface->height |= fileBuffer << 16;271FileRead(&fileBuffer, 1);272surface->height |= fileBuffer << 24;273274SetFilePosition(info.vfileSize - surface->height * surface->width);275surface->dataPosition = gfxDataPosition;276byte *gfxData = &graphicData[surface->dataPosition + surface->width * (surface->height - 1)];277for (int y = 0; y < surface->height; ++y) {278for (int x = 0; x < surface->width; ++x) {279FileRead(&fileBuffer, 1);280*gfxData++ = fileBuffer;281}282gfxData -= 2 * surface->width;283}284gfxDataPosition += surface->height * surface->width;285286#if RETRO_SOFTWARE_RENDER287surface->widthShift = 0;288int w = surface->width;289while (w > 1) {290w >>= 1;291++surface->widthShift;292}293#endif294295if (gfxDataPosition >= GFXDATA_SIZE) {296gfxDataPosition = 0;297PrintLog("WARNING: Exceeded max gfx size!");298}299300CloseFile();301return true;302}303return false;304}305int LoadGIFFile(const char *filePath, byte sheetID)306{307FileInfo info;308if (LoadFile(filePath, &info)) {309GFXSurface *surface = &gfxSurface[sheetID];310StrCopy(surface->fileName, filePath);311312byte fileBuffer = 0;313314SetFilePosition(6); // GIF89a315FileRead(&fileBuffer, 1);316surface->width = fileBuffer;317FileRead(&fileBuffer, 1);318surface->width |= fileBuffer << 8;319FileRead(&fileBuffer, 1);320surface->height = fileBuffer;321FileRead(&fileBuffer, 1);322surface->height |= fileBuffer << 8;323324FileRead(&fileBuffer, 1); // Palette Size325// int has_pallete = (fileBuffer & 0x80) >> 7;326// int colors = ((fileBuffer & 0x70) >> 4) + 1;327int palette_size = (fileBuffer & 0x7) + 1;328if (palette_size > 0)329palette_size = 1 << palette_size;330FileRead(&fileBuffer, 1); // BG Color index (thrown away)331FileRead(&fileBuffer, 1); // idk actually (still thrown away)332333int c = 0;334byte clr[3];335do {336++c;337FileRead(clr, 3);338} while (c != palette_size);339340FileRead(&fileBuffer, 1);341while (fileBuffer != ',') FileRead(&fileBuffer, 1); // gif image start identifier342343ushort fileBuffer2 = 0;344FileRead(&fileBuffer2, 2);345FileRead(&fileBuffer2, 2);346FileRead(&fileBuffer2, 2);347FileRead(&fileBuffer2, 2);348FileRead(&fileBuffer, 1);349bool interlaced = (fileBuffer & 0x40) >> 6;350if (fileBuffer >> 7 == 1) {351int c = 0x80;352do {353++c;354FileRead(clr, 3);355} while (c != 0x100);356}357358surface->dataPosition = gfxDataPosition;359360#if RETRO_SOFTWARE_RENDER361surface->widthShift = 0;362int w = surface->width;363while (w > 1) {364w >>= 1;365++surface->widthShift;366}367#endif368369gfxDataPosition += surface->width * surface->height;370if (gfxDataPosition < GFXDATA_SIZE) {371ReadGifPictureData(surface->width, surface->height, interlaced, graphicData, surface->dataPosition);372}373else {374gfxDataPosition = 0;375PrintLog("WARNING: Exceeded max gfx size!");376}377378CloseFile();379return true;380}381return false;382}383int LoadPVRFile(const char *filePath, byte sheetID)384{385// ONLY READS "PVRTC 2bpp RGB" PVR FILES386FileInfo info;387if (LoadFile(filePath, &info)) {388GFXSurface *surface = &gfxSurface[sheetID];389StrCopy(surface->fileName, filePath);390391byte fileBuffer[2];392393SetFilePosition(28);394FileRead(fileBuffer, 1);395int width = fileBuffer[0];396FileRead(fileBuffer, 1);397width |= fileBuffer[0] << 8;398FileRead(fileBuffer, 1);399int height = fileBuffer[0];400FileRead(fileBuffer, 1);401height = fileBuffer[0] << 8;402403surface->width = width;404surface->height = height;405surface->dataPosition = gfxDataPosition;406gfxDataPosition += surface->width * surface->height;407408if (gfxDataPosition >= GFXDATA_SIZE) {409gfxDataPosition = 0;410PrintLog("WARNING: Exceeded max gfx size!");411}412413#if RETRO_SOFTWARE_RENDER414surface->widthShift = 0;415int w = surface->width;416while (w > 1) {417w >>= 1;418++surface->widthShift;419}420#endif421422return false; // yeah I have no clue how to handle this, cd lite has this be loaded every frame on framebuffer update and does it that way423424ushort *buffer = NULL;425for (int h = 0; h < height; ++h) {426for (int w = 0; w < width; ++w) {427FileRead(fileBuffer, 2);428buffer[w] = 2 * (fileBuffer[0] + (fileBuffer[1] << 8)) | 1;429}430buffer += width;431}432buffer += 0x400 - width;433434CloseFile();435return true;436}437return false;438}439440441