1815 lines
		
	
	
		
			53 KiB
		
	
	
	
		
			C
		
	
	
	
	
	
			
		
		
	
	
			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);
 | 
						|
}
 |