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)
*