diff --git a/freesprite/FileIO.cpp b/freesprite/FileIO.cpp index 24ba969..9e44847 100644 --- a/freesprite/FileIO.cpp +++ b/freesprite/FileIO.cpp @@ -1620,7 +1620,7 @@ Layer* readXComBDY(PlatformNativePathString path, uint64_t seek) uint8_t pixel; fread(&pixel, 1, 1, f); for (int x = 0; x < 257 - a; x++) { - *(pxd++) = pixel; + *(pxd++) = (uint32_t)pixel; if (pxd >= end) { reachedImageEnd = true; break; @@ -1628,10 +1628,10 @@ Layer* readXComBDY(PlatformNativePathString path, uint64_t seek) } } else { - for (int x = 0; x < a+1; x++) { + for (uint32_t x = 0; x < a+1; x++) { uint8_t pixel; fread(&pixel, 1, 1, f); - *(pxd++) = pal2[pixel * 3]; + *(pxd++) = (uint32_t)pixel; if (pxd >= end) { reachedImageEnd = true; break; @@ -1645,6 +1645,24 @@ Layer* readXComBDY(PlatformNativePathString path, uint64_t seek) return NULL; } +Layer* readXComSCR(PlatformNativePathString path, uint64_t seek) +{ + FILE* f = platformOpenFile(path, PlatformFileModeRB); + if (f != NULL) { + LayerPalettized* ret = new LayerPalettized(320, 200); + uint32_t* pxd = (uint32_t*)ret->pixelData; + ret->palette = g_palettes[PALETTE_DEFAULT]; + for (int x = 0; x < 320 * 200; x++) { + uint8_t pixel; + fread(&pixel, 1, 1, f); + pxd[x] = pixel; + } + fclose(f); + return ret; + } + return NULL; +} + MainEditor* readOpenRaster(PlatformNativePathString path) { FILE* f = platformOpenFile(path, PlatformFileModeRB); @@ -2543,6 +2561,64 @@ public class VoidspriteImage {{\n\ return false; } +std::pair> readPltVOIDPLT(PlatformNativePathString name) +{ + FILE* f = platformOpenFile(name, PlatformFileModeRB); + if (f != NULL) { + char header[7]; + fread(header, 7, 1, f); + if (memcmp(header, "VOIDPLT", 7) == 0) { + uint8_t fileversion; + fread(&fileversion, 1, 1, f); + if (fileversion == 1) { + std::vector newPalette; + uint32_t count; + fread(&count, 1, 4, f); + for (int x = 0; x < count; x++) { + uint32_t col; + fread(&col, 1, 4, f); + newPalette.push_back(col); + } + fclose(f); + return { true, newPalette }; + } + else { + g_addNotification(ErrorNotification("Error", "Unsupported VOIDPLT file version")); + } + } + else { + g_addNotification(ErrorNotification("Error", "Invalid palette file")); + } + fclose(f); + } + return { false, {} }; +} + +std::pair> readPltJASCPAL(PlatformNativePathString name) +{ + std::ifstream f(name, std::ios::in); + if (f.is_open()) { + std::vector newPalette; + std::string line; + std::getline(f, line); + if (line.substr(0, 8) == "JASC-PAL") { + f >> line; + //should be 0100 + int count; + f >> count; + for (int x = 0; x < count; x++) { + int r, g, b; + f >> r >> g >> b; + newPalette.push_back((0xff << 24) | (r << 16) | (g << 8) | b); + } + f.close(); + return { true, newPalette }; + } + f.close(); + } + return { false, {} }; +} + bool writeHTMLBase64(PlatformNativePathString path, Layer* data) { if (data->isPalettized) { diff --git a/freesprite/FileIO.h b/freesprite/FileIO.h index 414c078..cb75e65 100644 --- a/freesprite/FileIO.h +++ b/freesprite/FileIO.h @@ -36,6 +36,7 @@ Layer* readMSP(PlatformNativePathString path, uint64_t seek = 0); Layer* readMarioPaintSRM(PlatformNativePathString path, uint64_t seek = 0); Layer* readXComSPK(PlatformNativePathString path, uint64_t seek = 0); Layer* readXComBDY(PlatformNativePathString path, uint64_t seek = 0); +Layer* readXComSCR(PlatformNativePathString path, uint64_t seek = 0); MainEditor* readOpenRaster(PlatformNativePathString path); MainEditor* readVOIDSN(PlatformNativePathString path); @@ -55,6 +56,9 @@ bool writePythonNPArray(PlatformNativePathString path, Layer* data); bool writeHTMLBase64(PlatformNativePathString path, Layer* data); bool writeJavaBufferedImage(PlatformNativePathString path, Layer* data); +std::pair> readPltVOIDPLT(PlatformNativePathString name); +std::pair> readPltJASCPAL(PlatformNativePathString name); + struct FileSessionImportNPath { std::string name; std::string extension; @@ -83,28 +87,16 @@ struct FileImportUTF8Path { }; }; -struct FileExportMultiLayerNPath { - std::string name; - std::string extension; - bool (*exportFunction)(PlatformNativePathString, MainEditor*); - int exportFormats = FORMAT_RGB; - bool (*canExport)(Layer*) = - [](Layer* p) { - return true; - }; -}; -struct FileExportFlatNPath { - std::string name; - std::string extension; - bool (*exportFunction)(PlatformNativePathString, Layer*); - int exportFormats = FORMAT_RGB; - bool (*canExport)(Layer*) = - [](Layer* p) { - return true; - }; +class FileOperation { +public: + virtual std::string name() { return _name; } + virtual std::string extension() { return _extension; } +protected: + std::string _name = "Palette type"; + std::string _extension = ""; }; -class FileExporter { +class FileExporter : public FileOperation { public: static FileExporter* sessionExporter(std::string name, std::string extension, bool (*exportFunction)(PlatformNativePathString, MainEditor*), int formatflags = FORMAT_RGB, bool (*canExport)(MainEditor*) = NULL) { @@ -129,8 +121,6 @@ class FileExporter { } virtual int formatFlags() { return _formatFlags; } - virtual std::string name() { return _name; } - virtual std::string extension() { return _extension; } virtual bool exportsWholeSession() { return _isSessionExporter; } virtual bool canExport(void* data) { if (exportsWholeSession()) { @@ -149,8 +139,6 @@ class FileExporter { } } protected: - std::string _name = "File type (library)"; - std::string _extension = ""; int _formatFlags = FORMAT_RGB; bool _isSessionExporter = false; @@ -160,24 +148,60 @@ class FileExporter { bool (*_flatExportFunction)(PlatformNativePathString, Layer*) = NULL; bool (*_flatCheckExportFunction)(Layer*) = NULL; }; +class PaletteImporter : public FileOperation { +public: + static PaletteImporter* paletteImporter(std::string name, std::string extension, std::pair>(*importFunction)(PlatformNativePathString), bool (*canImport)(PlatformNativePathString) = NULL) { + PaletteImporter* ret = new PaletteImporter(); + ret->_name = name; + ret->_extension = extension; + ret->_importFunction = importFunction; + ret->_canImport = canImport; + return ret; + } + + virtual bool canImport(PlatformNativePathString path) { + return _canImport != NULL ? _canImport(path) : true; + } + virtual std::pair> importPalette(PlatformNativePathString path) { + return _importFunction(path); + }; +protected: + std::pair> (*_importFunction)(PlatformNativePathString) = NULL; + bool (*_canImport)(PlatformNativePathString) = NULL; +}; inline std::vector g_fileExporters; inline std::vector g_palettizedFileExporters; +inline std::vector g_paletteImporters; + inline void g_setupIO() { g_fileExporters.push_back(FileExporter::sessionExporter("voidsprite Session version 3", ".voidsn", &writeVOIDSNv3)); g_fileExporters.push_back(FileExporter::sessionExporter("voidsprite Session version 2", ".voidsnv2", &writeVOIDSNv2)); g_fileExporters.push_back(FileExporter::sessionExporter("OpenRaster", ".ora", &writeOpenRaster)); g_fileExporters.push_back(FileExporter::flatExporter("PNG (libpng)", ".png", &writePNG, FORMAT_RGB | FORMAT_PALETTIZED)); - g_fileExporters.push_back(FileExporter::flatExporter("RPG2000/2003 XYZ (voidsprite custom)", ".xyz", &writeXYZ, FORMAT_RGB | FORMAT_PALETTIZED)); + g_fileExporters.push_back(FileExporter::flatExporter("RPG2000/2003 XYZ", ".xyz", &writeXYZ, FORMAT_RGB | FORMAT_PALETTIZED)); g_fileExporters.push_back(FileExporter::flatExporter("BMP (EasyBMP)", ".bmp", &writeBMP)); - g_fileExporters.push_back(FileExporter::flatExporter("TGA (voidsprite custom)", ".tga", &writeTGA)); + g_fileExporters.push_back(FileExporter::flatExporter("TGA", ".tga", &writeTGA)); g_fileExporters.push_back(FileExporter::flatExporter("CaveStory PBM (EasyBMP)", ".pbm", &writeCaveStoryPBM)); - g_fileExporters.push_back(FileExporter::flatExporter("C Header (voidsprite custom)", ".h", &writeCHeader)); - g_fileExporters.push_back(FileExporter::flatExporter("Python NumPy array (voidsprite custom)", ".py", &writePythonNPArray)); + g_fileExporters.push_back(FileExporter::flatExporter("C Header", ".h", &writeCHeader)); + g_fileExporters.push_back(FileExporter::flatExporter("Python NumPy array", ".py", &writePythonNPArray)); g_fileExporters.push_back(FileExporter::flatExporter("HTML Base64 image (base64)", ".html", &writeHTMLBase64)); - g_fileExporters.push_back(FileExporter::flatExporter("Java Buffered Image (voidsprite custom)", ".java", &writeJavaBufferedImage)); + g_fileExporters.push_back(FileExporter::flatExporter("Java Buffered Image", ".java", &writeJavaBufferedImage)); + + g_paletteImporters.push_back(PaletteImporter::paletteImporter("voidsprite palette", ".voidplt", &readPltVOIDPLT)); + g_paletteImporters.push_back(PaletteImporter::paletteImporter("JASC-PAL palette", ".pal", &readPltJASCPAL, + [](PlatformNativePathString path) { + FILE* f = platformOpenFile(path, PlatformFileModeRB); + char headerBytes[9]; + memset(headerBytes, 0, 9); + fread(headerBytes, 8, 1, f); + fclose(f); + return std::string(headerBytes) == "JASC-PAL"; + })); + + for (auto& exporter : g_fileExporters) { if (exporter->formatFlags() & FORMAT_PALETTIZED) { @@ -223,34 +247,47 @@ inline std::vector g_fileImportersNPaths = { } }, { - "RPG2000/2003 XYZ (voidsprite custom)", ".xyz", &readXYZ + "RPG2000/2003 XYZ", ".xyz", &readXYZ }, { "Atrophy Engine AETEX v1/v2 (SDL_Image:tga, DDS[fallthrough])", ".aetex", &readAETEX }, { - "Wii/GC TPL (voidsprite custom)", ".tpl", &readWiiGCTPL + "Wii/GC TPL", ".tpl", &readWiiGCTPL }, { - "NES: dump CHR-ROM (voidsprite custom)", ".nes", &readNES + "NES: dump CHR-ROM", ".nes", &readNES }, { "DDS (ddspp+s3tc open source+voidsprite custom)", ".dds", &readDDS }, { - "VTF (voidsprite custom)", ".vtf", &readVTF + "VTF", ".vtf", &readVTF + }, + { + "MSP", ".msp", &readMSP }, { - "MSP (voidsprite custom)", ".msp", &readMSP + "Mario Paint save file", ".srm", &readMarioPaintSRM }, { - "Mario Paint save file (voidsprite custom)", ".srm", &readMarioPaintSRM + "X-Com SPK file", ".spk", &readXComSPK }, { - "X-Com SPK file (voidsprite custom)", ".spk", &readXComSPK + "X-Com BDY file", ".bdy", &readXComBDY }, { - "X-Com BDY file (voidsprite custom)", ".bdy", &readXComBDY + "X-Com SCR file", ".scr", &readXComSCR, + [](PlatformNativePathString path) { + FILE* f = platformOpenFile(path, PlatformFileModeRB); + //check if file isn't an exe + char c[2]; + fread(c, 2, 1, f); + fseek(f, 0, SEEK_END); + uint64_t len = ftell(f); + fclose(f); + return !(c[0] == 'M' && c[1] == 'Z') || len == (320 * 200); + } } }; diff --git a/freesprite/PalettizedEditorColorPicker.cpp b/freesprite/PalettizedEditorColorPicker.cpp index 1736618..14f05ce 100644 --- a/freesprite/PalettizedEditorColorPicker.cpp +++ b/freesprite/PalettizedEditorColorPicker.cpp @@ -4,6 +4,7 @@ #include "Notification.h" #include "PopupPickColor.h" #include "UIColorInputField.h" +#include "FileIO.h" PalettizedEditorColorPicker::PalettizedEditorColorPicker(MainEditorPalettized* c) { @@ -22,6 +23,11 @@ PalettizedEditorColorPicker::PalettizedEditorColorPicker(MainEditorPalettized* c eraserButton->setCallbackListener(EVENT_COLORPICKER_TOGGLEERASER, this); subWidgets.addDrawable(eraserButton); + pickedColorLabel = new UILabel(); + pickedColorLabel->position = { 60, 350 }; + pickedColorLabel->text = ""; + subWidgets.addDrawable(pickedColorLabel); + std::vector palettes; for (auto& pal : g_palettes) { palettes.push_back(pal.first); @@ -86,6 +92,9 @@ void PalettizedEditorColorPicker::render(XY position) SDL_SetRenderDrawColor(g_rd, valCol.r, valCol.g, valCol.b, 0xff); SDL_RenderDrawRect(g_rd, &r); + pickedColorLabel->text = std::format("#{}/x{:02X} - #{:08X}", upcastCaller->pickedPaletteIndex, upcastCaller->pickedPaletteIndex, colorNow); + pickedColorLabel->color = {valCol.r, valCol.g, valCol.b, 0xff}; + g_fnt->RenderString("COLOR PICKER", position.x + 5, position.y + 1); subWidgets.renderAll(position); @@ -103,7 +112,11 @@ void PalettizedEditorColorPicker::eventButtonPressed(int evt_id) platformTrySaveOtherFile(this, { {".voidplt", "voidsprite palette"} }, "save palette", EVENT_PALETTECOLORPICKER_SAVEPALETTE); } else if (evt_id == EVENT_PALETTECOLORPICKER_LOADPALETTE) { - platformTryLoadOtherFile(this, { {".voidplt", "voidsprite palette"} }, "load palette", EVENT_PALETTECOLORPICKER_LOADPALETTE); + std::vector> filetypes; + for (auto& importer : g_paletteImporters) { + filetypes.push_back({ importer->extension(), importer->name()}); + } + platformTryLoadOtherFile(this, filetypes, "load palette", EVENT_PALETTECOLORPICKER_LOADPALETTE); } else if (evt_id >= 200) { //uint32_t col = upcastCaller->palette[evt_id - 200]; @@ -153,43 +166,20 @@ void PalettizedEditorColorPicker::eventFileSaved(int evt_id, PlatformNativePathS } } -void PalettizedEditorColorPicker::eventFileOpen(int evt_id, PlatformNativePathString name, int exporterIndex) +void PalettizedEditorColorPicker::eventFileOpen(int evt_id, PlatformNativePathString name, int importerIndex) { if (evt_id == EVENT_PALETTECOLORPICKER_LOADPALETTE) { - FILE* f = platformOpenFile(name, PlatformFileModeRB); - if (f != NULL) { - char header[7]; - fread(header, 7, 1, f); - if (memcmp(header, "VOIDPLT", 7) == 0) { - uint8_t fileversion; - fread(&fileversion, 1, 1, f); - if (fileversion == 1) { - std::vector newPalette; - uint32_t count; - fread(&count, 1, 4, f); - for (int x = 0; x < count; x++) { - uint32_t col; - fread(&col, 1, 4, f); - newPalette.push_back(col); - } - upcastCaller->setPalette(newPalette); - updateForcedColorPaletteButtons(); - g_addNotification(Notification("Success", "Loaded palette file", 4000, NULL, COLOR_INFO)); - } - else { - g_addNotification(ErrorNotification("Error", "Unsupported palette file version")); - } - } - else { - g_addNotification(ErrorNotification("Error", "Invalid palette file")); - } - fclose(f); - } + importerIndex--; + auto result = g_paletteImporters[importerIndex]->importPalette(name); + if (result.first) { + upcastCaller->setPalette(result.second); + updateForcedColorPaletteButtons(); + g_addNotification(Notification("Success", "Palette file loaded", 4000, NULL, COLOR_INFO)); + } else { - g_addNotification(ErrorNotification("Error", "Could not open palette file")); + g_addNotification(ErrorNotification("Error", "Could not load palette file")); } } - } void PalettizedEditorColorPicker::eventColorSet(int evt_id, uint32_t color) diff --git a/freesprite/PalettizedEditorColorPicker.h b/freesprite/PalettizedEditorColorPicker.h index c0d5ac9..3d7dffe 100644 --- a/freesprite/PalettizedEditorColorPicker.h +++ b/freesprite/PalettizedEditorColorPicker.h @@ -8,14 +8,16 @@ class PalettizedEditorColorPicker : MainEditorPalettized* upcastCaller; TabbedView* colorPaletteTabs; + UILabel* pickedColorLabel = NULL; + PalettizedEditorColorPicker(MainEditorPalettized* caller); void render(XY position) override; void eventButtonPressed(int evt_id) override; void eventButtonRightClicked(int evt_id) override; void eventDropdownItemSelected(int evt_id, int index, std::string name) override; - void eventFileSaved(int evt_id, PlatformNativePathString name, int exporterIndex = -1) override; - void eventFileOpen(int evt_id, PlatformNativePathString name, int exporterIndex = -1) override; + void eventFileSaved(int evt_id, PlatformNativePathString name, int importerIndex = -1) override; + void eventFileOpen(int evt_id, PlatformNativePathString name, int importerIndex = -1) override; void eventColorSet(int evt_id, uint32_t color) override; void setMainEditorColorRGB(SDL_Color col, bool updateHSVSliders = true, bool updateRGBSliders = true, bool updateHSVTextBoxes = true) override {} diff --git a/freesprite/UILabel.cpp b/freesprite/UILabel.cpp index f3fccb8..e168c6f 100644 --- a/freesprite/UILabel.cpp +++ b/freesprite/UILabel.cpp @@ -3,5 +3,5 @@ void UILabel::render(XY pos) { - g_fnt->RenderString(text, pos.x, pos.y); + g_fnt->RenderString(text, pos.x, pos.y, color); } diff --git a/freesprite/UILabel.h b/freesprite/UILabel.h index bf69f26..937ddbe 100644 --- a/freesprite/UILabel.h +++ b/freesprite/UILabel.h @@ -5,6 +5,7 @@ class UILabel : { public: std::string text = ""; + SDL_Color color = { 255, 255, 255, 255 }; bool focusable() override { return false; } void render(XY pos) override; diff --git a/freesprite/globals.h b/freesprite/globals.h index dec1da4..fdfb86c 100644 --- a/freesprite/globals.h +++ b/freesprite/globals.h @@ -17,6 +17,7 @@ #include #include #include +#include #ifdef __GNUC__ #include