grafX2/src/c64formats.c
2019-12-07 15:59:05 +01:00

1815 lines
53 KiB
C

/* 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 <http://www.gnu.org/licenses/>
*/
///@file c64formats.c
/// Formats for the Commodore 64
#include <stdlib.h>
#include <string.h>
#include <zlib.h>
#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; i<sizeof(what_label)/sizeof(what_label[0]); i++)
Window_dropdown_add_item(what,i,what_label[i]);
Print_in_window(113,18,"Address:",MC_Dark,MC_Light);
addr = Window_set_dropdown_button(110,28,70,15,70,address_label[*loadAddr/0x2000],1, 0, 1, LEFT_SIDE,0); // 4
Window_dropdown_clear_items(addr);
for (i=0; i<sizeof(address_label)/sizeof(address_label[0]); i++)
Window_dropdown_add_item(addr,i,address_label[i]);
Print_in_window(13,46,"Format:",MC_Dark,MC_Light);
format = Window_set_dropdown_button(10,56,90,15,88,c64_format_names[*saveFormat],1, 0, 1, LEFT_SIDE,0); // 5
if (*saveFormat == F_hires || *saveFormat == F_bitmap)
{
Window_dropdown_add_item(format, F_hires, c64_format_names[F_hires]);
Window_dropdown_add_item(format, F_bitmap, c64_format_names[F_bitmap]);
}
else
{
Window_dropdown_add_item(format, F_multi, c64_format_names[F_multi]);
Window_dropdown_add_item(format, F_fli, c64_format_names[F_fli]);
}
Update_window_area(0,0,Window_width,Window_height);
Display_cursor();
do
{
button = Window_clicked_button();
if (Is_shortcut(Key, 0x100+BUTTON_HELP))
{
Key = 0;
Window_help(BUTTON_SAVE, "COMMODORE 64 FORMATS");
}
else switch(button)
{
case 3: // Save what
*saveWhat = Window_attribute2;
GFX2_Log(GFX2_DEBUG, "Save_C64_Window() : what=%d (%s)\n", *saveWhat, what_label[*saveWhat]);
break;
case 4: // Load addr
*loadAddr = Window_attribute2*0x2000;
GFX2_Log(GFX2_DEBUG, "Save_C64_Window() : addr=$%04x (%d)\n",*loadAddr,Window_attribute2);
break;
case 5:
*saveFormat = Window_attribute2;
GFX2_Log(GFX2_DEBUG, "Save_C64_Window() : format=%d\n", Window_attribute2);
break;
case 0: break;
}
} while(button!=1 && button!=2);
Close_window();
Display_cursor();
return button==1;
}
/// Save a C64 hires picture
///
/// c64 hires is 320x200 with only 2 colors per 8x8 block.
static int Save_C64_hires(T_IO_Context *context, byte saveWhat, word loadAddr)
{
int i, pos = 0;
word cx, cy, x, y;
byte screen_ram[1000],bitmap[8000];
FILE *file;
for(cy=0; cy<25; cy++) // Character line, 25 lines
{
for(cx=0; cx<40; cx++) // Character column, 40 columns
{
byte fg, bg; // foreground and background colors for the 8x8 block
byte c[2];
int count = 0;
// first pass : find colors used
for(y=0; y<8; y++)
{
for(x=0; x<8; x++)
{
byte pixel = Get_pixel(context, x+cx*8,y+cy*8);
if(pixel>15)
{
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);
}