From 713fd9ff7144cc63c3f4f0799db264d9ec0afde8 Mon Sep 17 00:00:00 2001 From: Thomas Bernard Date: Sun, 20 Dec 2020 02:47:36 +0100 Subject: [PATCH] Load SGX SymbOS graphics --- src/const.h | 1 + src/cpcformats.c | 223 ++++++++++++++++++++++++++++++++++++++++++++++ src/fileformats.h | 4 + src/helpfile.h | 1 + src/loadsave.c | 3 +- 5 files changed, 231 insertions(+), 1 deletion(-) diff --git a/src/const.h b/src/const.h index 1875a6bb..b865b1a4 100644 --- a/src/const.h +++ b/src/const.h @@ -129,6 +129,7 @@ enum FILE_FORMATS FORMAT_CM5, ///< Amstrad CPC Mode 5 FORMAT_PPH, ///< Amstrad CPC Perfect Pix FORMAT_GOS, ///< Amstrad Plus Graphos + FORMAT_SGX, ///< SymbOS graphic files FORMAT_XPM, ///< X PixMap FORMAT_ICO, ///< Windows icons FORMAT_INFO, ///< Amiga OS icons diff --git a/src/cpcformats.c b/src/cpcformats.c index bf312ba5..146aa973 100644 --- a/src/cpcformats.c +++ b/src/cpcformats.c @@ -33,6 +33,10 @@ #include "gfx2mem.h" #include "gfx2log.h" +#ifndef MAX +#define MAX(a,b) (((a)>(b)) ? (a) : (b)) +#endif + /** * @defgroup cpcformats Amstrad CPC/CPC+ picture formats * @ingroup loadsaveformats @@ -48,10 +52,229 @@ * - SCR : OCP Art Studio / iMPdraw v2 / etc. * - CM5 : Mode 5 Viewer * - PPH : Perfect Pix + * - SGX : SymbOS graphic files * * @{ */ +/** + * Test for SGX file (SymbOS) + * + * http://www.cpcwiki.eu/index.php/Format:SGX_(SymbOS_graphic_files) + */ +void Test_SGX(T_IO_Context * context, FILE * file) +{ + byte header[8]; + (void)context; + + if (Read_bytes(file, header, 8)) + { + unsigned long file_size = File_length_file(file); + if (header[0] > 0 && header[0] < 64 + && ((header[1] + 3) >> 2) == header[0] + && file_size >= (3 + (unsigned long)header[0] * (unsigned long)header[2])) + { + // Simple 4 colour graphic + File_error = 0; + } + else if (header[0] == 64 && (header[1] == 0 || header[1] == 5)) + { + // extended + word byte_width = header[2] | ((word)header[3] << 8); + word pixel_width = header[4] | ((word)header[5] << 8); + word height = header[6] | ((word)header[7] << 8); + if (height == 0 || byte_width == 0 || byte_width > 255) return; + if (header[1] == 0 && byte_width != ((pixel_width + 3) >> 2)) return; + if (header[1] == 5 && byte_width != ((pixel_width + 1) >> 1)) return; + File_error = 0; + } + } +} + +static void Set_SGX_Palette(T_IO_Context * context) +{ + static const byte sgx_palette[] = { + 0xff, 0xff, 0x80, // 0 + 0x00, 0x00, 0x00, + 0xff, 0x80, 0x00, + 0x80, 0x00, 0x00, + 0x00, 0xff, 0xff, // 4 + 0x00, 0x00, 0x80, + 0x80, 0x80, 0xff, + 0x80, 0x00, 0xff, + 0xff, 0xff, 0xff, // 8 + 0x00, 0x80, 0x00, + 0x00, 0xff, 0x00, + 0xff, 0x00, 0xff, + 0xff, 0xff, 0x00, // 12 + 0x80, 0x80, 0x80, + 0xff, 0x80, 0x80, + 0xff, 0x00, 0x00 + }; + if (Config.Clear_palette) + memset(context->Palette,0,sizeof(T_Palette)); + memcpy(context->Palette, sgx_palette, sizeof(sgx_palette)); +} + +/** + * Structure for current information about loading SGX file + */ +struct sgx_data { + T_IO_Context * context; + FILE * file; + unsigned long file_size; + word width, height; + byte ncolors; +}; + +/** + * Callback function for Read_SGX() + */ +typedef int (*Read_SGX_Callback)(struct sgx_data *, word, word, word, word, word, byte); + +/** + * Read a SGX file structure. + * + * @return 0 for error + */ +static int Read_SGX(struct sgx_data * data, Read_SGX_Callback cb) +{ + word posx = 0; + word posy = 0; + byte b; + + data->width = 0; + data->height = 0; + while (Read_byte(data->file, &b)) + { + if (b == 0) + { + // EOF + GFX2_Log(GFX2_DEBUG, "SGX EOF\n"); + return 1; + } + else if (b == 255) + { + GFX2_Log(GFX2_DEBUG, "SGX LF\n"); + // skip 2 bytes + if (!(Read_byte(data->file, &b) && Read_byte(data->file, &b))) + return 0; + posx = 0; + posy = data->height; + } + else if (b < 64) + { + byte byte_width, width, height; + byte_width = b; + if (!(Read_byte(data->file, &width) && Read_byte(data->file, &height))) + return 0; + GFX2_Log(GFX2_DEBUG, "SGX Simple 4c : %dx%d\n", (int)width, (int)height); + if (!cb(data, posx, posy, width, height, byte_width, 4)) + return 0; + posx += width; + data->width = MAX(data->width, posx); + data->height = MAX(data->height, posy + height); + if (data->ncolors == 0) + data->ncolors = 4; + } + else if (b == 64) + { + byte type; + word byte_width, width, height; + if (!(Read_byte(data->file, &type) && Read_word_le(data->file, &byte_width) + && Read_word_le(data->file, &width) && Read_word_le(data->file, &height))) + return 0; + GFX2_Log(GFX2_DEBUG, "SGX Extended %dc : %dx%d\n", type == 0 ? 4 : 16, (int)width, (int)height); + if (!cb(data, posx, posy, width, height, byte_width, type == 0 ? 4 : 16)) + return 0; + posx += width; + data->width = MAX(data->width, posx); + data->height = MAX(data->height, posy + height); + data->ncolors = MAX(data->ncolors, type == 0 ? 4 : 16); + } + else + { + GFX2_Log(GFX2_WARNING, "SGX : unrecognized chunk. Byte 0x%02x at offset 0x%06x\n", b, ftell(data->file) - 1); + } + } + return 1; // it is OK if the file does finish with a EOF mark +} + +/** + * Only skip bytes + */ +int SGX_Get_dimensions(struct sgx_data * data, word posx, word posy, word width, word height, word byte_width, byte ncolors) +{ + (void)posx; + (void)posy; + (void)width; + (void)ncolors; + if (fseek(data->file, (long)byte_width * height, SEEK_CUR) < 0) + return 0; + return 1; +} + +/** + * Set the pixels + * @return 0 in case of error + */ +int SGX_Load_Picture(struct sgx_data * data, word posx, word posy, word width, word height, word byte_width, byte ncolors) +{ + word y; + + for (y = posy; y < (posy + height); y++) + { + word i, x; + for (i = 0, x = posx; i < byte_width; i++) + { + byte b; + if (!Read_byte(data->file, &b)) + return 0; + if (ncolors == 4) + { + do { + // upper nibble is 4 lower color bits, lower nibble is 4 upper color bits + Set_pixel(data->context, x++, y, (b & 0x80) >> 7 | (b & 0x08) >> 2); + b <<= 1; + } + while (((x ^ posx) & 3) && (x < (posx + width))); + } + else + { + Set_pixel(data->context, x++, y, b >> 4); + if (x < (posx + width)) + Set_pixel(data->context, x++, y, b & 0x0f); + } + } + } + return 1; +} + +void Load_SGX(T_IO_Context * context) +{ + struct sgx_data data = { context, NULL, 0, 0, 0, 0 }; + + File_error = 1; + data.file = Open_file_read(context); + if (data.file == NULL) return; + data.file_size = File_length_file(data.file); + if (Read_SGX(&data, SGX_Get_dimensions)) + { + GFX2_Log(GFX2_DEBUG, "SGX total dimensions : %ux%u, %d colors\n", + (unsigned)data.width, (unsigned)data.height, (int)data.ncolors); + File_error = 0; + Pre_load(context, data.width, data.height, data.file_size, FORMAT_SGX, PIXEL_SIMPLE, data.ncolors == 16 ? 4 : 2); + if (File_error == 0) + { + if (fseek(data.file, 0, SEEK_SET) < 0 || !Read_SGX(&data, SGX_Load_Picture)) + File_error = 1; + else + Set_SGX_Palette(context); + } + } + fclose(data.file); +} + /** * Test for SCR file (Amstrad CPC) * diff --git a/src/fileformats.h b/src/fileformats.h index adbc3b2d..c8719326 100644 --- a/src/fileformats.h +++ b/src/fileformats.h @@ -148,6 +148,10 @@ void Test_GOS(T_IO_Context *, FILE *); void Load_GOS(T_IO_Context *); void Save_GOS(T_IO_Context *); +// -- SGX (SymbOS) +void Test_SGX(T_IO_Context *, FILE *); +void Load_SGX(T_IO_Context *); + // -- XPM (X PixMap) // Loading is done through SDL_Image void Save_XPM(T_IO_Context*); diff --git a/src/helpfile.h b/src/helpfile.h index 69d5f8ba..7105cb8f 100644 --- a/src/helpfile.h +++ b/src/helpfile.h @@ -541,6 +541,7 @@ static const T_Help_table helptable_credits[] = HELP_TEXT (" PPH : Rhino / Batman Group") HELP_TEXT (" SCx : Colorix (?)") HELP_TEXT (" SCR : Advanced OCP Art Studio + iMPdraw") + HELP_TEXT (" SGX : SymbOS") HELP_TEXT (" TIFF : Aldus (now Adobe)") HELP_TEXT (" TNY : David Mumper") HELP_TEXT (" XPM : C.Nahaboo and D.Dardailler / Bull") diff --git a/src/loadsave.c b/src/loadsave.c index 5fddd166..b61427d8 100644 --- a/src/loadsave.c +++ b/src/loadsave.c @@ -112,7 +112,7 @@ const T_Format File_formats[] = { "pi1;pc1;pi2;pc2;pi3;pc3;neo;tny;tn1;tn2;tn3;tn4;ca1;ca2;ca3;" "c64;p64;a64;pi;rp;aas;art;dd;iph;ipt;hpc;ocp;koa;koala;fli;bml;cdu;prg;pmg;rpm;" "gpx;" - "cpc;scr;win;pph;cm5;go1;" + "cpc;scr;win;pph;cm5;go1;sgx;" "hgr;dhgr;" "grb;grob;" "sc2;" @@ -148,6 +148,7 @@ const T_Format File_formats[] = { {FORMAT_CM5, " cm5", Test_CM5, Load_CM5, Save_CM5, 0, 0, 1, "cm5", "cm5"}, {FORMAT_PPH, " pph", Test_PPH, Load_PPH, Save_PPH, 0, 0, 1, "pph", "pph"}, {FORMAT_GOS, " go1", Test_GOS, Load_GOS, Save_GOS, 0, 0, 0, "go1", "go1"}, + {FORMAT_SGX, " sgx", Test_SGX, Load_SGX, NULL, 0, 0, 1, "sgx", "sgx"}, {FORMAT_XPM, " xpm", NULL, NULL, Save_XPM, 0, 0, 0, "xpm", "xpm"}, {FORMAT_ICO, " ico", Test_ICO, Load_ICO, Save_ICO, 0, 0, 0, "ico", "ico;ic2;cur"}, {FORMAT_INFO," info",Test_INFO,Load_INFO,NULL, 0, 0, 0, "info", "info"},