From 640a80a9e5c43a4808e1254030dc1f9f1bb3c086 Mon Sep 17 00:00:00 2001 From: Thomas Bernard Date: Sat, 7 Dec 2019 15:59:05 +0100 Subject: [PATCH] C64 formats (KOALA/GPX/PRG/etc.) to c64formats.c --- src/Makefile | 4 +- src/c64formats.c | 1814 +++++++++++++++++++++++++++++++++++++++++ src/miscfileformats.c | 1778 ---------------------------------------- 3 files changed, 1816 insertions(+), 1780 deletions(-) create mode 100644 src/c64formats.c diff --git a/src/Makefile b/src/Makefile index d00e3aeb..fe2b9cfd 100644 --- a/src/Makefile +++ b/src/Makefile @@ -809,7 +809,7 @@ OBJS = main.o init.o graph.o $(APIOBJ) misc.o special.o \ windows.o brush.o realpath.o mountlist.o input.o hotkeys.o \ transform.o pversion.o factory.o $(PLATFORMOBJ) \ loadsave.o loadsavefuncs.o \ - pngformat.o motoformats.o stformats.o \ + pngformat.o motoformats.o stformats.o c64formats.o \ fileformats.o miscfileformats.o libraw2crtc.o \ brush_ops.o buttons_effects.o layers.o \ oldies.o tiles.o colorred.o unicode.o gfx2surface.o \ @@ -821,7 +821,7 @@ endif TESTSOBJS = $(patsubst %.c,%.o,$(wildcard tests/*.c)) \ miscfileformats.o fileformats.o oldies.o libraw2crtc.o \ loadsavefuncs.o packbits.o tifformat.o c64load.o 6502.o \ - pngformat.o motoformats.o stformats.o \ + pngformat.o motoformats.o stformats.o c64formats.o \ op_c.o colorred.o \ unicode.o \ io.o realpath.o version.o pversion.o \ diff --git a/src/c64formats.c b/src/c64formats.c new file mode 100644 index 00000000..4a4f8087 --- /dev/null +++ b/src/c64formats.c @@ -0,0 +1,1814 @@ +/* vim:expandtab:ts=2 sw=2: +*/ +/* Grafx2 - The Ultimate 256-color bitmap paint program + + Copyright 2018-2019 Thomas Bernard + Copyright 2011 Pawel Góralski + Copyright 2009 Petter Lindquist + Copyright 2008 Yves Rizoud + Copyright 2008 Franck Charlet + Copyright 2007-2011 Adrien Destugues + Copyright 1996-2001 Sunset Design (Guillaume Dorme & Karl Maritaud) + + Grafx2 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; version 2 + of the License. + + Grafx2 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 Grafx2; if not, see +*/ + +///@file c64formats.c +/// Formats for the Commodore 64 + +#include +#include +#include +#include "engine.h" +#include "screen.h" +#include "windows.h" +#include "input.h" +#include "help.h" +#include "fileformats.h" +#include "loadsavefuncs.h" +#include "io.h" +#include "misc.h" +#include "oldies.h" +#include "c64load.h" +#include "keycodes.h" +#include "gfx2mem.h" +#include "gfx2log.h" + +//////////////////////////////////// C64 //////////////////////////////////// + +/** C64 file formats + */ +enum c64_format +{ + F_invalid = -1, + F_hires = 0, ///< 320x200 + F_multi = 1, ///< 160x200 + F_bitmap = 2, ///< 320x200 monochrome + F_fli = 3 ///< FLI (Flexible Line Interpretation) +}; + +/** C64 file formats names + */ +static const char *c64_format_names[] = { + "Hires", + "Multicolor", + "Bitmap", + "FLI" +}; + +static long C64_unpack_doodle(byte ** file_buffer, long file_size); + +/** + * Test for a C64 picture file + * + * Checks the file size and the load address + * + * References : + * - http://unusedino.de/ec64/technical/formats/bitmap.html + * - http://codebase64.org/doku.php?id=base:c64_grafix_files_specs_list_v0.03 + * - https://sourceforge.net/p/view64/code/HEAD/tree/trunk/libview64.c#l3737 + */ +void Test_C64(T_IO_Context * context, FILE * file) +{ + unsigned long file_size; + word load_addr; + byte header[14]; + + (void)context; + File_error = 1; + file_size = File_length_file(file); + if (file_size < 16 || file_size > 48*1024) + return; // File too short or too long, exit now + // First test for formats without load address + switch (file_size) + { + // case 1000: // screen or color + case 8000: // raw bitmap + case 9000: // bitmap + ScreenRAM + case 10001: // multicolor + case 17472: // FLI (BlackMail) + File_error = 0; + return; + default: // then we don't know for now. + if (!Read_word_le(file, &load_addr)) + return; + } + GFX2_Log(GFX2_DEBUG, "Test_C64() file_size=%ld LoadAddr=$%04X\n", file_size, load_addr); + if (!Read_bytes(file, header, sizeof(header))) + return; + if (memcmp(header, "DRAZPAINT", 9) == 0) + { + GFX2_Log(GFX2_DEBUG, "Test_C64() header=%.13s RLE code = $%02X\n", header, header[13]); + File_error = 0; + return; + } + // check last 2 bytes + if (fseek(file, -2, SEEK_END) < 0) + return; + if (!Read_bytes(file, header, 2)) + return; + if (load_addr == 0x4000 && header[0] == 0xC2 && header[1] == 0x00) // Amica Paint EOF mark + { + File_error = 0; + return; + } + switch (file_size) + { + // case 1002: // (screen or color) + loadaddr + case 8002: // raw bitmap with loadaddr + case 9002: // bitmap + ScreenRAM + loadaddr + // $4000 => InterPaint Hi-Res (.iph) + case 9003: // bitmap + ScreenRAM + loadaddr (+ border ?) + case 9009: // bitmap + ScreenRAM + loadaddr + // $2000 => Art Studio + case 9218: + // $5C00 => Doodle + case 9332: + // $3F8E => Paint Magic (.pmg) 'JEDI' at offset $0010 and $2010 + case 10003: // multicolor + loadaddr + // $4000 => InterPaint multicolor + // $6000 => Koala Painter + case 10004: + // $4000 => Face Paint (.fpt) + case 10006: + // $6000 => Run Paint (.rpm) + case 10018: + // $2000 => Advanced Art Studio + case 10022: + // $18DC => Micro Illustrator (uncompressed) + case 10050: + // $1800 => Picasso64 + case 10218: + // $3C00 => Image System (.ism) + case 10219: + // $7800 => Saracen Paint (.sar) + File_error = 0; + break; + case 10242: + // $4000 => Artist 64 (.a64) + // $A000 => Blazing paddles (.pi) + // $5C00 => Rainbow Painter (.rp) + if (load_addr != 0x4000 && load_addr != 0xa000 && load_addr != 0x5c00) + { + File_error = 1; + return; + } + File_error = 0; + break; + case 10608: + // $0801 = BASIC programs loading address + File_error = 0; + break; + case 17218: + case 17409: + // $3c00 => FLI-designer v1.1 + // ? $3ff0 => FLI designer 2 ? + case 17410: + // $3c00 => FLI MATIC + case 17474: // FLI (BlackMail) + loadaddr + // $3b00 => FLI Graph 2 + case 17665: + // $3b00 => FLI editor + case 17666: + // $3b00 => FLI Graph + case 10277: // multicolor CDU-Paint + loadaddr + // $7EEF + File_error = 0; + break; + default: // then we don't know for now. + if (load_addr == 0x6000 || load_addr == 0x5c00) + { + long unpacked_size; + byte * buffer = GFX2_malloc(file_size); + if (buffer == NULL) + return; + fseek(file, SEEK_SET, 0); + if (!Read_bytes(file, buffer, file_size)) + return; + unpacked_size = C64_unpack_doodle(&buffer, file_size); + free(buffer); + switch (unpacked_size) + { + case 9024: // Doodle hi color + case 9216: + case 10001: // Koala painter 2 + case 10070: + File_error = 0; + } + } + } +} + +/** + * Test for a C64 auto-load machine language program + * which could be a picture + */ +void Test_PRG(T_IO_Context * context, FILE * file) +{ + unsigned long file_size; + word load_addr; + (void)context; + + file_size = File_length_file(file); + if (file_size > (38911 + 2)) // maximum length of PRG loaded at $0801 + return; + if (!Read_word_le(file, &load_addr)) + return; + if (load_addr != 0x0801) + return; + // 6502 emulators : + // https://github.com/redcode/6502 + // http://rubbermallet.org/fake6502.c + // https://github.com/jamestn/cpu6502 + // https://github.com/dennis-chen/6502-Emu + // https://github.com/DavidBuchanan314/6502-emu + // basic program + if (C64_isBinaryProgram(file) != 0) + File_error = 0; +} + +/** + * Load C64 hires (320x200) + * + * @param context the IO context + * @param bitmap the bitmap RAM (8000 bytes) + * @param screen_ram the screen RAM (1000 bytes) + */ +static void Load_C64_hires(T_IO_Context *context, byte *bitmap, byte *screen_ram) +{ + int cx,cy,x,y,c[4],pixel,color; + + for(cy=0; cy<25; cy++) + { + for(cx=0; cx<40; cx++) + { + if(screen_ram != NULL) + { + c[0]=screen_ram[cy*40+cx]&15; + c[1]=screen_ram[cy*40+cx]>>4; + } + else + { /// If screen_ram is NULL, uses default C64 basic colors + c[0] = 6; + c[1] = 14; + } + for(y=0; y<8; y++) + { + pixel=bitmap[cy*320+cx*8+y]; + for(x=0; x<8; x++) + { + color=c[pixel&(1<<(7-x))?1:0]; + Set_pixel(context, cx*8+x,cy*8+y,color); + } + } + } + } +} + +/** + * Load C64 multicolor (160x200) + * + * @param context the IO context + * @param bitmap the bitmap RAM (8000 bytes) + * @param screen_ram the screen RAM (1000 bytes) + * @param color_ram the color RAM (1000 bytes) + * @param background the background color + */ +static void Load_C64_multi(T_IO_Context *context, byte *bitmap, byte *screen_ram, byte *color_ram, byte background) +{ + int cx,cy,x,y,c[4],pixel,color; + c[0]=background&15; + for(cy=0; cy<25; cy++) + { + for(cx=0; cx<40; cx++) + { + c[1]=screen_ram[cy*40+cx]>>4; + c[2]=screen_ram[cy*40+cx]&15; + c[3]=color_ram[cy*40+cx]&15; + + for(y=0; y<8; y++) + { + pixel=bitmap[cy*320+cx*8+y]; + for(x=0; x<4; x++) + { + color=c[(pixel&3)]; + pixel>>=2; + Set_pixel(context, cx*4+(3-x),cy*8+y,color); + } + } + } + } +} + +/** + * Loads a C64 FLI (Flexible Line Interpretation) picture. + * Sets 4 layers : + * - Layer 0 : filled with background colors (1 per line) + * - Layer 1 : "Color RAM" 4x8 blocks + * - Layer 2 : pixels (From Screen RAMs + Bitmap) + * - Layer 3 : Transparency layer filled with color 16 + * + * @param context the IO context + * @param bitmap 8000 bytes buffer + * @param screen_ram 8 x 1024 bytes buffers + * @param color_ram 1000 byte buffer + * @param background 200 byte buffer + */ +void Load_C64_fli(T_IO_Context *context, byte *bitmap, byte *screen_ram, byte *color_ram, byte *background) +{ + // Thanks to MagerValp for complement of specifications. + // + // background : length: 200 (+ padding 56) + // These are the BG colors for lines 0-199 (top to bottom) + // Low nybble: the color. + // High nybble: garbage. ignore it. + // color_ram : length: 1000 (+ padding 24) + // Color RAM. Contains one color per 4x8 block. + // There are 40x25 such blocks, arranged from top left to bottom + // right, starting in right direction. For each block there is one byte. + // Low nybble: the color. + // High nybble: garbage. ignore it. + // screen_ram : length: 8192 + // Screen RAMs. The s is important. + // This is actually 8 blocks of 1000 bytes, each separated by a filler of + // 24 bytes. Each byte contains data for a 4x1 pixel group, and a complete + // block will contain 40x25 of them. 40 is from left to right, and 25 is from + // top to bottom, spacing them 8 lines apart. + // The second block start at y=1, the third block starts at y=2, etc... + // Each byte contains 2 colors that *can* be used by the 4x1 pixel group: + // Low nybble: Color 1 + // High nybble: Color 2 + // + // bitmap : length: 8000 + // This is the final structure that refers to all others. It describes + // 160x200 pixels linearly, from top left to bottom right, starting in + // right direction. For each pixel, two bits say which color is displayed + // (So 4 pixels are described by the same byte) + // 00 Use the BG color of the current line (background[y]) + // 01 Use the Color 2 from the current 4x8 block of Screen RAM + // ((screen_ram[y/8][x/4] & 0xF0) >> 8) + // 10 Use the Color 1 from the current 4x8 block of Screen RAM + // (screen_ram[y/8][x/4] & 0x0F) + // 11 Use the color from Color RAM + // (color_ram[y/8][x/4] & 0x0F) + // + + int cx,cy,x,y,c[4]; + + if (context->Type == CONTEXT_MAIN_IMAGE) + { + // Fill layer 0 with background colors + for(y=0; y<200; y++) + { + byte bg_color = 0; + if (background != NULL) + bg_color = background[y]; + for(x=0; x<160; x++) + Set_pixel(context, x,y, bg_color); + } + + // Fill layer 1 with color ram (1 color per 4x8 block) + Set_loading_layer(context, 1); + for(cy=0; cy<25; cy++) + { + for(cx=0; cx<40; cx++) + { + c[3]=color_ram[cy*40+cx]&15; + for(y=0; y<8; y++) + { + for(x=0; x<4; x++) + { + Set_pixel(context, cx*4+x,cy*8+y,c[3]); + } + } + } + } + } + + // Layer 2 are actual pixels + Set_loading_layer(context, 2); + for(cy=0; cy<25; cy++) + { + for(cx=0; cx<40; cx++) + { + c[3]=color_ram[cy*40+cx]&15; + for(y=0; y<8; y++) + { + int pixel=bitmap[cy*320+cx*8+y]; + + c[0] = 0; + if(background != NULL) + c[0] = background[cy*8+y]&15; + c[1]=screen_ram[y*1024+cy*40+cx]>>4; + c[2]=screen_ram[y*1024+cy*40+cx]&15; + for(x=0; x<4; x++) + { + int color=c[(pixel&3)]; + pixel>>=2; + Set_pixel(context, cx*4+(3-x),cy*8+y,color); + } + } + } + } + if (context->Type == CONTEXT_MAIN_IMAGE) + { + // Fill layer 3 with color 16 + Set_loading_layer(context, 3); + for(y=0; y<200; y++) + { + for(x=0; x<160; x++) + Set_pixel(context, x,y,16); + } + } +} + +/** + * Count the length of the unpacked data + * + * RLE encoding is either ESCAPE CODE, COUNT, VALUE + * or ESCAPE CODE, VALUE, COUNT + * + * @param buffer the packed data + * @param input_size the packed data byte count + * @param RLE_code the escape code + * @param order 0 for ESCAPE, COUNT, VALUE, 1 for ESCAPE, VALUE, COUNT + * @return the unpacked data byte count + */ +static long C64_unpack_get_length(const byte * buffer, long input_size, byte RLE_code, int order) +{ + const byte * end; + long unpacked_size = 0; + + end = buffer + input_size; + while(buffer < end) + { + if (*buffer == RLE_code) + { + if (order) + { // ESCAPE, VALUE, COUNT + buffer += 2; // skip value + unpacked_size += *buffer; + } + else + { // ESCAPE, COUNT, VALUE + buffer++; + if (*buffer == 0) + break; + unpacked_size += *buffer++; + } + } + else + unpacked_size++; + buffer++; + } + return unpacked_size; +} + +/** + * unpack RLE packed data + * + * RLE encoding is either ESCAPE CODE, COUNT, VALUE + * or ESCAPE CODE, VALUE, COUNT + * + * @param unpacked buffer to received unpacked data + * @param buffer the packed data + * @param input_size the packed data byte count + * @param RLE_code the escape code + * @param order 0 for ESCAPE, COUNT, VALUE, 1 for ESCAPE, VALUE, COUNT + */ +static void C64_unpack(byte * unpacked, const byte * buffer, long input_size, byte RLE_code, int order) +{ + const byte * end; + + end = buffer + input_size; + while(buffer < end) + { + if (*buffer == RLE_code) + { + byte count; + byte value; + buffer++; + if (order) + { // ESCAPE, VALUE, COUNT + value = *buffer++; + count = *buffer; + } + else + { // ESCAPE, COUNT, VALUE + count = *buffer++; + value = *buffer; + } + if (count == 0) + break; + while (count-- > 0) + *unpacked++ = value; + } + else + *unpacked++ = *buffer; + buffer++; + } +} + +/** + * Unpack the Amica Paint RLE packing + * + * @param[in,out] file_buffer will contain the unpacked buffer on return + * @param[in] file_size packed buffer size + * @return the unpacked data size or -1 in case of error + * + * Ref: + * - http://codebase64.org/doku.php?id=base:c64_grafix_files_specs_list_v0.03 + */ +static long C64_unpack_amica(byte ** file_buffer, long file_size) +{ + long unpacked_size; + byte * unpacked_buffer; + const byte RLE_code = 0xC2; + + if (file_size <= 16 || file_buffer == NULL || *file_buffer == NULL) + return -1; + unpacked_size = C64_unpack_get_length(*file_buffer + 2, file_size - 2, RLE_code, 0); + GFX2_Log(GFX2_DEBUG, "C64_unpack_amica() unpacked_size=%ld\n", unpacked_size); + // 2nd pass to unpack + unpacked_buffer = GFX2_malloc(unpacked_size); + if (unpacked_buffer == NULL) + return -1; + C64_unpack(unpacked_buffer, *file_buffer + 2, file_size - 2, RLE_code, 0); + + free(*file_buffer); + *file_buffer = unpacked_buffer; + return unpacked_size; +} + +/** + * Unpack the DRAZPAINT RLE packing + * + * @param[in,out] file_buffer will contain the unpacked buffer on return + * @param[in] file_size packed buffer size + * @return the unpacked data size or -1 in case of error + * + * Ref: + * - https://www.godot64.de/german/l_draz.htm + * - https://sourceforge.net/p/view64/code/HEAD/tree/trunk/libview64.c#l2805 + */ +static long C64_unpack_draz(byte ** file_buffer, long file_size) +{ + long unpacked_size; + byte * unpacked_buffer; + byte RLE_code; + + if (file_size <= 16 || file_buffer == NULL || *file_buffer == NULL) + return -1; + RLE_code = (*file_buffer)[15]; + // First pass to know unpacked size + unpacked_size = C64_unpack_get_length(*file_buffer + 16, file_size - 16, RLE_code, 0); + GFX2_Log(GFX2_DEBUG, "C64_unpack_draz() \"%.13s\" RLE code=$%02X RLE data length=%ld unpacked_size=%ld\n", + *file_buffer + 2, RLE_code, file_size - 16, unpacked_size); + // 2nd pass to unpack + unpacked_buffer = GFX2_malloc(unpacked_size); + if (unpacked_buffer == NULL) + return -1; + C64_unpack(unpacked_buffer, *file_buffer + 16, file_size - 16, RLE_code, 0); + free(*file_buffer); + *file_buffer = unpacked_buffer; + return unpacked_size; +} + +/** + * Unpack doodle/koala painter 2 data + * + * @return the unpacked data size or -1 in case of error + */ +static long C64_unpack_doodle(byte ** file_buffer, long file_size) +{ + long unpacked_size; + byte * unpacked_buffer; + const byte RLE_code = 0xFE; + + if (file_size <= 16 || file_buffer == NULL || *file_buffer == NULL) + return -1; + // First pass to know unpacked size + unpacked_size = C64_unpack_get_length(*file_buffer + 2, file_size - 2, RLE_code, 1); + GFX2_Log(GFX2_DEBUG, "C64_unpack_doodle() unpacked_size=%ld\n", unpacked_size); + // 2nd pass to unpack + unpacked_buffer = GFX2_malloc(unpacked_size); + if (unpacked_buffer == NULL) + return -1; + C64_unpack(unpacked_buffer, *file_buffer + 2, file_size - 2, RLE_code, 1); + free(*file_buffer); + *file_buffer = unpacked_buffer; + return unpacked_size; +} + +/** + * Load C64 pictures formats. + * + * Supports: + * - Hires (with or without ScreenRAM) + * - Multicolor (Koala or CDU-paint format) + * - FLI + * + * see http://unusedino.de/ec64/technical/formats/bitmap.html + * + * @param context the IO context + */ +void Load_C64(T_IO_Context * context) +{ + FILE* file; + long file_size; + byte hasLoadAddr=0; + word load_addr; + enum c64_format loadFormat = F_invalid; + + byte *file_buffer; + byte *bitmap, *screen_ram, *color_ram=NULL, *background=NULL; // Only pointers to existing data + byte *temp_buffer = NULL; + word width, height=200; + + file = Open_file_read(context); + + if (file) + { + File_error=0; + file_size = File_length_file(file); + + // Load entire file in memory + file_buffer = GFX2_malloc(file_size); + if (!file_buffer) + { + File_error = 1; + fclose(file); + return; + } + if (!Read_bytes(file,file_buffer,file_size)) + { + File_error = 1; + free(file_buffer); + fclose(file); + return; + } + fclose(file); + + // get load address (valid only if hasLoadAddr = 1) + load_addr = file_buffer[0] | (file_buffer[1] << 8); + + // Unpack if needed + if (memcmp(file_buffer + 2, "DRAZPAINT", 9) == 0) + file_size = C64_unpack_draz(&file_buffer, file_size); + else if(load_addr == 0x4000 && file_buffer[file_size-2] == 0xC2 && file_buffer[file_size-1] == 0) + file_size = C64_unpack_amica(&file_buffer, file_size); + else if (file_size < 8000 && (load_addr == 0x6000 || load_addr == 0x5c00)) + file_size = C64_unpack_doodle(&file_buffer, file_size); + + switch (file_size) + { + case 8000: // raw bitmap + hasLoadAddr=0; + loadFormat=F_bitmap; + bitmap=file_buffer+0; // length: 8000 + screen_ram=NULL; + break; + + case 8002: // raw bitmap with loadaddr + hasLoadAddr=1; + loadFormat=F_bitmap; + bitmap=file_buffer+2; // length: 8000 + screen_ram=NULL; + break; + + case 9000: // bitmap + ScreenRAM + hasLoadAddr=0; + loadFormat=F_hires; + bitmap=file_buffer+0; // length: 8000 + screen_ram=file_buffer+8000; // length: 1000 + break; + + case 9003: // bitmap + ScreenRAM + loadaddr (+ border ?) + case 9002: // bitmap + ScreenRAM + loadaddr + hasLoadAddr=1; + loadFormat=F_hires; + bitmap=file_buffer+2; // length: 8000 + screen_ram=file_buffer+8002; // length: 1000 + break; + + case 9009: // Art Studio (.aas) + hasLoadAddr=1; + loadFormat=F_hires; + bitmap=file_buffer+2; // length: 8000 + screen_ram=file_buffer+8002; // length: 1000 + break; + + case 9024: // Doodle (unpacked from .jj) + case 9216: + hasLoadAddr=0; + loadFormat=F_hires; + screen_ram=file_buffer; // length: 1000 (+24 padding) + bitmap=file_buffer+1024; // length: 8000 + break; + + case 9218: // Doodle (.dd) + hasLoadAddr=1; + loadFormat=F_hires; + screen_ram=file_buffer+2; // length: 1000 (+24 padding) + bitmap=file_buffer+1024+2; // length: 8000 + break; + + case 9332: // Paint Magic .pmg + hasLoadAddr=1; + loadFormat=F_multi; + // Display routine between offset $0002 and $0073 (114 bytes) + // duplicated between offset $2002 and $2073 + bitmap=file_buffer+114+2; // $0074 + background=file_buffer+8000+114+2;// $1FB4 + temp_buffer = GFX2_malloc(1000); + memset(temp_buffer, file_buffer[3+8000+114+2], 1000); // color RAM Byte + color_ram=temp_buffer; + //border byte = file_buffer[4+8000+114+2]; + screen_ram=file_buffer+8192+114+2; // $2074 + break; + + case 10001: // multicolor + case 10070: // unpacked file. + hasLoadAddr=0; + loadFormat=F_multi; + bitmap=file_buffer+0; // length: 8000 + screen_ram=file_buffer+8000; // length: 1000 + color_ram=file_buffer+9000; // length: 1000 + background=file_buffer+10000; // only 1 + break; + + case 10003: // multicolor + loadaddr + case 10004: // extra byte is border color + case 10006: // Run Paint + hasLoadAddr=1; + loadFormat=F_multi; + bitmap=file_buffer+2; // length: 8000 + screen_ram=file_buffer+8002; // length: 1000 + color_ram=file_buffer+9002; // length: 1000 + background=file_buffer+10002; // only 1 + break; + + case 10018: // Advanced Art Studio (.ocp) + loadaddr + hasLoadAddr=1; + loadFormat=F_multi; + bitmap=file_buffer+2; // length: 8000 + screen_ram=file_buffer+8000+2; // length: 1000 + color_ram=file_buffer+9016+2; // length: 1000 + // filebuffer+9000+2 is border + background=file_buffer+9001+2; // only 1 + break; + + case 10022: // Micro Illustrator (.mil) + hasLoadAddr=1; + loadFormat=F_multi; + screen_ram=file_buffer+20+2; + color_ram=file_buffer+1000+20+2; + bitmap=file_buffer+2*1000+20+2; + break; + + case 10049: // unpacked DrazPaint + hasLoadAddr=1; + loadFormat=F_multi; + color_ram=file_buffer; // length: 1000 + (padding 24) + screen_ram=file_buffer+1024; // length: 1000 + (padding 24) + bitmap=file_buffer+1024*2; // length: 8000 + background=file_buffer+8000+1024*2; + break; + + case 10050: // Picasso64 multicolor + loadaddr + hasLoadAddr=1; + loadFormat=F_multi; + color_ram=file_buffer+2; // length: 1000 + (padding 24) + screen_ram=file_buffer+1024+2; // length: 1000 + (padding 24) + bitmap=file_buffer+1024*2+2; // length: 8000 + background=file_buffer+1024*2+2-1; // only 1 + break; + + case 10218: // Image System + hasLoadAddr=1; + loadFormat=F_multi; + color_ram=file_buffer+2; // Length: 1000 (+ padding 24) + bitmap=file_buffer+1024+2; // Length: 8000 (+padding 192) + screen_ram=file_buffer+8192+1024+2; // Length: 1000 (no padding) + background=file_buffer+8192+1024+2-1; // only 1 + break; + + case 10219: // Saracen Paint (.sar) + hasLoadAddr=1; + loadFormat=F_multi; + screen_ram=file_buffer+2; // Length: 1000 (+ padding24) + background=file_buffer+1008+2; // offset 0x3F0 (only 1 byte) + bitmap=file_buffer+1024+2; // Length: 8000 (+padding 192) + color_ram=file_buffer+8192+1024+2; // Length: 1000 (+ padding 24) + break; + + case 10242: // Artist 64/Blazing Paddles/Rainbow Painter multicolor + loadaddr + hasLoadAddr=1; + loadFormat=F_multi; + switch(load_addr) + { + default: + case 0x4000: // Artist 64 + bitmap=file_buffer+2; // length: 8000 (+padding 192) + screen_ram=file_buffer+8192+2; // length: 1000 + (padding 24) + color_ram=file_buffer+1024+8192+2; // length: 1000 + (padding 24) + background=file_buffer+1024*2+8192+2-1; // only 1 + break; + case 0xa000: // Blazing Paddles + bitmap=file_buffer+2; // length: 8000 (+padding 192) + screen_ram=file_buffer+8192+2; // length: 1000 + (padding 24) + color_ram=file_buffer+1024+8192+2; // length: 1000 + (padding 24) + background=file_buffer+8064+2; // only 1 + break; + case 0x5c00: // Rainbow Painter + screen_ram=file_buffer+2; // length: 1000 + (padding 24) + bitmap=file_buffer+1024+2; // length: 8000 (+padding 192) + color_ram=file_buffer+1024+8192+2; // length: 1000 + (padding 24) + background=file_buffer; // only 1 + break; + } + break; + + case 10257: // unpacked Amica Paint (.ami) + hasLoadAddr=1; + loadFormat=F_multi; + bitmap=file_buffer; // length 8000 + screen_ram=file_buffer+8000; // length: 1000 + color_ram=file_buffer+1000+8000;// length:1000 + background=file_buffer+2*1000+8000;//1 + // remaining bytes (offset 10001, length 256) are a "Color Rotation Table" + // we should decode if we learn its format... + break; + + case 10277: // multicolor CDU-Paint + loadaddr + hasLoadAddr=1; + loadFormat=F_multi; + // 273 bytes of display routine + bitmap=file_buffer+275; // length: 8000 + screen_ram=file_buffer+8275; // length: 1000 + color_ram=file_buffer+9275; // length: 1000 + background=file_buffer+10275; // only 1 + break; + + case 10608: // prg + hasLoadAddr=1; + loadFormat=F_multi; + bitmap = file_buffer + 0x239; + // border = bitmap + 8000 + background = bitmap + 8000 + 1; + screen_ram = bitmap + 8000 + 2; + color_ram = screen_ram + 1000; + break; + + case 17472: // FLI (BlackMail) + hasLoadAddr=0; + loadFormat=F_fli; + background=file_buffer+0; // length: 200 (+ padding 56) + color_ram=file_buffer+256; // length: 1000 (+ padding 24) + screen_ram=file_buffer+1280; // length: 8192 + bitmap=file_buffer+9472; // length: 8000 + break; + + case 17474: // FLI (BlackMail) + loadaddr + hasLoadAddr=1; + loadFormat=F_fli; + background=file_buffer+2; // length: 200 (+ padding 56) + color_ram=file_buffer+258; // length: 1000 (+ padding 24) + screen_ram=file_buffer+1282; // length: 8192 + bitmap=file_buffer+9474; // length: 8000 + break; + + case 17218: + case 17409: // FLI-Designer v1.1 (+loadaddr) + case 17410: // => FLI MATIC (background at 2+1024+8192+8000+65 ?) + hasLoadAddr=1; + loadFormat=F_fli; + background=NULL; + color_ram=file_buffer+2; // length: 1000 (+ padding 24) + screen_ram=file_buffer+1024+2; // length: 8192 + bitmap=file_buffer+8192+1024+2; // length: 8000 + break; + + case 17666: // FLI Graph + hasLoadAddr=1; + loadFormat=F_fli; + background=file_buffer+2; + color_ram=file_buffer+256+2; // length: 1000 (+ padding 24) + screen_ram=file_buffer+1024+256+2; // length: 8192 + bitmap=file_buffer+8192+1024+256+2; // length: 8000 + break; + + case 17665: // FLI Editor + hasLoadAddr=1; + loadFormat=F_fli; + background=file_buffer+8; + color_ram=file_buffer+256+2; // length: 1000 (+ padding 24) + screen_ram=file_buffer+1024+256+2; // length: 8192 + bitmap=file_buffer+8192+1024+256+2; // length: 8000 + break; + + default: + File_error = 1; + free(file_buffer); + return; + } + + if (loadFormat == F_invalid) + { + File_error = 1; + free(file_buffer); + return; + } + + if (loadFormat == F_fli || loadFormat == F_multi) + { + context->Ratio = PIXEL_WIDE; + width = 160; + } + else + { + context->Ratio = PIXEL_SIMPLE; + width = 320; + } + + // Write detailed format in comment + if (hasLoadAddr) + snprintf(context->Comment,COMMENT_SIZE+1,"%s, load at $%4.4X",c64_format_names[loadFormat],load_addr); + else + snprintf(context->Comment,COMMENT_SIZE+1,"%s, no addr",c64_format_names[loadFormat]); + + Pre_load(context, width, height, file_size, FORMAT_C64, context->Ratio, (loadFormat == F_bitmap) ? 1 : 4); // Do this as soon as you can + + if (Config.Clear_palette) + memset(context->Palette,0, sizeof(T_Palette)); + C64_set_palette(context->Palette); + context->Transparent_color=16; + + switch(loadFormat) + { + case F_fli: + Load_C64_fli(context,bitmap,screen_ram,color_ram,background); + Set_image_mode(context, IMAGE_MODE_C64FLI); + break; + case F_multi: + Load_C64_multi(context,bitmap,screen_ram,color_ram, + (background==NULL) ? 0 : *background); + Set_image_mode(context, IMAGE_MODE_C64MULTI); + break; + default: + Load_C64_hires(context,bitmap,screen_ram); + if (loadFormat == F_hires) + Set_image_mode(context, IMAGE_MODE_C64HIRES); + } + + free(file_buffer); + if (temp_buffer) + free(temp_buffer); + } + else + File_error = 1; +} + +/** + * Load C64 autoload pictures + * + * @param context the IO context + */ +void Load_PRG(T_IO_Context * context) +{ + FILE* file; + unsigned long file_size; + struct c64state c64; + enum c64_format loadFormat = F_invalid; + word load_addr; + word width, height = 200; + + memset(&c64, 0, sizeof(c64)); + + File_error = 1; + file = Open_file_read(context); + if (file == NULL) + return; + file_size = File_length_file(file); + if (!Read_word_le(file, &load_addr)) + return; + if (load_addr == 0x801) + { + word start_addr = C64_isBinaryProgram(file); + if (start_addr == 0) + return; + if (fseek(file, 2, SEEK_SET) < 0) + return; + if (C64_LoadPrg(&c64, file, start_addr)) + { + File_error = 0; + if (c64.vicmode & C64_VICMODE_FLI) + loadFormat = F_fli; + else if (c64.vicmode & C64_VICMODE_MULTI) + loadFormat = F_multi; + else + loadFormat = F_hires; + + if (loadFormat == F_fli || loadFormat == F_multi) + { + context->Ratio = PIXEL_WIDE; + width = 160; + } + else + { + context->Ratio = PIXEL_SIMPLE; + width = 320; + } + + Pre_load(context, width, height, file_size, FORMAT_PRG, context->Ratio, 4); // Do this as soon as you can + + if (Config.Clear_palette) + memset(context->Palette, 0, sizeof(T_Palette)); + C64_set_palette(context->Palette); + context->Transparent_color = 16; + + switch(loadFormat) + { + case F_fli: + Load_C64_fli(context, c64.ram + c64.bitmap, c64.ram + c64.screen, c64.ram + 0xd800, c64.backgrounds); + Set_image_mode(context, IMAGE_MODE_C64FLI); + break; + case F_multi: + Load_C64_multi(context, c64.ram + c64.bitmap, c64.ram + c64.screen, c64.ram + 0xd800, c64.ram[0xd021]); + Set_image_mode(context, IMAGE_MODE_C64MULTI); + break; + default: + Load_C64_hires(context, c64.ram + c64.bitmap, c64.ram + c64.screen); + if (loadFormat == F_hires) + Set_image_mode(context, IMAGE_MODE_C64HIRES); + } + } + if (c64.ram != NULL) + free(c64.ram); + } +} + +/** + * Display the dialog for C64 save parameters + * + * @param[in,out] saveFormat one of the C64 mode from @ref c64_format + * @param[in,out] saveWhat 0=All, 1=Only bitmap, 2=Only Screen RAM, 3=Only color RAM + * @param[in,out] loadAddr actual load address or 0 for "None" + * @return true to proceed, false to abort + */ +static int Save_C64_window(enum c64_format *saveFormat, byte *saveWhat, word *loadAddr) +{ + int button; + unsigned int i; + T_Dropdown_button *what, *addr; + T_Dropdown_button *format; + static const char * what_label[] = { + "All", + "Bitmap", + "Screen", + "Color" + }; + static const char * address_label[] = { + "None", + "$2000", + "$4000", + "$6000", + "$8000", + "$A000", + "$C000", + "$E000" + }; + // default addresses : + // - FLI Fli Graph 2 (BlackMail) => $3b00 + // - multicolor (Koala Painter) => $6000 + // - hires (InterPaint) => $4000 + + Open_window(200,120,"C64 saving settings"); + Window_set_normal_button(110,100,80,15,"Save",1,1,KEY_RETURN); // 1 + Window_set_normal_button(10,100,80,15,"Cancel",1,1,KEY_ESCAPE); // 2 + + Print_in_window(13,18,"Data:",MC_Dark,MC_Light); + what = Window_set_dropdown_button(10,28,90,15,70,what_label[*saveWhat],1, 0, 1, LEFT_SIDE,0); // 3 + Window_dropdown_clear_items(what); + for (i=0; i15) + { + Warning_message("Color above 15 used"); + // TODO hilite offending block here too? + // or make it smarter with color allocation? + // However, the palette is fixed to the 16 first colors + return 1; + } + for (i = 0; i < count; i++) + { + if (c[i] == pixel) + break; + } + if (i >= 2) + { + Warning_with_format("More than 2 colors\nin 8x8 pixel cell: (%d, %d)\nRect: (%d, %d, %d, %d)", cx, cy, cx * 8, cy * 8, cx * 8 + 7, cy * 8 + 7); + // TODO here we should hilite the offending block + return 1; + } + if (i >= count) + c[count++] = pixel; + } + } + + if (count == 1) + { + if (c[0] == 0) // only black + fg = 1; // white + else + fg = c[0]; + bg = 0; // black + } + else + { + // set lower color index as background + if (c[0] < c[1]) + { + fg = c[1]; + bg = c[0]; + } + else + { + fg = c[0]; + bg = c[1]; + } + } + screen_ram[cx+cy*40] = (fg<<4) | bg; + + // 2nd pass : store bitmap (0 = background, 1 = foreground) + for(y=0; y<8; y++) + { + byte bits = 0; + for(x=0; x<8; x++) + { + bits <<= 1; + if (Get_pixel(context, x+cx*8, y+cy*8) == fg) + bits |= 1; + } + bitmap[pos++] = bits; + } + } + } + + file = Open_file_write(context); + + if(!file) + { + Warning_message("File open failed"); + File_error = 1; + return 1; + } + + if (loadAddr) + Write_word_le(file,loadAddr); + + if (saveWhat==0 || saveWhat==1) + Write_bytes(file,bitmap,8000); + if (saveWhat==0 || saveWhat==2) + Write_bytes(file,screen_ram,1000); + + fclose(file); + return 0; +} + + +/** + * Save a C64 FLI (Flexible Line Interpretation) picture. + * + * This function is able to save a one layer picture, by finding + * itself the background colors and color RAM value to be used. + * + * The algorithm is : + * - first choose the lowest value for all possible background colors for each line + * - first the lowest value from the possible colors for color RAM + * - encode bitmap and screen RAMs + * + * The algorithm can fail by picking a "wrong" background color for a line, + * that make the choice for the color RAM value of one of the 40 blocks impossible. + * + * @param context the IO context + * @param saveWhat what part of the data to save + * @param loadAddr The load address + */ +int Save_C64_fli_monolayer(T_IO_Context *context, byte saveWhat, word loadAddr) +{ + FILE * file; + byte bitmap[8000],screen_ram[1024*8],color_ram[1024]; + byte background[256]; + + memset(bitmap, 0, sizeof(bitmap)); + memset(screen_ram, 0, sizeof(screen_ram)); + memset(color_ram, 0, sizeof(color_ram)); + memset(background, 0, sizeof(background)); + + memset(color_ram, 0xff, 40*25); // no hint + memset(background, 0xff, 200); + + if (C64_pixels_to_FLI(bitmap, screen_ram, color_ram, background, context->Target_address, context->Pitch, 0) > 0) + return 1; + + file = Open_file_write(context); + + if(!file) + { + Warning_message("File open failed"); + File_error = 1; + return 1; + } + + if (loadAddr) + Write_word_le(file, loadAddr); + + if (saveWhat==0) + Write_bytes(file,background,256); // Background colors for lines 0-199 (+ 56bytes padding) + + if (saveWhat==0 || saveWhat==3) + Write_bytes(file,color_ram,1024); // Color RAM (1000 bytes + padding 24) + + if (saveWhat==0 || saveWhat==1) + Write_bytes(file,screen_ram,8192); // Screen RAMs 8 x (1000 bytes + padding 24) + + if (saveWhat==0 || saveWhat==2) + Write_bytes(file,bitmap,8000); // BitMap + + fclose(file); + + return 0; +} + +/** + * Save a C64 multicolor picture + * + * @param context the IO context + * @param saveWhat what part of the data to save + * @param loadAddr The load address + */ +int Save_C64_multi(T_IO_Context *context, byte saveWhat, word loadAddr) +{ + /* + BITS COLOR INFORMATION COMES FROM + 00 Background color #0 (screen color) + 01 Upper 4 bits of Screen RAM + 10 Lower 4 bits of Screen RAM + 11 Color RAM nybble (nybble = 1/2 byte = 4 bits) + */ + + int cx,cy,x,y,c[4]={0,0,0,0},color,lut[16],bits,pixel,pos=0; + int cand,n,used; + word cols, candidates = 0, invalids = 0; + + // FIXME allocating this on the stack is not a good idea. On some platforms + // the stack has a rather small size... + byte bitmap[8000],screen_ram[1000],color_ram[1000]; + + word numcolors; + dword cusage[256]; + byte i,background=0; + FILE *file; + + // Detect the background color the image should be using. It's the one that's + // used on all tiles having 4 colors. + for(y=0;y<200;y=y+8) + { + for (x = 0; x<160; x=x+4) + { + cols = 0; + + // Compute the usage count of each color in the tile + for (cy=0;cy<8;cy++) + for (cx=0;cx<4;cx++) + { + pixel=Get_pixel(context, x+cx,y+cy); + if(pixel>15) + { + Warning_message("Color above 15 used"); + // TODO hilite as in hires, you should stay to + // the fixed 16 color palette + return 1; + } + cols |= (1 << pixel); + } + + cand = 0; + used = 0; + // Count the number of used colors in the tile + for (n = 0; n<16; n++) + { + if (cols & (1 << n)) + used++; + } + + if (used>3) + { + GFX2_Log(GFX2_DEBUG, "(%3d,%3d) used=%d cols=%04x\n", x, y, used,(unsigned)cols); + // This is a tile that uses the background color (and 3 others) + + // Try to guess which color is most likely the background one + for (n = 0; n<16; n++) + { + if ((cols & (1 << n)) && !((candidates | invalids) & (1 << n))) { + // This color is used in this tile but + // was not used in any other tile yet, + // so it could be the background one. + candidates |= 1 << n; + } + + if ((cols & (1 << n)) == 0 ) { + // This color isn't used at all in this tile: + // Can't be the global background + invalids |= 1 << n; + candidates &= ~(1 << n); + } + + if (candidates & (1 << n)) { + // We have a candidate, mark it as such + cand++; + } + } + + // After checking the constraints for this tile, do we have + // candidate background colors left ? + if (cand==0) + { + Warning_message("No possible global background color"); + return 1; + } + } + } + } + + // Now just pick the first valid candidate + for (n = 0; n<16; n++) + { + if (candidates & (1 << n)) { + background = n; + break; + } + } + GFX2_Log(GFX2_DEBUG, "Save_C64_multi() background=%d ($%x) candidates=%x invalid=%x\n", + (int)background, (int)background, (unsigned)candidates, (unsigned)invalids); + + + // Now that we know which color is the background, we can encode the cells + for(cy=0; cy<25; cy++) + { + for(cx=0; cx<40; cx++) + { + numcolors=Count_used_colors_area(cusage,cx*4,cy*8,4,8); + if(numcolors>4) + { + Warning_with_format("More than 4 colors\nin 4x8 pixel cell: (%d, %d)\nRect: (%d, %d, %d, %d)", cx, cy, cx * 4, cy * 8, cx * 4 + 3, cy * 8 + 7); + // TODO hilite offending block + return 1; + } + color=1; + c[0]=background; + for(i=0; i<16; i++) + { + lut[i]=0; + if(cusage[i] && (i!=background)) + { + lut[i]=color; + c[color]=i; + color++; + } + } + // add to screen_ram and color_ram + screen_ram[cx+cy*40]=c[1]<<4|c[2]; + color_ram[cx+cy*40]=c[3]; + + for(y=0;y<8;y++) + { + bits=0; + for(x=0;x<4;x++) + { + pixel = Get_pixel(context, cx*4+x,cy*8+y); + bits = (bits << 2) | lut[pixel]; + } + bitmap[pos++]=bits; + } + } + } + + file = Open_file_write(context); + + if(!file) + { + Warning_message("File open failed"); + File_error = 2; + return 2; + } + + setvbuf(file, NULL, _IOFBF, 64*1024); + + if (loadAddr) + Write_word_le(file,loadAddr); + + if (saveWhat==0 || saveWhat==1) + Write_bytes(file,bitmap,8000); + + if (saveWhat==0 || saveWhat==2) + Write_bytes(file,screen_ram,1000); + + if (saveWhat==0 || saveWhat==3) + Write_bytes(file,color_ram,1000); + + if (saveWhat==0) + Write_byte(file,background); + + fclose(file); + return 0; +} + +/** + * Save a C64 FLI (Flexible Line Interpretation) picture. + * + * This function need a 3 layer image : + * - layer 0 is background colors + * - layer 1 is color RAM values (4x8 blocks) + * - layer 2 is the actual picture + * + * @param context the IO context + * @param saveWhat what part of the data to save + * @param loadAddr The load address + */ +int Save_C64_fli(T_IO_Context * context, byte saveWhat, word loadAddr) +{ + FILE *file; + byte file_buffer[17474]; + + memset(file_buffer,0,sizeof(file_buffer)); + + switch(C64_FLI(context, file_buffer+9474, file_buffer+1282, file_buffer+258, file_buffer+2)) + { + case 0: // OK + break; + case 1: + Warning_message("Less than 3 layers"); + File_error=1; + return 1; + case 2: + Warning_message("Picture must be 160x200"); + File_error=1; + return 1; + default: + File_error=1; + return 1; + } + + file = Open_file_write(context); + + if(!file) + { + Warning_message("File open failed"); + File_error = 1; + return 1; + } + + if (loadAddr) + Write_word_le(file, loadAddr); + + if (saveWhat==0) + Write_bytes(file,file_buffer+2,256); // Background colors for lines 0-199 (+ 56bytes padding) + + if (saveWhat==0 || saveWhat==3) + Write_bytes(file,file_buffer+258,1024); // Color RAM (1000 bytes + padding 24) + + if (saveWhat==0 || saveWhat==1) + Write_bytes(file,file_buffer+1282,8192); // Screen RAMs 8 x (1000 bytes + padding 24) + + if (saveWhat==0 || saveWhat==2) + Write_bytes(file,file_buffer+9474,8000); // BitMap + + fclose(file); + return 0; +} + +/** + * Save C64 picture. + * + * Supports : + * - HiRes (320x200) + * - Multicolor + * - FLI + * + * @param context the IO context + */ +void Save_C64(T_IO_Context * context) +{ + enum c64_format saveFormat = F_invalid; + static byte saveWhat=0; + static word loadAddr=0; + + if (((context->Width!=320) && (context->Width!=160)) || context->Height!=200) + { + Warning_message("must be 320x200 or 160x200"); + File_error = 1; + return; + } + + saveFormat = (context->Width == 320) ? F_hires : F_multi; + + GFX2_Log(GFX2_DEBUG, "Save_C64() extension : %s\n", context->File_name + strlen(context->File_name) - 4); + if (strcasecmp(context->File_name + strlen(context->File_name) - 4, ".fli") == 0) + saveFormat = F_fli; + + if(!Save_C64_window(&saveFormat, &saveWhat,&loadAddr)) + { + File_error = 1; + return; + } + + Set_saving_layer(context, 0); + switch (saveFormat) + { + case F_fli: + if (context->Nb_layers < 3) + File_error = Save_C64_fli_monolayer(context, saveWhat, loadAddr); + else + File_error = Save_C64_fli(context, saveWhat, loadAddr); + break; + case F_multi: + File_error = Save_C64_multi(context, saveWhat, loadAddr); + break; + case F_bitmap: + saveWhat = 1; // force save bitmap +#if defined(__GNUC__) && (__GNUC__ >= 7) + __attribute__ ((fallthrough)); +#endif + case F_hires: + default: + File_error = Save_C64_hires(context, saveWhat, loadAddr); + } +} + + +/////////////////////////// pixcen *.GPX /////////////////////////// +void Test_GPX(T_IO_Context * context, FILE * file) +{ + byte header[2]; + (void)context; + + // check for a Zlib compressed stream + File_error = 1; + if (!Read_bytes(file, header, 2)) + return; + if ((header[0] & 0x0f) != 8) + return; + if (((header[0] << 8) + header[1]) % 31) + return; + File_error = 0; +} + +void Load_GPX(T_IO_Context * context) +{ + FILE * file; + unsigned long file_size; + byte * buffer; + + File_error = 1; + file = Open_file_read(context); + if (file == NULL) + return; + file_size = File_length_file(file); + buffer = GFX2_malloc(file_size); + if (buffer == NULL) + { + fclose(file); + return; + } + if (Read_bytes(file, buffer, file_size)) + { + byte * gpx = NULL; + unsigned long gpx_size = 0; + int r = Z_MEM_ERROR; + + do + { + free(gpx); + gpx_size += 65536; + gpx = GFX2_malloc(gpx_size); + if (gpx == NULL) + break; + r = uncompress(gpx, &gpx_size, buffer, file_size); + if (r != Z_BUF_ERROR && r != Z_OK) + GFX2_Log(GFX2_ERROR, "uncompress() failed with error %d: %s\n", r, zError(r)); + } + while (r == Z_BUF_ERROR); // there was not enough room in the output buffer + if (r == Z_OK) + { + byte * p; + dword version, mode; +/* + mode : +0 BITMAP, +1 MC_BITMAP, +2 SPRITE, +3 MC_SPRITE, +4 CHAR, +5 MC_CHAR, +6 UNUSED1, +7 UNUSED2, +8 UNRESTRICTED, +9 W_UNRESTRICTED +*/ + GFX2_Log(GFX2_DEBUG, "inflated %lu bytes to %lu\n", file_size, gpx_size); +#define READU32LE(p) ((p)[0] | (p)[1] << 8 | (p)[2] << 16 | (p)[3] << 24) + version = READU32LE(gpx); + mode = READU32LE(gpx+4); + GFX2_Log(GFX2_DEBUG, "gpx version %u mode %u\n", version, mode); + snprintf(context->Comment, COMMENT_SIZE, "pixcen file version %u mode %u", version, mode); + if (version >= 4) + { + dword count; + const char * key; + word value[256]; + int xsize = -1; + int ysize = -1; + int mapsize = -1; + int screensize = -1; + int colorsize = -1; + int backbuffers = -1; + + count = READU32LE(gpx+8); + p = gpx + 12; + while (count--) + { + int i = 0; + int int_value = 0; + + key = (const char *)p; + while (*p++); + for (;;) + { + value[i] = p[0] + (p[1] << 8); + p += 2; + if (value[i] == 0) + break; + int_value = int_value * 10 + (value[i] - '0'); + i++; + } + GFX2_Log(GFX2_DEBUG, "%s=%d\n", key, int_value); + if (0 == strcmp(key, "xsize")) + xsize = int_value; + else if (0 == strcmp(key, "ysize")) + ysize = int_value; + else if (0 == strcmp(key, "mapsize")) + mapsize = int_value; + else if (0 == strcmp(key, "screensize")) + screensize = int_value; + else if (0 == strcmp(key, "colorsize")) + colorsize = int_value; + else if (0 == strcmp(key, "backbuffers")) + backbuffers = int_value; + } +//buffersize = 64 + (64 + mapsize + screensize + colorsize) * backbuffers; + p += 64; // 64 empty bytes ? + File_error = 0; + if (mode & 1) + context->Ratio = PIXEL_WIDE; + else + context->Ratio = PIXEL_SIMPLE; + Pre_load(context, xsize, ysize, file_size, FORMAT_GPX, context->Ratio, 4); // Do this as soon as you can + if (Config.Clear_palette) + memset(context->Palette,0, sizeof(T_Palette)); + C64_set_palette(context->Palette); + context->Transparent_color=16; + + //foreach backbuffer + if (backbuffers >= 1) + { + byte border, background; + //byte ext0, ext1, ext2; + byte * bitmap, * color, * screen; + + //GFX2_LogHexDump(GFX2_DEBUG, "GPX ", p, 0, 64); + p += 47; // Extra bytes + //crippled = p; + p += 6; + //lock = p; + p += 6; + border = *p++; + background = *p++; + /*ext0 = *p++; + ext1 = *p++; + ext2 = *p++;*/ + p += 3; + bitmap = p; + p += mapsize; + color = p; + p += colorsize; + screen = p; + p += screensize; + + GFX2_Log(GFX2_DEBUG, "background color #%d, border color #%d\n", (int)background, (int)border); + Load_C64_multi(context, bitmap, screen, color, background); + Set_image_mode(context, (mode & 1) ? IMAGE_MODE_C64MULTI : IMAGE_MODE_C64HIRES); + } + } + else + { + GFX2_Log(GFX2_ERROR, "GPX file version %d unsupported\n", version); + } + } + free(gpx); + } + free(buffer); + fclose(file); +} diff --git a/src/miscfileformats.c b/src/miscfileformats.c index b02a78e4..26b693b3 100644 --- a/src/miscfileformats.c +++ b/src/miscfileformats.c @@ -39,25 +39,15 @@ #define strdup _strdup #endif -#include - -#include "engine.h" -#include "errors.h" #include "global.h" #include "io.h" #include "libraw2crtc.h" #include "loadsave.h" #include "loadsavefuncs.h" #include "misc.h" -#include "screen.h" #include "struct.h" #include "windows.h" #include "oldies.h" -#include "c64load.h" -#include "pages.h" -#include "keycodes.h" -#include "input.h" -#include "help.h" #include "fileformats.h" #include "gfx2mem.h" #include "gfx2log.h" @@ -1451,1774 +1441,6 @@ void Save_KCF(T_IO_Context * context) } -//////////////////////////////////// C64 //////////////////////////////////// - -/** C64 file formats - */ -enum c64_format -{ - F_invalid = -1, - F_hires = 0, ///< 320x200 - F_multi = 1, ///< 160x200 - F_bitmap = 2, ///< 320x200 monochrome - F_fli = 3 ///< FLI (Flexible Line Interpretation) -}; - -/** C64 file formats names - */ -static const char *c64_format_names[] = { - "Hires", - "Multicolor", - "Bitmap", - "FLI" -}; - -static long C64_unpack_doodle(byte ** file_buffer, long file_size); - -/** - * Test for a C64 picture file - * - * Checks the file size and the load address - * - * References : - * - http://unusedino.de/ec64/technical/formats/bitmap.html - * - http://codebase64.org/doku.php?id=base:c64_grafix_files_specs_list_v0.03 - * - https://sourceforge.net/p/view64/code/HEAD/tree/trunk/libview64.c#l3737 - */ -void Test_C64(T_IO_Context * context, FILE * file) -{ - unsigned long file_size; - word load_addr; - byte header[14]; - - (void)context; - File_error = 1; - file_size = File_length_file(file); - if (file_size < 16 || file_size > 48*1024) - return; // File too short or too long, exit now - // First test for formats without load address - switch (file_size) - { - // case 1000: // screen or color - case 8000: // raw bitmap - case 9000: // bitmap + ScreenRAM - case 10001: // multicolor - case 17472: // FLI (BlackMail) - File_error = 0; - return; - default: // then we don't know for now. - if (!Read_word_le(file, &load_addr)) - return; - } - GFX2_Log(GFX2_DEBUG, "Test_C64() file_size=%ld LoadAddr=$%04X\n", file_size, load_addr); - if (!Read_bytes(file, header, sizeof(header))) - return; - if (memcmp(header, "DRAZPAINT", 9) == 0) - { - GFX2_Log(GFX2_DEBUG, "Test_C64() header=%.13s RLE code = $%02X\n", header, header[13]); - File_error = 0; - return; - } - // check last 2 bytes - if (fseek(file, -2, SEEK_END) < 0) - return; - if (!Read_bytes(file, header, 2)) - return; - if (load_addr == 0x4000 && header[0] == 0xC2 && header[1] == 0x00) // Amica Paint EOF mark - { - File_error = 0; - return; - } - switch (file_size) - { - // case 1002: // (screen or color) + loadaddr - case 8002: // raw bitmap with loadaddr - case 9002: // bitmap + ScreenRAM + loadaddr - // $4000 => InterPaint Hi-Res (.iph) - case 9003: // bitmap + ScreenRAM + loadaddr (+ border ?) - case 9009: // bitmap + ScreenRAM + loadaddr - // $2000 => Art Studio - case 9218: - // $5C00 => Doodle - case 9332: - // $3F8E => Paint Magic (.pmg) 'JEDI' at offset $0010 and $2010 - case 10003: // multicolor + loadaddr - // $4000 => InterPaint multicolor - // $6000 => Koala Painter - case 10004: - // $4000 => Face Paint (.fpt) - case 10006: - // $6000 => Run Paint (.rpm) - case 10018: - // $2000 => Advanced Art Studio - case 10022: - // $18DC => Micro Illustrator (uncompressed) - case 10050: - // $1800 => Picasso64 - case 10218: - // $3C00 => Image System (.ism) - case 10219: - // $7800 => Saracen Paint (.sar) - File_error = 0; - break; - case 10242: - // $4000 => Artist 64 (.a64) - // $A000 => Blazing paddles (.pi) - // $5C00 => Rainbow Painter (.rp) - if (load_addr != 0x4000 && load_addr != 0xa000 && load_addr != 0x5c00) - { - File_error = 1; - return; - } - File_error = 0; - break; - case 10608: - // $0801 = BASIC programs loading address - File_error = 0; - break; - case 17218: - case 17409: - // $3c00 => FLI-designer v1.1 - // ? $3ff0 => FLI designer 2 ? - case 17410: - // $3c00 => FLI MATIC - case 17474: // FLI (BlackMail) + loadaddr - // $3b00 => FLI Graph 2 - case 17665: - // $3b00 => FLI editor - case 17666: - // $3b00 => FLI Graph - case 10277: // multicolor CDU-Paint + loadaddr - // $7EEF - File_error = 0; - break; - default: // then we don't know for now. - if (load_addr == 0x6000 || load_addr == 0x5c00) - { - long unpacked_size; - byte * buffer = GFX2_malloc(file_size); - if (buffer == NULL) - return; - fseek(file, SEEK_SET, 0); - if (!Read_bytes(file, buffer, file_size)) - return; - unpacked_size = C64_unpack_doodle(&buffer, file_size); - free(buffer); - switch (unpacked_size) - { - case 9024: // Doodle hi color - case 9216: - case 10001: // Koala painter 2 - case 10070: - File_error = 0; - } - } - } -} - -/** - * Test for a C64 auto-load machine language program - * which could be a picture - */ -void Test_PRG(T_IO_Context * context, FILE * file) -{ - unsigned long file_size; - word load_addr; - (void)context; - - file_size = File_length_file(file); - if (file_size > (38911 + 2)) // maximum length of PRG loaded at $0801 - return; - if (!Read_word_le(file, &load_addr)) - return; - if (load_addr != 0x0801) - return; - // 6502 emulators : - // https://github.com/redcode/6502 - // http://rubbermallet.org/fake6502.c - // https://github.com/jamestn/cpu6502 - // https://github.com/dennis-chen/6502-Emu - // https://github.com/DavidBuchanan314/6502-emu - // basic program - if (C64_isBinaryProgram(file) != 0) - File_error = 0; -} - -/** - * Load C64 hires (320x200) - * - * @param context the IO context - * @param bitmap the bitmap RAM (8000 bytes) - * @param screen_ram the screen RAM (1000 bytes) - */ -static void Load_C64_hires(T_IO_Context *context, byte *bitmap, byte *screen_ram) -{ - int cx,cy,x,y,c[4],pixel,color; - - for(cy=0; cy<25; cy++) - { - for(cx=0; cx<40; cx++) - { - if(screen_ram != NULL) - { - c[0]=screen_ram[cy*40+cx]&15; - c[1]=screen_ram[cy*40+cx]>>4; - } - else - { /// If screen_ram is NULL, uses default C64 basic colors - c[0] = 6; - c[1] = 14; - } - for(y=0; y<8; y++) - { - pixel=bitmap[cy*320+cx*8+y]; - for(x=0; x<8; x++) - { - color=c[pixel&(1<<(7-x))?1:0]; - Set_pixel(context, cx*8+x,cy*8+y,color); - } - } - } - } -} - -/** - * Load C64 multicolor (160x200) - * - * @param context the IO context - * @param bitmap the bitmap RAM (8000 bytes) - * @param screen_ram the screen RAM (1000 bytes) - * @param color_ram the color RAM (1000 bytes) - * @param background the background color - */ -static void Load_C64_multi(T_IO_Context *context, byte *bitmap, byte *screen_ram, byte *color_ram, byte background) -{ - int cx,cy,x,y,c[4],pixel,color; - c[0]=background&15; - for(cy=0; cy<25; cy++) - { - for(cx=0; cx<40; cx++) - { - c[1]=screen_ram[cy*40+cx]>>4; - c[2]=screen_ram[cy*40+cx]&15; - c[3]=color_ram[cy*40+cx]&15; - - for(y=0; y<8; y++) - { - pixel=bitmap[cy*320+cx*8+y]; - for(x=0; x<4; x++) - { - color=c[(pixel&3)]; - pixel>>=2; - Set_pixel(context, cx*4+(3-x),cy*8+y,color); - } - } - } - } -} - -/** - * Loads a C64 FLI (Flexible Line Interpretation) picture. - * Sets 4 layers : - * - Layer 0 : filled with background colors (1 per line) - * - Layer 1 : "Color RAM" 4x8 blocks - * - Layer 2 : pixels (From Screen RAMs + Bitmap) - * - Layer 3 : Transparency layer filled with color 16 - * - * @param context the IO context - * @param bitmap 8000 bytes buffer - * @param screen_ram 8 x 1024 bytes buffers - * @param color_ram 1000 byte buffer - * @param background 200 byte buffer - */ -void Load_C64_fli(T_IO_Context *context, byte *bitmap, byte *screen_ram, byte *color_ram, byte *background) -{ - // Thanks to MagerValp for complement of specifications. - // - // background : length: 200 (+ padding 56) - // These are the BG colors for lines 0-199 (top to bottom) - // Low nybble: the color. - // High nybble: garbage. ignore it. - // color_ram : length: 1000 (+ padding 24) - // Color RAM. Contains one color per 4x8 block. - // There are 40x25 such blocks, arranged from top left to bottom - // right, starting in right direction. For each block there is one byte. - // Low nybble: the color. - // High nybble: garbage. ignore it. - // screen_ram : length: 8192 - // Screen RAMs. The s is important. - // This is actually 8 blocks of 1000 bytes, each separated by a filler of - // 24 bytes. Each byte contains data for a 4x1 pixel group, and a complete - // block will contain 40x25 of them. 40 is from left to right, and 25 is from - // top to bottom, spacing them 8 lines apart. - // The second block start at y=1, the third block starts at y=2, etc... - // Each byte contains 2 colors that *can* be used by the 4x1 pixel group: - // Low nybble: Color 1 - // High nybble: Color 2 - // - // bitmap : length: 8000 - // This is the final structure that refers to all others. It describes - // 160x200 pixels linearly, from top left to bottom right, starting in - // right direction. For each pixel, two bits say which color is displayed - // (So 4 pixels are described by the same byte) - // 00 Use the BG color of the current line (background[y]) - // 01 Use the Color 2 from the current 4x8 block of Screen RAM - // ((screen_ram[y/8][x/4] & 0xF0) >> 8) - // 10 Use the Color 1 from the current 4x8 block of Screen RAM - // (screen_ram[y/8][x/4] & 0x0F) - // 11 Use the color from Color RAM - // (color_ram[y/8][x/4] & 0x0F) - // - - int cx,cy,x,y,c[4]; - - if (context->Type == CONTEXT_MAIN_IMAGE) - { - // Fill layer 0 with background colors - for(y=0; y<200; y++) - { - byte bg_color = 0; - if (background != NULL) - bg_color = background[y]; - for(x=0; x<160; x++) - Set_pixel(context, x,y, bg_color); - } - - // Fill layer 1 with color ram (1 color per 4x8 block) - Set_loading_layer(context, 1); - for(cy=0; cy<25; cy++) - { - for(cx=0; cx<40; cx++) - { - c[3]=color_ram[cy*40+cx]&15; - for(y=0; y<8; y++) - { - for(x=0; x<4; x++) - { - Set_pixel(context, cx*4+x,cy*8+y,c[3]); - } - } - } - } - } - - // Layer 2 are actual pixels - Set_loading_layer(context, 2); - for(cy=0; cy<25; cy++) - { - for(cx=0; cx<40; cx++) - { - c[3]=color_ram[cy*40+cx]&15; - for(y=0; y<8; y++) - { - int pixel=bitmap[cy*320+cx*8+y]; - - c[0] = 0; - if(background != NULL) - c[0] = background[cy*8+y]&15; - c[1]=screen_ram[y*1024+cy*40+cx]>>4; - c[2]=screen_ram[y*1024+cy*40+cx]&15; - for(x=0; x<4; x++) - { - int color=c[(pixel&3)]; - pixel>>=2; - Set_pixel(context, cx*4+(3-x),cy*8+y,color); - } - } - } - } - if (context->Type == CONTEXT_MAIN_IMAGE) - { - // Fill layer 3 with color 16 - Set_loading_layer(context, 3); - for(y=0; y<200; y++) - { - for(x=0; x<160; x++) - Set_pixel(context, x,y,16); - } - } -} - -/** - * Count the length of the unpacked data - * - * RLE encoding is either ESCAPE CODE, COUNT, VALUE - * or ESCAPE CODE, VALUE, COUNT - * - * @param buffer the packed data - * @param input_size the packed data byte count - * @param RLE_code the escape code - * @param order 0 for ESCAPE, COUNT, VALUE, 1 for ESCAPE, VALUE, COUNT - * @return the unpacked data byte count - */ -static long C64_unpack_get_length(const byte * buffer, long input_size, byte RLE_code, int order) -{ - const byte * end; - long unpacked_size = 0; - - end = buffer + input_size; - while(buffer < end) - { - if (*buffer == RLE_code) - { - if (order) - { // ESCAPE, VALUE, COUNT - buffer += 2; // skip value - unpacked_size += *buffer; - } - else - { // ESCAPE, COUNT, VALUE - buffer++; - if (*buffer == 0) - break; - unpacked_size += *buffer++; - } - } - else - unpacked_size++; - buffer++; - } - return unpacked_size; -} - -/** - * unpack RLE packed data - * - * RLE encoding is either ESCAPE CODE, COUNT, VALUE - * or ESCAPE CODE, VALUE, COUNT - * - * @param unpacked buffer to received unpacked data - * @param buffer the packed data - * @param input_size the packed data byte count - * @param RLE_code the escape code - * @param order 0 for ESCAPE, COUNT, VALUE, 1 for ESCAPE, VALUE, COUNT - */ -static void C64_unpack(byte * unpacked, const byte * buffer, long input_size, byte RLE_code, int order) -{ - const byte * end; - - end = buffer + input_size; - while(buffer < end) - { - if (*buffer == RLE_code) - { - byte count; - byte value; - buffer++; - if (order) - { // ESCAPE, VALUE, COUNT - value = *buffer++; - count = *buffer; - } - else - { // ESCAPE, COUNT, VALUE - count = *buffer++; - value = *buffer; - } - if (count == 0) - break; - while (count-- > 0) - *unpacked++ = value; - } - else - *unpacked++ = *buffer; - buffer++; - } -} - -/** - * Unpack the Amica Paint RLE packing - * - * @param[in,out] file_buffer will contain the unpacked buffer on return - * @param[in] file_size packed buffer size - * @return the unpacked data size or -1 in case of error - * - * Ref: - * - http://codebase64.org/doku.php?id=base:c64_grafix_files_specs_list_v0.03 - */ -static long C64_unpack_amica(byte ** file_buffer, long file_size) -{ - long unpacked_size; - byte * unpacked_buffer; - const byte RLE_code = 0xC2; - - if (file_size <= 16 || file_buffer == NULL || *file_buffer == NULL) - return -1; - unpacked_size = C64_unpack_get_length(*file_buffer + 2, file_size - 2, RLE_code, 0); - GFX2_Log(GFX2_DEBUG, "C64_unpack_amica() unpacked_size=%ld\n", unpacked_size); - // 2nd pass to unpack - unpacked_buffer = GFX2_malloc(unpacked_size); - if (unpacked_buffer == NULL) - return -1; - C64_unpack(unpacked_buffer, *file_buffer + 2, file_size - 2, RLE_code, 0); - - free(*file_buffer); - *file_buffer = unpacked_buffer; - return unpacked_size; -} - -/** - * Unpack the DRAZPAINT RLE packing - * - * @param[in,out] file_buffer will contain the unpacked buffer on return - * @param[in] file_size packed buffer size - * @return the unpacked data size or -1 in case of error - * - * Ref: - * - https://www.godot64.de/german/l_draz.htm - * - https://sourceforge.net/p/view64/code/HEAD/tree/trunk/libview64.c#l2805 - */ -static long C64_unpack_draz(byte ** file_buffer, long file_size) -{ - long unpacked_size; - byte * unpacked_buffer; - byte RLE_code; - - if (file_size <= 16 || file_buffer == NULL || *file_buffer == NULL) - return -1; - RLE_code = (*file_buffer)[15]; - // First pass to know unpacked size - unpacked_size = C64_unpack_get_length(*file_buffer + 16, file_size - 16, RLE_code, 0); - GFX2_Log(GFX2_DEBUG, "C64_unpack_draz() \"%.13s\" RLE code=$%02X RLE data length=%ld unpacked_size=%ld\n", - *file_buffer + 2, RLE_code, file_size - 16, unpacked_size); - // 2nd pass to unpack - unpacked_buffer = GFX2_malloc(unpacked_size); - if (unpacked_buffer == NULL) - return -1; - C64_unpack(unpacked_buffer, *file_buffer + 16, file_size - 16, RLE_code, 0); - free(*file_buffer); - *file_buffer = unpacked_buffer; - return unpacked_size; -} - -/** - * Unpack doodle/koala painter 2 data - * - * @return the unpacked data size or -1 in case of error - */ -static long C64_unpack_doodle(byte ** file_buffer, long file_size) -{ - long unpacked_size; - byte * unpacked_buffer; - const byte RLE_code = 0xFE; - - if (file_size <= 16 || file_buffer == NULL || *file_buffer == NULL) - return -1; - // First pass to know unpacked size - unpacked_size = C64_unpack_get_length(*file_buffer + 2, file_size - 2, RLE_code, 1); - GFX2_Log(GFX2_DEBUG, "C64_unpack_doodle() unpacked_size=%ld\n", unpacked_size); - // 2nd pass to unpack - unpacked_buffer = GFX2_malloc(unpacked_size); - if (unpacked_buffer == NULL) - return -1; - C64_unpack(unpacked_buffer, *file_buffer + 2, file_size - 2, RLE_code, 1); - free(*file_buffer); - *file_buffer = unpacked_buffer; - return unpacked_size; -} - -/** - * Load C64 pictures formats. - * - * Supports: - * - Hires (with or without ScreenRAM) - * - Multicolor (Koala or CDU-paint format) - * - FLI - * - * see http://unusedino.de/ec64/technical/formats/bitmap.html - * - * @param context the IO context - */ -void Load_C64(T_IO_Context * context) -{ - FILE* file; - long file_size; - byte hasLoadAddr=0; - word load_addr; - enum c64_format loadFormat = F_invalid; - - byte *file_buffer; - byte *bitmap, *screen_ram, *color_ram=NULL, *background=NULL; // Only pointers to existing data - byte *temp_buffer = NULL; - word width, height=200; - - file = Open_file_read(context); - - if (file) - { - File_error=0; - file_size = File_length_file(file); - - // Load entire file in memory - file_buffer = GFX2_malloc(file_size); - if (!file_buffer) - { - File_error = 1; - fclose(file); - return; - } - if (!Read_bytes(file,file_buffer,file_size)) - { - File_error = 1; - free(file_buffer); - fclose(file); - return; - } - fclose(file); - - // get load address (valid only if hasLoadAddr = 1) - load_addr = file_buffer[0] | (file_buffer[1] << 8); - - // Unpack if needed - if (memcmp(file_buffer + 2, "DRAZPAINT", 9) == 0) - file_size = C64_unpack_draz(&file_buffer, file_size); - else if(load_addr == 0x4000 && file_buffer[file_size-2] == 0xC2 && file_buffer[file_size-1] == 0) - file_size = C64_unpack_amica(&file_buffer, file_size); - else if (file_size < 8000 && (load_addr == 0x6000 || load_addr == 0x5c00)) - file_size = C64_unpack_doodle(&file_buffer, file_size); - - switch (file_size) - { - case 8000: // raw bitmap - hasLoadAddr=0; - loadFormat=F_bitmap; - bitmap=file_buffer+0; // length: 8000 - screen_ram=NULL; - break; - - case 8002: // raw bitmap with loadaddr - hasLoadAddr=1; - loadFormat=F_bitmap; - bitmap=file_buffer+2; // length: 8000 - screen_ram=NULL; - break; - - case 9000: // bitmap + ScreenRAM - hasLoadAddr=0; - loadFormat=F_hires; - bitmap=file_buffer+0; // length: 8000 - screen_ram=file_buffer+8000; // length: 1000 - break; - - case 9003: // bitmap + ScreenRAM + loadaddr (+ border ?) - case 9002: // bitmap + ScreenRAM + loadaddr - hasLoadAddr=1; - loadFormat=F_hires; - bitmap=file_buffer+2; // length: 8000 - screen_ram=file_buffer+8002; // length: 1000 - break; - - case 9009: // Art Studio (.aas) - hasLoadAddr=1; - loadFormat=F_hires; - bitmap=file_buffer+2; // length: 8000 - screen_ram=file_buffer+8002; // length: 1000 - break; - - case 9024: // Doodle (unpacked from .jj) - case 9216: - hasLoadAddr=0; - loadFormat=F_hires; - screen_ram=file_buffer; // length: 1000 (+24 padding) - bitmap=file_buffer+1024; // length: 8000 - break; - - case 9218: // Doodle (.dd) - hasLoadAddr=1; - loadFormat=F_hires; - screen_ram=file_buffer+2; // length: 1000 (+24 padding) - bitmap=file_buffer+1024+2; // length: 8000 - break; - - case 9332: // Paint Magic .pmg - hasLoadAddr=1; - loadFormat=F_multi; - // Display routine between offset $0002 and $0073 (114 bytes) - // duplicated between offset $2002 and $2073 - bitmap=file_buffer+114+2; // $0074 - background=file_buffer+8000+114+2;// $1FB4 - temp_buffer = GFX2_malloc(1000); - memset(temp_buffer, file_buffer[3+8000+114+2], 1000); // color RAM Byte - color_ram=temp_buffer; - //border byte = file_buffer[4+8000+114+2]; - screen_ram=file_buffer+8192+114+2; // $2074 - break; - - case 10001: // multicolor - case 10070: // unpacked file. - hasLoadAddr=0; - loadFormat=F_multi; - bitmap=file_buffer+0; // length: 8000 - screen_ram=file_buffer+8000; // length: 1000 - color_ram=file_buffer+9000; // length: 1000 - background=file_buffer+10000; // only 1 - break; - - case 10003: // multicolor + loadaddr - case 10004: // extra byte is border color - case 10006: // Run Paint - hasLoadAddr=1; - loadFormat=F_multi; - bitmap=file_buffer+2; // length: 8000 - screen_ram=file_buffer+8002; // length: 1000 - color_ram=file_buffer+9002; // length: 1000 - background=file_buffer+10002; // only 1 - break; - - case 10018: // Advanced Art Studio (.ocp) + loadaddr - hasLoadAddr=1; - loadFormat=F_multi; - bitmap=file_buffer+2; // length: 8000 - screen_ram=file_buffer+8000+2; // length: 1000 - color_ram=file_buffer+9016+2; // length: 1000 - // filebuffer+9000+2 is border - background=file_buffer+9001+2; // only 1 - break; - - case 10022: // Micro Illustrator (.mil) - hasLoadAddr=1; - loadFormat=F_multi; - screen_ram=file_buffer+20+2; - color_ram=file_buffer+1000+20+2; - bitmap=file_buffer+2*1000+20+2; - break; - - case 10049: // unpacked DrazPaint - hasLoadAddr=1; - loadFormat=F_multi; - color_ram=file_buffer; // length: 1000 + (padding 24) - screen_ram=file_buffer+1024; // length: 1000 + (padding 24) - bitmap=file_buffer+1024*2; // length: 8000 - background=file_buffer+8000+1024*2; - break; - - case 10050: // Picasso64 multicolor + loadaddr - hasLoadAddr=1; - loadFormat=F_multi; - color_ram=file_buffer+2; // length: 1000 + (padding 24) - screen_ram=file_buffer+1024+2; // length: 1000 + (padding 24) - bitmap=file_buffer+1024*2+2; // length: 8000 - background=file_buffer+1024*2+2-1; // only 1 - break; - - case 10218: // Image System - hasLoadAddr=1; - loadFormat=F_multi; - color_ram=file_buffer+2; // Length: 1000 (+ padding 24) - bitmap=file_buffer+1024+2; // Length: 8000 (+padding 192) - screen_ram=file_buffer+8192+1024+2; // Length: 1000 (no padding) - background=file_buffer+8192+1024+2-1; // only 1 - break; - - case 10219: // Saracen Paint (.sar) - hasLoadAddr=1; - loadFormat=F_multi; - screen_ram=file_buffer+2; // Length: 1000 (+ padding24) - background=file_buffer+1008+2; // offset 0x3F0 (only 1 byte) - bitmap=file_buffer+1024+2; // Length: 8000 (+padding 192) - color_ram=file_buffer+8192+1024+2; // Length: 1000 (+ padding 24) - break; - - case 10242: // Artist 64/Blazing Paddles/Rainbow Painter multicolor + loadaddr - hasLoadAddr=1; - loadFormat=F_multi; - switch(load_addr) - { - default: - case 0x4000: // Artist 64 - bitmap=file_buffer+2; // length: 8000 (+padding 192) - screen_ram=file_buffer+8192+2; // length: 1000 + (padding 24) - color_ram=file_buffer+1024+8192+2; // length: 1000 + (padding 24) - background=file_buffer+1024*2+8192+2-1; // only 1 - break; - case 0xa000: // Blazing Paddles - bitmap=file_buffer+2; // length: 8000 (+padding 192) - screen_ram=file_buffer+8192+2; // length: 1000 + (padding 24) - color_ram=file_buffer+1024+8192+2; // length: 1000 + (padding 24) - background=file_buffer+8064+2; // only 1 - break; - case 0x5c00: // Rainbow Painter - screen_ram=file_buffer+2; // length: 1000 + (padding 24) - bitmap=file_buffer+1024+2; // length: 8000 (+padding 192) - color_ram=file_buffer+1024+8192+2; // length: 1000 + (padding 24) - background=file_buffer; // only 1 - break; - } - break; - - case 10257: // unpacked Amica Paint (.ami) - hasLoadAddr=1; - loadFormat=F_multi; - bitmap=file_buffer; // length 8000 - screen_ram=file_buffer+8000; // length: 1000 - color_ram=file_buffer+1000+8000;// length:1000 - background=file_buffer+2*1000+8000;//1 - // remaining bytes (offset 10001, length 256) are a "Color Rotation Table" - // we should decode if we learn its format... - break; - - case 10277: // multicolor CDU-Paint + loadaddr - hasLoadAddr=1; - loadFormat=F_multi; - // 273 bytes of display routine - bitmap=file_buffer+275; // length: 8000 - screen_ram=file_buffer+8275; // length: 1000 - color_ram=file_buffer+9275; // length: 1000 - background=file_buffer+10275; // only 1 - break; - - case 10608: // prg - hasLoadAddr=1; - loadFormat=F_multi; - bitmap = file_buffer + 0x239; - // border = bitmap + 8000 - background = bitmap + 8000 + 1; - screen_ram = bitmap + 8000 + 2; - color_ram = screen_ram + 1000; - break; - - case 17472: // FLI (BlackMail) - hasLoadAddr=0; - loadFormat=F_fli; - background=file_buffer+0; // length: 200 (+ padding 56) - color_ram=file_buffer+256; // length: 1000 (+ padding 24) - screen_ram=file_buffer+1280; // length: 8192 - bitmap=file_buffer+9472; // length: 8000 - break; - - case 17474: // FLI (BlackMail) + loadaddr - hasLoadAddr=1; - loadFormat=F_fli; - background=file_buffer+2; // length: 200 (+ padding 56) - color_ram=file_buffer+258; // length: 1000 (+ padding 24) - screen_ram=file_buffer+1282; // length: 8192 - bitmap=file_buffer+9474; // length: 8000 - break; - - case 17218: - case 17409: // FLI-Designer v1.1 (+loadaddr) - case 17410: // => FLI MATIC (background at 2+1024+8192+8000+65 ?) - hasLoadAddr=1; - loadFormat=F_fli; - background=NULL; - color_ram=file_buffer+2; // length: 1000 (+ padding 24) - screen_ram=file_buffer+1024+2; // length: 8192 - bitmap=file_buffer+8192+1024+2; // length: 8000 - break; - - case 17666: // FLI Graph - hasLoadAddr=1; - loadFormat=F_fli; - background=file_buffer+2; - color_ram=file_buffer+256+2; // length: 1000 (+ padding 24) - screen_ram=file_buffer+1024+256+2; // length: 8192 - bitmap=file_buffer+8192+1024+256+2; // length: 8000 - break; - - case 17665: // FLI Editor - hasLoadAddr=1; - loadFormat=F_fli; - background=file_buffer+8; - color_ram=file_buffer+256+2; // length: 1000 (+ padding 24) - screen_ram=file_buffer+1024+256+2; // length: 8192 - bitmap=file_buffer+8192+1024+256+2; // length: 8000 - break; - - default: - File_error = 1; - free(file_buffer); - return; - } - - if (loadFormat == F_invalid) - { - File_error = 1; - free(file_buffer); - return; - } - - if (loadFormat == F_fli || loadFormat == F_multi) - { - context->Ratio = PIXEL_WIDE; - width = 160; - } - else - { - context->Ratio = PIXEL_SIMPLE; - width = 320; - } - - // Write detailed format in comment - if (hasLoadAddr) - snprintf(context->Comment,COMMENT_SIZE+1,"%s, load at $%4.4X",c64_format_names[loadFormat],load_addr); - else - snprintf(context->Comment,COMMENT_SIZE+1,"%s, no addr",c64_format_names[loadFormat]); - - Pre_load(context, width, height, file_size, FORMAT_C64, context->Ratio, (loadFormat == F_bitmap) ? 1 : 4); // Do this as soon as you can - - if (Config.Clear_palette) - memset(context->Palette,0, sizeof(T_Palette)); - C64_set_palette(context->Palette); - context->Transparent_color=16; - - switch(loadFormat) - { - case F_fli: - Load_C64_fli(context,bitmap,screen_ram,color_ram,background); - Set_image_mode(context, IMAGE_MODE_C64FLI); - break; - case F_multi: - Load_C64_multi(context,bitmap,screen_ram,color_ram, - (background==NULL) ? 0 : *background); - Set_image_mode(context, IMAGE_MODE_C64MULTI); - break; - default: - Load_C64_hires(context,bitmap,screen_ram); - if (loadFormat == F_hires) - Set_image_mode(context, IMAGE_MODE_C64HIRES); - } - - free(file_buffer); - if (temp_buffer) - free(temp_buffer); - } - else - File_error = 1; -} - -/** - * Load C64 autoload pictures - * - * @param context the IO context - */ -void Load_PRG(T_IO_Context * context) -{ - FILE* file; - unsigned long file_size; - struct c64state c64; - enum c64_format loadFormat = F_invalid; - word load_addr; - word width, height = 200; - - memset(&c64, 0, sizeof(c64)); - - File_error = 1; - file = Open_file_read(context); - if (file == NULL) - return; - file_size = File_length_file(file); - if (!Read_word_le(file, &load_addr)) - return; - if (load_addr == 0x801) - { - word start_addr = C64_isBinaryProgram(file); - if (start_addr == 0) - return; - if (fseek(file, 2, SEEK_SET) < 0) - return; - if (C64_LoadPrg(&c64, file, start_addr)) - { - File_error = 0; - if (c64.vicmode & C64_VICMODE_FLI) - loadFormat = F_fli; - else if (c64.vicmode & C64_VICMODE_MULTI) - loadFormat = F_multi; - else - loadFormat = F_hires; - - if (loadFormat == F_fli || loadFormat == F_multi) - { - context->Ratio = PIXEL_WIDE; - width = 160; - } - else - { - context->Ratio = PIXEL_SIMPLE; - width = 320; - } - - Pre_load(context, width, height, file_size, FORMAT_PRG, context->Ratio, 4); // Do this as soon as you can - - if (Config.Clear_palette) - memset(context->Palette, 0, sizeof(T_Palette)); - C64_set_palette(context->Palette); - context->Transparent_color = 16; - - switch(loadFormat) - { - case F_fli: - Load_C64_fli(context, c64.ram + c64.bitmap, c64.ram + c64.screen, c64.ram + 0xd800, c64.backgrounds); - Set_image_mode(context, IMAGE_MODE_C64FLI); - break; - case F_multi: - Load_C64_multi(context, c64.ram + c64.bitmap, c64.ram + c64.screen, c64.ram + 0xd800, c64.ram[0xd021]); - Set_image_mode(context, IMAGE_MODE_C64MULTI); - break; - default: - Load_C64_hires(context, c64.ram + c64.bitmap, c64.ram + c64.screen); - if (loadFormat == F_hires) - Set_image_mode(context, IMAGE_MODE_C64HIRES); - } - } - if (c64.ram != NULL) - free(c64.ram); - } -} - -/** - * Display the dialog for C64 save parameters - * - * @param[in,out] saveFormat one of the C64 mode from @ref c64_format - * @param[in,out] saveWhat 0=All, 1=Only bitmap, 2=Only Screen RAM, 3=Only color RAM - * @param[in,out] loadAddr actual load address or 0 for "None" - * @return true to proceed, false to abort - */ -static int Save_C64_window(enum c64_format *saveFormat, byte *saveWhat, word *loadAddr) -{ - int button; - unsigned int i; - T_Dropdown_button *what, *addr; - T_Dropdown_button *format; - static const char * what_label[] = { - "All", - "Bitmap", - "Screen", - "Color" - }; - static const char * address_label[] = { - "None", - "$2000", - "$4000", - "$6000", - "$8000", - "$A000", - "$C000", - "$E000" - }; - // default addresses : - // - FLI Fli Graph 2 (BlackMail) => $3b00 - // - multicolor (Koala Painter) => $6000 - // - hires (InterPaint) => $4000 - - Open_window(200,120,"C64 saving settings"); - Window_set_normal_button(110,100,80,15,"Save",1,1,KEY_RETURN); // 1 - Window_set_normal_button(10,100,80,15,"Cancel",1,1,KEY_ESCAPE); // 2 - - Print_in_window(13,18,"Data:",MC_Dark,MC_Light); - what = Window_set_dropdown_button(10,28,90,15,70,what_label[*saveWhat],1, 0, 1, LEFT_SIDE,0); // 3 - Window_dropdown_clear_items(what); - for (i=0; i15) - { - Warning_message("Color above 15 used"); - // TODO hilite offending block here too? - // or make it smarter with color allocation? - // However, the palette is fixed to the 16 first colors - return 1; - } - for (i = 0; i < count; i++) - { - if (c[i] == pixel) - break; - } - if (i >= 2) - { - Warning_with_format("More than 2 colors\nin 8x8 pixel cell: (%d, %d)\nRect: (%d, %d, %d, %d)", cx, cy, cx * 8, cy * 8, cx * 8 + 7, cy * 8 + 7); - // TODO here we should hilite the offending block - return 1; - } - if (i >= count) - c[count++] = pixel; - } - } - - if (count == 1) - { - if (c[0] == 0) // only black - fg = 1; // white - else - fg = c[0]; - bg = 0; // black - } - else - { - // set lower color index as background - if (c[0] < c[1]) - { - fg = c[1]; - bg = c[0]; - } - else - { - fg = c[0]; - bg = c[1]; - } - } - screen_ram[cx+cy*40] = (fg<<4) | bg; - - // 2nd pass : store bitmap (0 = background, 1 = foreground) - for(y=0; y<8; y++) - { - byte bits = 0; - for(x=0; x<8; x++) - { - bits <<= 1; - if (Get_pixel(context, x+cx*8, y+cy*8) == fg) - bits |= 1; - } - bitmap[pos++] = bits; - } - } - } - - file = Open_file_write(context); - - if(!file) - { - Warning_message("File open failed"); - File_error = 1; - return 1; - } - - if (loadAddr) - Write_word_le(file,loadAddr); - - if (saveWhat==0 || saveWhat==1) - Write_bytes(file,bitmap,8000); - if (saveWhat==0 || saveWhat==2) - Write_bytes(file,screen_ram,1000); - - fclose(file); - return 0; -} - - -/** - * Save a C64 FLI (Flexible Line Interpretation) picture. - * - * This function is able to save a one layer picture, by finding - * itself the background colors and color RAM value to be used. - * - * The algorithm is : - * - first choose the lowest value for all possible background colors for each line - * - first the lowest value from the possible colors for color RAM - * - encode bitmap and screen RAMs - * - * The algorithm can fail by picking a "wrong" background color for a line, - * that make the choice for the color RAM value of one of the 40 blocks impossible. - * - * @param context the IO context - * @param saveWhat what part of the data to save - * @param loadAddr The load address - */ -int Save_C64_fli_monolayer(T_IO_Context *context, byte saveWhat, word loadAddr) -{ - FILE * file; - byte bitmap[8000],screen_ram[1024*8],color_ram[1024]; - byte background[256]; - - memset(bitmap, 0, sizeof(bitmap)); - memset(screen_ram, 0, sizeof(screen_ram)); - memset(color_ram, 0, sizeof(color_ram)); - memset(background, 0, sizeof(background)); - - memset(color_ram, 0xff, 40*25); // no hint - memset(background, 0xff, 200); - - if (C64_pixels_to_FLI(bitmap, screen_ram, color_ram, background, context->Target_address, context->Pitch, 0) > 0) - return 1; - - file = Open_file_write(context); - - if(!file) - { - Warning_message("File open failed"); - File_error = 1; - return 1; - } - - if (loadAddr) - Write_word_le(file, loadAddr); - - if (saveWhat==0) - Write_bytes(file,background,256); // Background colors for lines 0-199 (+ 56bytes padding) - - if (saveWhat==0 || saveWhat==3) - Write_bytes(file,color_ram,1024); // Color RAM (1000 bytes + padding 24) - - if (saveWhat==0 || saveWhat==1) - Write_bytes(file,screen_ram,8192); // Screen RAMs 8 x (1000 bytes + padding 24) - - if (saveWhat==0 || saveWhat==2) - Write_bytes(file,bitmap,8000); // BitMap - - fclose(file); - - return 0; -} - -/** - * Save a C64 multicolor picture - * - * @param context the IO context - * @param saveWhat what part of the data to save - * @param loadAddr The load address - */ -int Save_C64_multi(T_IO_Context *context, byte saveWhat, word loadAddr) -{ - /* - BITS COLOR INFORMATION COMES FROM - 00 Background color #0 (screen color) - 01 Upper 4 bits of Screen RAM - 10 Lower 4 bits of Screen RAM - 11 Color RAM nybble (nybble = 1/2 byte = 4 bits) - */ - - int cx,cy,x,y,c[4]={0,0,0,0},color,lut[16],bits,pixel,pos=0; - int cand,n,used; - word cols, candidates = 0, invalids = 0; - - // FIXME allocating this on the stack is not a good idea. On some platforms - // the stack has a rather small size... - byte bitmap[8000],screen_ram[1000],color_ram[1000]; - - word numcolors; - dword cusage[256]; - byte i,background=0; - FILE *file; - - // Detect the background color the image should be using. It's the one that's - // used on all tiles having 4 colors. - for(y=0;y<200;y=y+8) - { - for (x = 0; x<160; x=x+4) - { - cols = 0; - - // Compute the usage count of each color in the tile - for (cy=0;cy<8;cy++) - for (cx=0;cx<4;cx++) - { - pixel=Get_pixel(context, x+cx,y+cy); - if(pixel>15) - { - Warning_message("Color above 15 used"); - // TODO hilite as in hires, you should stay to - // the fixed 16 color palette - return 1; - } - cols |= (1 << pixel); - } - - cand = 0; - used = 0; - // Count the number of used colors in the tile - for (n = 0; n<16; n++) - { - if (cols & (1 << n)) - used++; - } - - if (used>3) - { - GFX2_Log(GFX2_DEBUG, "(%3d,%3d) used=%d cols=%04x\n", x, y, used,(unsigned)cols); - // This is a tile that uses the background color (and 3 others) - - // Try to guess which color is most likely the background one - for (n = 0; n<16; n++) - { - if ((cols & (1 << n)) && !((candidates | invalids) & (1 << n))) { - // This color is used in this tile but - // was not used in any other tile yet, - // so it could be the background one. - candidates |= 1 << n; - } - - if ((cols & (1 << n)) == 0 ) { - // This color isn't used at all in this tile: - // Can't be the global background - invalids |= 1 << n; - candidates &= ~(1 << n); - } - - if (candidates & (1 << n)) { - // We have a candidate, mark it as such - cand++; - } - } - - // After checking the constraints for this tile, do we have - // candidate background colors left ? - if (cand==0) - { - Warning_message("No possible global background color"); - return 1; - } - } - } - } - - // Now just pick the first valid candidate - for (n = 0; n<16; n++) - { - if (candidates & (1 << n)) { - background = n; - break; - } - } - GFX2_Log(GFX2_DEBUG, "Save_C64_multi() background=%d ($%x) candidates=%x invalid=%x\n", - (int)background, (int)background, (unsigned)candidates, (unsigned)invalids); - - - // Now that we know which color is the background, we can encode the cells - for(cy=0; cy<25; cy++) - { - for(cx=0; cx<40; cx++) - { - numcolors=Count_used_colors_area(cusage,cx*4,cy*8,4,8); - if(numcolors>4) - { - Warning_with_format("More than 4 colors\nin 4x8 pixel cell: (%d, %d)\nRect: (%d, %d, %d, %d)", cx, cy, cx * 4, cy * 8, cx * 4 + 3, cy * 8 + 7); - // TODO hilite offending block - return 1; - } - color=1; - c[0]=background; - for(i=0; i<16; i++) - { - lut[i]=0; - if(cusage[i] && (i!=background)) - { - lut[i]=color; - c[color]=i; - color++; - } - } - // add to screen_ram and color_ram - screen_ram[cx+cy*40]=c[1]<<4|c[2]; - color_ram[cx+cy*40]=c[3]; - - for(y=0;y<8;y++) - { - bits=0; - for(x=0;x<4;x++) - { - pixel = Get_pixel(context, cx*4+x,cy*8+y); - bits = (bits << 2) | lut[pixel]; - } - bitmap[pos++]=bits; - } - } - } - - file = Open_file_write(context); - - if(!file) - { - Warning_message("File open failed"); - File_error = 2; - return 2; - } - - setvbuf(file, NULL, _IOFBF, 64*1024); - - if (loadAddr) - Write_word_le(file,loadAddr); - - if (saveWhat==0 || saveWhat==1) - Write_bytes(file,bitmap,8000); - - if (saveWhat==0 || saveWhat==2) - Write_bytes(file,screen_ram,1000); - - if (saveWhat==0 || saveWhat==3) - Write_bytes(file,color_ram,1000); - - if (saveWhat==0) - Write_byte(file,background); - - fclose(file); - return 0; -} - -/** - * Save a C64 FLI (Flexible Line Interpretation) picture. - * - * This function need a 3 layer image : - * - layer 0 is background colors - * - layer 1 is color RAM values (4x8 blocks) - * - layer 2 is the actual picture - * - * @param context the IO context - * @param saveWhat what part of the data to save - * @param loadAddr The load address - */ -int Save_C64_fli(T_IO_Context * context, byte saveWhat, word loadAddr) -{ - FILE *file; - byte file_buffer[17474]; - - memset(file_buffer,0,sizeof(file_buffer)); - - switch(C64_FLI(context, file_buffer+9474, file_buffer+1282, file_buffer+258, file_buffer+2)) - { - case 0: // OK - break; - case 1: - Warning_message("Less than 3 layers"); - File_error=1; - return 1; - case 2: - Warning_message("Picture must be 160x200"); - File_error=1; - return 1; - default: - File_error=1; - return 1; - } - - file = Open_file_write(context); - - if(!file) - { - Warning_message("File open failed"); - File_error = 1; - return 1; - } - - if (loadAddr) - Write_word_le(file, loadAddr); - - if (saveWhat==0) - Write_bytes(file,file_buffer+2,256); // Background colors for lines 0-199 (+ 56bytes padding) - - if (saveWhat==0 || saveWhat==3) - Write_bytes(file,file_buffer+258,1024); // Color RAM (1000 bytes + padding 24) - - if (saveWhat==0 || saveWhat==1) - Write_bytes(file,file_buffer+1282,8192); // Screen RAMs 8 x (1000 bytes + padding 24) - - if (saveWhat==0 || saveWhat==2) - Write_bytes(file,file_buffer+9474,8000); // BitMap - - fclose(file); - return 0; -} - -/** - * Save C64 picture. - * - * Supports : - * - HiRes (320x200) - * - Multicolor - * - FLI - * - * @param context the IO context - */ -void Save_C64(T_IO_Context * context) -{ - enum c64_format saveFormat = F_invalid; - static byte saveWhat=0; - static word loadAddr=0; - - if (((context->Width!=320) && (context->Width!=160)) || context->Height!=200) - { - Warning_message("must be 320x200 or 160x200"); - File_error = 1; - return; - } - - saveFormat = (context->Width == 320) ? F_hires : F_multi; - - GFX2_Log(GFX2_DEBUG, "Save_C64() extension : %s\n", context->File_name + strlen(context->File_name) - 4); - if (strcasecmp(context->File_name + strlen(context->File_name) - 4, ".fli") == 0) - saveFormat = F_fli; - - if(!Save_C64_window(&saveFormat, &saveWhat,&loadAddr)) - { - File_error = 1; - return; - } - - Set_saving_layer(context, 0); - switch (saveFormat) - { - case F_fli: - if (context->Nb_layers < 3) - File_error = Save_C64_fli_monolayer(context, saveWhat, loadAddr); - else - File_error = Save_C64_fli(context, saveWhat, loadAddr); - break; - case F_multi: - File_error = Save_C64_multi(context, saveWhat, loadAddr); - break; - case F_bitmap: - saveWhat = 1; // force save bitmap -#if defined(__GNUC__) && (__GNUC__ >= 7) - __attribute__ ((fallthrough)); -#endif - case F_hires: - default: - File_error = Save_C64_hires(context, saveWhat, loadAddr); - } -} - - -/////////////////////////// pixcen *.GPX /////////////////////////// -void Test_GPX(T_IO_Context * context, FILE * file) -{ - byte header[2]; - (void)context; - - // check for a Zlib compressed stream - File_error = 1; - if (!Read_bytes(file, header, 2)) - return; - if ((header[0] & 0x0f) != 8) - return; - if (((header[0] << 8) + header[1]) % 31) - return; - File_error = 0; -} - -void Load_GPX(T_IO_Context * context) -{ - FILE * file; - unsigned long file_size; - byte * buffer; - - File_error = 1; - file = Open_file_read(context); - if (file == NULL) - return; - file_size = File_length_file(file); - buffer = GFX2_malloc(file_size); - if (buffer == NULL) - { - fclose(file); - return; - } - if (Read_bytes(file, buffer, file_size)) - { - byte * gpx = NULL; - unsigned long gpx_size = 0; - int r = Z_MEM_ERROR; - - do - { - free(gpx); - gpx_size += 65536; - gpx = GFX2_malloc(gpx_size); - if (gpx == NULL) - break; - r = uncompress(gpx, &gpx_size, buffer, file_size); - if (r != Z_BUF_ERROR && r != Z_OK) - GFX2_Log(GFX2_ERROR, "uncompress() failed with error %d: %s\n", r, zError(r)); - } - while (r == Z_BUF_ERROR); // there was not enough room in the output buffer - if (r == Z_OK) - { - byte * p; - dword version, mode; -/* - mode : -0 BITMAP, -1 MC_BITMAP, -2 SPRITE, -3 MC_SPRITE, -4 CHAR, -5 MC_CHAR, -6 UNUSED1, -7 UNUSED2, -8 UNRESTRICTED, -9 W_UNRESTRICTED -*/ - GFX2_Log(GFX2_DEBUG, "inflated %lu bytes to %lu\n", file_size, gpx_size); -#define READU32LE(p) ((p)[0] | (p)[1] << 8 | (p)[2] << 16 | (p)[3] << 24) - version = READU32LE(gpx); - mode = READU32LE(gpx+4); - GFX2_Log(GFX2_DEBUG, "gpx version %u mode %u\n", version, mode); - snprintf(context->Comment, COMMENT_SIZE, "pixcen file version %u mode %u", version, mode); - if (version >= 4) - { - dword count; - const char * key; - word value[256]; - int xsize = -1; - int ysize = -1; - int mapsize = -1; - int screensize = -1; - int colorsize = -1; - int backbuffers = -1; - - count = READU32LE(gpx+8); - p = gpx + 12; - while (count--) - { - int i = 0; - int int_value = 0; - - key = (const char *)p; - while (*p++); - for (;;) - { - value[i] = p[0] + (p[1] << 8); - p += 2; - if (value[i] == 0) - break; - int_value = int_value * 10 + (value[i] - '0'); - i++; - } - GFX2_Log(GFX2_DEBUG, "%s=%d\n", key, int_value); - if (0 == strcmp(key, "xsize")) - xsize = int_value; - else if (0 == strcmp(key, "ysize")) - ysize = int_value; - else if (0 == strcmp(key, "mapsize")) - mapsize = int_value; - else if (0 == strcmp(key, "screensize")) - screensize = int_value; - else if (0 == strcmp(key, "colorsize")) - colorsize = int_value; - else if (0 == strcmp(key, "backbuffers")) - backbuffers = int_value; - } -//buffersize = 64 + (64 + mapsize + screensize + colorsize) * backbuffers; - p += 64; // 64 empty bytes ? - File_error = 0; - if (mode & 1) - context->Ratio = PIXEL_WIDE; - else - context->Ratio = PIXEL_SIMPLE; - Pre_load(context, xsize, ysize, file_size, FORMAT_GPX, context->Ratio, 4); // Do this as soon as you can - if (Config.Clear_palette) - memset(context->Palette,0, sizeof(T_Palette)); - C64_set_palette(context->Palette); - context->Transparent_color=16; - - //foreach backbuffer - if (backbuffers >= 1) - { - byte border, background; - //byte ext0, ext1, ext2; - byte * bitmap, * color, * screen; - - //GFX2_LogHexDump(GFX2_DEBUG, "GPX ", p, 0, 64); - p += 47; // Extra bytes - //crippled = p; - p += 6; - //lock = p; - p += 6; - border = *p++; - background = *p++; - /*ext0 = *p++; - ext1 = *p++; - ext2 = *p++;*/ - p += 3; - bitmap = p; - p += mapsize; - color = p; - p += colorsize; - screen = p; - p += screensize; - - GFX2_Log(GFX2_DEBUG, "background color #%d, border color #%d\n", (int)background, (int)border); - Load_C64_multi(context, bitmap, screen, color, background); - Set_image_mode(context, (mode & 1) ? IMAGE_MODE_C64MULTI : IMAGE_MODE_C64HIRES); - } - } - else - { - GFX2_Log(GFX2_ERROR, "GPX file version %d unsupported\n", version); - } - } - free(gpx); - } - free(buffer); - fclose(file); -} - /** * Test for SCR file (Amstrad CPC) *