7326 lines
		
	
	
		
			206 KiB
		
	
	
	
		
			C
		
	
	
	
	
	
			
		
		
	
	
			7326 lines
		
	
	
		
			206 KiB
		
	
	
	
		
			C
		
	
	
	
	
	
/* vim:expandtab:ts=2 sw=2:
 | 
						|
*/
 | 
						|
/*  Grafx2 - The Ultimate 256-color bitmap paint program
 | 
						|
 | 
						|
    Copyright 2018 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 miscfileformats.c
 | 
						|
/// Formats that aren't fully saving, either because of palette restrictions or other things
 | 
						|
 | 
						|
#include <limits.h>
 | 
						|
#include <stdlib.h>
 | 
						|
#include <string.h>
 | 
						|
 | 
						|
#ifdef _MSC_VER
 | 
						|
#include <stdio.h>
 | 
						|
#if _MSC_VER < 1900
 | 
						|
#define snprintf _snprintf
 | 
						|
#endif
 | 
						|
#define strdup _strdup
 | 
						|
#endif
 | 
						|
 | 
						|
#include <zlib.h>
 | 
						|
 | 
						|
#include "engine.h"
 | 
						|
#include "errors.h"
 | 
						|
#include "global.h"
 | 
						|
#include "io.h"
 | 
						|
#include "libraw2crtc.h"
 | 
						|
#include "loadsave.h"
 | 
						|
#include "misc.h"
 | 
						|
#include "screen.h"
 | 
						|
#include "struct.h"
 | 
						|
#include "windows.h"
 | 
						|
#include "oldies.h"
 | 
						|
#include "pages.h"
 | 
						|
#include "keycodes.h"
 | 
						|
#include "input.h"
 | 
						|
#include "help.h"
 | 
						|
#include "fileformats.h"
 | 
						|
 | 
						|
extern char Program_version[]; // generated in pversion.c
 | 
						|
extern const char SVN_revision[]; // generated in version.c
 | 
						|
 | 
						|
//////////////////////////////////// PAL ////////////////////////////////////
 | 
						|
//
 | 
						|
 | 
						|
// -- Test wether a file is in PAL format --------------------------------
 | 
						|
void Test_PAL(T_IO_Context * context, FILE * file)
 | 
						|
{
 | 
						|
  char buffer[32];
 | 
						|
  long file_size;
 | 
						|
 | 
						|
  (void)context;
 | 
						|
  File_error = 1;
 | 
						|
 | 
						|
  file_size = File_length_file(file);
 | 
						|
  // First check for GrafX2 legacy palette format. The simplest one, 768 bytes
 | 
						|
  // of RGB data. It is a raw dump of the T_Palette structure. There is no
 | 
						|
  // header at all, so we check for the file size.
 | 
						|
  if (file_size == sizeof(T_Palette))
 | 
						|
    File_error = 0;
 | 
						|
  else if (file_size > 8)
 | 
						|
  {
 | 
						|
    // Bigger (or smaller ?) files may be in other formats. These have an
 | 
						|
    // header, so look for it.
 | 
						|
    if (!Read_bytes(file, buffer, 8))
 | 
						|
      return;
 | 
						|
    if (strncmp(buffer,"JASC-PAL",8) == 0)
 | 
						|
    {
 | 
						|
      // JASC file format, used by Paint Shop Pro and GIMP. This is also the
 | 
						|
      // one used for saving, as it brings greater interoperability.
 | 
						|
      File_error = 0;
 | 
						|
    }
 | 
						|
    else if(strncmp(buffer,"RIFF", 4) == 0)
 | 
						|
    {
 | 
						|
      // Microsoft RIFF file
 | 
						|
      // This is a data container (similar to IFF). We only check the first
 | 
						|
      // chunk header, and give up if that's not a palette.
 | 
						|
      fseek(file, 8, SEEK_SET);
 | 
						|
      if (!Read_bytes(file, buffer, 8))
 | 
						|
        return;
 | 
						|
      if (strncmp(buffer, "PAL data", 8) == 0)
 | 
						|
      {
 | 
						|
        File_error = 0;
 | 
						|
      }
 | 
						|
    }
 | 
						|
  }
 | 
						|
}
 | 
						|
 | 
						|
/// Test for GPL (Gimp Palette) file format
 | 
						|
void Test_GPL(T_IO_Context * context, FILE * file)
 | 
						|
{
 | 
						|
  char buffer[16];
 | 
						|
  long file_size;
 | 
						|
 | 
						|
  (void)context;
 | 
						|
  File_error = 1;
 | 
						|
 | 
						|
  file_size = File_length_file(file);
 | 
						|
  if (file_size > 33) {
 | 
						|
    // minimum header length == 33
 | 
						|
    // "GIMP Palette" == 12
 | 
						|
    if (!Read_bytes(file, buffer, 12))
 | 
						|
      return;
 | 
						|
    if (strncmp(buffer,"GIMP Palette",12) == 0)
 | 
						|
      File_error = 0;
 | 
						|
  }
 | 
						|
}
 | 
						|
 | 
						|
/// skip the padding before a space-padded field.
 | 
						|
static int skip_padding(FILE *file, int max_chars)
 | 
						|
{
 | 
						|
  byte b;
 | 
						|
  int chars_read = 0;
 | 
						|
 | 
						|
  do {
 | 
						|
    if (chars_read == max_chars)
 | 
						|
      return chars_read; // eof
 | 
						|
    if (!Read_byte(file, &b))
 | 
						|
      return chars_read;
 | 
						|
    chars_read++;
 | 
						|
  } while (b == ' ');
 | 
						|
 | 
						|
  fseek(file, -1, SEEK_CUR);
 | 
						|
  return chars_read;
 | 
						|
}
 | 
						|
 | 
						|
/// Load GPL (Gimp Palette) file format
 | 
						|
void Load_GPL(T_IO_Context * context)
 | 
						|
{
 | 
						|
  FILE *file;
 | 
						|
  char buffer[256];
 | 
						|
 | 
						|
  File_error = 1;
 | 
						|
 | 
						|
  file = Open_file_read(context);
 | 
						|
  if (file == NULL)
 | 
						|
    return;
 | 
						|
 | 
						|
  if (!Read_byte_line(file, buffer, sizeof(buffer)))
 | 
						|
    return;
 | 
						|
 | 
						|
  if (memcmp(buffer,"GIMP Palette",12) == 0)
 | 
						|
  {
 | 
						|
    int i, r, g, b, columns;
 | 
						|
    size_t len;
 | 
						|
    // Name: xxxxx
 | 
						|
    if (!Read_byte_line(file, buffer, sizeof(buffer)))
 | 
						|
      return;
 | 
						|
    len = strlen(buffer);
 | 
						|
    while (len > 0)
 | 
						|
    {
 | 
						|
      len--;
 | 
						|
      if (buffer[len] == '\r' || buffer[len] == '\n')
 | 
						|
        buffer[len] = '\0';
 | 
						|
    }
 | 
						|
    GFX2_Log(GFX2_DEBUG, "GPL %s\n", buffer);
 | 
						|
    if (0 == memcmp(buffer, "Name: ", 6))
 | 
						|
      snprintf(context->Comment, sizeof(context->Comment), "GPL: %s", buffer + 6);
 | 
						|
 | 
						|
    // Columns: 16
 | 
						|
    if (fscanf(file, "Columns: %d", &columns) != 1)
 | 
						|
      return;
 | 
						|
    Read_byte_line(file, buffer, sizeof(buffer));
 | 
						|
    /// @todo set grafx2 columns setting to match.
 | 
						|
    // #<newline>
 | 
						|
    if (!Read_byte_line(file, buffer, sizeof(buffer)))
 | 
						|
      return;
 | 
						|
 | 
						|
    for (i = 0; i < 256; i++)
 | 
						|
    {
 | 
						|
      skip_padding(file, 32);
 | 
						|
      if (fscanf(file, "%d", &r) != 1)
 | 
						|
        break;
 | 
						|
      skip_padding(file, 32);
 | 
						|
      if (fscanf(file, "%d", &g) != 1)
 | 
						|
        break;
 | 
						|
      skip_padding(file, 32);
 | 
						|
      if (fscanf(file, "%d\t", &b) != 1)
 | 
						|
        break;
 | 
						|
      if (!Read_byte_line(file, buffer, sizeof(buffer)))
 | 
						|
        break;
 | 
						|
      len = strlen(buffer);
 | 
						|
      while (len > 1)
 | 
						|
      {
 | 
						|
        len--;
 | 
						|
        if (buffer[len] == '\r' || buffer[len] == '\n')
 | 
						|
          buffer[len] = '\0';
 | 
						|
      }
 | 
						|
      /// @todo analyze color names to build shade table
 | 
						|
 | 
						|
      GFX2_Log(GFX2_DEBUG, "GPL: %3d: RGB(%3d,%3d,%3d) %s\n", i, r,g,b, buffer);
 | 
						|
      context->Palette[i].R = r;
 | 
						|
      context->Palette[i].G = g;
 | 
						|
      context->Palette[i].B = b;
 | 
						|
    }
 | 
						|
    if (i > 0)  // at least one color was read
 | 
						|
      File_error = 0;
 | 
						|
  }
 | 
						|
  else
 | 
						|
    File_error = 2;
 | 
						|
 | 
						|
  // close the file
 | 
						|
  fclose(file);
 | 
						|
}
 | 
						|
 | 
						|
 | 
						|
/// Save GPL (Gimp Palette) file format
 | 
						|
void
 | 
						|
Save_GPL (T_IO_Context * context)
 | 
						|
{
 | 
						|
  // Gimp is a unix program, so use Unix file endings (LF aka '\n')
 | 
						|
  FILE *file;
 | 
						|
 | 
						|
  file = Open_file_write(context);
 | 
						|
 | 
						|
  if (file != NULL )
 | 
						|
  {
 | 
						|
    int i;
 | 
						|
 | 
						|
    File_error = 0;
 | 
						|
    fprintf (file, "GIMP Palette\n");
 | 
						|
    fprintf (file, "Name: %s\n", context->File_name);
 | 
						|
    // TODO: use actual columns value
 | 
						|
    fprintf (file, "Columns: %d\n#\n", 16);
 | 
						|
 | 
						|
    for (i = 0; i < 256 && File_error==0; i++)
 | 
						|
    {
 | 
						|
      // TODO: build names from shade table data
 | 
						|
      if (fprintf(file,"%d %d %d\tUntitled\n",context->Palette[i].R, context->Palette[i].G, context->Palette[i].B) <= 0)
 | 
						|
        File_error=1;
 | 
						|
    }
 | 
						|
    fclose(file);
 | 
						|
 | 
						|
    if (File_error)
 | 
						|
      Remove_file(context);
 | 
						|
  }
 | 
						|
  else
 | 
						|
  {
 | 
						|
    // unable to open output file, nothing saved.
 | 
						|
    File_error=1;
 | 
						|
  }
 | 
						|
}
 | 
						|
 | 
						|
 | 
						|
 | 
						|
// -- Lire un fichier au format PAL -----------------------------------------
 | 
						|
void Load_PAL(T_IO_Context * context)
 | 
						|
{
 | 
						|
  FILE *file;              // Fichier du fichier
 | 
						|
 | 
						|
  File_error=0;
 | 
						|
 | 
						|
  // Ouverture du fichier
 | 
						|
  if ((file=Open_file_read(context)))
 | 
						|
  {
 | 
						|
    long file_size = File_length_file(file);
 | 
						|
    // Le fichier ne peut être au format PAL que si sa taille vaut 768 octets
 | 
						|
    if (file_size == sizeof(T_Palette))
 | 
						|
    {
 | 
						|
      T_Palette palette_64;
 | 
						|
      // Pre_load(context, ?); // Pas possible... pas d'image...
 | 
						|
 | 
						|
      // Lecture du fichier dans context->Palette
 | 
						|
      if (Read_bytes(file, palette_64, sizeof(T_Palette)))
 | 
						|
      {
 | 
						|
        Palette_64_to_256(palette_64);
 | 
						|
        memcpy(context->Palette, palette_64, sizeof(T_Palette));
 | 
						|
      }
 | 
						|
      else
 | 
						|
        File_error = 2;
 | 
						|
    } else {
 | 
						|
      char buffer[16];
 | 
						|
      if (!Read_bytes(file, buffer, 8))
 | 
						|
      {
 | 
						|
        File_error = 2;
 | 
						|
        fclose(file);
 | 
						|
        return;
 | 
						|
      }
 | 
						|
      buffer[8] = '\0';
 | 
						|
      if (strncmp(buffer,"JASC-PAL",8) == 0)
 | 
						|
      {
 | 
						|
        int i, n, r, g, b;
 | 
						|
        i = fscanf(file, "%d",&n);
 | 
						|
        if(i != 1 || n != 100)
 | 
						|
        {
 | 
						|
          File_error = 2;
 | 
						|
          fclose(file);
 | 
						|
          return;
 | 
						|
        }
 | 
						|
        // Read color count
 | 
						|
        if (fscanf(file, "%d",&n) == 1)
 | 
						|
        {
 | 
						|
          for (i = 0; i < n; i++)
 | 
						|
          {
 | 
						|
            if (fscanf(file, "%d %d %d",&r, &g, &b) == 3)
 | 
						|
            {
 | 
						|
              context->Palette[i].R = r;
 | 
						|
              context->Palette[i].G = g;
 | 
						|
              context->Palette[i].B = b;
 | 
						|
            }
 | 
						|
            else
 | 
						|
              File_error = 2;
 | 
						|
          }
 | 
						|
        }
 | 
						|
        else
 | 
						|
          File_error = 2;
 | 
						|
      }
 | 
						|
      else if(strncmp(buffer, "RIFF", 4) == 0)
 | 
						|
      {
 | 
						|
        // Microsoft RIFF format.
 | 
						|
        fseek(file, 8, SEEK_SET);
 | 
						|
        Read_bytes(file, buffer, 8);
 | 
						|
        if (strncmp(buffer, "PAL data", 8) == 0)
 | 
						|
        {
 | 
						|
          word color_count;
 | 
						|
          word i = 0;
 | 
						|
 | 
						|
          fseek(file, 22, SEEK_SET);
 | 
						|
          if (!Read_word_le(file, &color_count))
 | 
						|
            File_error = 2;
 | 
						|
          else
 | 
						|
            for(i = 0; i < color_count && File_error == 0; i++)
 | 
						|
            {
 | 
						|
              byte colors[4];
 | 
						|
              if (!Read_bytes(file, colors, 4))
 | 
						|
                File_error = 2;
 | 
						|
              context->Palette[i].R = colors[0];
 | 
						|
              context->Palette[i].G = colors[1];
 | 
						|
              context->Palette[i].B = colors[2];
 | 
						|
            }
 | 
						|
        } else File_error = 2;
 | 
						|
      } else
 | 
						|
        File_error = 2;
 | 
						|
    }
 | 
						|
 | 
						|
    fclose(file);
 | 
						|
  }
 | 
						|
  else
 | 
						|
    // Si on n'a pas réussi à ouvrir le fichier, alors il y a eu une erreur
 | 
						|
    File_error=1;
 | 
						|
}
 | 
						|
 | 
						|
 | 
						|
// -- Sauver un fichier au format PAL ---------------------------------------
 | 
						|
void Save_PAL(T_IO_Context * context)
 | 
						|
{
 | 
						|
  // JASC-PAL is a DOS/Windows format, so use CRLF line endings "\r\n"
 | 
						|
  FILE *file;
 | 
						|
 | 
						|
  File_error=0;
 | 
						|
 | 
						|
  // Open output file
 | 
						|
  if ((file=Open_file_write(context)) != NULL)
 | 
						|
  {
 | 
						|
    int i;
 | 
						|
 | 
						|
    setvbuf(file, NULL, _IOFBF, 64*1024);
 | 
						|
 | 
						|
    if (fputs("JASC-PAL\r\n0100\r\n256\r\n", file)==EOF)
 | 
						|
      File_error=1;
 | 
						|
    for (i = 0; i < 256 && File_error==0; i++)
 | 
						|
    {
 | 
						|
      if (fprintf(file,"%d %d %d\r\n",context->Palette[i].R, context->Palette[i].G, context->Palette[i].B) <= 0)
 | 
						|
        File_error=1;
 | 
						|
    }
 | 
						|
 | 
						|
    fclose(file);
 | 
						|
 | 
						|
    if (File_error)
 | 
						|
      Remove_file(context);
 | 
						|
  }
 | 
						|
  else
 | 
						|
  {
 | 
						|
    // unable to open output file, nothing saved.
 | 
						|
    File_error=1;
 | 
						|
  }
 | 
						|
}
 | 
						|
 | 
						|
 | 
						|
//////////////////////////////////// PKM ////////////////////////////////////
 | 
						|
typedef struct
 | 
						|
{
 | 
						|
  char Ident[3];    // String "PKM" }
 | 
						|
  byte Method;      // Compression method
 | 
						|
                    //   0 = per-line compression (c)KM
 | 
						|
                    //   others = unknown at the moment
 | 
						|
  byte Recog1;      // Recognition byte 1
 | 
						|
  byte Recog2;      // Recognition byte 2
 | 
						|
  word Width;       // Image width
 | 
						|
  word Height;      // Image height
 | 
						|
  T_Palette Palette;// RGB Palette 256*3, on a 1-64 scale for each component
 | 
						|
  word Jump;        // Size of the jump between header and image:
 | 
						|
                    //   Used to insert a comment
 | 
						|
} T_PKM_Header;
 | 
						|
 | 
						|
// -- Tester si un fichier est au format PKM --------------------------------
 | 
						|
void Test_PKM(T_IO_Context * context, FILE * file)
 | 
						|
{
 | 
						|
  T_PKM_Header header;
 | 
						|
 | 
						|
  (void)context;
 | 
						|
  File_error=1;
 | 
						|
 | 
						|
  // Lecture du header du fichier
 | 
						|
  if (Read_bytes(file,&header.Ident,3) &&
 | 
						|
      Read_byte(file,&header.Method) &&
 | 
						|
      Read_byte(file,&header.Recog1) &&
 | 
						|
      Read_byte(file,&header.Recog2) &&
 | 
						|
      Read_word_le(file,&header.Width) &&
 | 
						|
      Read_word_le(file,&header.Height) &&
 | 
						|
      Read_bytes(file,&header.Palette,sizeof(T_Palette)) &&
 | 
						|
      Read_word_le(file,&header.Jump))
 | 
						|
  {
 | 
						|
    // On regarde s'il y a la signature PKM suivie de la méthode 0.
 | 
						|
    // La constante "PKM" étant un chaîne, elle se termine toujours par 0.
 | 
						|
    // Donc pas la peine de s'emm...er à regarder si la méthode est à 0.
 | 
						|
    if ( (!memcmp(&header,"PKM",4)) && header.Width && header.Height)
 | 
						|
      File_error=0;
 | 
						|
  }
 | 
						|
}
 | 
						|
 | 
						|
 | 
						|
// -- Lire un fichier au format PKM -----------------------------------------
 | 
						|
void Load_PKM(T_IO_Context * context)
 | 
						|
{
 | 
						|
  FILE *file;             // Fichier du fichier
 | 
						|
  T_PKM_Header header;
 | 
						|
  byte  color;
 | 
						|
  byte  temp_byte;
 | 
						|
  word  len;
 | 
						|
  word  index;
 | 
						|
  dword Compteur_de_pixels;
 | 
						|
  dword Compteur_de_donnees_packees;
 | 
						|
  dword image_size;
 | 
						|
  dword Taille_pack;
 | 
						|
  long  file_size;
 | 
						|
 | 
						|
  File_error=0;
 | 
						|
 | 
						|
  if ((file=Open_file_read(context)))
 | 
						|
  {
 | 
						|
    file_size=File_length_file(file);
 | 
						|
 | 
						|
    if (Read_bytes(file,&header.Ident,3) &&
 | 
						|
        Read_byte(file,&header.Method) &&
 | 
						|
        Read_byte(file,&header.Recog1) &&
 | 
						|
        Read_byte(file,&header.Recog2) &&
 | 
						|
        Read_word_le(file,&header.Width) &&
 | 
						|
        Read_word_le(file,&header.Height) &&
 | 
						|
        Read_bytes(file,&header.Palette,sizeof(T_Palette)) &&
 | 
						|
        Read_word_le(file,&header.Jump))
 | 
						|
    {
 | 
						|
      context->Comment[0]='\0'; // On efface le commentaire
 | 
						|
      if (header.Jump)
 | 
						|
      {
 | 
						|
        index=0;
 | 
						|
        while ( (index<header.Jump) && (!File_error) )
 | 
						|
        {
 | 
						|
          if (Read_byte(file,&temp_byte))
 | 
						|
          {
 | 
						|
            index+=2; // On rajoute le "Field-id" et "le Field-size" pas encore lu
 | 
						|
            switch (temp_byte)
 | 
						|
            {
 | 
						|
              case 0 : // Commentaire
 | 
						|
                if (Read_byte(file,&temp_byte))
 | 
						|
                {
 | 
						|
                  if (temp_byte>COMMENT_SIZE)
 | 
						|
                  {
 | 
						|
                    color=temp_byte;              // On se sert de color comme
 | 
						|
                    temp_byte=COMMENT_SIZE;   // variable temporaire
 | 
						|
                    color-=COMMENT_SIZE;
 | 
						|
                  }
 | 
						|
                  else
 | 
						|
                    color=0;
 | 
						|
 | 
						|
                  if (Read_bytes(file,context->Comment,temp_byte))
 | 
						|
                  {
 | 
						|
                    index+=temp_byte;
 | 
						|
                    context->Comment[temp_byte]='\0';
 | 
						|
                    if (color)
 | 
						|
                      if (fseek(file,color,SEEK_CUR))
 | 
						|
                        File_error=2;
 | 
						|
                  }
 | 
						|
                  else
 | 
						|
                    File_error=2;
 | 
						|
                }
 | 
						|
                else
 | 
						|
                  File_error=2;
 | 
						|
                break;
 | 
						|
 | 
						|
              case 1 : // Dimensions de l'écran d'origine
 | 
						|
                if (Read_byte(file,&temp_byte))
 | 
						|
                {
 | 
						|
                  if (temp_byte==4)
 | 
						|
                  {
 | 
						|
                    index+=4;
 | 
						|
                    if ( ! Read_word_le(file,(word *) &Original_screen_X)
 | 
						|
                      || !Read_word_le(file,(word *) &Original_screen_Y) )
 | 
						|
                      File_error=2;
 | 
						|
                    else
 | 
						|
                      GFX2_Log(GFX2_DEBUG, "PKM original screen %dx%d\n", (int)Original_screen_X, (int)Original_screen_Y);
 | 
						|
                  }
 | 
						|
                  else
 | 
						|
                    File_error=2;
 | 
						|
                }
 | 
						|
                else
 | 
						|
                  File_error=2;
 | 
						|
                break;
 | 
						|
 | 
						|
              case 2 : // color de transparence
 | 
						|
                if (Read_byte(file,&temp_byte))
 | 
						|
                {
 | 
						|
                  if (temp_byte==1)
 | 
						|
                  {
 | 
						|
                    index++;
 | 
						|
                    if (! Read_byte(file,&Back_color))
 | 
						|
                      File_error=2;
 | 
						|
                  }
 | 
						|
                  else
 | 
						|
                    File_error=2;
 | 
						|
                }
 | 
						|
                else
 | 
						|
                  File_error=2;
 | 
						|
                break;
 | 
						|
 | 
						|
              default:
 | 
						|
                if (Read_byte(file,&temp_byte))
 | 
						|
                {
 | 
						|
                  index+=temp_byte;
 | 
						|
                  if (fseek(file,temp_byte,SEEK_CUR))
 | 
						|
                    File_error=2;
 | 
						|
                }
 | 
						|
                else
 | 
						|
                  File_error=2;
 | 
						|
            }
 | 
						|
          }
 | 
						|
          else
 | 
						|
            File_error=2;
 | 
						|
        }
 | 
						|
        if ( (!File_error) && (index!=header.Jump) )
 | 
						|
          File_error=2;
 | 
						|
      }
 | 
						|
 | 
						|
      /*Init_lecture();*/
 | 
						|
 | 
						|
      if (!File_error)
 | 
						|
      {
 | 
						|
        Pre_load(context, header.Width,header.Height,file_size,FORMAT_PKM,PIXEL_SIMPLE,0);
 | 
						|
        if (File_error==0)
 | 
						|
        {
 | 
						|
 | 
						|
          context->Width=header.Width;
 | 
						|
          context->Height=header.Height;
 | 
						|
          image_size=(dword)(context->Width*context->Height);
 | 
						|
          // Palette lue en 64
 | 
						|
          memcpy(context->Palette,header.Palette,sizeof(T_Palette));
 | 
						|
          Palette_64_to_256(context->Palette);
 | 
						|
 | 
						|
          Compteur_de_donnees_packees=0;
 | 
						|
          Compteur_de_pixels=0;
 | 
						|
          // Header size is 780
 | 
						|
          Taille_pack=(file_size)-780-header.Jump;
 | 
						|
 | 
						|
          // Boucle de décompression:
 | 
						|
          while ( (Compteur_de_pixels<image_size) && (Compteur_de_donnees_packees<Taille_pack) && (!File_error) )
 | 
						|
          {
 | 
						|
            if(Read_byte(file, &temp_byte)!=1)
 | 
						|
            {
 | 
						|
              File_error=2;
 | 
						|
              break;
 | 
						|
            }
 | 
						|
 | 
						|
            // Si ce n'est pas un octet de reconnaissance, c'est un pixel brut
 | 
						|
            if ( (temp_byte!=header.Recog1) && (temp_byte!=header.Recog2) )
 | 
						|
            {
 | 
						|
              Set_pixel(context, Compteur_de_pixels % context->Width,
 | 
						|
                                  Compteur_de_pixels / context->Width,
 | 
						|
                                  temp_byte);
 | 
						|
              Compteur_de_donnees_packees++;
 | 
						|
              Compteur_de_pixels++;
 | 
						|
            }
 | 
						|
            else // Sinon, On regarde si on va décompacter un...
 | 
						|
            { // ... nombre de pixels tenant sur un byte
 | 
						|
                if (temp_byte==header.Recog1)
 | 
						|
                {
 | 
						|
                  if(Read_byte(file, &color)!=1)
 | 
						|
                {
 | 
						|
                    File_error=2;
 | 
						|
                    break;
 | 
						|
                }
 | 
						|
                if(Read_byte(file, &temp_byte)!=1)
 | 
						|
                {
 | 
						|
                    File_error=2;
 | 
						|
                    break;
 | 
						|
                }
 | 
						|
                for (index=0; index<temp_byte; index++)
 | 
						|
                  Set_pixel(context, (Compteur_de_pixels+index) % context->Width,
 | 
						|
                                      (Compteur_de_pixels+index) / context->Width,
 | 
						|
                                      color);
 | 
						|
                Compteur_de_pixels+=temp_byte;
 | 
						|
                Compteur_de_donnees_packees+=3;
 | 
						|
              }
 | 
						|
              else // ... nombre de pixels tenant sur un word
 | 
						|
              {
 | 
						|
                if(Read_byte(file, &color)!=1)
 | 
						|
                {
 | 
						|
                    File_error=2;
 | 
						|
                    break;
 | 
						|
        }
 | 
						|
                Read_word_be(file, &len);
 | 
						|
                for (index=0; index<len; index++)
 | 
						|
                  Set_pixel(context, (Compteur_de_pixels+index) % context->Width,
 | 
						|
                                      (Compteur_de_pixels+index) / context->Width,
 | 
						|
                                      color);
 | 
						|
                Compteur_de_pixels+=len;
 | 
						|
                Compteur_de_donnees_packees+=4;
 | 
						|
              }
 | 
						|
            }
 | 
						|
          }
 | 
						|
        }
 | 
						|
      }
 | 
						|
      /*Close_lecture();*/
 | 
						|
    }
 | 
						|
    else // Lecture header impossible: Error ne modifiant pas l'image
 | 
						|
      File_error=1;
 | 
						|
 | 
						|
    fclose(file);
 | 
						|
  }
 | 
						|
  else // Ouv. fichier impossible: Error ne modifiant pas l'image
 | 
						|
    File_error=1;
 | 
						|
}
 | 
						|
 | 
						|
 | 
						|
// -- Sauver un fichier au format PKM ---------------------------------------
 | 
						|
 | 
						|
  // Trouver quels sont les octets de reconnaissance
 | 
						|
  void Find_recog(byte * recog1, byte * recog2)
 | 
						|
  {
 | 
						|
    dword Find_recon[256]; // Table d'utilisation de couleurs
 | 
						|
    byte  best;   // Meilleure couleur pour recon (recon1 puis recon2)
 | 
						|
    dword NBest;  // Nombre d'occurences de cette couleur
 | 
						|
    word  index;
 | 
						|
 | 
						|
 | 
						|
    // On commence par compter l'utilisation de chaque couleurs
 | 
						|
    Count_used_colors(Find_recon);
 | 
						|
 | 
						|
    // Ensuite recog1 devient celle la moins utilisée de celles-ci
 | 
						|
    *recog1=0;
 | 
						|
    best=1;
 | 
						|
    NBest=INT_MAX; // Une même couleur ne pourra jamais être utilisée 1M de fois.
 | 
						|
    for (index=1;index<=255;index++)
 | 
						|
      if (Find_recon[index]<NBest)
 | 
						|
      {
 | 
						|
        best=index;
 | 
						|
        NBest=Find_recon[index];
 | 
						|
      }
 | 
						|
    *recog1=best;
 | 
						|
 | 
						|
    // Enfin recog2 devient la 2ème moins utilisée
 | 
						|
    *recog2=0;
 | 
						|
    best=0;
 | 
						|
    NBest=INT_MAX;
 | 
						|
    for (index=0;index<=255;index++)
 | 
						|
      if ( (Find_recon[index]<NBest) && (index!=*recog1) )
 | 
						|
      {
 | 
						|
        best=index;
 | 
						|
        NBest=Find_recon[index];
 | 
						|
      }
 | 
						|
    *recog2=best;
 | 
						|
  }
 | 
						|
 | 
						|
 | 
						|
void Save_PKM(T_IO_Context * context)
 | 
						|
{
 | 
						|
  FILE *file;
 | 
						|
  T_PKM_Header header;
 | 
						|
  dword Compteur_de_pixels;
 | 
						|
  dword image_size;
 | 
						|
  word  repetitions;
 | 
						|
  byte  last_color;
 | 
						|
  byte  pixel_value;
 | 
						|
  size_t comment_size;
 | 
						|
 | 
						|
 | 
						|
 | 
						|
  // Construction du header
 | 
						|
  memcpy(header.Ident,"PKM",3);
 | 
						|
  header.Method=0;
 | 
						|
  Find_recog(&header.Recog1,&header.Recog2);
 | 
						|
  header.Width=context->Width;
 | 
						|
  header.Height=context->Height;
 | 
						|
  memcpy(header.Palette,context->Palette,sizeof(T_Palette));
 | 
						|
  Palette_256_to_64(header.Palette);
 | 
						|
 | 
						|
  // Calcul de la taille du Post-header
 | 
						|
  header.Jump=9; // 6 pour les dimensions de l'ecran + 3 pour la back-color
 | 
						|
  comment_size=strlen(context->Comment);
 | 
						|
  if (comment_size > 255) comment_size = 255;
 | 
						|
  if (comment_size)
 | 
						|
    header.Jump+=(word)comment_size+2;
 | 
						|
 | 
						|
 | 
						|
  File_error=0;
 | 
						|
 | 
						|
  // Ouverture du fichier
 | 
						|
  if ((file=Open_file_write(context)))
 | 
						|
  {
 | 
						|
    setvbuf(file, NULL, _IOFBF, 64*1024);
 | 
						|
 | 
						|
    // Ecriture du header
 | 
						|
    if (Write_bytes(file,&header.Ident,3) &&
 | 
						|
        Write_byte(file,header.Method) &&
 | 
						|
        Write_byte(file,header.Recog1) &&
 | 
						|
        Write_byte(file,header.Recog2) &&
 | 
						|
        Write_word_le(file,header.Width) &&
 | 
						|
        Write_word_le(file,header.Height) &&
 | 
						|
        Write_bytes(file,&header.Palette,sizeof(T_Palette)) &&
 | 
						|
        Write_word_le(file,header.Jump))
 | 
						|
    {
 | 
						|
 | 
						|
      // Ecriture du commentaire
 | 
						|
      // (Compteur_de_pixels est utilisé ici comme simple index de comptage)
 | 
						|
      if (comment_size > 0)
 | 
						|
      {
 | 
						|
        Write_one_byte(file,0);
 | 
						|
        Write_one_byte(file,(byte)comment_size);
 | 
						|
        for (Compteur_de_pixels=0; Compteur_de_pixels<comment_size; Compteur_de_pixels++)
 | 
						|
          Write_one_byte(file,context->Comment[Compteur_de_pixels]);
 | 
						|
      }
 | 
						|
      // Ecriture des dimensions de l'écran
 | 
						|
      Write_one_byte(file,1);
 | 
						|
      Write_one_byte(file,4);
 | 
						|
      Write_one_byte(file,Screen_width&0xFF);
 | 
						|
      Write_one_byte(file,Screen_width>>8);
 | 
						|
      Write_one_byte(file,Screen_height&0xFF);
 | 
						|
      Write_one_byte(file,Screen_height>>8);
 | 
						|
      // Ecriture de la back-color
 | 
						|
      Write_one_byte(file,2);
 | 
						|
      Write_one_byte(file,1);
 | 
						|
      Write_one_byte(file,Back_color);
 | 
						|
 | 
						|
      // Routine de compression PKM de l'image
 | 
						|
      image_size=(dword)(context->Width*context->Height);
 | 
						|
      Compteur_de_pixels=0;
 | 
						|
      pixel_value=Get_pixel(context, 0,0);
 | 
						|
 | 
						|
      while ( (Compteur_de_pixels<image_size) && (!File_error) )
 | 
						|
      {
 | 
						|
        Compteur_de_pixels++;
 | 
						|
        repetitions=1;
 | 
						|
        last_color=pixel_value;
 | 
						|
        if(Compteur_de_pixels<image_size)
 | 
						|
        {
 | 
						|
          pixel_value=Get_pixel(context, Compteur_de_pixels % context->Width,Compteur_de_pixels / context->Width);
 | 
						|
        }
 | 
						|
        while ( (pixel_value==last_color)
 | 
						|
             && (Compteur_de_pixels<image_size)
 | 
						|
             && (repetitions<65535) )
 | 
						|
        {
 | 
						|
          Compteur_de_pixels++;
 | 
						|
          repetitions++;
 | 
						|
          if(Compteur_de_pixels>=image_size) break;
 | 
						|
          pixel_value=Get_pixel(context, Compteur_de_pixels % context->Width,Compteur_de_pixels / context->Width);
 | 
						|
        }
 | 
						|
 | 
						|
        if ( (last_color!=header.Recog1) && (last_color!=header.Recog2) )
 | 
						|
        {
 | 
						|
          if (repetitions==1)
 | 
						|
            Write_one_byte(file,last_color);
 | 
						|
          else
 | 
						|
          if (repetitions==2)
 | 
						|
          {
 | 
						|
            Write_one_byte(file,last_color);
 | 
						|
            Write_one_byte(file,last_color);
 | 
						|
          }
 | 
						|
          else
 | 
						|
          if ( (repetitions>2) && (repetitions<256) )
 | 
						|
          { // RECON1/couleur/nombre
 | 
						|
            Write_one_byte(file,header.Recog1);
 | 
						|
            Write_one_byte(file,last_color);
 | 
						|
            Write_one_byte(file,repetitions&0xFF);
 | 
						|
          }
 | 
						|
          else
 | 
						|
          if (repetitions>=256)
 | 
						|
          { // RECON2/couleur/hi(nombre)/lo(nombre)
 | 
						|
            Write_one_byte(file,header.Recog2);
 | 
						|
            Write_one_byte(file,last_color);
 | 
						|
            Write_one_byte(file,repetitions>>8);
 | 
						|
            Write_one_byte(file,repetitions&0xFF);
 | 
						|
          }
 | 
						|
        }
 | 
						|
        else
 | 
						|
        {
 | 
						|
          if (repetitions<256)
 | 
						|
          {
 | 
						|
            Write_one_byte(file,header.Recog1);
 | 
						|
            Write_one_byte(file,last_color);
 | 
						|
            Write_one_byte(file,repetitions&0xFF);
 | 
						|
          }
 | 
						|
          else
 | 
						|
          {
 | 
						|
            Write_one_byte(file,header.Recog2);
 | 
						|
            Write_one_byte(file,last_color);
 | 
						|
            Write_one_byte(file,repetitions>>8);
 | 
						|
            Write_one_byte(file,repetitions&0xFF);
 | 
						|
          }
 | 
						|
        }
 | 
						|
      }
 | 
						|
    }
 | 
						|
    else
 | 
						|
      File_error=1;
 | 
						|
    fclose(file);
 | 
						|
  }
 | 
						|
  else
 | 
						|
  {
 | 
						|
    File_error=1;
 | 
						|
    fclose(file);
 | 
						|
  }
 | 
						|
  //   S'il y a eu une erreur de sauvegarde, on ne va tout de même pas laisser
 | 
						|
  // ce fichier pourri traîner... Ca fait pas propre.
 | 
						|
  if (File_error)
 | 
						|
    Remove_file(context);
 | 
						|
}
 | 
						|
 | 
						|
 | 
						|
//////////////////////////////////// CEL ////////////////////////////////////
 | 
						|
typedef struct
 | 
						|
{
 | 
						|
  word Width;              // width de l'image
 | 
						|
  word Height;             // height de l'image
 | 
						|
} T_CEL_Header1;
 | 
						|
 | 
						|
typedef struct
 | 
						|
{
 | 
						|
  byte Signature[4];           // Signature du format
 | 
						|
  byte Kind;               // Type de fichier ($10=PALette $20=BitMaP)
 | 
						|
  byte Nb_bits;             // Nombre de bits
 | 
						|
  word Filler1;            // ???
 | 
						|
  word Width;            // width de l'image
 | 
						|
  word Height;            // height de l'image
 | 
						|
  word X_offset;         // Offset en X de l'image
 | 
						|
  word Y_offset;         // Offset en Y de l'image
 | 
						|
  byte Filler2[16];        // ???
 | 
						|
} T_CEL_Header2;
 | 
						|
 | 
						|
// -- Tester si un fichier est au format CEL --------------------------------
 | 
						|
 | 
						|
void Test_CEL(T_IO_Context * context, FILE * file)
 | 
						|
{
 | 
						|
  int  size;
 | 
						|
  T_CEL_Header1 header1;
 | 
						|
  T_CEL_Header2 header2;
 | 
						|
  int file_size;
 | 
						|
 | 
						|
  (void)context;
 | 
						|
  File_error=0;
 | 
						|
 | 
						|
  file_size = File_length_file(file);
 | 
						|
  if (Read_word_le(file,&header1.Width) &&
 | 
						|
      Read_word_le(file,&header1.Height) )
 | 
						|
  {
 | 
						|
      //   Vu que ce header n'a pas de signature, il va falloir tester la
 | 
						|
      // cohérence de la dimension de l'image avec celle du fichier.
 | 
						|
 | 
						|
      size=file_size-4;
 | 
						|
      if ( (!size) || ( (((header1.Width+1)>>1)*header1.Height)!=size ) )
 | 
						|
      {
 | 
						|
        // Tentative de reconnaissance de la signature des nouveaux fichiers
 | 
						|
 | 
						|
        fseek(file,0,SEEK_SET);
 | 
						|
        if (Read_bytes(file,&header2.Signature,4) &&
 | 
						|
            !memcmp(header2.Signature,"KiSS",4) &&
 | 
						|
            Read_byte(file,&header2.Kind) &&
 | 
						|
            (header2.Kind==0x20) &&
 | 
						|
            Read_byte(file,&header2.Nb_bits) &&
 | 
						|
            Read_word_le(file,&header2.Filler1) &&
 | 
						|
            Read_word_le(file,&header2.Width) &&
 | 
						|
            Read_word_le(file,&header2.Height) &&
 | 
						|
            Read_word_le(file,&header2.X_offset) &&
 | 
						|
            Read_word_le(file,&header2.Y_offset) &&
 | 
						|
            Read_bytes(file,&header2.Filler2,16))
 | 
						|
        {
 | 
						|
          // ok
 | 
						|
        }
 | 
						|
        else
 | 
						|
          File_error=1;
 | 
						|
      }
 | 
						|
      else
 | 
						|
        File_error=1;
 | 
						|
  }
 | 
						|
  else
 | 
						|
  {
 | 
						|
    File_error=1;
 | 
						|
  }
 | 
						|
}
 | 
						|
 | 
						|
 | 
						|
// -- Lire un fichier au format CEL -----------------------------------------
 | 
						|
 | 
						|
void Load_CEL(T_IO_Context * context)
 | 
						|
{
 | 
						|
  FILE *file;
 | 
						|
  T_CEL_Header1 header1;
 | 
						|
  T_CEL_Header2 header2;
 | 
						|
  short x_pos;
 | 
						|
  short y_pos;
 | 
						|
  byte  last_byte=0;
 | 
						|
  long  file_size;
 | 
						|
  const long int header_size = 4;
 | 
						|
 | 
						|
  File_error=0;
 | 
						|
  if ((file=Open_file_read(context)))
 | 
						|
  {
 | 
						|
    if (Read_word_le(file,&(header1.Width))
 | 
						|
    &&  Read_word_le(file,&(header1.Height)))
 | 
						|
    {
 | 
						|
      file_size=File_length_file(file);
 | 
						|
      if ( (file_size>header_size)
 | 
						|
        && ( (((header1.Width+1)>>1)*header1.Height)==(file_size-header_size) ) )
 | 
						|
      {
 | 
						|
        // Chargement d'un fichier CEL sans signature (vieux fichiers)
 | 
						|
        context->Width=header1.Width;
 | 
						|
        context->Height=header1.Height;
 | 
						|
        Original_screen_X=context->Width;
 | 
						|
        Original_screen_Y=context->Height;
 | 
						|
        Pre_load(context, context->Width,context->Height,file_size,FORMAT_CEL,PIXEL_SIMPLE,0);
 | 
						|
        if (File_error==0)
 | 
						|
        {
 | 
						|
          // Chargement de l'image
 | 
						|
          /*Init_lecture();*/
 | 
						|
          for (y_pos=0;((y_pos<context->Height) && (!File_error));y_pos++)
 | 
						|
            for (x_pos=0;((x_pos<context->Width) && (!File_error));x_pos++)
 | 
						|
              if ((x_pos & 1)==0)
 | 
						|
              {
 | 
						|
                if(Read_byte(file,&last_byte)!=1) File_error = 2;
 | 
						|
                Set_pixel(context, x_pos,y_pos,(last_byte >> 4));
 | 
						|
              }
 | 
						|
              else
 | 
						|
                Set_pixel(context, x_pos,y_pos,(last_byte & 15));
 | 
						|
          /*Close_lecture();*/
 | 
						|
        }
 | 
						|
      }
 | 
						|
      else
 | 
						|
      {
 | 
						|
        // On réessaye avec le nouveau format
 | 
						|
 | 
						|
        fseek(file,0,SEEK_SET);
 | 
						|
        if (Read_bytes(file,header2.Signature,4)
 | 
						|
        && Read_byte(file,&(header2.Kind))
 | 
						|
        && Read_byte(file,&(header2.Nb_bits))
 | 
						|
        && Read_word_le(file,&(header2.Filler1))
 | 
						|
        && Read_word_le(file,&(header2.Width))
 | 
						|
        && Read_word_le(file,&(header2.Height))
 | 
						|
        && Read_word_le(file,&(header2.X_offset))
 | 
						|
        && Read_word_le(file,&(header2.Y_offset))
 | 
						|
        && Read_bytes(file,header2.Filler2,16)
 | 
						|
        )
 | 
						|
        {
 | 
						|
          // Chargement d'un fichier CEL avec signature (nouveaux fichiers)
 | 
						|
 | 
						|
          context->Width=header2.Width+header2.X_offset;
 | 
						|
          context->Height=header2.Height+header2.Y_offset;
 | 
						|
          Original_screen_X=context->Width;
 | 
						|
          Original_screen_Y=context->Height;
 | 
						|
          Pre_load(context, context->Width,context->Height,file_size,FORMAT_CEL,PIXEL_SIMPLE,0);
 | 
						|
          if (File_error==0)
 | 
						|
          {
 | 
						|
            // Chargement de l'image
 | 
						|
            /*Init_lecture();*/
 | 
						|
 | 
						|
            if (!File_error)
 | 
						|
            {
 | 
						|
              // Effacement du décalage
 | 
						|
              for (y_pos=0;y_pos<header2.Y_offset;y_pos++)
 | 
						|
                for (x_pos=0;x_pos<context->Width;x_pos++)
 | 
						|
                  Set_pixel(context, x_pos,y_pos,0);
 | 
						|
              for (y_pos=header2.Y_offset;y_pos<context->Height;y_pos++)
 | 
						|
                for (x_pos=0;x_pos<header2.X_offset;x_pos++)
 | 
						|
                  Set_pixel(context, x_pos,y_pos,0);
 | 
						|
 | 
						|
              switch(header2.Nb_bits)
 | 
						|
              {
 | 
						|
                case 4:
 | 
						|
                  for (y_pos=0;((y_pos<header2.Height) && (!File_error));y_pos++)
 | 
						|
                    for (x_pos=0;((x_pos<header2.Width) && (!File_error));x_pos++)
 | 
						|
                      if ((x_pos & 1)==0)
 | 
						|
                      {
 | 
						|
                        if(Read_byte(file,&last_byte)!=1) File_error=2;
 | 
						|
                        Set_pixel(context, x_pos+header2.X_offset,y_pos+header2.Y_offset,(last_byte >> 4));
 | 
						|
                      }
 | 
						|
                      else
 | 
						|
                        Set_pixel(context, x_pos+header2.X_offset,y_pos+header2.Y_offset,(last_byte & 15));
 | 
						|
                  break;
 | 
						|
 | 
						|
                case 8:
 | 
						|
                  for (y_pos=0;((y_pos<header2.Height) && (!File_error));y_pos++)
 | 
						|
                    for (x_pos=0;((x_pos<header2.Width) && (!File_error));x_pos++)
 | 
						|
                    {
 | 
						|
                      byte byte_read;
 | 
						|
                      if(Read_byte(file,&byte_read)!=1) File_error = 2;
 | 
						|
                      Set_pixel(context, x_pos+header2.X_offset,y_pos+header2.Y_offset,byte_read);
 | 
						|
                      }
 | 
						|
                  break;
 | 
						|
 | 
						|
                default:
 | 
						|
                  File_error=1;
 | 
						|
              }
 | 
						|
            }
 | 
						|
            /*Close_lecture();*/
 | 
						|
          }
 | 
						|
        }
 | 
						|
        else
 | 
						|
          File_error=1;
 | 
						|
      }
 | 
						|
      fclose(file);
 | 
						|
    }
 | 
						|
    else
 | 
						|
      File_error=1;
 | 
						|
  }
 | 
						|
  else
 | 
						|
    File_error=1;
 | 
						|
}
 | 
						|
 | 
						|
 | 
						|
// -- Ecrire un fichier au format CEL ---------------------------------------
 | 
						|
 | 
						|
void Save_CEL(T_IO_Context * context)
 | 
						|
{
 | 
						|
  FILE *file;
 | 
						|
  T_CEL_Header1 header1;
 | 
						|
  T_CEL_Header2 header2;
 | 
						|
  short x_pos;
 | 
						|
  short y_pos;
 | 
						|
  byte  last_byte=0;
 | 
						|
  dword color_usage[256]; // Table d'utilisation de couleurs
 | 
						|
 | 
						|
 | 
						|
  // On commence par compter l'utilisation de chaque couleurs
 | 
						|
  Count_used_colors(color_usage);
 | 
						|
 | 
						|
  File_error=0;
 | 
						|
  if ((file=Open_file_write(context)))
 | 
						|
  {
 | 
						|
    setvbuf(file, NULL, _IOFBF, 64*1024);
 | 
						|
 | 
						|
    // On regarde si des couleurs >16 sont utilisées dans l'image
 | 
						|
    for (x_pos=16;((x_pos<256) && (!color_usage[x_pos]));x_pos++);
 | 
						|
 | 
						|
    if (x_pos==256)
 | 
						|
    {
 | 
						|
      // Cas d'une image 16 couleurs (écriture à l'ancien format)
 | 
						|
 | 
						|
      header1.Width =context->Width;
 | 
						|
      header1.Height=context->Height;
 | 
						|
 | 
						|
      if (Write_word_le(file,header1.Width)
 | 
						|
      && Write_word_le(file,header1.Height)
 | 
						|
      )
 | 
						|
      {
 | 
						|
        // Sauvegarde de l'image
 | 
						|
        for (y_pos=0;((y_pos<context->Height) && (!File_error));y_pos++)
 | 
						|
        {
 | 
						|
          for (x_pos=0;((x_pos<context->Width) && (!File_error));x_pos++)
 | 
						|
            if ((x_pos & 1)==0)
 | 
						|
              last_byte=(Get_pixel(context, x_pos,y_pos) << 4);
 | 
						|
            else
 | 
						|
            {
 | 
						|
              last_byte=last_byte | (Get_pixel(context, x_pos,y_pos) & 15);
 | 
						|
              Write_one_byte(file,last_byte);
 | 
						|
            }
 | 
						|
 | 
						|
          if ((x_pos & 1)==1)
 | 
						|
            Write_one_byte(file,last_byte);
 | 
						|
        }
 | 
						|
      }
 | 
						|
      else
 | 
						|
        File_error=1;
 | 
						|
      fclose(file);
 | 
						|
    }
 | 
						|
    else
 | 
						|
    {
 | 
						|
      // Cas d'une image 256 couleurs (écriture au nouveau format)
 | 
						|
 | 
						|
      // Recherche du décalage
 | 
						|
      for (y_pos=0;y_pos<context->Height;y_pos++)
 | 
						|
      {
 | 
						|
        for (x_pos=0;x_pos<context->Width;x_pos++)
 | 
						|
          if (Get_pixel(context, x_pos,y_pos)!=0)
 | 
						|
            break;
 | 
						|
        if (Get_pixel(context, x_pos,y_pos)!=0)
 | 
						|
          break;
 | 
						|
      }
 | 
						|
      header2.Y_offset=y_pos;
 | 
						|
      for (x_pos=0;x_pos<context->Width;x_pos++)
 | 
						|
      {
 | 
						|
        for (y_pos=0;y_pos<context->Height;y_pos++)
 | 
						|
          if (Get_pixel(context, x_pos,y_pos)!=0)
 | 
						|
            break;
 | 
						|
        if (Get_pixel(context, x_pos,y_pos)!=0)
 | 
						|
          break;
 | 
						|
      }
 | 
						|
      header2.X_offset=x_pos;
 | 
						|
 | 
						|
      memcpy(header2.Signature,"KiSS",4); // Initialisation de la signature
 | 
						|
      header2.Kind=0x20;              // Initialisation du type (BitMaP)
 | 
						|
      header2.Nb_bits=8;               // Initialisation du nombre de bits
 | 
						|
      header2.Filler1=0;              // Initialisation du filler 1 (?)
 | 
						|
      header2.Width=context->Width-header2.X_offset; // Initialisation de la largeur
 | 
						|
      header2.Height=context->Height-header2.Y_offset; // Initialisation de la hauteur
 | 
						|
      for (x_pos=0;x_pos<16;x_pos++)  // Initialisation du filler 2 (?)
 | 
						|
        header2.Filler2[x_pos]=0;
 | 
						|
 | 
						|
      if (Write_bytes(file,header2.Signature,4)
 | 
						|
      && Write_byte(file,header2.Kind)
 | 
						|
      && Write_byte(file,header2.Nb_bits)
 | 
						|
      && Write_word_le(file,header2.Filler1)
 | 
						|
      && Write_word_le(file,header2.Width)
 | 
						|
      && Write_word_le(file,header2.Height)
 | 
						|
      && Write_word_le(file,header2.X_offset)
 | 
						|
      && Write_word_le(file,header2.Y_offset)
 | 
						|
      && Write_bytes(file,header2.Filler2,14)
 | 
						|
      )
 | 
						|
      {
 | 
						|
        // Sauvegarde de l'image
 | 
						|
        for (y_pos=0;((y_pos<header2.Height) && (!File_error));y_pos++)
 | 
						|
          for (x_pos=0;((x_pos<header2.Width) && (!File_error));x_pos++)
 | 
						|
            Write_one_byte(file,Get_pixel(context, x_pos+header2.X_offset,y_pos+header2.Y_offset));
 | 
						|
      }
 | 
						|
      else
 | 
						|
        File_error=1;
 | 
						|
      fclose(file);
 | 
						|
    }
 | 
						|
 | 
						|
    if (File_error)
 | 
						|
      Remove_file(context);
 | 
						|
  }
 | 
						|
  else
 | 
						|
    File_error=1;
 | 
						|
}
 | 
						|
 | 
						|
 | 
						|
//////////////////////////////////// KCF ////////////////////////////////////
 | 
						|
typedef struct
 | 
						|
{
 | 
						|
  struct
 | 
						|
  {
 | 
						|
    struct
 | 
						|
    {
 | 
						|
      byte Byte1;
 | 
						|
      byte Byte2;
 | 
						|
    } color[16];
 | 
						|
  } Palette[10];
 | 
						|
} T_KCF_Header;
 | 
						|
 | 
						|
// -- Tester si un fichier est au format KCF --------------------------------
 | 
						|
 | 
						|
void Test_KCF(T_IO_Context * context, FILE * file)
 | 
						|
{
 | 
						|
  T_KCF_Header header1;
 | 
						|
  T_CEL_Header2 header2;
 | 
						|
  int pal_index;
 | 
						|
  int color_index;
 | 
						|
 | 
						|
  (void)context;
 | 
						|
  File_error=0;
 | 
						|
    if (File_length_file(file)==320)
 | 
						|
    {
 | 
						|
      for (pal_index=0;pal_index<10 && !File_error;pal_index++)
 | 
						|
        for (color_index=0;color_index<16 && !File_error;color_index++)
 | 
						|
          if (!Read_byte(file,&header1.Palette[pal_index].color[color_index].Byte1) ||
 | 
						|
              !Read_byte(file,&header1.Palette[pal_index].color[color_index].Byte2))
 | 
						|
            File_error=1;
 | 
						|
      // On vérifie une propriété de la structure de palette:
 | 
						|
      for (pal_index=0;pal_index<10;pal_index++)
 | 
						|
        for (color_index=0;color_index<16;color_index++)
 | 
						|
          if ((header1.Palette[pal_index].color[color_index].Byte2>>4)!=0)
 | 
						|
            File_error=1;
 | 
						|
    }
 | 
						|
    else
 | 
						|
    {
 | 
						|
      if (Read_bytes(file,header2.Signature,4)
 | 
						|
        && Read_byte(file,&(header2.Kind))
 | 
						|
        && Read_byte(file,&(header2.Nb_bits))
 | 
						|
        && Read_word_le(file,&(header2.Filler1))
 | 
						|
        && Read_word_le(file,&(header2.Width))
 | 
						|
        && Read_word_le(file,&(header2.Height))
 | 
						|
        && Read_word_le(file,&(header2.X_offset))
 | 
						|
        && Read_word_le(file,&(header2.Y_offset))
 | 
						|
        && Read_bytes(file,header2.Filler2,14)
 | 
						|
        )
 | 
						|
      {
 | 
						|
        if (memcmp(header2.Signature,"KiSS",4)==0)
 | 
						|
        {
 | 
						|
          if (header2.Kind!=0x10)
 | 
						|
            File_error=1;
 | 
						|
        }
 | 
						|
        else
 | 
						|
          File_error=1;
 | 
						|
      }
 | 
						|
      else
 | 
						|
        File_error=1;
 | 
						|
    }
 | 
						|
}
 | 
						|
 | 
						|
 | 
						|
// -- Lire un fichier au format KCF -----------------------------------------
 | 
						|
 | 
						|
void Load_KCF(T_IO_Context * context)
 | 
						|
{
 | 
						|
  FILE *file;
 | 
						|
  T_KCF_Header header1;
 | 
						|
  T_CEL_Header2 header2;
 | 
						|
  byte bytes[3];
 | 
						|
  int pal_index;
 | 
						|
  int color_index;
 | 
						|
  int index;
 | 
						|
  long  file_size;
 | 
						|
 | 
						|
 | 
						|
  File_error=0;
 | 
						|
  if ((file=Open_file_read(context)))
 | 
						|
  {
 | 
						|
    file_size=File_length_file(file);
 | 
						|
    if (file_size==320)
 | 
						|
    {
 | 
						|
      // Fichier KCF à l'ancien format
 | 
						|
      for (pal_index=0;pal_index<10 && !File_error;pal_index++)
 | 
						|
        for (color_index=0;color_index<16 && !File_error;color_index++)
 | 
						|
          if (!Read_byte(file,&header1.Palette[pal_index].color[color_index].Byte1) ||
 | 
						|
              !Read_byte(file,&header1.Palette[pal_index].color[color_index].Byte2))
 | 
						|
            File_error=1;
 | 
						|
 | 
						|
      if (!File_error)
 | 
						|
      {
 | 
						|
        // Pre_load(context, ?); // Pas possible... pas d'image...
 | 
						|
 | 
						|
        if (Config.Clear_palette)
 | 
						|
          memset(context->Palette,0,sizeof(T_Palette));
 | 
						|
 | 
						|
        // Chargement de la palette
 | 
						|
        for (pal_index=0;pal_index<10;pal_index++)
 | 
						|
          for (color_index=0;color_index<16;color_index++)
 | 
						|
          {
 | 
						|
            index=16+(pal_index*16)+color_index;
 | 
						|
            context->Palette[index].R=((header1.Palette[pal_index].color[color_index].Byte1 >> 4) << 4);
 | 
						|
            context->Palette[index].B=((header1.Palette[pal_index].color[color_index].Byte1 & 15) << 4);
 | 
						|
            context->Palette[index].G=((header1.Palette[pal_index].color[color_index].Byte2 & 15) << 4);
 | 
						|
          }
 | 
						|
 | 
						|
        for (index=0;index<16;index++)
 | 
						|
        {
 | 
						|
          context->Palette[index].R=context->Palette[index+16].R;
 | 
						|
          context->Palette[index].G=context->Palette[index+16].G;
 | 
						|
          context->Palette[index].B=context->Palette[index+16].B;
 | 
						|
        }
 | 
						|
 | 
						|
      }
 | 
						|
      else
 | 
						|
        File_error=1;
 | 
						|
    }
 | 
						|
    else
 | 
						|
    {
 | 
						|
      // Fichier KCF au nouveau format
 | 
						|
 | 
						|
      if (Read_bytes(file,header2.Signature,4)
 | 
						|
        && Read_byte(file,&(header2.Kind))
 | 
						|
        && Read_byte(file,&(header2.Nb_bits))
 | 
						|
        && Read_word_le(file,&(header2.Filler1))
 | 
						|
        && Read_word_le(file,&(header2.Width))
 | 
						|
        && Read_word_le(file,&(header2.Height))
 | 
						|
        && Read_word_le(file,&(header2.X_offset))
 | 
						|
        && Read_word_le(file,&(header2.Y_offset))
 | 
						|
        && Read_bytes(file,header2.Filler2,14)
 | 
						|
        )
 | 
						|
      {
 | 
						|
        // Pre_load(context, ?); // Pas possible... pas d'image...
 | 
						|
 | 
						|
        index=(header2.Nb_bits==12)?16:0;
 | 
						|
        for (pal_index=0;pal_index<header2.Height;pal_index++)
 | 
						|
        {
 | 
						|
           // Pour chaque palette
 | 
						|
 | 
						|
           for (color_index=0;color_index<header2.Width;color_index++)
 | 
						|
           {
 | 
						|
             // Pour chaque couleur
 | 
						|
 | 
						|
             switch(header2.Nb_bits)
 | 
						|
             {
 | 
						|
               case 12: // RRRR BBBB | 0000 VVVV
 | 
						|
                 Read_bytes(file,bytes,2);
 | 
						|
                 context->Palette[index].R=(bytes[0] >> 4) << 4;
 | 
						|
                 context->Palette[index].B=(bytes[0] & 15) << 4;
 | 
						|
                 context->Palette[index].G=(bytes[1] & 15) << 4;
 | 
						|
                 break;
 | 
						|
 | 
						|
               case 24: // RRRR RRRR | VVVV VVVV | BBBB BBBB
 | 
						|
                 Read_bytes(file,bytes,3);
 | 
						|
                 context->Palette[index].R=bytes[0];
 | 
						|
                 context->Palette[index].G=bytes[1];
 | 
						|
                 context->Palette[index].B=bytes[2];
 | 
						|
             }
 | 
						|
 | 
						|
             index++;
 | 
						|
           }
 | 
						|
        }
 | 
						|
 | 
						|
        if (header2.Nb_bits==12)
 | 
						|
          for (index=0;index<16;index++)
 | 
						|
          {
 | 
						|
            context->Palette[index].R=context->Palette[index+16].R;
 | 
						|
            context->Palette[index].G=context->Palette[index+16].G;
 | 
						|
            context->Palette[index].B=context->Palette[index+16].B;
 | 
						|
          }
 | 
						|
 | 
						|
      }
 | 
						|
      else
 | 
						|
        File_error=1;
 | 
						|
    }
 | 
						|
    fclose(file);
 | 
						|
  }
 | 
						|
  else
 | 
						|
    File_error=1;
 | 
						|
}
 | 
						|
 | 
						|
 | 
						|
// -- Ecrire un fichier au format KCF ---------------------------------------
 | 
						|
 | 
						|
void Save_KCF(T_IO_Context * context)
 | 
						|
{
 | 
						|
  FILE *file;
 | 
						|
  T_KCF_Header header1;
 | 
						|
  T_CEL_Header2 header2;
 | 
						|
  byte bytes[3];
 | 
						|
  int pal_index;
 | 
						|
  int color_index;
 | 
						|
  int index;
 | 
						|
  dword color_usage[256]; // Table d'utilisation de couleurs
 | 
						|
 | 
						|
  // On commence par compter l'utilisation de chaque couleurs
 | 
						|
  Count_used_colors(color_usage);
 | 
						|
 | 
						|
  File_error=0;
 | 
						|
  if ((file=Open_file_write(context)))
 | 
						|
  {
 | 
						|
    setvbuf(file, NULL, _IOFBF, 64*1024);
 | 
						|
    // Sauvegarde de la palette
 | 
						|
 | 
						|
    // On regarde si des couleurs >16 sont utilisées dans l'image
 | 
						|
    for (index=16;((index<256) && (!color_usage[index]));index++);
 | 
						|
 | 
						|
    if (index==256)
 | 
						|
    {
 | 
						|
      // Cas d'une image 16 couleurs (écriture à l'ancien format)
 | 
						|
 | 
						|
      for (pal_index=0;pal_index<10;pal_index++)
 | 
						|
        for (color_index=0;color_index<16;color_index++)
 | 
						|
        {
 | 
						|
          index=16+(pal_index*16)+color_index;
 | 
						|
          header1.Palette[pal_index].color[color_index].Byte1=((context->Palette[index].R>>4)<<4) | (context->Palette[index].B>>4);
 | 
						|
          header1.Palette[pal_index].color[color_index].Byte2=context->Palette[index].G>>4;
 | 
						|
        }
 | 
						|
 | 
						|
      // Write all
 | 
						|
      for (pal_index=0;pal_index<10 && !File_error;pal_index++)
 | 
						|
        for (color_index=0;color_index<16 && !File_error;color_index++)
 | 
						|
          if (!Write_byte(file,header1.Palette[pal_index].color[color_index].Byte1) ||
 | 
						|
              !Write_byte(file,header1.Palette[pal_index].color[color_index].Byte2))
 | 
						|
            File_error=1;
 | 
						|
    }
 | 
						|
    else
 | 
						|
    {
 | 
						|
      // Cas d'une image 256 couleurs (écriture au nouveau format)
 | 
						|
 | 
						|
      memcpy(header2.Signature,"KiSS",4); // Initialisation de la signature
 | 
						|
      header2.Kind=0x10;              // Initialisation du type (PALette)
 | 
						|
      header2.Nb_bits=24;              // Initialisation du nombre de bits
 | 
						|
      header2.Filler1=0;              // Initialisation du filler 1 (?)
 | 
						|
      header2.Width=256;            // Initialisation du nombre de couleurs
 | 
						|
      header2.Height=1;              // Initialisation du nombre de palettes
 | 
						|
      header2.X_offset=0;           // Initialisation du décalage X
 | 
						|
      header2.Y_offset=0;           // Initialisation du décalage Y
 | 
						|
      for (index=0;index<16;index++) // Initialisation du filler 2 (?)
 | 
						|
        header2.Filler2[index]=0;
 | 
						|
 | 
						|
      if (!Write_bytes(file,header2.Signature,4)
 | 
						|
      || !Write_byte(file,header2.Kind)
 | 
						|
      || !Write_byte(file,header2.Nb_bits)
 | 
						|
      || !Write_word_le(file,header2.Filler1)
 | 
						|
      || !Write_word_le(file,header2.Width)
 | 
						|
      || !Write_word_le(file,header2.Height)
 | 
						|
      || !Write_word_le(file,header2.X_offset)
 | 
						|
      || !Write_word_le(file,header2.Y_offset)
 | 
						|
      || !Write_bytes(file,header2.Filler2,14)
 | 
						|
      )
 | 
						|
        File_error=1;
 | 
						|
 | 
						|
      for (index=0;(index<256) && (!File_error);index++)
 | 
						|
      {
 | 
						|
        bytes[0]=context->Palette[index].R;
 | 
						|
        bytes[1]=context->Palette[index].G;
 | 
						|
        bytes[2]=context->Palette[index].B;
 | 
						|
        if (! Write_bytes(file,bytes,3))
 | 
						|
          File_error=1;
 | 
						|
      }
 | 
						|
    }
 | 
						|
 | 
						|
    fclose(file);
 | 
						|
 | 
						|
    if (File_error)
 | 
						|
      Remove_file(context);
 | 
						|
  }
 | 
						|
  else
 | 
						|
    File_error=1;
 | 
						|
}
 | 
						|
 | 
						|
 | 
						|
/**
 | 
						|
 * @defgroup atarist Atari ST picture formats
 | 
						|
 * @ingroup loadsaveformats
 | 
						|
 *
 | 
						|
 * Support for Atari ST picture formats. The Atari ST has
 | 
						|
 * 3 video modes :
 | 
						|
 * - low res : 320x200 16 colors
 | 
						|
 * - med res : 640x200 4 colors
 | 
						|
 * - high res : 640x400 monochrome
 | 
						|
 *
 | 
						|
 * Supported formats :
 | 
						|
 * - PI1 : Degas
 | 
						|
 * - PC1 : Degas elite compressed
 | 
						|
 * - NEO : Neochrome
 | 
						|
 *
 | 
						|
 * @{
 | 
						|
 */
 | 
						|
//////////////////////////////////// PI1 ////////////////////////////////////
 | 
						|
 | 
						|
//// DECODAGE d'une partie d'IMAGE ////
 | 
						|
 | 
						|
void PI1_8b_to_16p(const byte * src,byte * dest)
 | 
						|
{
 | 
						|
  int  i;           // index du pixel à calculer
 | 
						|
  word byte_mask;      // Masque de decodage
 | 
						|
  word w0,w1,w2,w3; // Les 4 words bien ordonnés de la source
 | 
						|
 | 
						|
  byte_mask=0x8000;
 | 
						|
  w0=(((word)src[0])<<8) | src[1];
 | 
						|
  w1=(((word)src[2])<<8) | src[3];
 | 
						|
  w2=(((word)src[4])<<8) | src[5];
 | 
						|
  w3=(((word)src[6])<<8) | src[7];
 | 
						|
  for (i=0;i<16;i++)
 | 
						|
  {
 | 
						|
    // Pour décoder le pixel n°i, il faut traiter les 4 words sur leur bit
 | 
						|
    // correspondant à celui du masque
 | 
						|
 | 
						|
    dest[i]=((w0 & byte_mask)?0x01:0x00) |
 | 
						|
           ((w1 & byte_mask)?0x02:0x00) |
 | 
						|
           ((w2 & byte_mask)?0x04:0x00) |
 | 
						|
           ((w3 & byte_mask)?0x08:0x00);
 | 
						|
    byte_mask>>=1;
 | 
						|
  }
 | 
						|
}
 | 
						|
 | 
						|
void PI2_8b_to_16p(const byte * src,byte * dest)
 | 
						|
{
 | 
						|
  int  i;           // index du pixel à calculer
 | 
						|
  word mask;      // Masque de decodage
 | 
						|
  word w0,w1;
 | 
						|
 | 
						|
  w0=(((word)src[0])<<8) | src[1];
 | 
						|
  w1=(((word)src[2])<<8) | src[3];
 | 
						|
  mask=0x8000;
 | 
						|
  for (i = 0; i < 16; i++)
 | 
						|
  {
 | 
						|
    dest[i] = ((w0 & mask) ? 0x01 : 0) | ((w1 & mask) ? 0x02 : 0);
 | 
						|
    mask >>= 1;
 | 
						|
  }
 | 
						|
}
 | 
						|
 | 
						|
//// CODAGE d'une partie d'IMAGE ////
 | 
						|
 | 
						|
void PI1_16p_to_8b(byte * src,byte * dest)
 | 
						|
{
 | 
						|
  int  i;           // index du pixel à calculer
 | 
						|
  word byte_mask;      // Masque de codage
 | 
						|
  word w0,w1,w2,w3; // Les 4 words bien ordonnés de la destination
 | 
						|
 | 
						|
  byte_mask=0x8000;
 | 
						|
  w0=w1=w2=w3=0;
 | 
						|
  for (i=0;i<16;i++)
 | 
						|
  {
 | 
						|
    // Pour coder le pixel n°i, il faut modifier les 4 words sur leur bit
 | 
						|
    // correspondant à celui du masque
 | 
						|
 | 
						|
    w0|=(src[i] & 0x01)?byte_mask:0x00;
 | 
						|
    w1|=(src[i] & 0x02)?byte_mask:0x00;
 | 
						|
    w2|=(src[i] & 0x04)?byte_mask:0x00;
 | 
						|
    w3|=(src[i] & 0x08)?byte_mask:0x00;
 | 
						|
    byte_mask>>=1;
 | 
						|
  }
 | 
						|
  dest[0]=w0 >> 8;
 | 
						|
  dest[1]=w0 & 0x00FF;
 | 
						|
  dest[2]=w1 >> 8;
 | 
						|
  dest[3]=w1 & 0x00FF;
 | 
						|
  dest[4]=w2 >> 8;
 | 
						|
  dest[5]=w2 & 0x00FF;
 | 
						|
  dest[6]=w3 >> 8;
 | 
						|
  dest[7]=w3 & 0x00FF;
 | 
						|
}
 | 
						|
 | 
						|
//// DECODAGE de la PALETTE ////
 | 
						|
 | 
						|
static void PI1_decode_palette(const byte * src, T_Components * palette)
 | 
						|
{
 | 
						|
  int i;  // Numéro de la couleur traitée
 | 
						|
  word w; // Word contenant le code
 | 
						|
 | 
						|
  // Schéma d'un word =
 | 
						|
  //
 | 
						|
  //    Low        High
 | 
						|
  // VVVV RRRR | 0000 BBBB
 | 
						|
  // 0321 0321 |      0321
 | 
						|
 | 
						|
  for (i=0;i<16;i++)
 | 
						|
  {
 | 
						|
    w = (word)src[0] << 8 | (word)src[1];
 | 
						|
    src += 2;
 | 
						|
 | 
						|
    palette[i].R = (((w & 0x0700)>>7) | ((w & 0x0800) >> 11)) * 0x11 ;
 | 
						|
    palette[i].G = (((w & 0x0070)>>3) | ((w & 0x0080) >> 7)) * 0x11 ;
 | 
						|
    palette[i].B = (((w & 0x0007)<<1) | ((w & 0x0008) >> 3)) * 0x11 ;
 | 
						|
  }
 | 
						|
}
 | 
						|
 | 
						|
//// CODAGE de la PALETTE ////
 | 
						|
 | 
						|
void PI1_code_palette(const T_Components * palette, byte * dest)
 | 
						|
{
 | 
						|
  int i;  // Numéro de la couleur traitée
 | 
						|
  word w; // Word contenant le code
 | 
						|
 | 
						|
  // Schéma d'un word =
 | 
						|
  //
 | 
						|
  // Low        High
 | 
						|
  // VVVV RRRR | 0000 BBBB
 | 
						|
  // 0321 0321 |      0321
 | 
						|
 | 
						|
  for (i=0;i<16;i++)
 | 
						|
  {
 | 
						|
    w  = ((word)(palette[i].R & 0xe0) << 3) | ((word)(palette[i].R & 0x10) << 7);
 | 
						|
    w |= ((word)(palette[i].G & 0xe0) >> 1) | ((word)(palette[i].G & 0x10) << 3);
 | 
						|
    w |= ((word)(palette[i].B & 0xe0) >> 5) | ((word)(palette[i].B & 0x10) >> 1);
 | 
						|
 | 
						|
    *dest++ = (w >> 8);
 | 
						|
    *dest++ = (w & 0xff);
 | 
						|
  }
 | 
						|
}
 | 
						|
 | 
						|
/// Load color cycling data from a PI1 or PC1 image (Degas Elite format)
 | 
						|
static void PI1_load_ranges(T_IO_Context * context, const byte * buffer, int size)
 | 
						|
{
 | 
						|
  int range;
 | 
						|
 | 
						|
  if (buffer==NULL || size<32)
 | 
						|
    return;
 | 
						|
 | 
						|
  for (range=0; range < 4; range ++)
 | 
						|
  {
 | 
						|
    word min_col, max_col, direction, delay;
 | 
						|
 | 
						|
    min_col   = (buffer[size - 32 + range*2 +  0] << 8) | buffer[size - 32 + range*2 +  1];
 | 
						|
    max_col   = (buffer[size - 32 + range*2 +  8] << 8) | buffer[size - 32 + range*2 +  9];
 | 
						|
    direction = (buffer[size - 32 + range*2 + 16] << 8) | buffer[size - 32 + range*2 + 17];
 | 
						|
    delay     = (buffer[size - 32 + range*2 + 24] << 8) | buffer[size - 32 + range*2 + 25];
 | 
						|
 | 
						|
    if (max_col < min_col)
 | 
						|
      SWAP_WORDS(min_col,max_col)
 | 
						|
 | 
						|
    GFX2_Log(GFX2_DEBUG, "Degas Color cycling : [#%d:#%d] direction=%d delay=%d\n", min_col, max_col, direction, delay);
 | 
						|
    // Sanity checks
 | 
						|
    if (min_col < 256 && max_col < 256 && direction < 3 && (direction == 1 || delay < 128))
 | 
						|
    {
 | 
						|
      int speed = 1;
 | 
						|
      if (delay < 128)
 | 
						|
        speed = 210/(128-delay);
 | 
						|
      // Grafx2's slider has a limit of COLOR_CYCLING_SPEED_MAX
 | 
						|
      if (speed > COLOR_CYCLING_SPEED_MAX)
 | 
						|
        speed = COLOR_CYCLING_SPEED_MAX;
 | 
						|
      context->Cycle_range[context->Color_cycles].Start=min_col;
 | 
						|
      context->Cycle_range[context->Color_cycles].End=max_col;
 | 
						|
      context->Cycle_range[context->Color_cycles].Inverse= (direction==0);
 | 
						|
      context->Cycle_range[context->Color_cycles].Speed=direction == 1 ? 0 : speed;
 | 
						|
      context->Color_cycles++;
 | 
						|
    }
 | 
						|
  }
 | 
						|
}
 | 
						|
 | 
						|
/// Saves color ranges from a PI1 or PC1 image (Degas Elite format)
 | 
						|
void PI1_save_ranges(T_IO_Context * context, byte * buffer, int size)
 | 
						|
{
 | 
						|
  // empty by default
 | 
						|
  memset(buffer+size - 32, 0, 32);
 | 
						|
  if (context->Color_cycles)
 | 
						|
  {
 | 
						|
    int i; // index in context->Cycle_range[] : < context->Color_cycles
 | 
						|
    int saved_range; // index in resulting buffer : < 4
 | 
						|
 | 
						|
    for (i=0, saved_range=0; i<context->Color_cycles && saved_range<4; i++)
 | 
						|
    {
 | 
						|
      if (context->Cycle_range[i].Start < 16 && context->Cycle_range[i].End < 16)
 | 
						|
      {
 | 
						|
        int speed;
 | 
						|
        if (context->Cycle_range[i].Speed == 0)
 | 
						|
          speed = 0;
 | 
						|
        else if (context->Cycle_range[i].Speed == 1)
 | 
						|
          // has to "round" manually to closest valid number for this format
 | 
						|
          speed = 1;
 | 
						|
        else
 | 
						|
          speed = 128 - 210 / context->Cycle_range[i].Speed;
 | 
						|
 | 
						|
        buffer[size - 32 + saved_range*2 +  1] = context->Cycle_range[i].Start;
 | 
						|
        buffer[size - 32 + saved_range*2 +  9] = context->Cycle_range[i].End;
 | 
						|
        buffer[size - 32 + saved_range*2 + 17] = (context->Cycle_range[i].Speed == 0) ? 1 : (context->Cycle_range[i].Inverse ? 0 : 2);
 | 
						|
        buffer[size - 32 + saved_range*2 + 25] = speed;
 | 
						|
 | 
						|
        saved_range ++;
 | 
						|
      }
 | 
						|
    }
 | 
						|
  }
 | 
						|
}
 | 
						|
 | 
						|
/// Test for Degas file format
 | 
						|
void Test_PI1(T_IO_Context * context, FILE * file)
 | 
						|
{
 | 
						|
  unsigned long size;              // Taille du fichier
 | 
						|
  word resolution;                 // Résolution de l'image
 | 
						|
 | 
						|
  (void)context;
 | 
						|
  File_error=1;
 | 
						|
 | 
						|
  if (!Read_word_be(file,&resolution))
 | 
						|
    return;
 | 
						|
 | 
						|
  size = File_length_file(file);
 | 
						|
  if ((size==32034) || (size==32066)) // size check
 | 
						|
  {
 | 
						|
    if (resolution < 3)
 | 
						|
      File_error=0;
 | 
						|
  }
 | 
						|
}
 | 
						|
 | 
						|
 | 
						|
/// Load Degas file format
 | 
						|
void Load_PI1(T_IO_Context * context)
 | 
						|
{
 | 
						|
  enum PIXEL_RATIO ratio = PIXEL_SIMPLE;
 | 
						|
  word resolution;
 | 
						|
  word width, height;
 | 
						|
  FILE *file;
 | 
						|
  word x_pos,y_pos;
 | 
						|
  byte buffer[160];
 | 
						|
  byte * ptr;
 | 
						|
  byte pixels[320];
 | 
						|
  byte bpp;
 | 
						|
 | 
						|
  File_error = 1;
 | 
						|
  file = Open_file_read(context);
 | 
						|
  if (file == NULL)
 | 
						|
    return;
 | 
						|
 | 
						|
  if (!Read_word_be(file, &resolution))
 | 
						|
    return;
 | 
						|
  GFX2_Log(GFX2_DEBUG, "Degas UnCompressed. Resolution = %04x\n", resolution);
 | 
						|
  // Read palette
 | 
						|
  if (!Read_bytes(file, buffer, 32))
 | 
						|
  {
 | 
						|
    fclose(file);
 | 
						|
    return;
 | 
						|
  }
 | 
						|
  if (Config.Clear_palette)
 | 
						|
    memset(context->Palette,0,sizeof(T_Palette));
 | 
						|
  PI1_decode_palette(buffer, context->Palette);
 | 
						|
 | 
						|
  switch (resolution)
 | 
						|
  {
 | 
						|
    case 0:  // Low Res
 | 
						|
      width = 320;
 | 
						|
      height = 200;
 | 
						|
      bpp = 4;
 | 
						|
      break;
 | 
						|
    case 1:  // Med Res
 | 
						|
      width = 640;
 | 
						|
      height = 200;
 | 
						|
      bpp = 2;
 | 
						|
      ratio = PIXEL_TALL;
 | 
						|
      break;
 | 
						|
    case 2:  // High Res
 | 
						|
      width = 640;
 | 
						|
      height = 400;
 | 
						|
      bpp = 1;
 | 
						|
      break;
 | 
						|
    default:
 | 
						|
      fclose(file);
 | 
						|
      return;
 | 
						|
  }
 | 
						|
  Pre_load(context, width, height, File_length_file(file),FORMAT_PI1,ratio, bpp);
 | 
						|
 | 
						|
  for (y_pos=0;y_pos<height;y_pos++)
 | 
						|
  {
 | 
						|
    if (!Read_bytes(file, buffer, (resolution == 2) ? 80 : 160))
 | 
						|
    {
 | 
						|
      fclose(file);
 | 
						|
      return;
 | 
						|
    }
 | 
						|
    ptr = buffer;
 | 
						|
    for (x_pos=0; x_pos < width;)
 | 
						|
    {
 | 
						|
      int i;
 | 
						|
      switch (resolution)
 | 
						|
      {
 | 
						|
        case 0:
 | 
						|
          PI1_8b_to_16p(ptr, pixels);
 | 
						|
          ptr += 8;
 | 
						|
          break;
 | 
						|
        case 1:
 | 
						|
          PI2_8b_to_16p(ptr, pixels);
 | 
						|
          ptr += 4;
 | 
						|
          break;
 | 
						|
        case 2:
 | 
						|
          for (i = 0; i < 8; i++)
 | 
						|
            pixels[i] = (ptr[0] & (0x80 >> i)) ? 1 : 0;
 | 
						|
          for (; i < 16; i++)
 | 
						|
            pixels[i] = (ptr[1] & (0x80 >> (i - 8))) ? 1 : 0;
 | 
						|
          ptr += 2;
 | 
						|
      }
 | 
						|
      for (i = 0; i < 16; i++)
 | 
						|
        Set_pixel(context, x_pos++, y_pos, pixels[i]);
 | 
						|
    }
 | 
						|
  }
 | 
						|
  // load color cycling information
 | 
						|
  if (Read_bytes(file, buffer, 32))
 | 
						|
  {
 | 
						|
    PI1_load_ranges(context, buffer, 32);
 | 
						|
  }
 | 
						|
  fclose(file);
 | 
						|
  File_error = 0;
 | 
						|
}
 | 
						|
 | 
						|
 | 
						|
// -- Sauver un fichier au format PI1 ---------------------------------------
 | 
						|
void Save_PI1(T_IO_Context * context)
 | 
						|
{
 | 
						|
  FILE *file;
 | 
						|
  short x_pos,y_pos;
 | 
						|
  byte * buffer;
 | 
						|
  byte * ptr;
 | 
						|
  byte pixels[320];
 | 
						|
 | 
						|
  File_error=0;
 | 
						|
  // Ouverture du fichier
 | 
						|
  if ((file=Open_file_write(context)))
 | 
						|
  {
 | 
						|
    setvbuf(file, NULL, _IOFBF, 64*1024);
 | 
						|
 | 
						|
    // allocation d'un buffer mémoire
 | 
						|
    buffer=(byte *)malloc(32034);
 | 
						|
    // Codage de la résolution
 | 
						|
    buffer[0]=0x00;
 | 
						|
    buffer[1]=0x00;
 | 
						|
    // Codage de la palette
 | 
						|
    PI1_code_palette(context->Palette, buffer+2);
 | 
						|
    // Codage de l'image
 | 
						|
    ptr=buffer+34;
 | 
						|
    for (y_pos=0;y_pos<200;y_pos++)
 | 
						|
    {
 | 
						|
      // Codage de la ligne
 | 
						|
      memset(pixels,0,320);
 | 
						|
      if (y_pos<context->Height)
 | 
						|
      {
 | 
						|
        for (x_pos=0;(x_pos<320) && (x_pos<context->Width);x_pos++)
 | 
						|
          pixels[x_pos]=Get_pixel(context, x_pos,y_pos);
 | 
						|
      }
 | 
						|
 | 
						|
      for (x_pos=0;x_pos<(320>>4);x_pos++)
 | 
						|
      {
 | 
						|
        PI1_16p_to_8b(pixels+(x_pos<<4),ptr);
 | 
						|
        ptr+=8;
 | 
						|
      }
 | 
						|
    }
 | 
						|
 | 
						|
    if (Write_bytes(file,buffer,32034))
 | 
						|
    {
 | 
						|
      if (context->Color_cycles)
 | 
						|
      {
 | 
						|
        PI1_save_ranges(context, buffer, 32);
 | 
						|
        if (!Write_bytes(file,buffer,32))
 | 
						|
          File_error=1;
 | 
						|
      }
 | 
						|
      fclose(file);
 | 
						|
    }
 | 
						|
    else // Error d'écriture (disque plein ou protégé)
 | 
						|
    {
 | 
						|
      fclose(file);
 | 
						|
      Remove_file(context);
 | 
						|
      File_error=1;
 | 
						|
    }
 | 
						|
    // Libération du buffer mémoire
 | 
						|
    free(buffer);
 | 
						|
    buffer = NULL;
 | 
						|
  }
 | 
						|
  else
 | 
						|
  {
 | 
						|
    fclose(file);
 | 
						|
    Remove_file(context);
 | 
						|
    File_error=1;
 | 
						|
  }
 | 
						|
}
 | 
						|
 | 
						|
 | 
						|
//////////////////////////////////// PC1 ////////////////////////////////////
 | 
						|
 | 
						|
/// uncompress degas elite compressed stream (PACKBITS)
 | 
						|
/// @return 1 for success
 | 
						|
/// @return 0 for failure
 | 
						|
static int PC1_uncompress_packbits_file(FILE *f, byte * dest)
 | 
						|
{
 | 
						|
  int id = 0;
 | 
						|
  unsigned count;
 | 
						|
  byte code, value;
 | 
						|
 | 
						|
  while (id < 32000)
 | 
						|
  {
 | 
						|
    if (!Read_byte(f, &code))
 | 
						|
      return 0;
 | 
						|
 | 
						|
    if (code & 0x80)
 | 
						|
    {
 | 
						|
      /// Code is negative :
 | 
						|
      /// Repeat (1-code) times next byte
 | 
						|
      count = 257 - code;
 | 
						|
      if (id + count > 32000)
 | 
						|
        return 0;
 | 
						|
      if (!Read_byte(f, &value))
 | 
						|
        return 0;
 | 
						|
      while (count-- > 0)
 | 
						|
        dest[id++] = value;
 | 
						|
    }
 | 
						|
    else
 | 
						|
    {
 | 
						|
      /// Code is positive :
 | 
						|
      /// Copy (code+1) bytes
 | 
						|
      count = code + 1;
 | 
						|
      if (id + count > 32000)
 | 
						|
        return 0;
 | 
						|
      if (!Read_bytes(f, dest + id, count))
 | 
						|
        return 0;
 | 
						|
      id += count;
 | 
						|
    }
 | 
						|
  }
 | 
						|
  return 1;
 | 
						|
}
 | 
						|
 | 
						|
//// COMPRESSION d'un buffer selon la méthode PACKBITS ////
 | 
						|
 | 
						|
static void PC1_compress_packbits(byte * src,byte * dest,int source_size,int * dest_size)
 | 
						|
{
 | 
						|
 | 
						|
  *dest_size = 0;
 | 
						|
  while (source_size > 0)
 | 
						|
  {
 | 
						|
    int is = 0; // index dans la source
 | 
						|
    int id = 0; // index dans la destination
 | 
						|
    int ir; // index de   la répétition
 | 
						|
    int n;  // Taille des séquences
 | 
						|
    int repet; // "Il y a répétition"
 | 
						|
 | 
						|
    while(is<40)
 | 
						|
    {
 | 
						|
      // On recherche le 1er endroit où il y a répétition d'au moins 3 valeurs
 | 
						|
      // identiques
 | 
						|
 | 
						|
      repet=0;
 | 
						|
      for (ir=is;ir<40-2;ir++)
 | 
						|
      {
 | 
						|
        if ((src[ir]==src[ir+1]) && (src[ir+1]==src[ir+2]))
 | 
						|
        {
 | 
						|
          repet=1;
 | 
						|
          break;
 | 
						|
        }
 | 
						|
      }
 | 
						|
 | 
						|
      // On code la partie sans répétitions
 | 
						|
      if (!repet || ir!=is)
 | 
						|
      {
 | 
						|
        n=(ir-is)+1;
 | 
						|
        dest[id++]=n-1;
 | 
						|
        for (;n>0;n--)
 | 
						|
          dest[id++]=src[is++];
 | 
						|
      }
 | 
						|
 | 
						|
      // On code la partie sans répétitions
 | 
						|
      if (repet)
 | 
						|
      {
 | 
						|
        // On compte la quantité de fois qu'il faut répéter la valeur
 | 
						|
        for (ir+=3;ir<40;ir++)
 | 
						|
        {
 | 
						|
          if (src[ir]!=src[is])
 | 
						|
            break;
 | 
						|
        }
 | 
						|
        n=(ir-is);
 | 
						|
        dest[id++]=257-n;
 | 
						|
        dest[id++]=src[is];
 | 
						|
        is=ir;
 | 
						|
      }
 | 
						|
    }
 | 
						|
    // On renseigne la taille du buffer compressé
 | 
						|
    *dest_size+=id;
 | 
						|
    // Move for next 40-byte block
 | 
						|
    src += 40;
 | 
						|
    dest += id;
 | 
						|
    source_size -= 40;
 | 
						|
  }
 | 
						|
}
 | 
						|
 | 
						|
//// DECODAGE d'une partie d'IMAGE ////
 | 
						|
 | 
						|
// Transformation de 4 plans de bits en 1 ligne de pixels
 | 
						|
 | 
						|
static void PC1_4bp_to_1line(byte * src0,byte * src1,byte * src2,byte * src3,byte * dest)
 | 
						|
{
 | 
						|
  int  i,j;         // Compteurs
 | 
						|
  int  ip;          // index du pixel à calculer
 | 
						|
  byte byte_mask;      // Masque de decodage
 | 
						|
  byte b0,b1,b2,b3; // Les 4 octets des plans bits sources
 | 
						|
 | 
						|
  ip=0;
 | 
						|
  // Pour chacun des 40 octets des plans de bits
 | 
						|
  for (i=0;i<40;i++)
 | 
						|
  {
 | 
						|
    b0=src0[i];
 | 
						|
    b1=src1[i];
 | 
						|
    b2=src2[i];
 | 
						|
    b3=src3[i];
 | 
						|
    // Pour chacun des 8 bits des octets
 | 
						|
    byte_mask=0x80;
 | 
						|
    for (j=0;j<8;j++)
 | 
						|
    {
 | 
						|
      dest[ip++]=((b0 & byte_mask)?0x01:0x00) |
 | 
						|
                ((b1 & byte_mask)?0x02:0x00) |
 | 
						|
                ((b2 & byte_mask)?0x04:0x00) |
 | 
						|
                ((b3 & byte_mask)?0x08:0x00);
 | 
						|
      byte_mask>>=1;
 | 
						|
    }
 | 
						|
  }
 | 
						|
}
 | 
						|
 | 
						|
//// CODAGE d'une partie d'IMAGE ////
 | 
						|
 | 
						|
// Transformation d'1 ligne de pixels en 4 plans de bits
 | 
						|
 | 
						|
static void PC1_1line_to_4bp(byte * src,byte * dst0,byte * dst1,byte * dst2,byte * dst3)
 | 
						|
{
 | 
						|
  int  i,j;         // Compteurs
 | 
						|
  int  ip;          // index du pixel à calculer
 | 
						|
  byte byte_mask;      // Masque de decodage
 | 
						|
  byte b0,b1,b2,b3; // Les 4 octets des plans bits sources
 | 
						|
 | 
						|
  ip=0;
 | 
						|
  // Pour chacun des 40 octets des plans de bits
 | 
						|
  for (i=0;i<40;i++)
 | 
						|
  {
 | 
						|
    // Pour chacun des 8 bits des octets
 | 
						|
    byte_mask=0x80;
 | 
						|
    b0=b1=b2=b3=0;
 | 
						|
    for (j=0;j<8;j++)
 | 
						|
    {
 | 
						|
      b0|=(src[ip] & 0x01)?byte_mask:0x00;
 | 
						|
      b1|=(src[ip] & 0x02)?byte_mask:0x00;
 | 
						|
      b2|=(src[ip] & 0x04)?byte_mask:0x00;
 | 
						|
      b3|=(src[ip] & 0x08)?byte_mask:0x00;
 | 
						|
      ip++;
 | 
						|
      byte_mask>>=1;
 | 
						|
    }
 | 
						|
    dst0[i]=b0;
 | 
						|
    dst1[i]=b1;
 | 
						|
    dst2[i]=b2;
 | 
						|
    dst3[i]=b3;
 | 
						|
  }
 | 
						|
}
 | 
						|
 | 
						|
 | 
						|
/// Test for Degas Elite Compressed format
 | 
						|
void Test_PC1(T_IO_Context * context, FILE * file)
 | 
						|
{
 | 
						|
  int  size;              // Taille du fichier
 | 
						|
  word resolution;        // Résolution de l'image
 | 
						|
 | 
						|
  (void)context;
 | 
						|
  File_error=1;
 | 
						|
 | 
						|
  size = File_length_file(file);
 | 
						|
  if (!Read_word_be(file,&resolution))
 | 
						|
    return;
 | 
						|
 | 
						|
  if ((size <= 32066) && (resolution & 0x8000))
 | 
						|
  {
 | 
						|
    if ((resolution & 0x7fff) < 3)
 | 
						|
      File_error=0;
 | 
						|
  }
 | 
						|
}
 | 
						|
 | 
						|
 | 
						|
/// Load Degas Elite compressed files
 | 
						|
void Load_PC1(T_IO_Context * context)
 | 
						|
{
 | 
						|
  enum PIXEL_RATIO ratio = PIXEL_SIMPLE;
 | 
						|
  unsigned long size;
 | 
						|
  word width, height;
 | 
						|
  byte bpp;
 | 
						|
  FILE *file;
 | 
						|
  word x_pos,y_pos;
 | 
						|
  byte buffer[32];
 | 
						|
  byte * bufferdecomp;
 | 
						|
  byte * ptr;
 | 
						|
  byte pixels[640];
 | 
						|
  word resolution;
 | 
						|
 | 
						|
  File_error = 1;
 | 
						|
  file = Open_file_read(context);
 | 
						|
  if (file == NULL)
 | 
						|
    return;
 | 
						|
  size = File_length_file(file);
 | 
						|
 | 
						|
  if (!Read_word_be(file, &resolution))
 | 
						|
    return;
 | 
						|
  GFX2_Log(GFX2_DEBUG, "Degas Elite Compressed. Resolution = %04x\n", resolution);
 | 
						|
  // Read palette
 | 
						|
  if (!Read_bytes(file, buffer, 32))
 | 
						|
  {
 | 
						|
    fclose(file);
 | 
						|
    return;
 | 
						|
  }
 | 
						|
  if (Config.Clear_palette)
 | 
						|
    memset(context->Palette,0,sizeof(T_Palette));
 | 
						|
  PI1_decode_palette(buffer, context->Palette);
 | 
						|
 | 
						|
  switch (resolution)
 | 
						|
  {
 | 
						|
    case 0x8000:  // Low Res
 | 
						|
      width = 320;
 | 
						|
      height = 200;
 | 
						|
      bpp = 4;
 | 
						|
      break;
 | 
						|
    case 0x8001:  // Med Res
 | 
						|
      width = 640;
 | 
						|
      height = 200;
 | 
						|
      bpp = 2;
 | 
						|
      ratio = PIXEL_TALL;
 | 
						|
      break;
 | 
						|
    case 0x8002:  // High Res
 | 
						|
      width = 640;
 | 
						|
      height = 400;
 | 
						|
      bpp = 1;
 | 
						|
      break;
 | 
						|
    default:
 | 
						|
      fclose(file);
 | 
						|
      return;
 | 
						|
  }
 | 
						|
 | 
						|
  bufferdecomp=(byte *)malloc(32000);
 | 
						|
  if (bufferdecomp == NULL)
 | 
						|
  {
 | 
						|
    fclose(file);
 | 
						|
    return;
 | 
						|
  }
 | 
						|
 | 
						|
  // Initialisation de la preview
 | 
						|
  Pre_load(context, width, height, size, FORMAT_PC1, ratio, bpp);
 | 
						|
 | 
						|
  //PC1_uncompress_packbits(buffercomp, bufferdecomp);
 | 
						|
  if (!PC1_uncompress_packbits_file(file, bufferdecomp))
 | 
						|
  {
 | 
						|
    GFX2_Log(GFX2_INFO, "PC1_uncompress_packbits_file() failed\n");
 | 
						|
    free(bufferdecomp);
 | 
						|
    fclose(file);
 | 
						|
    return;
 | 
						|
  }
 | 
						|
 | 
						|
  // Décodage de l'image
 | 
						|
  ptr=bufferdecomp;
 | 
						|
  for (y_pos = 0; y_pos < height; y_pos++)
 | 
						|
  {
 | 
						|
    // Décodage de la scanline
 | 
						|
    switch (resolution)
 | 
						|
    {
 | 
						|
      case 0x8000:  // Low Res
 | 
						|
        PC1_4bp_to_1line(ptr,ptr+40,ptr+80,ptr+120,pixels);
 | 
						|
        ptr+=160;
 | 
						|
        break;
 | 
						|
      case 0x8001:  // Med Res
 | 
						|
        x_pos = 0;
 | 
						|
        while (x_pos < width)
 | 
						|
        {
 | 
						|
          int i;
 | 
						|
          for (i = 7; i >= 0; i--, x_pos++)
 | 
						|
            pixels[x_pos] =  ((ptr[0] >> i) & 1)
 | 
						|
                            | (((ptr[80] >> i) << 1) & 2);
 | 
						|
          ptr++;
 | 
						|
        }
 | 
						|
        ptr += 80;
 | 
						|
        break;
 | 
						|
      case 0x8002:  // High Res
 | 
						|
        x_pos = 0;
 | 
						|
        while (x_pos < width)
 | 
						|
        {
 | 
						|
          int i;
 | 
						|
          for (i = 7; i >= 0; i--)
 | 
						|
            pixels[x_pos++] = (*ptr >> i) & 1;
 | 
						|
          ptr++;
 | 
						|
        }
 | 
						|
    }
 | 
						|
    for (x_pos=0;x_pos<width;x_pos++)
 | 
						|
      Set_pixel(context, x_pos, y_pos, pixels[x_pos]);
 | 
						|
  }
 | 
						|
  // Try to load color cycling information
 | 
						|
  GFX2_Log(GFX2_DEBUG, "remaining bytes = %ld\n", size - ftell(file));
 | 
						|
  if (Read_bytes(file, buffer, 32))
 | 
						|
  {
 | 
						|
    PI1_load_ranges(context, buffer, 32);
 | 
						|
  }
 | 
						|
  free(bufferdecomp);
 | 
						|
  fclose(file);
 | 
						|
  File_error = 0;
 | 
						|
}
 | 
						|
 | 
						|
 | 
						|
// -- Sauver un fichier au format PC1 ---------------------------------------
 | 
						|
void Save_PC1(T_IO_Context * context)
 | 
						|
{
 | 
						|
  FILE *file;
 | 
						|
  int   size;
 | 
						|
  short x_pos,y_pos;
 | 
						|
  byte * buffercomp;
 | 
						|
  byte * bufferdecomp;
 | 
						|
  byte * ptr;
 | 
						|
  byte pixels[320];
 | 
						|
 | 
						|
  File_error=0;
 | 
						|
  // Ouverture du fichier
 | 
						|
  if ((file=Open_file_write(context)))
 | 
						|
  {
 | 
						|
    setvbuf(file, NULL, _IOFBF, 64*1024);
 | 
						|
 | 
						|
    // Allocation des buffers mémoire
 | 
						|
    bufferdecomp=(byte *)malloc(32000);
 | 
						|
    buffercomp  =(byte *)malloc(64066);
 | 
						|
    // Codage de la résolution
 | 
						|
    buffercomp[0]=0x80;
 | 
						|
    buffercomp[1]=0x00;
 | 
						|
    // Codage de la palette
 | 
						|
    PI1_code_palette(context->Palette, buffercomp+2);
 | 
						|
    // Codage de l'image
 | 
						|
    ptr=bufferdecomp;
 | 
						|
    for (y_pos=0;y_pos<200;y_pos++)
 | 
						|
    {
 | 
						|
      // Codage de la ligne
 | 
						|
      memset(pixels,0,320);
 | 
						|
      if (y_pos<context->Height)
 | 
						|
      {
 | 
						|
        for (x_pos=0;(x_pos<320) && (x_pos<context->Width);x_pos++)
 | 
						|
          pixels[x_pos]=Get_pixel(context, x_pos,y_pos);
 | 
						|
      }
 | 
						|
 | 
						|
      // Encodage de la scanline
 | 
						|
      PC1_1line_to_4bp(pixels,ptr,ptr+40,ptr+80,ptr+120);
 | 
						|
      ptr+=160;
 | 
						|
    }
 | 
						|
 | 
						|
    // Compression du buffer
 | 
						|
    PC1_compress_packbits(bufferdecomp,buffercomp+34,32000,&size);
 | 
						|
    size += 34;
 | 
						|
    size += 32;
 | 
						|
    PI1_save_ranges(context, buffercomp,size);
 | 
						|
 | 
						|
    if (Write_bytes(file,buffercomp,size))
 | 
						|
    {
 | 
						|
      fclose(file);
 | 
						|
    }
 | 
						|
    else // Error d'écriture (disque plein ou protégé)
 | 
						|
    {
 | 
						|
      fclose(file);
 | 
						|
      Remove_file(context);
 | 
						|
      File_error=1;
 | 
						|
    }
 | 
						|
    // Libération des buffers mémoire
 | 
						|
    free(bufferdecomp);
 | 
						|
    free(buffercomp);
 | 
						|
    buffercomp = bufferdecomp = NULL;
 | 
						|
  }
 | 
						|
  else
 | 
						|
  {
 | 
						|
    fclose(file);
 | 
						|
    Remove_file(context);
 | 
						|
    File_error=1;
 | 
						|
  }
 | 
						|
}
 | 
						|
 | 
						|
 | 
						|
//////////////////////////////////// NEO ////////////////////////////////////
 | 
						|
/**
 | 
						|
NeoChrome Format :
 | 
						|
<pre>
 | 
						|
1 word          flag word [always 0]
 | 
						|
1 word          resolution [0 = low res, 1 = medium res, 2 = high res]
 | 
						|
16 words        palette
 | 
						|
12 bytes        filename [usually "        .   "]
 | 
						|
1 word          color animation limits.  High bit (bit 15) set if color
 | 
						|
                animation data is valid.  Low byte contains color animation
 | 
						|
                limits (4 most significant bits are left/lower limit,
 | 
						|
                4 least significant bits are right/upper limit).
 | 
						|
1 word          color animation speed and direction.  High bit (bit 15) set
 | 
						|
                if animation is on.  Low order byte is # vblanks per step.
 | 
						|
                If negative, scroll is left (decreasing).  Number of vblanks
 | 
						|
                between cycles is |x| - 1
 | 
						|
1 word          # of color steps (as defined in previous word) to display
 | 
						|
                picture before going to the next.  (For use in slide shows)
 | 
						|
1 word          image X offset [unused, always 0]
 | 
						|
1 word          image Y offset [unused, always 0]
 | 
						|
1 word          image width [unused, always 320]
 | 
						|
1 word          image height [unused, always 200]
 | 
						|
33 words        reserved for future expansion
 | 
						|
32000 bytes     pixel data
 | 
						|
</pre>
 | 
						|
 | 
						|
Dali           *.SD0 (ST low resolution)
 | 
						|
               *.SD1 (ST medium resolution)
 | 
						|
               *.SD2 (ST high resolution)
 | 
						|
        
 | 
						|
Files do not seem to have any resolution or bit plane info stored in them. The file
 | 
						|
extension seems to be the only way to determine the contents.
 | 
						|
        
 | 
						|
1 long         file id? [always 0]
 | 
						|
16 words       palette
 | 
						|
92 bytes       reserved? [usually 0]
 | 
						|
*/
 | 
						|
void Test_NEO(T_IO_Context * context, FILE * file)
 | 
						|
{
 | 
						|
  word flag;
 | 
						|
  word resolution;   // Atari ST resolution
 | 
						|
 | 
						|
  (void)context;
 | 
						|
  File_error=1;
 | 
						|
 | 
						|
  if (File_length_file(file) != 32128)
 | 
						|
    return;
 | 
						|
 | 
						|
  if (!Read_word_be(file,&flag))
 | 
						|
    return;
 | 
						|
  // Flag word : always 0
 | 
						|
  if (flag != 0)
 | 
						|
    return;
 | 
						|
 | 
						|
  if (!Read_word_be(file,&resolution))
 | 
						|
    return;
 | 
						|
  // 0 = STlow, 1 = STmed, 2 = SThigh
 | 
						|
  if (resolution==0 || resolution==1 || resolution==2)
 | 
						|
    File_error = 0;
 | 
						|
}
 | 
						|
 | 
						|
 | 
						|
/// Load Neochrome file format
 | 
						|
void Load_NEO(T_IO_Context * context)
 | 
						|
{
 | 
						|
  enum PIXEL_RATIO ratio = PIXEL_SIMPLE;
 | 
						|
  word flag;
 | 
						|
  word resolution;   // Atari ST resolution
 | 
						|
  word width, height;
 | 
						|
  byte bpp;
 | 
						|
  word color_cycling_range, color_cycling_delay;
 | 
						|
  word display_time;
 | 
						|
  word image_width, image_height, image_X_pos, image_Y_pos;
 | 
						|
  FILE *file;
 | 
						|
  word x_pos,y_pos;
 | 
						|
  byte * ptr;
 | 
						|
  byte buffer[160];
 | 
						|
  byte pixels[16];
 | 
						|
 | 
						|
  File_error = 1;
 | 
						|
  file = Open_file_read(context);
 | 
						|
  if (file == NULL)
 | 
						|
    return;
 | 
						|
 | 
						|
  if (!Read_word_be(file,&flag))
 | 
						|
    goto error;
 | 
						|
  // Flag word : always 0
 | 
						|
  if (flag != 0)
 | 
						|
    goto error;
 | 
						|
 | 
						|
  if (!Read_word_be(file,&resolution))
 | 
						|
    goto error;
 | 
						|
 | 
						|
  switch (resolution)
 | 
						|
  {
 | 
						|
  case 0:
 | 
						|
    width = 320;
 | 
						|
    height = 200;
 | 
						|
    bpp = 4;
 | 
						|
    break;
 | 
						|
  case 1:
 | 
						|
    width = 640;
 | 
						|
    height = 200;
 | 
						|
    bpp = 2;
 | 
						|
    ratio = PIXEL_TALL;
 | 
						|
    break;
 | 
						|
  case 2:
 | 
						|
    width = 640;
 | 
						|
    height = 400;
 | 
						|
    bpp = 1;
 | 
						|
    break;
 | 
						|
  default:
 | 
						|
    goto error;
 | 
						|
  }
 | 
						|
 | 
						|
  Pre_load(context, width, height, File_length_file(file), FORMAT_NEO, ratio, bpp);
 | 
						|
 | 
						|
  if (!Read_bytes(file,buffer,32))
 | 
						|
    goto error;
 | 
						|
 | 
						|
  // Initialisation de la palette
 | 
						|
  if (Config.Clear_palette)
 | 
						|
    memset(context->Palette, 0, sizeof(T_Palette));
 | 
						|
  PI1_decode_palette(buffer, context->Palette);
 | 
						|
 | 
						|
  if (!Read_bytes(file, buffer, 12))
 | 
						|
    goto error;
 | 
						|
  buffer[12] = '\0';
 | 
						|
  GFX2_Log(GFX2_DEBUG, "NEO resolution %u name=\"%s\"\n", resolution, (char *)buffer);
 | 
						|
  if (!Read_word_be(file, &color_cycling_range)
 | 
						|
     || !Read_word_be(file, &color_cycling_delay)
 | 
						|
     || !Read_word_be(file, &display_time))
 | 
						|
    goto error;
 | 
						|
  GFX2_Log(GFX2_DEBUG, "  Color cycling : %04x %04x. Time to show %u\n", color_cycling_range, color_cycling_delay, display_time);
 | 
						|
  if (color_cycling_range & 0x8000)
 | 
						|
  {
 | 
						|
    context->Cycle_range[context->Color_cycles].Start = (color_cycling_range & 0x00f0) >> 4;
 | 
						|
    context->Cycle_range[context->Color_cycles].End = (color_cycling_range & 0x000f);
 | 
						|
    if (color_cycling_delay & 0x8000)
 | 
						|
    {
 | 
						|
      // color cycling on
 | 
						|
      color_cycling_delay &= 0xff;
 | 
						|
      if (color_cycling_delay & 0x0080)
 | 
						|
      {
 | 
						|
        context->Cycle_range[context->Color_cycles].Inverse = 1;
 | 
						|
        color_cycling_delay = 256 - color_cycling_delay;
 | 
						|
      }
 | 
						|
      else
 | 
						|
        context->Cycle_range[context->Color_cycles].Inverse = 0;
 | 
						|
      // Speed resolution is 0.2856Hz
 | 
						|
      // NEO color_cycling_delay is in 50Hz VBL
 | 
						|
      // Speed = (50/delay) / 0.2856 = 175 / delay
 | 
						|
      if (color_cycling_delay != 0)
 | 
						|
        context->Cycle_range[context->Color_cycles].Speed = 175 / color_cycling_delay;
 | 
						|
      else
 | 
						|
        context->Cycle_range[context->Color_cycles].Speed = COLOR_CYCLING_SPEED_MAX; // fastest
 | 
						|
      if (context->Cycle_range[context->Color_cycles].Speed > COLOR_CYCLING_SPEED_MAX)
 | 
						|
        context->Cycle_range[context->Color_cycles].Speed = COLOR_CYCLING_SPEED_MAX;
 | 
						|
    }
 | 
						|
    else
 | 
						|
      context->Cycle_range[context->Color_cycles].Speed = 0;  // cycling off
 | 
						|
    context->Color_cycles++;
 | 
						|
  }
 | 
						|
 | 
						|
  if (!Read_word_be(file, &image_X_pos) || !Read_word_be(file, &image_Y_pos)
 | 
						|
      || !Read_word_be(file, &image_width) || !Read_word_be(file, &image_height))
 | 
						|
    goto error;
 | 
						|
  GFX2_Log(GFX2_DEBUG, "  pos (%u,%u) size %ux%u\n", image_X_pos, image_Y_pos, image_width, image_height);
 | 
						|
  if (!Read_bytes(file, buffer, 128-4-32-12-6-8))
 | 
						|
    goto error;
 | 
						|
  GFX2_LogHexDump(GFX2_DEBUG, "NEO ", buffer, 0, 128-4-32-12-6-8);
 | 
						|
 | 
						|
  // Chargement/décompression de l'image
 | 
						|
  for (y_pos=0;y_pos<height;y_pos++)
 | 
						|
  {
 | 
						|
    if (!Read_bytes(file, buffer, (resolution==2) ? 80 : 160))
 | 
						|
      goto error;
 | 
						|
    
 | 
						|
    ptr = buffer;
 | 
						|
    for (x_pos = 0; x_pos < width; )
 | 
						|
    {
 | 
						|
      int i;
 | 
						|
      switch (resolution)
 | 
						|
      {
 | 
						|
        case 0:
 | 
						|
          PI1_8b_to_16p(ptr, pixels);
 | 
						|
          ptr += 8;
 | 
						|
          break;
 | 
						|
        case 1:
 | 
						|
          PI2_8b_to_16p(ptr, pixels);
 | 
						|
          ptr += 4;
 | 
						|
          break;
 | 
						|
        case 2:
 | 
						|
          for (i = 0; i < 8; i++)
 | 
						|
            pixels[i] = (ptr[0] & (0x80 >> i)) ? 1 : 0;
 | 
						|
          for (; i < 16; i++)
 | 
						|
            pixels[i] = (ptr[1] & (0x80 >> (i - 8))) ? 1 : 0;
 | 
						|
          ptr += 2;
 | 
						|
          break;
 | 
						|
        default:
 | 
						|
          goto error;
 | 
						|
      }
 | 
						|
      for (i = 0; i < 16; i++)
 | 
						|
        Set_pixel(context, x_pos++, y_pos, pixels[i]);
 | 
						|
    }
 | 
						|
  }
 | 
						|
  File_error = 0; // everything was ok
 | 
						|
 | 
						|
error:
 | 
						|
  fclose(file);
 | 
						|
}
 | 
						|
 | 
						|
/// Save in NeoChrome format
 | 
						|
void Save_NEO(T_IO_Context * context)
 | 
						|
{
 | 
						|
  word resolution = 0;
 | 
						|
  FILE *file = NULL;
 | 
						|
  short x_pos,y_pos;
 | 
						|
  word color_cycling_range = 0, color_cycling_delay = 0;
 | 
						|
  word display_time = 0;
 | 
						|
  word image_width = 320, image_height = 200;
 | 
						|
  byte buffer[32];
 | 
						|
  byte pixels[320];
 | 
						|
  char * ext;
 | 
						|
  int i, j;
 | 
						|
 | 
						|
  File_error = 1;
 | 
						|
  file = Open_file_write(context);
 | 
						|
  if (file == NULL)
 | 
						|
    return;
 | 
						|
 | 
						|
  // flags and resolution
 | 
						|
  if (!Write_word_be(file, 0) || !Write_word_be(file, resolution))
 | 
						|
    goto error;
 | 
						|
 | 
						|
  // palette
 | 
						|
  PI1_code_palette(context->Palette, buffer);
 | 
						|
  if (!Write_bytes(file, buffer, 16*2))
 | 
						|
    goto error;
 | 
						|
 | 
						|
  // file name
 | 
						|
  i = 0;
 | 
						|
  j = 0;
 | 
						|
  ext = strrchr(context->File_name, '.');
 | 
						|
  while (j < 8 && ext != (context->File_name + i))
 | 
						|
  {
 | 
						|
    byte c = context->File_name[i++];
 | 
						|
    if (c == 0)
 | 
						|
      break;
 | 
						|
    if (c >= 'a' && c <= 'z')
 | 
						|
      c -= 32;
 | 
						|
    if ((c >= 'A' && c <= 'Z') || (c >= '0' && c <= '9') || (c == '_'))
 | 
						|
      buffer[j++] = c;
 | 
						|
  }
 | 
						|
  while (j < 8)
 | 
						|
    buffer[j++] = ' ';
 | 
						|
  buffer[j++] = '.';
 | 
						|
  if (ext != NULL)
 | 
						|
  {
 | 
						|
    i = 0;
 | 
						|
    while (j < 12)
 | 
						|
    {
 | 
						|
      byte c = ext[i++];
 | 
						|
      if (c == 0)
 | 
						|
        break;
 | 
						|
      if (c >= 'a' && c <= 'z')
 | 
						|
        c -= 32;
 | 
						|
      if ((c >= 'A' && c <= 'Z') || (c >= '0' && c <= '9') || (c == '_'))
 | 
						|
        buffer[j++] = c;
 | 
						|
    }
 | 
						|
  }
 | 
						|
  while (j < 12)
 | 
						|
    buffer[j++] = ' ';
 | 
						|
 | 
						|
  if (!Write_bytes(file, buffer, 12))
 | 
						|
    goto error;
 | 
						|
 | 
						|
  // Save the 1st valid Color cycling range
 | 
						|
  for (i = 0; i < context->Color_cycles; i++)
 | 
						|
  {
 | 
						|
    if (context->Cycle_range[i].Start < 16 && context->Cycle_range[i].End < 16)
 | 
						|
    {
 | 
						|
      color_cycling_range = 0x8000 | (context->Cycle_range[i].Start << 4) | context->Cycle_range[i].End;
 | 
						|
      if (context->Cycle_range[i].Speed > 0)
 | 
						|
      {
 | 
						|
        color_cycling_delay = 175 / context->Cycle_range[i].Speed;
 | 
						|
        if (color_cycling_delay > 0 && context->Cycle_range[i].Inverse)
 | 
						|
          color_cycling_delay = 256 - color_cycling_delay;
 | 
						|
        color_cycling_delay |= 0x8000;
 | 
						|
      }
 | 
						|
      break;
 | 
						|
    }
 | 
						|
  }
 | 
						|
  if (!Write_word_be(file, color_cycling_range) || !Write_word_be(file, color_cycling_delay) || !Write_word_be(file, display_time))
 | 
						|
    goto error;
 | 
						|
 | 
						|
  // Save image position and size
 | 
						|
  if (!Write_word_be(file, 0) || !Write_word_be(file, 0)
 | 
						|
      || !Write_word_be(file, image_width) || !Write_word_be(file, image_height))
 | 
						|
    goto error;
 | 
						|
 | 
						|
  // Fill with 128 bytes header with 0's
 | 
						|
  // a few files have the string "NEO!" at offset 124 (0x7C)
 | 
						|
  for (i = ftell(file); i < 128; i++)
 | 
						|
  {
 | 
						|
    if (!Write_byte(file, 0))
 | 
						|
      goto error;
 | 
						|
  }
 | 
						|
 | 
						|
  // image coding
 | 
						|
  for (y_pos=0;y_pos<200;y_pos++)
 | 
						|
  {
 | 
						|
    // Codage de la ligne
 | 
						|
    memset(pixels, 0, 320);
 | 
						|
    if (y_pos < context->Height)
 | 
						|
    {
 | 
						|
      for (x_pos = 0; (x_pos < 320) && (x_pos < context->Width); x_pos++)
 | 
						|
        pixels[x_pos] = Get_pixel(context, x_pos, y_pos);
 | 
						|
    }
 | 
						|
 | 
						|
    for (x_pos=0; x_pos < 320; x_pos += 16)
 | 
						|
    {
 | 
						|
      PI1_16p_to_8b(pixels + x_pos, buffer);
 | 
						|
      if (!Write_bytes(file, buffer, 8))
 | 
						|
        goto error;
 | 
						|
    }
 | 
						|
  }
 | 
						|
 | 
						|
  fclose(file);
 | 
						|
  File_error = 0;
 | 
						|
  return;
 | 
						|
 | 
						|
error:
 | 
						|
  if (file != NULL)
 | 
						|
    fclose(file);
 | 
						|
  Remove_file(context);
 | 
						|
}
 | 
						|
 | 
						|
/* @} */
 | 
						|
 | 
						|
//////////////////////////////////// C64 ////////////////////////////////////
 | 
						|
 | 
						|
/** C64 file formats
 | 
						|
 */
 | 
						|
enum c64_format
 | 
						|
{
 | 
						|
  F_invalid = -1,
 | 
						|
  F_hires = 0,    ///< 320x200
 | 
						|
  F_multi = 1,    ///< 160x200
 | 
						|
  F_bitmap = 2,   ///< 320x200 monochrome
 | 
						|
  F_fli = 3       ///< FLI (Flexible Line Interpretation)
 | 
						|
};
 | 
						|
 | 
						|
/** C64 file formats names
 | 
						|
 */
 | 
						|
static const char *c64_format_names[] = {
 | 
						|
  "Hires",
 | 
						|
  "Multicolor",
 | 
						|
  "Bitmap",
 | 
						|
  "FLI"
 | 
						|
};
 | 
						|
 | 
						|
static long C64_unpack_doodle(byte ** file_buffer, long file_size);
 | 
						|
 | 
						|
/**
 | 
						|
 * Test for a C64 picture file
 | 
						|
 *
 | 
						|
 * Checks the file size and the load address
 | 
						|
 *
 | 
						|
 * References :
 | 
						|
 * - http://unusedino.de/ec64/technical/formats/bitmap.html
 | 
						|
 * - http://codebase64.org/doku.php?id=base:c64_grafix_files_specs_list_v0.03
 | 
						|
 * - https://sourceforge.net/p/view64/code/HEAD/tree/trunk/libview64.c#l3737
 | 
						|
 */
 | 
						|
void Test_C64(T_IO_Context * context, FILE * file)
 | 
						|
{
 | 
						|
  unsigned long file_size;
 | 
						|
  word load_addr;
 | 
						|
  byte header[14];
 | 
						|
 | 
						|
  (void)context;
 | 
						|
  File_error = 1;
 | 
						|
  file_size = File_length_file(file);
 | 
						|
  if (file_size < 16 || file_size > 48*1024)
 | 
						|
    return; // File too short or too long, exit now
 | 
						|
  // First test for formats without load address
 | 
						|
  switch (file_size)
 | 
						|
  {
 | 
						|
    // case 1000: // screen or color
 | 
						|
    case 8000: // raw bitmap
 | 
						|
    case 9000: // bitmap + ScreenRAM
 | 
						|
    case 10001: // multicolor
 | 
						|
    case 17472: // FLI (BlackMail)
 | 
						|
      File_error = 0;
 | 
						|
      return;
 | 
						|
    default: // then we don't know for now.
 | 
						|
      if (!Read_word_le(file, &load_addr))
 | 
						|
        return;
 | 
						|
  }
 | 
						|
  GFX2_Log(GFX2_DEBUG, "Test_C64() file_size=%ld LoadAddr=$%04X\n", file_size, load_addr);
 | 
						|
  if (!Read_bytes(file, header, sizeof(header)))
 | 
						|
    return;
 | 
						|
  if (memcmp(header, "DRAZPAINT", 9) == 0)
 | 
						|
  {
 | 
						|
    GFX2_Log(GFX2_DEBUG, "Test_C64() header=%.13s RLE code = $%02X\n", header, header[13]);
 | 
						|
    File_error = 0;
 | 
						|
    return;
 | 
						|
  }
 | 
						|
  // check last 2 bytes
 | 
						|
  if (fseek(file, -2, SEEK_END) < 0)
 | 
						|
    return;
 | 
						|
  if (!Read_bytes(file, header, 2))
 | 
						|
    return;
 | 
						|
  if (load_addr == 0x4000 && header[0] == 0xC2 && header[1] == 0x00) // Amica Paint EOF mark
 | 
						|
  {
 | 
						|
    File_error = 0;
 | 
						|
    return;
 | 
						|
  }
 | 
						|
  switch (file_size)
 | 
						|
  {
 | 
						|
    // case 1002: // (screen or color) + loadaddr
 | 
						|
    case 8002: // raw bitmap with loadaddr
 | 
						|
    case 9002: // bitmap + ScreenRAM + loadaddr
 | 
						|
      // $4000 => InterPaint Hi-Res (.iph)
 | 
						|
    case 9003: // bitmap + ScreenRAM + loadaddr (+ border ?)
 | 
						|
    case 9009: // bitmap + ScreenRAM + loadaddr
 | 
						|
      // $2000 => Art Studio
 | 
						|
    case 9218:
 | 
						|
      // $5C00 => Doodle
 | 
						|
    case 9332:
 | 
						|
      // $3F8E => Paint Magic (.pmg) 'JEDI' at offset $0010 and $2010
 | 
						|
    case 10003: // multicolor + loadaddr
 | 
						|
      // $4000 => InterPaint multicolor
 | 
						|
      // $6000 => Koala Painter
 | 
						|
    case 10004:
 | 
						|
      // $4000 => Face Paint (.fpt)
 | 
						|
    case 10006:
 | 
						|
      // $6000 => Run Paint (.rpm)
 | 
						|
    case 10018:
 | 
						|
      // $2000 => Advanced Art Studio
 | 
						|
    case 10022:
 | 
						|
      // $18DC => Micro Illustrator (uncompressed)
 | 
						|
    case 10050:
 | 
						|
      // $1800 => Picasso64
 | 
						|
    case 10218:
 | 
						|
      // $3C00 => Image System (.ism)
 | 
						|
    case 10219:
 | 
						|
      // $7800 => Saracen Paint (.sar)
 | 
						|
      File_error = 0;
 | 
						|
      break;
 | 
						|
    case 10242:
 | 
						|
      // $4000 => Artist 64 (.a64)
 | 
						|
      // $A000 => Blazing paddles (.pi)
 | 
						|
      // $5C00 => Rainbow Painter (.rp)
 | 
						|
      if (load_addr != 0x4000 && load_addr != 0xa000 && load_addr != 0x5c00)
 | 
						|
      {
 | 
						|
        File_error = 1;
 | 
						|
        return;
 | 
						|
      }
 | 
						|
      File_error = 0;
 | 
						|
      break;
 | 
						|
    case 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 = 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;
 | 
						|
        }
 | 
						|
      }
 | 
						|
  }
 | 
						|
}
 | 
						|
 | 
						|
/**
 | 
						|
 * 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 = 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 = 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 = 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=(byte *)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=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 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;
 | 
						|
}
 | 
						|
 | 
						|
/**
 | 
						|
 * 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 = malloc(file_size);
 | 
						|
  if (buffer == NULL)
 | 
						|
  {
 | 
						|
    GFX2_Log(GFX2_ERROR, "Failed to allocate %lu bytes.\n", file_size);
 | 
						|
    fclose(file);
 | 
						|
    return;
 | 
						|
  }
 | 
						|
  if (Read_bytes(file, buffer, file_size))
 | 
						|
  {
 | 
						|
    byte * gpx = NULL;
 | 
						|
    unsigned long gpx_size = 0;
 | 
						|
    int r;
 | 
						|
 | 
						|
    do
 | 
						|
    {
 | 
						|
      free(gpx);
 | 
						|
      gpx_size += 65536;
 | 
						|
      gpx = malloc(gpx_size);
 | 
						|
      if (gpx == NULL)
 | 
						|
      {
 | 
						|
        GFX2_Log(GFX2_ERROR, "Failed to allocate %lu bytes\n", gpx_size);
 | 
						|
        break;
 | 
						|
      }
 | 
						|
      r = uncompress(gpx, &gpx_size, buffer, file_size);
 | 
						|
      if (r != Z_BUF_ERROR && r != Z_OK)
 | 
						|
        GFX2_Log(GFX2_ERROR, "uncompress() failed with error %d: %s\n", r, zError(r));
 | 
						|
    }
 | 
						|
    while (r == Z_BUF_ERROR); // there was not enough room in the output buffer
 | 
						|
    if (r == Z_OK)
 | 
						|
    {
 | 
						|
      byte * p;
 | 
						|
      dword version, mode;
 | 
						|
/*
 | 
						|
 mode :
 | 
						|
0		BITMAP,
 | 
						|
1		MC_BITMAP,
 | 
						|
2		SPRITE,
 | 
						|
3		MC_SPRITE,
 | 
						|
4		CHAR,
 | 
						|
5   MC_CHAR,
 | 
						|
6		UNUSED1,
 | 
						|
7		UNUSED2,
 | 
						|
8		UNRESTRICTED,
 | 
						|
9		W_UNRESTRICTED
 | 
						|
*/
 | 
						|
      GFX2_Log(GFX2_DEBUG, "inflated %lu bytes to %lu\n", file_size, gpx_size);
 | 
						|
#define READU32LE(p) ((p)[0] | (p)[1] << 8 | (p)[2] << 16 | (p)[3] << 24)
 | 
						|
      version = READU32LE(gpx);
 | 
						|
      mode = READU32LE(gpx+4);
 | 
						|
      GFX2_Log(GFX2_DEBUG, "gpx version %u mode %u\n", version, mode);
 | 
						|
      snprintf(context->Comment, COMMENT_SIZE, "pixcen file version %u mode %u", version, mode);
 | 
						|
      if (version >= 4)
 | 
						|
      {
 | 
						|
        dword count;
 | 
						|
        const char * key;
 | 
						|
        word value[256];
 | 
						|
        int xsize = -1;
 | 
						|
        int ysize = -1;
 | 
						|
        int mapsize = -1;
 | 
						|
        int screensize = -1;
 | 
						|
        int colorsize = -1;
 | 
						|
        int backbuffers = -1;
 | 
						|
 | 
						|
        count = READU32LE(gpx+8);
 | 
						|
        p = gpx + 12;
 | 
						|
        while (count--)
 | 
						|
        {
 | 
						|
          int i = 0;
 | 
						|
          int int_value = 0;
 | 
						|
 | 
						|
          key = (const char *)p;
 | 
						|
          while (*p++);
 | 
						|
          for (;;)
 | 
						|
          {
 | 
						|
            value[i] = p[0] + (p[1] << 8);
 | 
						|
            p += 2;
 | 
						|
            if (value[i] == 0)
 | 
						|
              break;
 | 
						|
            int_value = int_value * 10 + (value[i] - '0');
 | 
						|
            i++;
 | 
						|
          }
 | 
						|
          GFX2_Log(GFX2_DEBUG, "%s=%d\n", key, int_value);
 | 
						|
          if (0 == strcmp(key, "xsize"))
 | 
						|
            xsize = int_value;
 | 
						|
          else if (0 == strcmp(key, "ysize"))
 | 
						|
            ysize = int_value;
 | 
						|
          else if (0 == strcmp(key, "mapsize"))
 | 
						|
            mapsize = int_value;
 | 
						|
          else if (0 == strcmp(key, "screensize"))
 | 
						|
            screensize = int_value;
 | 
						|
          else if (0 == strcmp(key, "colorsize"))
 | 
						|
            colorsize = int_value;
 | 
						|
          else if (0 == strcmp(key, "backbuffers"))
 | 
						|
            backbuffers = int_value;
 | 
						|
        }
 | 
						|
//buffersize = 64 + (64 + mapsize + screensize + colorsize) * backbuffers;
 | 
						|
        p += 64;  // 64 empty bytes ?
 | 
						|
        File_error = 0;
 | 
						|
        if (mode & 1)
 | 
						|
          context->Ratio = PIXEL_WIDE;
 | 
						|
        else
 | 
						|
          context->Ratio = PIXEL_SIMPLE;
 | 
						|
        Pre_load(context, xsize, ysize, file_size, FORMAT_GPX, context->Ratio, 4); // Do this as soon as you can
 | 
						|
        if (Config.Clear_palette)
 | 
						|
          memset(context->Palette,0, sizeof(T_Palette));
 | 
						|
        C64_set_palette(context->Palette);
 | 
						|
        context->Transparent_color=16;
 | 
						|
 | 
						|
        //foreach backbuffer
 | 
						|
        if (backbuffers >= 1)
 | 
						|
        {
 | 
						|
          byte border, background;
 | 
						|
          //byte ext0, ext1, ext2;
 | 
						|
          byte * bitmap, * color, * screen;
 | 
						|
 | 
						|
          //GFX2_LogHexDump(GFX2_DEBUG, "GPX ", p, 0, 64);
 | 
						|
          p += 47;  // Extra bytes
 | 
						|
          //crippled = p;
 | 
						|
          p += 6;
 | 
						|
          //lock = p;
 | 
						|
          p += 6;
 | 
						|
          border = *p++;
 | 
						|
          background = *p++;
 | 
						|
          /*ext0 = *p++;
 | 
						|
          ext1 = *p++;
 | 
						|
          ext2 = *p++;*/
 | 
						|
          p += 3;
 | 
						|
          bitmap = p;
 | 
						|
          p += mapsize;
 | 
						|
          color = p;
 | 
						|
          p += colorsize;
 | 
						|
          screen = p;
 | 
						|
          p += screensize;
 | 
						|
 | 
						|
          GFX2_Log(GFX2_DEBUG, "background color #%d, border color #%d\n", (int)background, (int)border);
 | 
						|
          Load_C64_multi(context, bitmap, screen, color, background);
 | 
						|
          Set_image_mode(context, (mode & 1) ? IMAGE_MODE_C64MULTI : IMAGE_MODE_C64HIRES);
 | 
						|
        }
 | 
						|
      }
 | 
						|
      else
 | 
						|
      {
 | 
						|
        GFX2_Log(GFX2_ERROR, "GPX file version %d unsupported\n", version);
 | 
						|
      }
 | 
						|
    }
 | 
						|
    free(gpx);
 | 
						|
  }
 | 
						|
  free(buffer);
 | 
						|
  fclose(file);
 | 
						|
}
 | 
						|
 | 
						|
/**
 | 
						|
 * Test for SCR file (Amstrad CPC)
 | 
						|
 *
 | 
						|
 * SCR file format is from "Advanced OCP Art Studio" :
 | 
						|
 * http://www.cpcwiki.eu/index.php/Format:Advanced_OCP_Art_Studio_File_Formats
 | 
						|
 *
 | 
						|
 * .WIN "window" format is also supported.
 | 
						|
 *
 | 
						|
 * For now we check the presence of a valid PAL file.
 | 
						|
 * If the PAL file is not there the pixel data may still be valid.
 | 
						|
 * The file size depends on the screen resolution.
 | 
						|
 * An AMSDOS header would be a good indication but in some cases it may not
 | 
						|
 * be there.
 | 
						|
 */
 | 
						|
void Test_SCR(T_IO_Context * context, FILE * file)
 | 
						|
{
 | 
						|
  FILE * pal_file;
 | 
						|
  unsigned long pal_size, file_size;
 | 
						|
  byte mode, color_anim_flag;
 | 
						|
 | 
						|
  File_error = 1;
 | 
						|
 | 
						|
  file_size = File_length_file(file);
 | 
						|
  if (file_size > 16384+128)
 | 
						|
    return;
 | 
						|
 | 
						|
  // requires the PAL file
 | 
						|
  pal_file = Open_file_read_with_alternate_ext(context, "pal");
 | 
						|
  if (pal_file == NULL)
 | 
						|
    return;
 | 
						|
  /** @todo the palette data can be hidden in the 48 "empty" bytes
 | 
						|
   * every 2048 bytes of a standard resolution SCR file.
 | 
						|
   * So we should detect the hidden Z80 code and load them.
 | 
						|
   * Load address of file is C000. Z80 code :<br>
 | 
						|
   * <tt>C7D0: 3a d0 d7 cd 1c bd 21 d1 d7 46 48 cd 38 bc af 21 | :.....!..FH.8..!</tt><br>
 | 
						|
   * <tt>C7E0: d1 d7 46 48 f5 e5 cd 32 bc e1 f1 23 3c fe 10 20 | ..FH...2...#<.. </tt><br>
 | 
						|
   * <tt>C7F0: f1 c3 18 bb 00 00 00 00 00 00 00 00 00 00 00 00 | ................</tt><br>
 | 
						|
   * mode and palette :<br>
 | 
						|
   * <tt>D7D0: 00 1a 00 0c 03 0b 01 0d 17 10 02 0f 09 19 06 00 | ................</tt><br>
 | 
						|
   * https://gitlab.com/GrafX2/grafX2/merge_requests/121#note_119964168
 | 
						|
   */
 | 
						|
 | 
						|
 | 
						|
  pal_size = File_length_file(pal_file);
 | 
						|
  if (pal_size == 239+128)
 | 
						|
  {
 | 
						|
    if (!CPC_check_AMSDOS(pal_file, NULL, NULL))
 | 
						|
    {
 | 
						|
      fclose(pal_file);
 | 
						|
      return;
 | 
						|
    }
 | 
						|
    fseek(pal_file, 128, SEEK_SET); // right after AMSDOS header
 | 
						|
  }
 | 
						|
  else if (pal_size != 239)
 | 
						|
  {
 | 
						|
    fclose(pal_file);
 | 
						|
    return;
 | 
						|
  }
 | 
						|
 | 
						|
  if (!Read_byte(pal_file, &mode) || !Read_byte(pal_file, &color_anim_flag))
 | 
						|
  {
 | 
						|
    fclose(pal_file);
 | 
						|
    return;
 | 
						|
  }
 | 
						|
  GFX2_Log(GFX2_DEBUG, "Test_SCR() mode=%d color animation flag %02X\n", mode, color_anim_flag);
 | 
						|
  if (mode <= 2 && (color_anim_flag == 0 || color_anim_flag == 0xff))
 | 
						|
    File_error = 0;
 | 
						|
  fclose(pal_file);
 | 
						|
}
 | 
						|
 | 
						|
/**
 | 
						|
 * Load Advanced OCP Art Studio files (Amstrad CPC)
 | 
						|
 *
 | 
						|
 * Only standard resolution files (Mode 0 160x200, mode 1 320x200 and
 | 
						|
 * mode 2 640x200) are supported. The .PAL file presence is required.
 | 
						|
 * "MJH" RLE packing is supported.
 | 
						|
 *
 | 
						|
 * .WIN "window" format is also supported.
 | 
						|
 *
 | 
						|
 * @todo Ask user for screen size (or register values) in order to support
 | 
						|
 * non standard resolutions.
 | 
						|
 */
 | 
						|
void Load_SCR(T_IO_Context * context)
 | 
						|
{
 | 
						|
    // The Amstrad CPC screen memory is mapped in a weird mode, somewhere
 | 
						|
    // between bitmap and textmode. Basically the only way to decode this is to
 | 
						|
    // emulate the video chip and read the bytes as needed...
 | 
						|
    // Moreover, the hardware allows the screen to have any size from 8x1 to
 | 
						|
    // 800x273 pixels, and there is no indication of that in the file besides
 | 
						|
    // its size. It can also use any of the 3 screen modes. Fortunately this
 | 
						|
    // last bit of information is stored in the palette file.
 | 
						|
    // Oh, and BTW, the picture can be offset, and it's even usual to do it,
 | 
						|
    // because letting 128 pixels unused at the beginning of the file make it a
 | 
						|
    // lot easier to handle screens using more than 16K of VRam.
 | 
						|
    // The pixel encoding change with the video mode so we have to know that
 | 
						|
    // before attempting to load anything...
 | 
						|
    // As if this wasn't enough, Advanced OCP Art Studio, the reference tool on
 | 
						|
    // Amstrad, can use RLE packing when saving files, meaning we also have to
 | 
						|
    // handle that.
 | 
						|
 | 
						|
    // All this mess enforces us to load (and unpack if needed) the file to a
 | 
						|
    // temporary 32k buffer before actually decoding it.
 | 
						|
  FILE * pal_file, * file;
 | 
						|
  unsigned long real_file_size, file_size, amsdos_file_size = 0;
 | 
						|
  byte mode, color_anim_flag, color_anim_delay;
 | 
						|
  byte pal_data[236]; // 12 palettes of 16+1 colors + 16 excluded inks + 16 protected inks
 | 
						|
  word width, height = 200;
 | 
						|
  byte bpp;
 | 
						|
  enum PIXEL_RATIO ratio;
 | 
						|
  byte * pixel_data;
 | 
						|
  word x, y;
 | 
						|
  int i;
 | 
						|
  byte sig[3];
 | 
						|
  word block_length;
 | 
						|
  word win_width, win_height;
 | 
						|
  int is_win = 0;
 | 
						|
  int columns = 80;
 | 
						|
 | 
						|
  File_error = 1;
 | 
						|
  // requires the PAL file
 | 
						|
  pal_file = Open_file_read_with_alternate_ext(context, "pal");
 | 
						|
  if (pal_file == NULL)
 | 
						|
    return;
 | 
						|
  file_size = File_length_file(pal_file);
 | 
						|
  if (file_size == 239+128)
 | 
						|
  {
 | 
						|
    if (!CPC_check_AMSDOS(pal_file, NULL, NULL))
 | 
						|
    {
 | 
						|
      fclose(pal_file);
 | 
						|
      return;
 | 
						|
    }
 | 
						|
    fseek(pal_file, 128, SEEK_SET); // right after AMSDOS header
 | 
						|
  }
 | 
						|
  if (!Read_byte(pal_file, &mode) || !Read_byte(pal_file, &color_anim_flag)
 | 
						|
      || !Read_byte(pal_file, &color_anim_delay) || !Read_bytes(pal_file, pal_data, 236))
 | 
						|
  {
 | 
						|
    GFX2_Log(GFX2_WARNING, "Load_SCR() failed to load .PAL file\n");
 | 
						|
    fclose(pal_file);
 | 
						|
    return;
 | 
						|
  }
 | 
						|
  fclose(pal_file);
 | 
						|
  GFX2_Log(GFX2_DEBUG, "Load_SCR() mode=%d color animation flag=%02X delay=%u\n",
 | 
						|
           mode, color_anim_flag, color_anim_delay);
 | 
						|
  switch (mode)
 | 
						|
  {
 | 
						|
    case 0:
 | 
						|
      width = 160;
 | 
						|
      bpp = 4;
 | 
						|
      ratio = PIXEL_WIDE;
 | 
						|
      break;
 | 
						|
    case 1:
 | 
						|
      width = 320;
 | 
						|
      bpp = 2;
 | 
						|
      ratio = PIXEL_SIMPLE;
 | 
						|
      break;
 | 
						|
    case 2:
 | 
						|
      width = 640;
 | 
						|
      bpp = 1;
 | 
						|
      ratio = PIXEL_TALL;
 | 
						|
      break;
 | 
						|
    default:
 | 
						|
      return; // unsupported
 | 
						|
  }
 | 
						|
 | 
						|
  if (Config.Clear_palette)
 | 
						|
    memset(context->Palette,0,sizeof(T_Palette));
 | 
						|
  // Setup the palette (amstrad hardware palette)
 | 
						|
  CPC_set_HW_palette(context->Palette + 0x40);
 | 
						|
 | 
						|
  // Set the palette for this picture
 | 
						|
  for (i = 0; i < 16; i++)
 | 
						|
    context->Palette[i] = context->Palette[pal_data[12*i]];
 | 
						|
 | 
						|
  file = Open_file_read(context);
 | 
						|
  if (file == NULL)
 | 
						|
    return;
 | 
						|
  file_size = File_length_file(file);
 | 
						|
  real_file_size = file_size;
 | 
						|
  if (CPC_check_AMSDOS(file, NULL, &amsdos_file_size))
 | 
						|
  {
 | 
						|
    if (file_size < (amsdos_file_size + 128))
 | 
						|
    {
 | 
						|
      GFX2_Log(GFX2_ERROR, "Load_SCR() mismatch in file size. AMSDOS file size %lu, should be %lu\n", amsdos_file_size, file_size - 128);
 | 
						|
      fclose(file);
 | 
						|
      return;
 | 
						|
    }
 | 
						|
    else if (file_size > (amsdos_file_size + 128))
 | 
						|
      GFX2_Log(GFX2_INFO, "Load_SCR() %lu extra bytes at end of file\n", file_size - 128 - amsdos_file_size);
 | 
						|
    fseek(file, 128, SEEK_SET); // right after AMSDOS header
 | 
						|
    file_size = amsdos_file_size;
 | 
						|
  }
 | 
						|
  else
 | 
						|
    fseek(file, 0, SEEK_SET);
 | 
						|
 | 
						|
  if (file_size > 16384)  // we don't support bigger files yet
 | 
						|
  {
 | 
						|
    fclose(file);
 | 
						|
    return;
 | 
						|
  }
 | 
						|
 | 
						|
  if (!Read_bytes(file, sig, 3) || !Read_word_le(file, &block_length))
 | 
						|
  {
 | 
						|
    fclose(file);
 | 
						|
    return;
 | 
						|
  }
 | 
						|
  fseek(file, -5, SEEK_CUR);
 | 
						|
 | 
						|
  pixel_data = malloc(16384);
 | 
						|
  memset(pixel_data, 0, 16384);
 | 
						|
 | 
						|
  if (0 != memcmp(sig, "MJH", 3) || block_length > 16384)
 | 
						|
  {
 | 
						|
    // raw data
 | 
						|
    Read_bytes(file, pixel_data, file_size);
 | 
						|
    i = file_size;
 | 
						|
  }
 | 
						|
  else
 | 
						|
  {
 | 
						|
    // MJH packed format
 | 
						|
    i = 0;
 | 
						|
    do
 | 
						|
    {
 | 
						|
      if (!Read_bytes(file, sig, 3) || !Read_word_le(file, &block_length))
 | 
						|
        break;
 | 
						|
      if (0 != memcmp(sig, "MJH", 3))
 | 
						|
        break;
 | 
						|
      GFX2_Log(GFX2_DEBUG, "  %.3s block %u\n", sig, block_length);
 | 
						|
      file_size -= 5;
 | 
						|
      while (block_length > 0)
 | 
						|
      {
 | 
						|
        byte code;
 | 
						|
        if (!Read_byte(file, &code))
 | 
						|
          break;
 | 
						|
        file_size--;
 | 
						|
        if (code == 1)
 | 
						|
        {
 | 
						|
          byte repeat, value;
 | 
						|
          if (!Read_byte(file, &repeat) || !Read_byte(file, &value))
 | 
						|
            break;
 | 
						|
          file_size -= 2;
 | 
						|
          do
 | 
						|
          {
 | 
						|
            pixel_data[i++] = value;
 | 
						|
            block_length--;
 | 
						|
          }
 | 
						|
          while(--repeat != 0);
 | 
						|
        }
 | 
						|
        else
 | 
						|
        {
 | 
						|
          pixel_data[i++] = code;
 | 
						|
          block_length--;
 | 
						|
        }
 | 
						|
      }
 | 
						|
      GFX2_Log(GFX2_DEBUG, "  unpacked %d bytes. remaining bytes in file=%lu\n",
 | 
						|
               i, file_size);
 | 
						|
    }
 | 
						|
    while(file_size > 0 && i < 16384);
 | 
						|
  }
 | 
						|
  fclose(file);
 | 
						|
 | 
						|
  if (i > 5)
 | 
						|
  {
 | 
						|
    win_width = pixel_data[i-4] + (pixel_data[i-3] << 8);  // in bits
 | 
						|
    win_height = pixel_data[i-2];
 | 
						|
    if (((win_width + 7) >> 3) * win_height + 5 == i) // that's a WIN file !
 | 
						|
    {
 | 
						|
      width = win_width >> (2 - mode);
 | 
						|
      height = win_height;
 | 
						|
      is_win = 1;
 | 
						|
      columns = (win_width + 7) >> 3;
 | 
						|
      GFX2_Log(GFX2_DEBUG, ".WIN file detected len=%d (%d,%d) %dcols %02X %02X %02X %02X %02X\n",
 | 
						|
          i, width, height, columns,
 | 
						|
          pixel_data[i-5], pixel_data[i-4], pixel_data[i-3],
 | 
						|
          pixel_data[i-2], pixel_data[i-1]);
 | 
						|
    }
 | 
						|
    else
 | 
						|
    {
 | 
						|
      ///@todo guess the picture size, or ask the user
 | 
						|
      GFX2_Log(GFX2_DEBUG, ".SCR file. Data length %d\n", i);
 | 
						|
      // i <= 16384 && i >= 16336 => Standard resolution
 | 
						|
      if (i <= 16384 && i >= 16336)
 | 
						|
      {
 | 
						|
        int j;
 | 
						|
        // Standard resolution files have the 200 lines stored in block
 | 
						|
        // of 25 lines of 80 bytes = 2000 bytes every 2048 bytes.
 | 
						|
        // so there are 48 bytes unused every 2048 bytes...
 | 
						|
        for (j = 0; j < i; j += 2048)
 | 
						|
          GFX2_LogHexDump(GFX2_DEBUG, "SCR ", pixel_data, j+2000, 48);
 | 
						|
      }
 | 
						|
    }
 | 
						|
  }
 | 
						|
 | 
						|
  Pre_load(context, width, height, real_file_size, FORMAT_SCR, ratio, bpp);
 | 
						|
 | 
						|
  for (y = 0; y < height; y++)
 | 
						|
  {
 | 
						|
    const byte * line;
 | 
						|
 | 
						|
    if (is_win)
 | 
						|
      line = pixel_data + y * columns;
 | 
						|
    else
 | 
						|
      line = pixel_data + ((y & 7) << 11) + ((y >> 3) * columns);
 | 
						|
    x = 0;
 | 
						|
    for (i = 0; i < columns; i++)
 | 
						|
    {
 | 
						|
      byte pixels = line[i];
 | 
						|
      switch (mode)
 | 
						|
      {
 | 
						|
        case 0:
 | 
						|
          Set_pixel(context, x++, y, (pixels & 0x80) >> 7 | (pixels & 0x08) >> 2 | (pixels & 0x20) >> 3 | (pixels & 0x02) << 2);
 | 
						|
          Set_pixel(context, x++, y, (pixels & 0x40) >> 6 | (pixels & 0x04) >> 1 | (pixels & 0x10) >> 2 | (pixels & 0x01) << 3);
 | 
						|
          break;
 | 
						|
        case 1:
 | 
						|
          do {
 | 
						|
            // upper nibble is 4 lower color bits, lower nibble is 4 upper color bits
 | 
						|
            Set_pixel(context, x++, y, (pixels & 0x80) >> 7 | (pixels & 0x08) >> 2);
 | 
						|
            pixels <<= 1;
 | 
						|
          }
 | 
						|
          while ((x & 3) != 0);
 | 
						|
          break;
 | 
						|
        case 2:
 | 
						|
          do {
 | 
						|
            Set_pixel(context, x++, y, (pixels & 0x80) >> 7);
 | 
						|
            pixels <<= 1;
 | 
						|
          }
 | 
						|
          while ((x & 7) != 0);
 | 
						|
      }
 | 
						|
    }
 | 
						|
  }
 | 
						|
 | 
						|
  free(pixel_data);
 | 
						|
 | 
						|
  File_error = 0;
 | 
						|
}
 | 
						|
 | 
						|
/**
 | 
						|
 * Save Amstrad SCR file
 | 
						|
 *
 | 
						|
 * guess mode from aspect ratio :
 | 
						|
 * - normal pixels are mode 1
 | 
						|
 * - wide pixels are mode 0
 | 
						|
 * - tall pixels are mode 2
 | 
						|
 *
 | 
						|
 * Mode and palette are stored in a .PAL file.
 | 
						|
 *
 | 
						|
 * The picture color index should be 0-15,
 | 
						|
 * The CPC Hardware palette is expected to be set (indexes 64 to 95)
 | 
						|
 *
 | 
						|
 * @todo Add possibility to set R9, R12, R13 values
 | 
						|
 * @todo Add OCP packing support
 | 
						|
 * @todo Add possibility to include AMSDOS header, with proper loading
 | 
						|
 *       address guessed from r12/r13 values.
 | 
						|
 */
 | 
						|
void Save_SCR(T_IO_Context * context)
 | 
						|
{
 | 
						|
  int i, j;
 | 
						|
  unsigned char* output;
 | 
						|
  unsigned long outsize = 0;
 | 
						|
  unsigned char r1 = 0;
 | 
						|
  int cpc_mode;
 | 
						|
  FILE* file;
 | 
						|
 | 
						|
 | 
						|
  switch(Pixel_ratio)
 | 
						|
  {
 | 
						|
    case PIXEL_WIDE:
 | 
						|
    case PIXEL_WIDE2:
 | 
						|
      cpc_mode = 0;
 | 
						|
      break;
 | 
						|
    case PIXEL_TALL:
 | 
						|
    case PIXEL_TALL2:
 | 
						|
    case PIXEL_TALL3:
 | 
						|
      cpc_mode = 2;
 | 
						|
      break;
 | 
						|
    default:
 | 
						|
      cpc_mode = 1;
 | 
						|
      break;
 | 
						|
  }
 | 
						|
 | 
						|
  file = Open_file_write_with_alternate_ext(context, "pal");
 | 
						|
  if (file == NULL)
 | 
						|
    return;
 | 
						|
  if (!Write_byte(file, cpc_mode) || !Write_byte(file, 0) || !Write_byte(file, 0))
 | 
						|
  {
 | 
						|
    fclose(file);
 | 
						|
    return;
 | 
						|
  }
 | 
						|
  for (i = 0; i < 16; i++)
 | 
						|
  {
 | 
						|
    // search for the color in the HW palette (0x40-0x5F)
 | 
						|
    byte index = 0x40;
 | 
						|
    while ((index < 0x60) &&
 | 
						|
        !CPC_compare_colors(context->Palette + i, context->Palette + index))
 | 
						|
      index++;
 | 
						|
    if (index >= 0x60)
 | 
						|
    {
 | 
						|
      GFX2_Log(GFX2_WARNING, "Save_SCR() color #%i not found in CPC HW palette.\n", i);
 | 
						|
      index = 0x54 - i; // default
 | 
						|
    }
 | 
						|
    for (j = 0; j < 12; j++)  // write the same color for the 12 frames
 | 
						|
    {
 | 
						|
      Write_byte(file, index);
 | 
						|
    }
 | 
						|
  }
 | 
						|
  // border
 | 
						|
  for (j = 0; j < 12; j++)
 | 
						|
  {
 | 
						|
    Write_byte(file, 0x54); // black
 | 
						|
  }
 | 
						|
  // excluded inks
 | 
						|
  for (i = 0; i < 16; i++)
 | 
						|
  {
 | 
						|
    Write_byte(file, 0);
 | 
						|
  }
 | 
						|
  // protected inks
 | 
						|
  for (i = 0; i < 16; i++)
 | 
						|
  {
 | 
						|
    Write_byte(file, 0);
 | 
						|
  }
 | 
						|
  fclose(file);
 | 
						|
 | 
						|
  output = raw2crtc(context, cpc_mode, 7, &outsize, &r1, 0x0C, 0);
 | 
						|
  GFX2_Log(GFX2_DEBUG, "Save_SCR() output=%p outsize=%lu r1=$%02X\n", output, outsize, r1);
 | 
						|
 | 
						|
  if (output == NULL)
 | 
						|
    return;
 | 
						|
 | 
						|
  file = Open_file_write(context);
 | 
						|
  if (file == NULL)
 | 
						|
    File_error = 1;
 | 
						|
  else
 | 
						|
  {
 | 
						|
    File_error = 0;
 | 
						|
    if (!Write_bytes(file, output, outsize))
 | 
						|
      File_error = 1;
 | 
						|
    fclose(file);
 | 
						|
  }
 | 
						|
  free (output);
 | 
						|
}
 | 
						|
 | 
						|
/**
 | 
						|
 * Test for CM5 - Amstrad CPC "Mode 5" picture
 | 
						|
 *
 | 
						|
 * This is a format designed by SyX.
 | 
						|
 * There is one .GFX file in the usual amstrad format
 | 
						|
 * and a .CM5 file with the palette, which varies over time.
 | 
						|
 *
 | 
						|
 * CM5 file is 2049 bytes, GFX is 18432 bytes.
 | 
						|
 *
 | 
						|
 * @todo check CM5 contains only valid values [0x40-0x5f]
 | 
						|
 */
 | 
						|
void Test_CM5(T_IO_Context * context, FILE * file)
 | 
						|
{
 | 
						|
  // check cm5 file size == 2049 bytes
 | 
						|
  FILE *file_gfx;
 | 
						|
  long file_size;
 | 
						|
 | 
						|
  File_error = 1;
 | 
						|
 | 
						|
  file_size = File_length_file(file);
 | 
						|
  if (file_size != 2049)
 | 
						|
    return;
 | 
						|
 | 
						|
  // check existence of a .GFX file with the same name
 | 
						|
  file_gfx = Open_file_read_with_alternate_ext(context, "gfx");
 | 
						|
  if (file_gfx == NULL)
 | 
						|
    return;
 | 
						|
  file_size = File_length_file(file_gfx);
 | 
						|
  fclose(file_gfx);
 | 
						|
  if (file_size != 18432)
 | 
						|
    return;
 | 
						|
 | 
						|
  File_error = 0;
 | 
						|
}
 | 
						|
 | 
						|
 | 
						|
/**
 | 
						|
 * Load Amstrad CPC "Mode 5" picture
 | 
						|
 *
 | 
						|
 * Only support 288x256 resolution as the Mode 5 Viewer app only handles this
 | 
						|
 * single resoltion.
 | 
						|
 */
 | 
						|
void Load_CM5(T_IO_Context* context)
 | 
						|
{
 | 
						|
  // Ensure "8bit" constraint mode is switched on
 | 
						|
  // Set palette to the CPC hardware colors
 | 
						|
  // Load the palette data to the 4 colorlayers
 | 
						|
  FILE *file;
 | 
						|
  byte value = 0;
 | 
						|
  int mod=0;
 | 
						|
  short line = 0;
 | 
						|
  int tx, ty;
 | 
						|
  // for preview :
 | 
						|
  byte ink0;
 | 
						|
  byte ink1[256];
 | 
						|
  byte ink2[256];
 | 
						|
  byte ink3[256*6];
 | 
						|
 | 
						|
  if (!(file = Open_file_read(context)))
 | 
						|
  {
 | 
						|
      File_error = 1;
 | 
						|
      return;
 | 
						|
  }
 | 
						|
 | 
						|
  Pre_load(context, 48*6, 256, 2049, FORMAT_CM5, PIXEL_SIMPLE, 0);
 | 
						|
 | 
						|
  if (Config.Clear_palette)
 | 
						|
  {
 | 
						|
    memset(context->Palette,0,sizeof(T_Palette));
 | 
						|
    // setup colors 0,1,2,3 to see something in the thumbnail preview of layer 5
 | 
						|
    context->Palette[1].R = 60;
 | 
						|
    context->Palette[2].B = 60;
 | 
						|
    context->Palette[3].G = 60;
 | 
						|
  }
 | 
						|
 | 
						|
  // Setup the palette (amstrad hardware palette)
 | 
						|
  CPC_set_HW_palette(context->Palette + 0x40);
 | 
						|
 | 
						|
  First_color_in_palette = 64;
 | 
						|
 | 
						|
  if (!Read_byte(file, &ink0))
 | 
						|
    File_error = 2;
 | 
						|
 | 
						|
  // This forces the creation of 5 layers total :
 | 
						|
  // Needed because the "pixel" functions will seek layer 4
 | 
						|
  Set_loading_layer(context, 4);
 | 
						|
  // Now select layer 1 again
 | 
						|
  Set_loading_layer(context, 0);
 | 
						|
 | 
						|
  if (context->Type == CONTEXT_MAIN_IMAGE)
 | 
						|
  {
 | 
						|
    Set_image_mode(context, IMAGE_MODE_MODE5);
 | 
						|
 | 
						|
    // Fill layer with color we just read (Layer 1 - INK 0)
 | 
						|
    for(ty=0; ty<context->Height; ty++)
 | 
						|
      for(tx=0; tx<context->Width; tx++)
 | 
						|
        Set_pixel(context, tx, ty, ink0);
 | 
						|
  }
 | 
						|
 | 
						|
  while(Read_byte(file, &value))
 | 
						|
  {
 | 
						|
    switch(mod)
 | 
						|
    {
 | 
						|
      case 0:
 | 
						|
        // This is color for layer 2 - INK 1
 | 
						|
        Set_loading_layer(context, 1);
 | 
						|
        for(tx=0; tx<context->Width; tx++)
 | 
						|
          Set_pixel(context, tx, line, value);
 | 
						|
        ink1[line] = value;
 | 
						|
        break;
 | 
						|
      case 1:
 | 
						|
        // This is color for layer 3 - INK 2
 | 
						|
        Set_loading_layer(context, 2);
 | 
						|
        for(tx=0; tx<context->Width; tx++)
 | 
						|
          Set_pixel(context, tx, line, value);
 | 
						|
        ink2[line] = value;
 | 
						|
        break;
 | 
						|
      default:
 | 
						|
        // This is color for a block in layer 4 - INK 3
 | 
						|
        Set_loading_layer(context, 3);
 | 
						|
        for(tx=(mod-2)*48; tx<(mod-1)*48; tx++)
 | 
						|
          Set_pixel(context, tx, line, value);
 | 
						|
        ink3[line*6+(mod-2)] = value;
 | 
						|
        break;
 | 
						|
    }
 | 
						|
    mod++;
 | 
						|
    if (mod > 7)
 | 
						|
    {
 | 
						|
      mod = 0;
 | 
						|
      line++;
 | 
						|
    }
 | 
						|
  }
 | 
						|
 | 
						|
  fclose(file);
 | 
						|
 | 
						|
  // Load the pixeldata to the 5th layer
 | 
						|
  file = Open_file_read_with_alternate_ext(context, "gfx");
 | 
						|
  if (file == NULL)
 | 
						|
  {
 | 
						|
    File_error = 1;
 | 
						|
    return;
 | 
						|
  }
 | 
						|
  Set_loading_layer(context, 4);
 | 
						|
 | 
						|
  if (context->Type == CONTEXT_PREVIEW)
 | 
						|
    for (ty = 0; ty < 256; ty++)
 | 
						|
      for (tx = 0; tx < 48*6; )
 | 
						|
      {
 | 
						|
        Read_byte(file, &value);
 | 
						|
        for (mod = 0; mod < 4; mod++, tx++, value <<= 1)
 | 
						|
        {
 | 
						|
          switch(3 ^ (((value&0x80) >> 7) | ((value&0x8)>>2)))  // INK
 | 
						|
          {
 | 
						|
            case 0:
 | 
						|
              Set_pixel(context, tx, ty, ink0);
 | 
						|
              break;
 | 
						|
            case 1:
 | 
						|
              Set_pixel(context, tx, ty, ink1[ty]);
 | 
						|
              break;
 | 
						|
            case 2:
 | 
						|
              Set_pixel(context, tx, ty, ink2[ty]);
 | 
						|
              break;
 | 
						|
            default:
 | 
						|
              Set_pixel(context, tx, ty, ink3[ty*6+(tx/48)]);
 | 
						|
          }
 | 
						|
        }
 | 
						|
      }
 | 
						|
  else
 | 
						|
    for (ty = 0; ty < 256; ty++)
 | 
						|
      for (tx = 0; tx < 48*6; )
 | 
						|
      {
 | 
						|
        Read_byte(file, &value);
 | 
						|
        Set_pixel(context, tx++, ty, 3 ^ (((value&0x80) >> 7) | ((value&0x8)>>2)));
 | 
						|
        Set_pixel(context, tx++, ty, 3 ^ (((value&0x40) >> 6) | ((value&0x4)>>1)));
 | 
						|
        Set_pixel(context, tx++, ty, 3 ^ (((value&0x20) >> 5) | ((value&0x2)>>0)));
 | 
						|
        Set_pixel(context, tx++, ty, 3 ^ (((value&0x10) >> 4) | ((value&0x1)<<1)));
 | 
						|
      }
 | 
						|
 | 
						|
  fclose(file);
 | 
						|
 | 
						|
}
 | 
						|
 | 
						|
 | 
						|
void Save_CM5(T_IO_Context* context)
 | 
						|
{
 | 
						|
  FILE* file;
 | 
						|
  int tx, ty;
 | 
						|
 | 
						|
  // TODO: Check picture has 5 layers
 | 
						|
  // TODO: Check the constraints on the layers
 | 
						|
  // Layer 1 : 1 color Only
 | 
						|
  // Layer 2 and 3 : 1 color/line
 | 
						|
  // Layer 4 : 1 color / 48x1 block
 | 
						|
  // TODO: handle filesize
 | 
						|
 | 
						|
  if (!(file = Open_file_write(context)))
 | 
						|
  {
 | 
						|
    File_error = 1;
 | 
						|
    return;
 | 
						|
  }
 | 
						|
  setvbuf(file, NULL, _IOFBF, 64*1024);
 | 
						|
 | 
						|
  // Write layer 0
 | 
						|
  Set_saving_layer(context, 0);
 | 
						|
  Write_byte(file, Get_pixel(context, 0, 0));
 | 
						|
  for(ty = 0; ty < 256; ty++)
 | 
						|
  {
 | 
						|
    Set_saving_layer(context, 1);
 | 
						|
    Write_byte(file, Get_pixel(context, 0, ty));
 | 
						|
    Set_saving_layer(context, 2);
 | 
						|
    Write_byte(file, Get_pixel(context, 0, ty));
 | 
						|
    Set_saving_layer(context, 3);
 | 
						|
    for(tx = 0; tx < 6; tx++)
 | 
						|
    {
 | 
						|
      Write_byte(file, Get_pixel(context, tx*48, ty));
 | 
						|
    }
 | 
						|
  }
 | 
						|
 | 
						|
  fclose(file);
 | 
						|
 | 
						|
  // Now the pixeldata
 | 
						|
  if (!(file = Open_file_write_with_alternate_ext(context, "gfx")))
 | 
						|
  {
 | 
						|
    File_error = 2;
 | 
						|
    return;
 | 
						|
  }
 | 
						|
  setvbuf(file, NULL, _IOFBF, 64*1024);
 | 
						|
 | 
						|
  Set_saving_layer(context, 4);
 | 
						|
 | 
						|
  for (ty = 0; ty < 256; ty++)
 | 
						|
  {
 | 
						|
    for (tx = 0; tx < 48*6; tx+=4)
 | 
						|
    {
 | 
						|
      byte code = 0;
 | 
						|
      byte pixel;
 | 
						|
 | 
						|
      pixel = 3-Get_pixel(context, tx+3, ty);
 | 
						|
      code |= (pixel&2)>>1 | ((pixel & 1)<<4);
 | 
						|
      pixel = 3-Get_pixel(context, tx+2, ty);
 | 
						|
      code |= ((pixel&2)<<0) | ((pixel & 1)<<5);
 | 
						|
      pixel = 3-Get_pixel(context, tx+1, ty);
 | 
						|
      code |= ((pixel&2)<<1) | ((pixel & 1)<<6);
 | 
						|
      pixel = 3-Get_pixel(context, tx, ty);
 | 
						|
      code |= ((pixel&2)<<2) | ((pixel & 1)<<7);
 | 
						|
      Write_byte(file, code);
 | 
						|
    }
 | 
						|
  }
 | 
						|
 | 
						|
  fclose(file);
 | 
						|
  File_error = 0;
 | 
						|
 | 
						|
}
 | 
						|
 | 
						|
 | 
						|
/* Amstrad CPC 'PPH' for Perfect Pix.
 | 
						|
// This is a format designed by Rhino.
 | 
						|
// There are 3 modes:
 | 
						|
// - Mode 'R': 1:1 pixels, 16 colors from the CPC 27 color palette.
 | 
						|
//   (this is implemented on CPC as two pictures with wide pixels, the "odd" one
 | 
						|
//   being shifted half a pixel to the right), and flipping)
 | 
						|
// - Mode 'B0': wide pixels, up to 126 out of 378 colors.
 | 
						|
//   (this is implemented as two pictures with wide pixels, sharing the same 16
 | 
						|
//   color palette, and flipping)
 | 
						|
// - Mode 'B1': 1:1 pixels, 1 fixed color, up to 34 palettes of 9 colors
 | 
						|
//   (actually 4 colors + flipping)
 | 
						|
//
 | 
						|
// - The standard CPC formats can also be encapsulated into a PPH file.
 | 
						|
//
 | 
						|
// http://www.pouet.net/prod.php?which=67770#c766959
 | 
						|
*/
 | 
						|
void Test_PPH(T_IO_Context * context, FILE * file)
 | 
						|
{
 | 
						|
  FILE *file_oddeve;
 | 
						|
  byte buffer[6];
 | 
						|
  unsigned long file_size;
 | 
						|
  unsigned int w, h;
 | 
						|
  unsigned int expected;
 | 
						|
 | 
						|
  File_error = 1;
 | 
						|
 | 
						|
  // First check file size is large enough to hold the header
 | 
						|
  file_size = File_length_file(file);
 | 
						|
  if (file_size < 11) {
 | 
						|
    File_error = 1;
 | 
						|
    return;
 | 
						|
  }
 | 
						|
 | 
						|
  // File is large enough for the header, now check if the data makes some sense
 | 
						|
  if (!Read_bytes(file, buffer, 6))
 | 
						|
    return;
 | 
						|
  if (buffer[0] > 5) {
 | 
						|
    // Unknown mode
 | 
						|
    File_error = 2;
 | 
						|
    return;
 | 
						|
  }
 | 
						|
 | 
						|
  w = buffer[1] | (buffer[2] << 8);
 | 
						|
  if (w < 2 || w > 384) {
 | 
						|
    // Invalid width
 | 
						|
    File_error = 3;
 | 
						|
    return;
 | 
						|
  }
 | 
						|
 | 
						|
  h = buffer[3] | (buffer[4] << 8);
 | 
						|
  if (h < 1 || h > 272) {
 | 
						|
    // Invalid height
 | 
						|
    File_error = 4;
 | 
						|
    return;
 | 
						|
  }
 | 
						|
 | 
						|
  if (buffer[5] < 1 || buffer[5] > 28)
 | 
						|
  {
 | 
						|
    // Invalid palettes count
 | 
						|
    File_error = 5;
 | 
						|
    return;
 | 
						|
  }
 | 
						|
  expected = 6; // Size of header
 | 
						|
  switch(buffer[0])
 | 
						|
  {
 | 
						|
    case 0:
 | 
						|
    case 3:
 | 
						|
    case 4:
 | 
						|
      // Palette size should be 16 bytes, only 1 palette.
 | 
						|
      if (buffer[5] != 1) {
 | 
						|
        File_error = 7;
 | 
						|
        return;
 | 
						|
      }
 | 
						|
      expected += 16;
 | 
						|
      break;
 | 
						|
 | 
						|
    case 1:
 | 
						|
    case 5:
 | 
						|
      expected += buffer[5] * 5 - 1;
 | 
						|
      break;
 | 
						|
 | 
						|
    case 2:
 | 
						|
      // Palette size should be 2 bytes
 | 
						|
      if (buffer[5] != 1) {
 | 
						|
        File_error = 7;
 | 
						|
        return;
 | 
						|
      }
 | 
						|
      expected += 2;
 | 
						|
      break;
 | 
						|
  }
 | 
						|
 | 
						|
  if (file_size != expected)
 | 
						|
  {
 | 
						|
    File_error = 6;
 | 
						|
    return;
 | 
						|
  }
 | 
						|
 | 
						|
  // check existence of .ODD/.EVE files with the same name
 | 
						|
  // and the right size
 | 
						|
  expected = w * h / 4;
 | 
						|
  file_oddeve = Open_file_read_with_alternate_ext(context, "odd");
 | 
						|
  if (file_oddeve == NULL)
 | 
						|
    return;
 | 
						|
  file_size = File_length_file(file_oddeve);
 | 
						|
  fclose (file_oddeve);
 | 
						|
  if (file_size != expected)
 | 
						|
  {
 | 
						|
    File_error = 8;
 | 
						|
    return;
 | 
						|
  }
 | 
						|
  file_oddeve = Open_file_read_with_alternate_ext(context, "eve");
 | 
						|
  if (file_oddeve == NULL)
 | 
						|
    return;
 | 
						|
  file_size = File_length_file(file_oddeve);
 | 
						|
  fclose(file_oddeve);
 | 
						|
  if (file_size != expected)
 | 
						|
  {
 | 
						|
    File_error = 8;
 | 
						|
    return;
 | 
						|
  }
 | 
						|
  File_error = 0;
 | 
						|
}
 | 
						|
 | 
						|
 | 
						|
static uint8_t pph_blend(uint8_t a, uint8_t b)
 | 
						|
{
 | 
						|
	uint32_t h,l;
 | 
						|
	if (a > b) { h = a; l = b; }
 | 
						|
	else       { h = b; l = a; }
 | 
						|
 | 
						|
	return (23 * h + 9 * l) / 32;
 | 
						|
}
 | 
						|
 | 
						|
 | 
						|
void Load_PPH(T_IO_Context* context)
 | 
						|
{
 | 
						|
  FILE *file;
 | 
						|
  FILE *feven;
 | 
						|
 | 
						|
  // Read in the header
 | 
						|
  uint8_t mode;
 | 
						|
  uint16_t width;
 | 
						|
  uint16_t height;
 | 
						|
  uint8_t npal;
 | 
						|
  int i,j;
 | 
						|
  uint8_t a,b,c,d;
 | 
						|
  int file_size;
 | 
						|
  uint8_t pl[16];
 | 
						|
 | 
						|
  static const T_Components CPCPAL[27] =
 | 
						|
  {
 | 
						|
      { 0x00, 0x02, 0x01 }, { 0x00, 0x02, 0x6B }, { 0x0C, 0x02, 0xF4 },
 | 
						|
      { 0x6C, 0x02, 0x01 }, { 0x69, 0x02, 0x68 }, { 0x6C, 0x02, 0xF2 },
 | 
						|
      { 0xF3, 0x05, 0x06 }, { 0xF0, 0x02, 0x68 }, { 0xF3, 0x02, 0xF4 },
 | 
						|
      { 0x02, 0x78, 0x01 }, { 0x00, 0x78, 0x68 }, { 0x0C, 0x7B, 0xF4 },
 | 
						|
      { 0x6E, 0x7B, 0x01 }, { 0x6E, 0x7D, 0x6B }, { 0x6E, 0x7B, 0xF6 },
 | 
						|
      { 0xF3, 0x7D, 0x0D }, { 0xF3, 0x7D, 0x6B }, { 0xFA, 0x80, 0xF9 },
 | 
						|
      { 0x02, 0xF0, 0x01 }, { 0x00, 0xF3, 0x6B }, { 0x0F, 0xF3, 0xF2 },
 | 
						|
      { 0x71, 0xF5, 0x04 }, { 0x71, 0xF3, 0x6B }, { 0x71, 0xF3, 0xF4 },
 | 
						|
      { 0xF3, 0xF3, 0x0D }, { 0xF3, 0xF3, 0x6D }, { 0xFF, 0xF3, 0xF9 }
 | 
						|
  };
 | 
						|
 | 
						|
  if (!(file = Open_file_read(context)))
 | 
						|
  {
 | 
						|
      File_error = 1;
 | 
						|
      return;
 | 
						|
  }
 | 
						|
 | 
						|
  file_size=File_length_file(file);
 | 
						|
 | 
						|
  Read_byte(file, &mode);
 | 
						|
  Read_word_le(file, &width);
 | 
						|
  Read_word_le(file, &height);
 | 
						|
  Read_byte(file, &npal);
 | 
						|
 | 
						|
  if (npal > 16)
 | 
						|
      npal = 16;
 | 
						|
 | 
						|
  // Switch to the proper aspect ratio
 | 
						|
  switch (mode)
 | 
						|
  {
 | 
						|
      case 0:
 | 
						|
      case 4:
 | 
						|
        context->Ratio = PIXEL_WIDE;
 | 
						|
        width /= 2;
 | 
						|
        break;
 | 
						|
 | 
						|
      case 2:
 | 
						|
        context->Ratio = PIXEL_TALL;
 | 
						|
        break;
 | 
						|
 | 
						|
      case 1:
 | 
						|
      case 5:
 | 
						|
      case 3:
 | 
						|
        context->Ratio = PIXEL_SIMPLE;
 | 
						|
        break;
 | 
						|
  }
 | 
						|
 | 
						|
  Pre_load(context, width, height, file_size, FORMAT_PPH, context->Ratio, 0);
 | 
						|
 | 
						|
  context->Width = width;
 | 
						|
  context->Height = height;
 | 
						|
 | 
						|
  // First of all, detect the mode
 | 
						|
  // 0, 1, 2 > Load as with SCR files?
 | 
						|
  // R(3)    > Load as single layer, square pixels, 16 colors
 | 
						|
  // B0(4)   > Load as single layer, wide pixels, expand palette with colorcycling
 | 
						|
  // B1(5)   > Load as ???
 | 
						|
  //           Maybe special mode similar to mode5, with 2 layers + auto-flicker?
 | 
						|
 | 
						|
  switch (mode)
 | 
						|
  {
 | 
						|
      case 0:
 | 
						|
      case 3: // R
 | 
						|
          // 16-color palette
 | 
						|
          for (i = 0; i < 16; i++)
 | 
						|
          {
 | 
						|
              uint8_t color;
 | 
						|
              Read_byte(file, &color);
 | 
						|
              context->Palette[i] = CPCPAL[color];
 | 
						|
          }
 | 
						|
          break;
 | 
						|
 | 
						|
      case 1:
 | 
						|
      case 5: // B1
 | 
						|
      {
 | 
						|
          // Single or multiple 4-color palettes
 | 
						|
          uint8_t base[4];
 | 
						|
          for (j = 0; j < npal; j++)
 | 
						|
          {
 | 
						|
            for (i = 0; i < 4; i++)
 | 
						|
            {
 | 
						|
              Read_byte(file,&base[i]);
 | 
						|
            }
 | 
						|
            for (i = 0; i < 16; i++)
 | 
						|
            {
 | 
						|
              context->Palette[i + 16*j].R = pph_blend(
 | 
						|
                  CPCPAL[base[i & 3]].R, CPCPAL[base[i >> 2]].R);
 | 
						|
              context->Palette[i + 16*j].G = pph_blend(
 | 
						|
                  CPCPAL[base[i & 3]].G, CPCPAL[base[i >> 2]].G);
 | 
						|
              context->Palette[i + 16*j].B = pph_blend(
 | 
						|
                  CPCPAL[base[i & 3]].B, CPCPAL[base[i >> 2]].B);
 | 
						|
            }
 | 
						|
            // TODO this byte marks where this palette stops being used and the
 | 
						|
            // next starts. We must handle this!
 | 
						|
            Read_byte(file,&pl[j]);
 | 
						|
          }
 | 
						|
          pl[npal - 1] = 255;
 | 
						|
          break;
 | 
						|
      }
 | 
						|
 | 
						|
      case 2:
 | 
						|
          // Single 2-color palette
 | 
						|
          break;
 | 
						|
 | 
						|
      case 4: // B0
 | 
						|
      {
 | 
						|
          // Single 16-color palette + flipping, need to expand palette and
 | 
						|
          // setup colorcycling ranges.
 | 
						|
          uint8_t base[16];
 | 
						|
          for (i = 0; i < 16; i++)
 | 
						|
          {
 | 
						|
              Read_byte(file,&base[i]);
 | 
						|
          }
 | 
						|
 | 
						|
          for (i = 0; i < 256; i++)
 | 
						|
          {
 | 
						|
              context->Palette[i].R = pph_blend(
 | 
						|
                  CPCPAL[base[i & 15]].R, CPCPAL[base[i >> 4]].R);
 | 
						|
              context->Palette[i].G = pph_blend(
 | 
						|
                  CPCPAL[base[i & 15]].G, CPCPAL[base[i >> 4]].G);
 | 
						|
              context->Palette[i].B = pph_blend(
 | 
						|
                  CPCPAL[base[i & 15]].B, CPCPAL[base[i >> 4]].B);
 | 
						|
          }
 | 
						|
      }
 | 
						|
      break;
 | 
						|
  }
 | 
						|
 | 
						|
  fclose(file);
 | 
						|
 | 
						|
  // Load the picture data
 | 
						|
  // There are two pages, each storing bytes in the CPC vram format but lines in
 | 
						|
  // linear order.
 | 
						|
  file = Open_file_read_with_alternate_ext(context, "odd");
 | 
						|
  if (file == NULL)
 | 
						|
  {
 | 
						|
    File_error = 3;
 | 
						|
    return;
 | 
						|
  }
 | 
						|
  feven = Open_file_read_with_alternate_ext(context, "eve");
 | 
						|
  if (feven == NULL)
 | 
						|
  {
 | 
						|
    File_error = 4;
 | 
						|
    fclose(file);
 | 
						|
    return;
 | 
						|
  }
 | 
						|
 | 
						|
  c = 0;
 | 
						|
  d = 0;
 | 
						|
 | 
						|
  for (j = 0; j < height; j++)
 | 
						|
  {
 | 
						|
      for (i = 0; i < width;)
 | 
						|
      {
 | 
						|
          uint8_t even, odd;
 | 
						|
          Read_byte(feven, &even);
 | 
						|
          Read_byte(file, &odd);
 | 
						|
 | 
						|
          switch (mode)
 | 
						|
          {
 | 
						|
              case 4:
 | 
						|
                a = ((even & 0x02) << 2) | ((even & 0x08) >> 2)
 | 
						|
                  | ((even & 0x20) >> 3) | ((even & 0x80) >> 7);
 | 
						|
                a <<= 4;
 | 
						|
                a |= ((odd & 0x02) << 2) | (( odd & 0x08) >> 2)
 | 
						|
                  | (( odd & 0x20) >> 3) | (( odd & 0x80) >> 7);
 | 
						|
 | 
						|
                b = ((even & 0x01) << 3) | ((even & 0x04) >> 1)
 | 
						|
                  | ((even & 0x10) >> 2) | ((even & 0x40) >> 6);
 | 
						|
                b <<= 4;
 | 
						|
                b |= ((odd & 0x01) << 3) | (( odd & 0x04) >> 1)
 | 
						|
                  | (( odd & 0x10) >> 2) | (( odd & 0x40) >> 6);
 | 
						|
 | 
						|
                Set_pixel(context, i++, j, a);
 | 
						|
                Set_pixel(context, i++, j, b);
 | 
						|
                break;
 | 
						|
 | 
						|
              case 3:
 | 
						|
                a = ((even & 0x02) << 2) | ((even & 0x08) >> 2)
 | 
						|
                  | ((even & 0x20) >> 3) | ((even & 0x80) >> 7);
 | 
						|
                b = (( odd & 0x02) << 2) | (( odd & 0x08) >> 2)
 | 
						|
                  | (( odd & 0x20) >> 3) | (( odd & 0x80) >> 7);
 | 
						|
                c = ((even & 0x01) << 3) | ((even & 0x04) >> 1)
 | 
						|
                  | ((even & 0x10) >> 2) | ((even & 0x40) >> 6);
 | 
						|
                d = (( odd & 0x01) << 3) | (( odd & 0x04) >> 1)
 | 
						|
                  | (( odd & 0x10) >> 2) | (( odd & 0x40) >> 6);
 | 
						|
                Set_pixel(context, i++, j, j & 1 ? b : a);
 | 
						|
                Set_pixel(context, i++, j, j & 1 ? a : b);
 | 
						|
                Set_pixel(context, i++, j, j & 1 ? d : c);
 | 
						|
                Set_pixel(context, i++, j, j & 1 ? c : d);
 | 
						|
                break;
 | 
						|
 | 
						|
              case 5:
 | 
						|
                if (d >= pl[c])
 | 
						|
                {
 | 
						|
                    d = 0;
 | 
						|
                    c++;
 | 
						|
                }
 | 
						|
                a = ((even & 0x80) >> 6) | ((even & 0x08) >> 3);
 | 
						|
                b = (( odd & 0x80) >> 6) | (( odd & 0x08) >> 3);
 | 
						|
                Set_pixel(context, i++, j, a + (b << 2) + c * 16);
 | 
						|
                a = ((even & 0x40) >> 5) | ((even & 0x04) >> 2);
 | 
						|
                b = (( odd & 0x40) >> 5) | (( odd & 0x04) >> 2);
 | 
						|
                Set_pixel(context, i++, j, a + (b << 2) + c * 16);
 | 
						|
                a = ((even & 0x20) >> 4) | ((even & 0x02) >> 1);
 | 
						|
                b = (( odd & 0x20) >> 4) | (( odd & 0x02) >> 1);
 | 
						|
                Set_pixel(context, i++, j, a + (b << 2) + c * 16);
 | 
						|
                a = ((even & 0x10) >> 3) | ((even & 0x01) >> 0);
 | 
						|
                b = (( odd & 0x10) >> 3) | (( odd & 0x01) >> 0);
 | 
						|
                Set_pixel(context, i++, j, a + (b << 2) + c * 16);
 | 
						|
 | 
						|
                break;
 | 
						|
 | 
						|
              default:
 | 
						|
                File_error = 2;
 | 
						|
                return;
 | 
						|
          }
 | 
						|
 | 
						|
      }
 | 
						|
      d++;
 | 
						|
  }
 | 
						|
  fclose(file);
 | 
						|
  fclose(feven);
 | 
						|
 | 
						|
  File_error = 0;
 | 
						|
}
 | 
						|
 | 
						|
void Save_PPH(T_IO_Context* context)
 | 
						|
{
 | 
						|
  (void)context; // unused
 | 
						|
    // TODO
 | 
						|
 | 
						|
    // Detect mode
 | 
						|
    // Wide pixels => B0 (4)
 | 
						|
    // Square pixels:
 | 
						|
    // - 16 colors used => R
 | 
						|
    // - more colors used => B1 (if <16 colors per line)
 | 
						|
 | 
						|
    // Check palette
 | 
						|
    // B0: use diagonal: 0, 17, 34, ... (assume the other are mixes)
 | 
						|
    // R: use 16 used colors (or 16 first?)
 | 
						|
    // B1: find the 16 colors used in a line? Or assume they are in-order already?
 | 
						|
}
 | 
						|
 | 
						|
 | 
						|
/////////////////////////////////// FLI/FLC /////////////////////////////////
 | 
						|
typedef struct {
 | 
						|
  dword size;          /* Size of FLIC including this header */
 | 
						|
  word  type;          /* File type 0xAF11, 0xAF12, 0xAF30, 0xAF44, ... */
 | 
						|
  word  frames;        /* Number of frames in first segment */
 | 
						|
  word  width;         /* FLIC width in pixels */
 | 
						|
  word  height;        /* FLIC height in pixels */
 | 
						|
  word  depth;         /* Bits per pixel (usually 8) */
 | 
						|
  word  flags;         /* Set to zero or to three */
 | 
						|
  dword speed;         /* Delay between frames */
 | 
						|
  word  reserved1;     /* Set to zero */
 | 
						|
  dword created;       /* Date of FLIC creation (FLC only) */
 | 
						|
  dword creator;       /* Serial number or compiler id (FLC only) */
 | 
						|
  dword updated;       /* Date of FLIC update (FLC only) */
 | 
						|
  dword updater;       /* Serial number (FLC only), see creator */
 | 
						|
  word  aspect_dx;     /* Width of square rectangle (FLC only) */
 | 
						|
  word  aspect_dy;     /* Height of square rectangle (FLC only) */
 | 
						|
  word  ext_flags;     /* EGI: flags for specific EGI extensions */
 | 
						|
  word  keyframes;     /* EGI: key-image frequency */
 | 
						|
  word  totalframes;   /* EGI: total number of frames (segments) */
 | 
						|
  dword req_memory;    /* EGI: maximum chunk size (uncompressed) */
 | 
						|
  word  max_regions;   /* EGI: max. number of regions in a CHK_REGION chunk */
 | 
						|
  word  transp_num;    /* EGI: number of transparent levels */
 | 
						|
  byte  reserved2[24]; /* Set to zero */
 | 
						|
  dword oframe1;       /* Offset to frame 1 (FLC only) */
 | 
						|
  dword oframe2;       /* Offset to frame 2 (FLC only) */
 | 
						|
  byte  reserved3[40]; /* Set to zero */
 | 
						|
} T_FLIC_Header;
 | 
						|
 | 
						|
static void Load_FLI_Header(FILE * file, T_FLIC_Header * header)
 | 
						|
{
 | 
						|
  if (!(Read_dword_le(file,&header->size)
 | 
						|
      && Read_word_le(file,&header->type)
 | 
						|
      && Read_word_le(file,&header->frames)
 | 
						|
      && Read_word_le(file,&header->width)
 | 
						|
      && Read_word_le(file,&header->height)
 | 
						|
      && Read_word_le(file,&header->depth)
 | 
						|
      && Read_word_le(file,&header->flags)
 | 
						|
      && Read_dword_le(file,&header->speed)
 | 
						|
      && Read_word_le(file,&header->reserved1)
 | 
						|
      && Read_dword_le(file,&header->created)
 | 
						|
      && Read_dword_le(file,&header->creator)
 | 
						|
      && Read_dword_le(file,&header->updated)
 | 
						|
      && Read_dword_le(file,&header->updater)
 | 
						|
      && Read_word_le(file,&header->aspect_dx)
 | 
						|
      && Read_word_le(file,&header->aspect_dy)
 | 
						|
      && Read_word_le(file,&header->ext_flags)
 | 
						|
      && Read_word_le(file,&header->keyframes)
 | 
						|
      && Read_word_le(file,&header->totalframes)
 | 
						|
      && Read_dword_le(file,&header->req_memory)
 | 
						|
      && Read_word_le(file,&header->max_regions)
 | 
						|
      && Read_word_le(file,&header->transp_num)
 | 
						|
      && Read_bytes(file,header->reserved2,24)
 | 
						|
      && Read_dword_le(file,&header->oframe1)
 | 
						|
      && Read_dword_le(file,&header->oframe2)
 | 
						|
      && Read_bytes(file,header->reserved2,40) ))
 | 
						|
  {
 | 
						|
    File_error=1;
 | 
						|
  }
 | 
						|
}
 | 
						|
 | 
						|
/**
 | 
						|
 * Test for the Autodesk Animator FLI/FLC format.
 | 
						|
 *
 | 
						|
 * Not to be confused with Commodore 64 FLI.
 | 
						|
 */
 | 
						|
void Test_FLI(T_IO_Context * context, FILE * file)
 | 
						|
{
 | 
						|
  T_FLIC_Header header;
 | 
						|
  (void)context;
 | 
						|
 | 
						|
  File_error=0;
 | 
						|
  Load_FLI_Header(file, &header);
 | 
						|
  if (File_error != 0) return;
 | 
						|
 | 
						|
  switch (header.type)
 | 
						|
  {
 | 
						|
    case 0xAF11:  // standard FLI
 | 
						|
    case 0xAF12:  // FLC (8bpp)
 | 
						|
#if 0
 | 
						|
    case 0xAF30:  // Huffman or BWT compression
 | 
						|
    case 0xAF31:  // frame shift compression
 | 
						|
    case 0xAF44:  // bpp != 8
 | 
						|
#endif
 | 
						|
      File_error=0;
 | 
						|
      break;
 | 
						|
    default:
 | 
						|
      File_error=1;
 | 
						|
  }
 | 
						|
}
 | 
						|
 | 
						|
/**
 | 
						|
 * Load file in the Autodesk Animator FLI/FLC format.
 | 
						|
 *
 | 
						|
 * Not to be confused with Commodore 64 FLI.
 | 
						|
 */
 | 
						|
void Load_FLI(T_IO_Context * context)
 | 
						|
{
 | 
						|
  FILE * file;
 | 
						|
  unsigned long file_size;
 | 
						|
  T_FLIC_Header header;
 | 
						|
  dword chunk_size;
 | 
						|
  word chunk_type;
 | 
						|
  word sub_chunk_count, sub_chunk_index;
 | 
						|
  dword sub_chunk_size;
 | 
						|
  word sub_chunk_type;
 | 
						|
  word frame_delay, frame_width, frame_height;
 | 
						|
  int current_frame = 0;
 | 
						|
 | 
						|
  file = Open_file_read(context);
 | 
						|
  if (file == NULL)
 | 
						|
  {
 | 
						|
    File_error=1;
 | 
						|
    return;
 | 
						|
  }
 | 
						|
  File_error=0;
 | 
						|
  file_size = File_length_file(file);
 | 
						|
  Load_FLI_Header(file, &header);
 | 
						|
  if (File_error != 0)
 | 
						|
  {
 | 
						|
    fclose(file);
 | 
						|
    return;
 | 
						|
  }
 | 
						|
  if (header.size == 12)
 | 
						|
  {
 | 
						|
    // special "magic carpet" format
 | 
						|
    header.depth = 8;
 | 
						|
    header.speed = 66; // about 15fps
 | 
						|
    fseek(file, 12, SEEK_SET);
 | 
						|
  }
 | 
						|
  else if (file_size != header.size)
 | 
						|
    Warning("Load_FLI(): file size mismatch in header");
 | 
						|
 | 
						|
  if (header.speed == 0)
 | 
						|
  {
 | 
						|
    if (header.type == 0xAF11) // FLI
 | 
						|
      header.speed = 1;   // 1/70th seconds
 | 
						|
    else
 | 
						|
      header.speed = 10;  // 10ms
 | 
						|
  }
 | 
						|
 | 
						|
  while (File_error == 0
 | 
						|
     && Read_dword_le(file,&chunk_size) && Read_word_le(file,&chunk_type))
 | 
						|
  {
 | 
						|
    chunk_size -= 6;
 | 
						|
    switch (chunk_type)
 | 
						|
    {
 | 
						|
      case 0xf1fa:  // FRAME
 | 
						|
        Read_word_le(file, &sub_chunk_count);
 | 
						|
        Read_word_le(file, &frame_delay);
 | 
						|
        fseek(file, 2, SEEK_CUR);
 | 
						|
        Read_word_le(file, &frame_width);
 | 
						|
        Read_word_le(file, &frame_height);
 | 
						|
        if (frame_width == 0)
 | 
						|
          frame_width = header.width;
 | 
						|
        if (frame_height == 0)
 | 
						|
          frame_height = header.height;
 | 
						|
        if (frame_delay == 0)
 | 
						|
          frame_delay = header.speed;
 | 
						|
        chunk_size -= 10;
 | 
						|
 | 
						|
        if (current_frame == 0)
 | 
						|
        {
 | 
						|
          Pre_load(context, header.width,header.height,file_size,FORMAT_FLI,PIXEL_SIMPLE,header.depth);
 | 
						|
          Set_image_mode(context, IMAGE_MODE_ANIMATION);
 | 
						|
          if (Config.Clear_palette)
 | 
						|
            memset(context->Palette,0,sizeof(T_Palette));
 | 
						|
        }
 | 
						|
        else
 | 
						|
        {
 | 
						|
          Set_loading_layer(context, current_frame);
 | 
						|
          if (context->Type == CONTEXT_MAIN_IMAGE && Main.backups->Pages->Image_mode == IMAGE_MODE_ANIMATION)
 | 
						|
          {
 | 
						|
            // Copy the content of previous frame
 | 
						|
            memcpy(
 | 
						|
                Main.backups->Pages->Image[Main.current_layer].Pixels,
 | 
						|
                Main.backups->Pages->Image[Main.current_layer-1].Pixels,
 | 
						|
                Main.backups->Pages->Width*Main.backups->Pages->Height);
 | 
						|
          }
 | 
						|
        }
 | 
						|
        if (header.type == 0xAF11) // FLI
 | 
						|
          Set_frame_duration(context, (frame_delay * 100) / 7); // 1/70th sec
 | 
						|
        else
 | 
						|
          Set_frame_duration(context, frame_delay); // msec
 | 
						|
        current_frame++;
 | 
						|
 | 
						|
        for (sub_chunk_index = 0; sub_chunk_index < sub_chunk_count; sub_chunk_index++)
 | 
						|
        {
 | 
						|
          if (!(Read_dword_le(file,&sub_chunk_size) && Read_word_le(file,&sub_chunk_type)))
 | 
						|
            File_error = 1;
 | 
						|
          else
 | 
						|
          {
 | 
						|
            chunk_size -= sub_chunk_size;
 | 
						|
            sub_chunk_size -= 6;
 | 
						|
            if (sub_chunk_type == 0x04 || sub_chunk_type == 0x0b)   // color map
 | 
						|
            {
 | 
						|
              word packet_count;
 | 
						|
              int i = 0;
 | 
						|
              sub_chunk_size -= 2;
 | 
						|
              if (!Read_word_le(file, &packet_count))
 | 
						|
                File_error = 1;
 | 
						|
              else
 | 
						|
                while (packet_count-- > 0 && File_error == 0)
 | 
						|
                {
 | 
						|
                  byte skip, count;
 | 
						|
                  if (!(Read_byte(file, &skip) && Read_byte(file, &count)))
 | 
						|
                    File_error = 1;
 | 
						|
                  else
 | 
						|
                  {
 | 
						|
                    sub_chunk_size -= 2;
 | 
						|
                    i += skip;  // count 0 means 256
 | 
						|
                    do
 | 
						|
                    {
 | 
						|
                      byte r, g, b;
 | 
						|
                      if (!(Read_byte(file, &r) && Read_byte(file, &g) && Read_byte(file, &b)))
 | 
						|
                      {
 | 
						|
                        File_error = 1;
 | 
						|
                        break;
 | 
						|
                      }
 | 
						|
                      if (sub_chunk_type == 0x0b || header.size == 12) // 6bit per color
 | 
						|
                      {
 | 
						|
                        r = (r << 2) | (r >> 4);
 | 
						|
                        g = (g << 2) | (g >> 4);
 | 
						|
                        b = (b << 2) | (b >> 4);
 | 
						|
                      }
 | 
						|
                      context->Palette[i].R = r;
 | 
						|
                      context->Palette[i].G = g;
 | 
						|
                      context->Palette[i].B = b;
 | 
						|
                      i++;
 | 
						|
                      sub_chunk_size -= 3;
 | 
						|
                    } while (--count != 0);
 | 
						|
                  }
 | 
						|
                }
 | 
						|
            }
 | 
						|
            else if (sub_chunk_type == 0x0f)  // full frame RLE
 | 
						|
            {
 | 
						|
              word x, y;
 | 
						|
              for (y = 0; y < frame_height && File_error == 0; y++)
 | 
						|
              {
 | 
						|
                byte count, data;
 | 
						|
                Read_byte(file, &count); // packet count, but dont rely on it
 | 
						|
                sub_chunk_size--;
 | 
						|
                for (x = 0; x < frame_width; )
 | 
						|
                {
 | 
						|
                  if (!Read_byte(file, &count))
 | 
						|
                  {
 | 
						|
                    File_error = 1;
 | 
						|
                    break;
 | 
						|
                  }
 | 
						|
                  sub_chunk_size--;
 | 
						|
                  if ((count & 0x80) == 0)
 | 
						|
                  {
 | 
						|
                    if (!Read_byte(file, &data))  // repeat data count times
 | 
						|
                    {
 | 
						|
                      File_error = 1;
 | 
						|
                      break;
 | 
						|
                    }
 | 
						|
                    sub_chunk_size--;
 | 
						|
                    while (count-- > 0 && x < frame_width)
 | 
						|
                      Set_pixel(context, x++, y, data);
 | 
						|
                  }
 | 
						|
                  else
 | 
						|
                    while (count++ != 0 && x < frame_width)  // copy count bytes
 | 
						|
                    {
 | 
						|
                      if (!Read_byte(file, &data))
 | 
						|
                      {
 | 
						|
                        File_error = 1;
 | 
						|
                        break;
 | 
						|
                      }
 | 
						|
                      Set_pixel(context, x++, y, data);
 | 
						|
                      sub_chunk_size--;
 | 
						|
                    }
 | 
						|
                }
 | 
						|
              }
 | 
						|
              if (context->Type == CONTEXT_PREVIEW || context->Type == CONTEXT_PREVIEW_PALETTE)
 | 
						|
              { // load only 1st frame in preview
 | 
						|
                fclose(file);
 | 
						|
                return;
 | 
						|
              }
 | 
						|
            }
 | 
						|
            else if (sub_chunk_type == 0x0c)  // delta image, RLE
 | 
						|
            {
 | 
						|
              word x, y, line_count;
 | 
						|
 | 
						|
              Read_word_le(file, &y);
 | 
						|
              Read_word_le(file, &line_count);
 | 
						|
              sub_chunk_size -= 4;
 | 
						|
              while (sub_chunk_size > 0 && line_count > 0 && File_error == 0)
 | 
						|
              {
 | 
						|
                byte packet_count;
 | 
						|
 | 
						|
                x = 0;
 | 
						|
                if (!Read_byte(file, &packet_count))
 | 
						|
                  File_error = 1;
 | 
						|
                else
 | 
						|
                {
 | 
						|
                  sub_chunk_size--;
 | 
						|
                  while (packet_count-- > 0 && File_error == 0)
 | 
						|
                  {
 | 
						|
                    byte skip, count, data;
 | 
						|
                    if (!(Read_byte(file, &skip) && Read_byte(file, &count)))
 | 
						|
                      File_error = 1;
 | 
						|
                    else
 | 
						|
                    {
 | 
						|
                      sub_chunk_size -= 2;
 | 
						|
                      x += skip;
 | 
						|
                      if (count & 0x80)
 | 
						|
                      {
 | 
						|
                        Read_byte(file, &data);
 | 
						|
                        sub_chunk_size--;
 | 
						|
                        while (count++ != 0)
 | 
						|
                          Set_pixel(context, x++, y, data);
 | 
						|
                      }
 | 
						|
                      else
 | 
						|
                        while (count-- > 0)
 | 
						|
                        {
 | 
						|
                          Read_byte(file, &data);
 | 
						|
                          sub_chunk_size--;
 | 
						|
                          Set_pixel(context, x++, y, data);
 | 
						|
                        }
 | 
						|
                    }
 | 
						|
                  }
 | 
						|
                }
 | 
						|
                y++;
 | 
						|
                line_count--;
 | 
						|
              }
 | 
						|
            }
 | 
						|
            else if (sub_chunk_type == 0x07)  // FLC delta image
 | 
						|
            {
 | 
						|
              word opcode, y, line_count;
 | 
						|
 | 
						|
              y = 0;
 | 
						|
              Read_word_le(file, &line_count);
 | 
						|
              sub_chunk_size -= 2;
 | 
						|
              while (line_count > 0)
 | 
						|
              {
 | 
						|
                Read_word_le(file, &opcode);
 | 
						|
                sub_chunk_size -= 2;
 | 
						|
                if ((opcode & 0xc000) == 0x0000) // packet count
 | 
						|
                {
 | 
						|
                  word x = 0;
 | 
						|
                  while (opcode-- > 0)
 | 
						|
                  {
 | 
						|
                    byte skip, count, data1, data2;
 | 
						|
                    if (!(Read_byte(file, &skip) && Read_byte(file, &count)))
 | 
						|
                      File_error = 1;
 | 
						|
                    else
 | 
						|
                    {
 | 
						|
                      sub_chunk_size -= 2;
 | 
						|
                      x += skip;
 | 
						|
                      if (count & 0x80)
 | 
						|
                      {
 | 
						|
                        Read_byte(file, &data1);
 | 
						|
                        Read_byte(file, &data2);
 | 
						|
                        sub_chunk_size -= 2;
 | 
						|
                        while (count++ != 0)
 | 
						|
                        {
 | 
						|
                          Set_pixel(context, x++, y, data1);
 | 
						|
                          Set_pixel(context, x++, y, data2);
 | 
						|
                        }
 | 
						|
                      }
 | 
						|
                      else
 | 
						|
                        while (count-- > 0)
 | 
						|
                        {
 | 
						|
                          Read_byte(file, &data1);
 | 
						|
                          Set_pixel(context, x++, y, data1);
 | 
						|
                          Read_byte(file, &data2);
 | 
						|
                          Set_pixel(context, x++, y, data2);
 | 
						|
                          sub_chunk_size -= 2;
 | 
						|
                        }
 | 
						|
                    }
 | 
						|
                  }
 | 
						|
                  y++;
 | 
						|
                  line_count--;
 | 
						|
                }
 | 
						|
                else if ((opcode & 0xc000) == 0xc000)  // line skip
 | 
						|
                {
 | 
						|
                  y -= opcode;
 | 
						|
                }
 | 
						|
                else if ((opcode & 0xc000) == 0x8000)  // last byte
 | 
						|
                {
 | 
						|
                  Set_pixel(context, frame_width - 1, y, opcode & 0xff);
 | 
						|
                }
 | 
						|
                else
 | 
						|
                {
 | 
						|
                  Warning("Unsupported opcode");
 | 
						|
                  File_error = 2;
 | 
						|
                  break;
 | 
						|
                }
 | 
						|
              }
 | 
						|
            }
 | 
						|
            if (sub_chunk_size > 0)
 | 
						|
            {
 | 
						|
              fseek(file, sub_chunk_size, SEEK_CUR);
 | 
						|
            }
 | 
						|
          }
 | 
						|
        }
 | 
						|
        break;
 | 
						|
      default:  // skip
 | 
						|
        Warning("Load_FLI(): unrecognized chunk");
 | 
						|
    }
 | 
						|
    if (chunk_size > 0 && header.size != 12)
 | 
						|
    {
 | 
						|
      fseek(file, chunk_size, SEEK_CUR);
 | 
						|
    }
 | 
						|
  }
 | 
						|
  fclose(file);
 | 
						|
}
 | 
						|
 | 
						|
/////////////////////////////// Thomson Files ///////////////////////////////
 | 
						|
 | 
						|
/**
 | 
						|
 * Test for Thomson file
 | 
						|
 */
 | 
						|
void Test_MOTO(T_IO_Context * context, FILE * file)
 | 
						|
{
 | 
						|
  long file_size;
 | 
						|
 | 
						|
  file_size = File_length_file(file);
 | 
						|
 | 
						|
  File_error = 1;
 | 
						|
  if (file_size <= 10)
 | 
						|
    return;
 | 
						|
  switch (MOTO_Check_binary_file(file))
 | 
						|
  {
 | 
						|
    case 0: // Not Thomson binary format
 | 
						|
      switch (file_size)
 | 
						|
      {
 | 
						|
        // Files in RAW formats (from TGA2teo)
 | 
						|
        case 8004:  // 2 colors palette
 | 
						|
        case 8008:  // 4 colors palette
 | 
						|
        case 8032:  // 16 colors palette
 | 
						|
          {
 | 
						|
            char * filename;
 | 
						|
            char * path;
 | 
						|
            char * ext;
 | 
						|
 | 
						|
            // Check there are both FORME and COULEUR files
 | 
						|
            filename = strdup(context->File_name);
 | 
						|
            ext = strrchr(filename, '.');
 | 
						|
            if (ext == NULL || ext == filename)
 | 
						|
            {
 | 
						|
              free(filename);
 | 
						|
              return;
 | 
						|
            }
 | 
						|
            if ((ext[-1] | 32) == 'c')
 | 
						|
              ext[-1] = (ext[-1] & 32) | 'P';
 | 
						|
            else if ((ext[-1] | 32) == 'p')
 | 
						|
              ext[-1] = (ext[-1] & 32) | 'C';
 | 
						|
            else
 | 
						|
            {
 | 
						|
              free(filename);
 | 
						|
              return;
 | 
						|
            }
 | 
						|
            path = Filepath_append_to_dir(context->File_directory, filename);
 | 
						|
            if (File_exists(path))
 | 
						|
              File_error = 0;
 | 
						|
            free(path);
 | 
						|
            free(filename);
 | 
						|
          }
 | 
						|
          return;
 | 
						|
        default:
 | 
						|
          break;
 | 
						|
      }
 | 
						|
      break;
 | 
						|
    case 2: // MAP file (SAVEP/LOADP)
 | 
						|
    case 3: // TO autoloading picture
 | 
						|
    case 4: // MO autoloading picture
 | 
						|
      File_error = 0;
 | 
						|
      return;
 | 
						|
  }
 | 
						|
}
 | 
						|
 | 
						|
/**
 | 
						|
 * Load a picture for Thomson TO8/TO8D/TO9/TO9+/MO6
 | 
						|
 *
 | 
						|
 * One of the supported format is the one produced by TGA2Teo :
 | 
						|
 * - Picture data is splitted into 2 files, one for each VRAM bank :
 | 
						|
 *   - The first VRAM bank is called "forme" (shape).
 | 
						|
 *     In 40col mode it stores pixels.
 | 
						|
 *   - The second VRAM bank is called "couleur" (color).
 | 
						|
 *     In 40col mode it store color indexes for foreground and background.
 | 
						|
 * - File extension is .BIN, character before extension is "P" for the first
 | 
						|
 *   file, and "C" for the second.
 | 
						|
 * - The color palette is stored in both files after the data.
 | 
						|
 *
 | 
						|
 * The mode is detected thanks to the number of color in the palette :
 | 
						|
 * - 2 colors is 80col (640x200)
 | 
						|
 * - 4 colors is bitmap4 (320x200 4 colors)
 | 
						|
 * - 16 colors is either bitmap16 (160x200 16colors)
 | 
						|
 *   or 40col (320x200 16 colors with 2 unique colors in each 8x1 pixels
 | 
						|
 *   block).
 | 
						|
 *
 | 
						|
 * As it is not possible to disriminate bitmap16 and 40col, opening the "P"
 | 
						|
 * file sets bitmap16, opening the "C" file sets 40col.
 | 
						|
 *
 | 
						|
 * This function also supports .MAP files (with optional TO-SNAP extension)
 | 
						|
 * and our own "autoloading" BIN files.
 | 
						|
 * See http://pulkomandy.tk/projects/GrafX2/wiki/Develop/FileFormats/MOTO for
 | 
						|
 * a detailled description.
 | 
						|
 */
 | 
						|
void Load_MOTO(T_IO_Context * context)
 | 
						|
{
 | 
						|
  // FORME / COULEUR
 | 
						|
  FILE * file;
 | 
						|
  byte * vram_forme = NULL;
 | 
						|
  byte * vram_couleur = NULL;
 | 
						|
  long file_size;
 | 
						|
  int file_type;
 | 
						|
  int bx, x, y, i;
 | 
						|
  byte bpp = 4;
 | 
						|
  byte code;
 | 
						|
  word length, address;
 | 
						|
  int transpose = 1;  // transpose the upper bits of the color plane bytes
 | 
						|
                      // FFFFBBBB becomes bfFFFBBB (for TO7 compatibility)
 | 
						|
  enum MOTO_Graphic_Mode mode = MOTO_MODE_40col;
 | 
						|
  enum PIXEL_RATIO ratio = PIXEL_SIMPLE;
 | 
						|
  int width = 320, height = 200, columns = 40;
 | 
						|
 | 
						|
  File_error = 1;
 | 
						|
  file = Open_file_read(context);
 | 
						|
  if (file == NULL)
 | 
						|
    return;
 | 
						|
  file_size = File_length_file(file);
 | 
						|
  // Load default palette
 | 
						|
  if (Config.Clear_palette)
 | 
						|
    memset(context->Palette,0,sizeof(T_Palette));
 | 
						|
  MOTO_set_TO7_palette(context->Palette);
 | 
						|
 | 
						|
  file_type = MOTO_Check_binary_file(file);
 | 
						|
  if (fseek(file, 0, SEEK_SET) < 0)
 | 
						|
  {
 | 
						|
    fclose(file);
 | 
						|
    return;
 | 
						|
  }
 | 
						|
 | 
						|
  if (file_type == 2) // MAP file
 | 
						|
  {
 | 
						|
    // http://collection.thomson.free.fr/code/articles/prehisto_bulletin/page.php?XI=0&XJ=13
 | 
						|
    byte map_mode, col_count, line_count;
 | 
						|
    byte * vram_current;
 | 
						|
    int end_marks;
 | 
						|
 | 
						|
    if (!(Read_byte(file,&code) && Read_word_be(file,&length) && Read_word_be(file,&address)))
 | 
						|
    {
 | 
						|
      fclose(file);
 | 
						|
      return;
 | 
						|
    }
 | 
						|
    if (length < 5 || !(Read_byte(file,&map_mode) && Read_byte(file,&col_count) && Read_byte(file,&line_count)))
 | 
						|
    {
 | 
						|
      fclose(file);
 | 
						|
      return;
 | 
						|
    }
 | 
						|
    length -= 3;
 | 
						|
    columns = col_count + 1;
 | 
						|
    height = 8 * (line_count + 1);
 | 
						|
    switch(map_mode)
 | 
						|
    {
 | 
						|
      default:
 | 
						|
      case 0: // bitmap4 or 40col
 | 
						|
        width = 8 * columns;
 | 
						|
        mode = MOTO_MODE_40col; // default to 40col
 | 
						|
        bpp = 4;
 | 
						|
        break;
 | 
						|
      case 0x40:  // bitmap16
 | 
						|
        columns >>= 1;
 | 
						|
        width = 4 * columns;
 | 
						|
        mode = MOTO_MODE_bm16;
 | 
						|
        bpp = 4;
 | 
						|
        ratio = PIXEL_WIDE;
 | 
						|
        break;
 | 
						|
      case 0x80:  // 80col
 | 
						|
        columns >>= 1;
 | 
						|
        width = 16 * columns;
 | 
						|
        mode = MOTO_MODE_80col;
 | 
						|
        bpp = 1;
 | 
						|
        ratio = PIXEL_TALL;
 | 
						|
        break;
 | 
						|
    }
 | 
						|
    GFX2_Log(GFX2_DEBUG, "Map mode &H%02X row=%u line=%u (%dx%d) %d\n", map_mode, col_count, line_count, width, height, columns * height);
 | 
						|
    vram_forme = malloc(columns * height);
 | 
						|
    vram_couleur = malloc(columns * height);
 | 
						|
    // Check extension (TO-SNAP / PPM / ???)
 | 
						|
    if (length > 36)
 | 
						|
    {
 | 
						|
      long pos_backup;
 | 
						|
      word data;
 | 
						|
 | 
						|
      pos_backup = ftell(file);
 | 
						|
      fseek(file, length-2, SEEK_CUR);  // go to last word of chunk
 | 
						|
      Read_word_be(file, &data);
 | 
						|
      GFX2_Log(GFX2_DEBUG, "%04X\n", data);
 | 
						|
      switch (data)
 | 
						|
      {
 | 
						|
      case 0xA55A:  // TO-SNAP
 | 
						|
        fseek(file, -40, SEEK_CUR); // go to begin of extension
 | 
						|
        Read_word_be(file, &data);  // SCRMOD. 0=>40col, 1=>bm4, $40=>bm16, $80=>80col
 | 
						|
        GFX2_Log(GFX2_DEBUG, "SCRMOD=&H%04X ", data);
 | 
						|
        Read_word_be(file, &data);  // Border color
 | 
						|
        GFX2_Log(GFX2_DEBUG, "BORDER=%u ", data);
 | 
						|
        Read_word_be(file, &data);  // Mode BASIC (CONSOLE,,,,X) 0=40col, 1=80col, 2=bm4, 3=bm16, etc.
 | 
						|
        GFX2_Log(GFX2_DEBUG, "CONSOLE,,,,%u\n", data);
 | 
						|
        if(data == 2)
 | 
						|
        {
 | 
						|
          mode = MOTO_MODE_bm4;
 | 
						|
          bpp = 2;
 | 
						|
        }
 | 
						|
        for (i = 0; i < 16; i++)
 | 
						|
        {
 | 
						|
          Read_word_be(file, &data);  // Palette entry
 | 
						|
          if (data & 0x8000) data = ~data;
 | 
						|
          MOTO_gamma_correct_MOTO_to_RGB(&context->Palette[i], data);
 | 
						|
        }
 | 
						|
        snprintf(context->Comment, sizeof(context->Comment), "TO-SNAP .MAP file");
 | 
						|
        break;
 | 
						|
      case 0x484C:  // 'HL' PPM
 | 
						|
        fseek(file, -36, SEEK_CUR); // go to begin of extension
 | 
						|
        for (i = 0; i < 16; i++)
 | 
						|
        {
 | 
						|
          Read_word_be(file, &data);  // Palette entry
 | 
						|
          if (data & 0x8000) data = ~data;
 | 
						|
          MOTO_gamma_correct_MOTO_to_RGB(&context->Palette[i], data);
 | 
						|
        }
 | 
						|
        Read_word_be(file, &data);  // Mode BASIC (CONSOLE,,,,X) 0=40col, 1=80col, 2=bm4, 3=bm16, etc.
 | 
						|
        GFX2_Log(GFX2_DEBUG, "CONSOLE,,,,%u\n", data);
 | 
						|
        if(data == 2)
 | 
						|
        {
 | 
						|
          mode = MOTO_MODE_bm4;
 | 
						|
          bpp = 2;
 | 
						|
        }
 | 
						|
        snprintf(context->Comment, sizeof(context->Comment), "PPM .MAP file");
 | 
						|
        break;
 | 
						|
      default:
 | 
						|
        snprintf(context->Comment, sizeof(context->Comment), "standard .MAP file");
 | 
						|
      }
 | 
						|
      fseek(file, pos_backup, SEEK_SET);  // RESET Position
 | 
						|
    }
 | 
						|
    i = 0;
 | 
						|
    vram_current = vram_forme;
 | 
						|
    end_marks = 0;
 | 
						|
    while (length > 1)
 | 
						|
    {
 | 
						|
      byte byte1, byte2;
 | 
						|
      Read_byte(file,&byte1);
 | 
						|
      Read_byte(file,&byte2);
 | 
						|
      length-=2;
 | 
						|
      if(byte1 == 0)
 | 
						|
      {
 | 
						|
        if (byte2 == 0)
 | 
						|
        {
 | 
						|
          // end of vram stream
 | 
						|
          GFX2_Log(GFX2_DEBUG, "0000 i=%d length=%ld\n", i, length);
 | 
						|
          if (end_marks == 1)
 | 
						|
            break;
 | 
						|
          i = 0;
 | 
						|
          vram_current = vram_couleur;
 | 
						|
          end_marks++;
 | 
						|
        }
 | 
						|
        else while(byte2-- > 0 && length > 0) // copy
 | 
						|
        {
 | 
						|
          Read_byte(file,vram_current + i);
 | 
						|
          length--;
 | 
						|
          i += columns; // to the next line
 | 
						|
          if (i >= columns * height)
 | 
						|
          {
 | 
						|
            if (mode == MOTO_MODE_bm4 || mode == MOTO_MODE_40col)
 | 
						|
              i -= (columns * height - 1);  // to the 1st line of the next column
 | 
						|
            else
 | 
						|
            {
 | 
						|
              i -= columns * height;  // back to the 1st line of the current column
 | 
						|
              if (vram_current == vram_forme)   // other VRAM
 | 
						|
                vram_current = vram_couleur;
 | 
						|
              else
 | 
						|
              {
 | 
						|
                vram_current = vram_forme;
 | 
						|
                i++;  // next column
 | 
						|
              }
 | 
						|
            }
 | 
						|
          }
 | 
						|
        }
 | 
						|
      }
 | 
						|
      else while(byte1-- > 0) // run length
 | 
						|
      {
 | 
						|
        vram_current[i] = byte2;
 | 
						|
        i += columns; // to the next line
 | 
						|
        if (i >= columns * height)
 | 
						|
        {
 | 
						|
          if (mode == MOTO_MODE_bm4 || mode == MOTO_MODE_40col)
 | 
						|
            i -= (columns * height - 1);  // to the 1st line of the next column
 | 
						|
          else
 | 
						|
          {
 | 
						|
            i -= columns * height;  // back to the 1st line of the current column
 | 
						|
            if (vram_current == vram_forme)   // other VRAM
 | 
						|
              vram_current = vram_couleur;
 | 
						|
            else
 | 
						|
            {
 | 
						|
              vram_current = vram_forme;
 | 
						|
              i++;  // next column
 | 
						|
            }
 | 
						|
          }
 | 
						|
        }
 | 
						|
      }
 | 
						|
    }
 | 
						|
    fclose(file);
 | 
						|
  }
 | 
						|
  else if(file_type == 3 || file_type == 4)
 | 
						|
  {
 | 
						|
    if (file_type == 4) // MO file
 | 
						|
    {
 | 
						|
      transpose = 0;
 | 
						|
      MOTO_set_MO5_palette(context->Palette);
 | 
						|
    }
 | 
						|
 | 
						|
    do
 | 
						|
    {
 | 
						|
      if (!(Read_byte(file,&code) && Read_word_be(file,&length) && Read_word_be(file,&address)))
 | 
						|
      {
 | 
						|
        if (vram_forme)
 | 
						|
          break;
 | 
						|
        fclose(file);
 | 
						|
        return;
 | 
						|
      }
 | 
						|
      // MO5/MO6 VRAM address is &H0000
 | 
						|
      // TO7/TO8/TO9 VRAM addres is &H4000
 | 
						|
      if (length >= 8000 && length <= 8192 && (address == 0x4000 || address == 0))
 | 
						|
      {
 | 
						|
        if (vram_forme == NULL)
 | 
						|
        {
 | 
						|
          vram_forme = calloc(8192, 1);
 | 
						|
          Read_bytes(file, vram_forme, length);
 | 
						|
          length = 0;
 | 
						|
        }
 | 
						|
        else if (vram_couleur == NULL)
 | 
						|
        {
 | 
						|
          vram_couleur = calloc(8192, 1);
 | 
						|
          Read_bytes(file, vram_couleur, length);
 | 
						|
          if (length >= 8032)
 | 
						|
          {
 | 
						|
            for (x = 0; x < 16; x++)
 | 
						|
            {
 | 
						|
              // 1 byte Blue (4 lower bits)
 | 
						|
              // 1 byte Green (4 upper bits) / Red (4 lower bits)
 | 
						|
              MOTO_gamma_correct_MOTO_to_RGB(&context->Palette[x],
 | 
						|
                                             vram_couleur[8000+x*2]<<8 | vram_couleur[8000+x*2+1]);
 | 
						|
            }
 | 
						|
            if (length >= 8064)
 | 
						|
            {
 | 
						|
              memcpy(context->Comment, vram_couleur + 8032, 32);
 | 
						|
              if (vram_couleur[8063] >= '0' && vram_couleur[8063] <= '3')
 | 
						|
                mode = vram_couleur[8063] - '0';
 | 
						|
            }
 | 
						|
            context->Comment[COMMENT_SIZE] = '\0';
 | 
						|
          }
 | 
						|
          length = 0;
 | 
						|
        }
 | 
						|
      }
 | 
						|
      if (length > 0)
 | 
						|
        fseek(file, length, SEEK_CUR);
 | 
						|
    } while(code == 0);
 | 
						|
    fclose(file);
 | 
						|
    switch (mode)
 | 
						|
    {
 | 
						|
      case MOTO_MODE_40col: // default
 | 
						|
        break;
 | 
						|
      case MOTO_MODE_bm4:
 | 
						|
        bpp = 2;
 | 
						|
        break;
 | 
						|
      case MOTO_MODE_80col:
 | 
						|
        bpp = 1;
 | 
						|
        width = 640;
 | 
						|
        ratio = PIXEL_TALL;
 | 
						|
        break;
 | 
						|
      case MOTO_MODE_bm16:
 | 
						|
        width = 160;
 | 
						|
        ratio = PIXEL_WIDE;
 | 
						|
        break;
 | 
						|
    }
 | 
						|
  }
 | 
						|
  else
 | 
						|
  {
 | 
						|
    char * filename;
 | 
						|
    char * path;
 | 
						|
    char * ext;
 | 
						|
    int n_colors;
 | 
						|
 | 
						|
    vram_forme = malloc(file_size);
 | 
						|
    if (vram_forme == NULL)
 | 
						|
    {
 | 
						|
      fclose(file);
 | 
						|
      return;
 | 
						|
    }
 | 
						|
    if (!Read_bytes(file, vram_forme, file_size))
 | 
						|
    {
 | 
						|
      free(vram_forme);
 | 
						|
      fclose(file);
 | 
						|
      return;
 | 
						|
    }
 | 
						|
    n_colors = (file_size - 8000) / 2;
 | 
						|
    switch(n_colors)
 | 
						|
    {
 | 
						|
      case 16:
 | 
						|
        bpp = 4;
 | 
						|
        // 16 colors : either 40col or bm16 mode !
 | 
						|
        // select later
 | 
						|
        break;
 | 
						|
      case 4:
 | 
						|
        bpp = 2;
 | 
						|
        mode = MOTO_MODE_bm4;
 | 
						|
        break;
 | 
						|
      default:
 | 
						|
        bpp = 1;
 | 
						|
        mode = MOTO_MODE_80col;
 | 
						|
        width = 640;
 | 
						|
        ratio = PIXEL_TALL;
 | 
						|
    }
 | 
						|
    filename = strdup(context->File_name);
 | 
						|
    ext = strrchr(filename, '.');
 | 
						|
    if (ext == NULL || ext == filename)
 | 
						|
    {
 | 
						|
      free(vram_forme);
 | 
						|
      free(filename);
 | 
						|
      return;
 | 
						|
    }
 | 
						|
    if ((ext[-1] | 32) == 'c')
 | 
						|
    {
 | 
						|
      vram_couleur = vram_forme;
 | 
						|
      vram_forme = NULL;
 | 
						|
      ext[-1] = (ext[-1] & 32) | 'P';
 | 
						|
    }
 | 
						|
    else if ((ext[-1] | 32) == 'p')
 | 
						|
    {
 | 
						|
      ext[-1] = (ext[-1] & 32) | 'C';
 | 
						|
      if (n_colors == 16)
 | 
						|
      {
 | 
						|
        mode = MOTO_MODE_bm16;
 | 
						|
        width = 160;
 | 
						|
        ratio = PIXEL_WIDE;
 | 
						|
      }
 | 
						|
    }
 | 
						|
    else
 | 
						|
    {
 | 
						|
      free(vram_forme);
 | 
						|
      free(filename);
 | 
						|
      return;
 | 
						|
    }
 | 
						|
    path = Filepath_append_to_dir(context->File_directory, filename);
 | 
						|
    file = fopen(path, "rb");
 | 
						|
    if (file == NULL)
 | 
						|
      GFX2_Log(GFX2_ERROR, "Failed to open %s\n", path);
 | 
						|
    free(path);
 | 
						|
    free(filename);
 | 
						|
    if (vram_forme == NULL)
 | 
						|
    {
 | 
						|
      vram_forme = malloc(file_size);
 | 
						|
      if (vram_forme == NULL)
 | 
						|
      {
 | 
						|
        free(vram_couleur);
 | 
						|
        fclose(file);
 | 
						|
        return;
 | 
						|
      }
 | 
						|
      Read_bytes(file,vram_forme,file_size);
 | 
						|
    }
 | 
						|
    else
 | 
						|
    {
 | 
						|
      vram_couleur = malloc(file_size);
 | 
						|
      if (vram_couleur == NULL)
 | 
						|
      {
 | 
						|
        free(vram_forme);
 | 
						|
        fclose(file);
 | 
						|
        return;
 | 
						|
      }
 | 
						|
      Read_bytes(file,vram_couleur,file_size);
 | 
						|
    }
 | 
						|
    fclose(file);
 | 
						|
    GFX2_Log(GFX2_DEBUG, "MO/TO: %s,%s file_size=%ld n_colors=%d\n", context->File_name, filename, file_size, n_colors);
 | 
						|
    for (x = 0; x < n_colors; x++)
 | 
						|
    {
 | 
						|
      // 1 byte Blue (4 lower bits)
 | 
						|
      // 1 byte Green (4 upper bits) / Red (4 lower bits)
 | 
						|
      MOTO_gamma_correct_MOTO_to_RGB(&context->Palette[x],
 | 
						|
                                     vram_couleur[8000+x*2]<<8 | vram_couleur[8000+x*2+1]);
 | 
						|
    }
 | 
						|
  }
 | 
						|
  Pre_load(context, width, height, file_size, FORMAT_MOTO, ratio, bpp);
 | 
						|
  if (mode == MOTO_MODE_40col)
 | 
						|
    Set_image_mode(context, IMAGE_MODE_THOMSON);
 | 
						|
  File_error = 0;
 | 
						|
  i = 0;
 | 
						|
  for (y = 0; y < height; y++)
 | 
						|
  {
 | 
						|
    for (bx = 0; bx < columns; bx++)
 | 
						|
    {
 | 
						|
      byte couleur_forme;
 | 
						|
      byte couleur_fond;
 | 
						|
      byte forme, couleurs;
 | 
						|
 | 
						|
      forme = vram_forme[i];
 | 
						|
      if (vram_couleur)
 | 
						|
        couleurs = vram_couleur[i];
 | 
						|
      else
 | 
						|
        couleurs = (mode == MOTO_MODE_40col) ? 0x01 : 0x00;
 | 
						|
      i++;
 | 
						|
      switch(mode)
 | 
						|
      {
 | 
						|
        case MOTO_MODE_bm4:
 | 
						|
          for (x = bx*8; x < bx*8+8; x++)
 | 
						|
          {
 | 
						|
            Set_pixel(context, x, y, ((forme & 0x80) >> 6) | ((couleurs & 0x80) >> 7));
 | 
						|
            forme <<= 1;
 | 
						|
            couleurs <<= 1;
 | 
						|
          }
 | 
						|
#if 0     // the following would be for the alternate bm4 mode
 | 
						|
          for (x = bx*8; x < bx*8+4; x++)
 | 
						|
          {
 | 
						|
            Set_pixel(context, x, y, couleurs >> 6);
 | 
						|
            couleurs <<= 2;
 | 
						|
          }
 | 
						|
          for (x = bx*8 + 4; x < bx*8+8; x++)
 | 
						|
          {
 | 
						|
            Set_pixel(context, x, y, forme >> 6);
 | 
						|
            forme <<= 2;
 | 
						|
          }
 | 
						|
#endif
 | 
						|
          break;
 | 
						|
        case MOTO_MODE_bm16:
 | 
						|
          Set_pixel(context, bx*4, y, forme >> 4);
 | 
						|
          Set_pixel(context, bx*4+1, y, forme & 0x0F);
 | 
						|
          Set_pixel(context, bx*4+2, y, couleurs >> 4);
 | 
						|
          Set_pixel(context, bx*4+3, y, couleurs & 0x0F);
 | 
						|
          break;
 | 
						|
        case MOTO_MODE_80col:
 | 
						|
          for (x = bx*16; x < bx*16+8; x++)
 | 
						|
          {
 | 
						|
            Set_pixel(context, x, y, (forme & 0x80) >> 7);
 | 
						|
            Set_pixel(context, x+8, y, (couleurs & 0x80) >> 7);
 | 
						|
            forme <<= 1;
 | 
						|
            couleurs <<= 1;
 | 
						|
          }
 | 
						|
          break;
 | 
						|
        case MOTO_MODE_40col:
 | 
						|
        default:
 | 
						|
          if (transpose)
 | 
						|
          {
 | 
						|
            // the color plane byte is bfFFFBBB (for TO7 compatibility)
 | 
						|
            // with the upper bits of both foreground (forme) and
 | 
						|
            // background (fond) inverted.
 | 
						|
            couleur_forme = ((couleurs & 0x78) >> 3) ^ 0x08;
 | 
						|
            couleur_fond = ((couleurs & 7) | ((couleurs & 0x80) >> 4)) ^ 0x08;
 | 
						|
          }
 | 
						|
          else
 | 
						|
          {
 | 
						|
            // MO5 : the color plane byte is FFFFBBBB
 | 
						|
            couleur_forme = couleurs >> 4;
 | 
						|
            couleur_fond = couleurs & 0x0F;
 | 
						|
          }
 | 
						|
          for (x = bx*8; x < bx*8+8; x++)
 | 
						|
          {
 | 
						|
            Set_pixel(context, x, y, (forme & 0x80)?couleur_forme:couleur_fond);
 | 
						|
            forme <<= 1;
 | 
						|
          }
 | 
						|
      }
 | 
						|
    }
 | 
						|
  }
 | 
						|
  free(vram_forme);
 | 
						|
  free(vram_couleur);
 | 
						|
}
 | 
						|
 | 
						|
/**
 | 
						|
 * Pack a stream of byte in the format used by Thomson MO/TO MAP files.
 | 
						|
 *
 | 
						|
 * - 00 cc xx yy .. : encodes a "copy run" (cc = bytes to copy)
 | 
						|
 * - cc xx          : encodes a "repeat run" (cc > 0 : count)
 | 
						|
 */
 | 
						|
//#define MOTO_MAP_NOPACKING
 | 
						|
static unsigned int MOTO_MAP_pack(byte * packed, const byte * unpacked, unsigned int unpacked_len)
 | 
						|
{
 | 
						|
  unsigned int src;
 | 
						|
  unsigned int dst = 0;
 | 
						|
  unsigned int count;
 | 
						|
#ifndef MOTO_MAP_NOPACKING
 | 
						|
  unsigned int repeat;
 | 
						|
  unsigned int i;
 | 
						|
  word * counts;
 | 
						|
#endif
 | 
						|
 | 
						|
  GFX2_Log(GFX2_DEBUG, "MOTO_MAP_pack(%p, %p, %u)\n", packed, unpacked, unpacked_len);
 | 
						|
  if (unpacked_len == 0)
 | 
						|
    return 0;
 | 
						|
  if (unpacked_len == 1)
 | 
						|
  {
 | 
						|
    packed[0] = 1;
 | 
						|
    packed[1] = unpacked[0];
 | 
						|
    return 2;
 | 
						|
  }
 | 
						|
#ifdef MOTO_MAP_NOPACKING
 | 
						|
  // compression disabled
 | 
						|
  src = 0;
 | 
						|
  while ((unpacked_len - src) > 255)
 | 
						|
  {
 | 
						|
    packed[dst++] = 0;
 | 
						|
    packed[dst++] = 255;
 | 
						|
    memcpy(packed+dst, unpacked+src, 255);
 | 
						|
    dst += 255;
 | 
						|
    src += 255;
 | 
						|
  }
 | 
						|
  count = unpacked_len - src;
 | 
						|
  packed[dst++] = 0;
 | 
						|
  packed[dst++] = count;
 | 
						|
  memcpy(packed+dst, unpacked+src, count);
 | 
						|
  dst += count;
 | 
						|
  src += count;
 | 
						|
  return dst;
 | 
						|
#else
 | 
						|
  counts = malloc(sizeof(word) * (unpacked_len + 1));
 | 
						|
  i = 0;
 | 
						|
  repeat = (unpacked[0] == unpacked[1]);
 | 
						|
  count = 2;
 | 
						|
  src = 2;
 | 
						|
  // 1st step : count lenght of the Copy runs and Repeat runs
 | 
						|
  while (src < unpacked_len)
 | 
						|
  {
 | 
						|
    if (repeat)
 | 
						|
    {
 | 
						|
      if (unpacked[src-1] == unpacked[src])
 | 
						|
        count++;
 | 
						|
      else
 | 
						|
      {
 | 
						|
        // flush the repeat run
 | 
						|
        counts[i++] = count | 0x8000; // 0x8000 is the marker for repeat runs
 | 
						|
        count = 1;
 | 
						|
        repeat = 0;
 | 
						|
      }
 | 
						|
    }
 | 
						|
    else
 | 
						|
    {
 | 
						|
      if (unpacked[src-1] != unpacked[src])
 | 
						|
        count++;
 | 
						|
      else if (count == 1)
 | 
						|
      {
 | 
						|
        count++;
 | 
						|
        repeat = 1;
 | 
						|
      }
 | 
						|
      else
 | 
						|
      {
 | 
						|
        // flush the copy run
 | 
						|
        counts[i++] = (count-1) | (count == 2 ? 0x8000 : 0); // mark copy run of 1 as repeat of 1
 | 
						|
        count = 2;
 | 
						|
        repeat = 1;
 | 
						|
      }
 | 
						|
    }
 | 
						|
    src++;
 | 
						|
  }
 | 
						|
  // flush the last run
 | 
						|
  counts[i++] = ((repeat || count == 1) ? 0x8000 : 0) | count;
 | 
						|
  counts[i++] = 0;  // end marker
 | 
						|
  // check consistency of counts
 | 
						|
  count = 0;
 | 
						|
  for (i = 0; counts[i] != 0; i++)
 | 
						|
    count += (counts[i] & ~0x8000);
 | 
						|
  if (count != unpacked_len)
 | 
						|
    GFX2_Log(GFX2_ERROR, "*** encoding error in MOTO_MAP_pack() *** count=%u unpacked_len=%u\n",
 | 
						|
             count, unpacked_len);
 | 
						|
  // output optimized packed stream
 | 
						|
  // repeat run are encoded cc xx
 | 
						|
  // copy run are encoded   00 cc xx xx xx xx
 | 
						|
  i = 0;
 | 
						|
  src = 0;
 | 
						|
  while (counts[i] != 0)
 | 
						|
  {
 | 
						|
    while (counts[i] & 0x8000)  // repeat run
 | 
						|
    {
 | 
						|
      count = counts[i] & ~0x8000;
 | 
						|
      GFX2_Log(GFX2_DEBUG, "MOTO_MAP_pack() %4u %4u repeat %u times %02x\n", src, i, count, unpacked[src]);
 | 
						|
      while(count > 255)
 | 
						|
      {
 | 
						|
        packed[dst++] = 255;
 | 
						|
        packed[dst++] = unpacked[src];
 | 
						|
        count -= 255;
 | 
						|
        src += 255;
 | 
						|
      }
 | 
						|
      packed[dst++] = count;
 | 
						|
      packed[dst++] = unpacked[src];
 | 
						|
      src += count;
 | 
						|
      i++;
 | 
						|
    }
 | 
						|
    while (counts[i] != 0 && !(counts[i] & 0x8000))  // copy run
 | 
						|
    {
 | 
						|
      // calculate the "savings" of repeat runs between 2 copy run
 | 
						|
      int savings = 0;
 | 
						|
      unsigned int j;
 | 
						|
      GFX2_Log(GFX2_DEBUG, "MOTO_MAP_pack() %4u %4u copy %u bytes\n", src, i, counts[i]);
 | 
						|
      for (j = i + 1; counts[j] & 0x8000; j++) // check repeat runs until the next copy run
 | 
						|
      {
 | 
						|
        count = counts[j] & ~0x8000;
 | 
						|
        if (savings < 0 && (savings + (int)count - 2) > 0)
 | 
						|
          break;
 | 
						|
        savings += count - 2; // a repeat run outputs 2 bytes for count bytes of input
 | 
						|
      }
 | 
						|
      count = counts[i];
 | 
						|
GFX2_Log(GFX2_DEBUG, " savings=%d i=%u j=%u (counts[j]=0x%04x)\n", savings, i, j, counts[j]);
 | 
						|
      if (savings < 2 && (j > i + 1))
 | 
						|
      {
 | 
						|
        unsigned int k;
 | 
						|
        if (counts[j] == 0) // go to the end of stream
 | 
						|
        {
 | 
						|
          for (k = i + 1; k < j; k++)
 | 
						|
            count += (counts[k] & ~0x8000);
 | 
						|
          GFX2_Log(GFX2_DEBUG, "MOTO_MAP_pack() src=%u extend copy from %u to %u\n", src, counts[i], count);
 | 
						|
          i = j - 1;
 | 
						|
        }
 | 
						|
        else
 | 
						|
        {
 | 
						|
          for (k = i + 1; k < j; k++)
 | 
						|
            count += (counts[k] & ~0x8000);
 | 
						|
          if (!(counts[j] & 0x8000))
 | 
						|
          { // merge with the next copy run (and the repeat runs between)
 | 
						|
            GFX2_Log(GFX2_DEBUG, "MOTO_MAP_pack() src=%u merge savings=%d\n", src, savings);
 | 
						|
            i = j;
 | 
						|
            counts[i] += count;
 | 
						|
            continue;
 | 
						|
          }
 | 
						|
          else
 | 
						|
          { // merge with the next few repeat runs
 | 
						|
            GFX2_Log(GFX2_DEBUG, "MOTO_MAP_pack() src=%u extends savings=%d\n", src, savings);
 | 
						|
            i = j - 1;
 | 
						|
          }
 | 
						|
        }
 | 
						|
      }
 | 
						|
      while (count > 255)
 | 
						|
      {
 | 
						|
        packed[dst++] = 0;
 | 
						|
        packed[dst++] = 255;
 | 
						|
        memcpy(packed+dst, unpacked+src, 255);
 | 
						|
        dst += 255;
 | 
						|
        src += 255;
 | 
						|
        count -= 255;
 | 
						|
      }
 | 
						|
      packed[dst++] = 0;
 | 
						|
      packed[dst++] = count;
 | 
						|
      memcpy(packed+dst, unpacked+src, count);
 | 
						|
      dst += count;
 | 
						|
      src += count;
 | 
						|
      i++;
 | 
						|
    }
 | 
						|
  }
 | 
						|
  free(counts);
 | 
						|
  return dst;
 | 
						|
#endif
 | 
						|
}
 | 
						|
 | 
						|
 | 
						|
/**
 | 
						|
 * GUI window to choose Thomson MO/TO saving parameters
 | 
						|
 *
 | 
						|
 * @param[out] machine target machine
 | 
						|
 * @param[out] format file format (0 = BIN, 1 = MAP)
 | 
						|
 * @param[in,out] mode video mode @ref MOTO_Graphic_Mode
 | 
						|
 */
 | 
						|
static int Save_MOTO_window(enum MOTO_Machine_Type * machine, int * format, enum MOTO_Graphic_Mode * mode)
 | 
						|
{
 | 
						|
  int button;
 | 
						|
  T_Dropdown_button * machine_dd;
 | 
						|
  T_Dropdown_button * format_dd;
 | 
						|
  T_Dropdown_button * mode_dd;
 | 
						|
  static const char * mode_list[] = { "40col", "80col", "bm4", "bm16" };
 | 
						|
  char text_info[24];
 | 
						|
 | 
						|
  Open_window(200, 125, "Thomson MO/TO Saving");
 | 
						|
  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,"Target Machine:",MC_Dark,MC_Light);
 | 
						|
  machine_dd = Window_set_dropdown_button(10,28,110,15,100,
 | 
						|
                                          (*mode == MOTO_MODE_40col) ? "TO7/TO7-70" : "TO9/TO8/TO9+",
 | 
						|
                                          1, 0, 1, LEFT_SIDE,0); // 3
 | 
						|
  if (*mode == MOTO_MODE_40col)
 | 
						|
    Window_dropdown_add_item(machine_dd, MACHINE_TO7, "TO7/TO7-70");
 | 
						|
  Window_dropdown_add_item(machine_dd, MACHINE_TO8, "TO9/TO8/TO9+");
 | 
						|
  if (*mode == MOTO_MODE_40col)
 | 
						|
    Window_dropdown_add_item(machine_dd, MACHINE_MO5, "MO5");
 | 
						|
  Window_dropdown_add_item(machine_dd, MACHINE_MO6, "MO6");
 | 
						|
 | 
						|
  Print_in_window(13,46,"Format:",MC_Dark,MC_Light);
 | 
						|
  format_dd = Window_set_dropdown_button(10,56,110,15,92,"BIN",1, 0, 1, LEFT_SIDE,0); // 4
 | 
						|
  Window_dropdown_add_item(format_dd, 0, "BIN");
 | 
						|
  Window_dropdown_add_item(format_dd, 1, "MAP/TO-SNAP");
 | 
						|
 | 
						|
  Print_in_window(136,46,"Mode:",MC_Dark,MC_Light);
 | 
						|
  mode_dd = Window_set_dropdown_button(136,56,54,15,44,mode_list[*mode],1, 0, 1, LEFT_SIDE,0); // 5
 | 
						|
  if (*mode == MOTO_MODE_40col)
 | 
						|
    Window_dropdown_add_item(mode_dd, *mode, mode_list[*mode]);
 | 
						|
  if (*mode == MOTO_MODE_80col)
 | 
						|
    Window_dropdown_add_item(mode_dd, *mode, mode_list[*mode]);
 | 
						|
  if (*mode == MOTO_MODE_40col)
 | 
						|
    Window_dropdown_add_item(mode_dd, MOTO_MODE_bm4, mode_list[MOTO_MODE_bm4]);
 | 
						|
  if (*mode == MOTO_MODE_bm16)
 | 
						|
    Window_dropdown_add_item(mode_dd, *mode, mode_list[*mode]);
 | 
						|
 | 
						|
  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, "THOMSON MO/TO FORMAT");
 | 
						|
    }
 | 
						|
    else switch (button)
 | 
						|
    {
 | 
						|
      case 3:
 | 
						|
        *machine = (enum MOTO_Machine_Type)Window_attribute2;
 | 
						|
        break;
 | 
						|
      case 4:
 | 
						|
        *format = Window_attribute2;
 | 
						|
        break;
 | 
						|
      case 5:
 | 
						|
        *mode = (enum MOTO_Graphic_Mode)Window_attribute2;
 | 
						|
        break;
 | 
						|
    }
 | 
						|
    Hide_cursor();
 | 
						|
    //"ABCDEFGHIJKLMNOPQRSTUVW"
 | 
						|
    memset(text_info, ' ', 23);
 | 
						|
    text_info[23] = '\0';
 | 
						|
    if (*machine == MACHINE_TO7 || *machine == MACHINE_TO770 || *machine == MACHINE_MO5)
 | 
						|
    {
 | 
						|
      if (*mode != MOTO_MODE_40col)
 | 
						|
        snprintf(text_info, sizeof(text_info), "%s only supports 40col",
 | 
						|
                 (*machine == MACHINE_MO5) ? "MO5" : "TO7");
 | 
						|
      else if (*format == 1)
 | 
						|
        strncpy(text_info, "No TO-SNAP extension.  ", sizeof(text_info));
 | 
						|
      else
 | 
						|
        strncpy(text_info, "No palette to save.    ", sizeof(text_info));
 | 
						|
    }
 | 
						|
    Print_in_window(9, 80, text_info, MC_Dark, MC_Light);
 | 
						|
    Display_cursor();
 | 
						|
  } while(button!=1 && button!=2);
 | 
						|
 | 
						|
  Close_window();
 | 
						|
  Display_cursor();
 | 
						|
  return button==1;
 | 
						|
}
 | 
						|
 | 
						|
/**
 | 
						|
 * Save a picture in MAP or BIN Thomson MO/TO file format.
 | 
						|
 *
 | 
						|
 * File format details :
 | 
						|
 * http://pulkomandy.tk/projects/GrafX2/wiki/Develop/FileFormats/MOTO
 | 
						|
 */
 | 
						|
void Save_MOTO(T_IO_Context * context)
 | 
						|
{
 | 
						|
  int transpose = 1;  // transpose upper bits in "couleur" vram
 | 
						|
  enum MOTO_Machine_Type target_machine = MACHINE_TO7;
 | 
						|
  int format = 0; // 0 = BIN, 1 = MAP
 | 
						|
  enum MOTO_Graphic_Mode mode;
 | 
						|
  FILE * file = NULL;
 | 
						|
  byte * vram_forme;
 | 
						|
  byte * vram_couleur;
 | 
						|
  int i, x, y, bx;
 | 
						|
  word reg_prc = 0xE7C3; // PRC : TO7/8/9 0xE7C3 ; MO5/MO6 0xA7C0
 | 
						|
  byte prc_value = 0x65;// Value to write to PRC to select VRAM bank
 | 
						|
  // MO5 : 0x51
 | 
						|
  word vram_address = 0x4000; // 4000 on TO7/TO8/TO9, 0000 on MO5/MO6
 | 
						|
 | 
						|
  File_error = 1;
 | 
						|
 | 
						|
  /**
 | 
						|
   * In the future we could support other resolution for .MAP
 | 
						|
   * format.
 | 
						|
   * And even in .BIN format, we could store less lines. */
 | 
						|
  if (context->Height != 200)
 | 
						|
  {
 | 
						|
    Warning_message("must be 640x200, 320x200 or 160x200");
 | 
						|
    return;
 | 
						|
  }
 | 
						|
 | 
						|
  switch (context->Width)
 | 
						|
  {
 | 
						|
    case 160:
 | 
						|
      mode = MOTO_MODE_bm16;
 | 
						|
      target_machine = MACHINE_TO8;
 | 
						|
      break;
 | 
						|
    case 640:
 | 
						|
      mode = MOTO_MODE_80col;
 | 
						|
      target_machine = MACHINE_TO8;
 | 
						|
      break;
 | 
						|
    case 320:
 | 
						|
      mode = MOTO_MODE_40col; // or bm4
 | 
						|
      break;
 | 
						|
    default:
 | 
						|
      Warning_message("must be 640x200, 320x200 or 160x200");
 | 
						|
      return;
 | 
						|
  }
 | 
						|
 | 
						|
  if (!Save_MOTO_window(&target_machine, &format, &mode))
 | 
						|
    return;
 | 
						|
 | 
						|
  if (target_machine == MACHINE_MO5 || target_machine == MACHINE_MO6)
 | 
						|
  {
 | 
						|
    reg_prc = 0xA7C0; // PRC : MO5/MO6 0xA7C0
 | 
						|
    prc_value = 0x51;
 | 
						|
    vram_address = 0;
 | 
						|
    transpose = 0;
 | 
						|
  }
 | 
						|
 | 
						|
  vram_forme = malloc(8192);
 | 
						|
  vram_couleur = malloc(8192);
 | 
						|
  switch (mode)
 | 
						|
  {
 | 
						|
  case MOTO_MODE_40col:
 | 
						|
    {
 | 
						|
      /**
 | 
						|
       * The 40col encoding algorithm is optimized for further vertical
 | 
						|
       * RLE packing. The "attibute" byte is kept as constant as possible
 | 
						|
       * between adjacent blocks.
 | 
						|
       */
 | 
						|
      unsigned color_freq[16];
 | 
						|
      unsigned max_freq = 0;
 | 
						|
      byte previous_fond = 0, previous_forme = 0;
 | 
						|
      byte most_used_color = 0;
 | 
						|
 | 
						|
      // search for most used color to prefer it as background color
 | 
						|
      for (i = 0; i < 16; i++)
 | 
						|
        color_freq[i] = 0;
 | 
						|
      for (y = 0; y < context->Height; y++)
 | 
						|
      {
 | 
						|
        for (x = 0; x < context->Width; x++)
 | 
						|
        {
 | 
						|
          byte col = Get_pixel(context, x, y);
 | 
						|
          if (col > 15)
 | 
						|
          {
 | 
						|
            Warning_with_format("color %u > 15 at pixel (%d,%d)", col, x, y);
 | 
						|
            goto error;
 | 
						|
          }
 | 
						|
          color_freq[col]++;
 | 
						|
        }
 | 
						|
      }
 | 
						|
      for (i = 0; i < 16; i++)
 | 
						|
      {
 | 
						|
        if (color_freq[i] > max_freq)
 | 
						|
        {
 | 
						|
          max_freq = color_freq[i];
 | 
						|
          most_used_color = (byte)i;  // most used color
 | 
						|
        }
 | 
						|
      }
 | 
						|
      previous_fond = most_used_color;
 | 
						|
      max_freq = 0;
 | 
						|
      for (i = 0; i < 16; i++)
 | 
						|
      {
 | 
						|
        if (i != most_used_color && color_freq[i] > max_freq)
 | 
						|
        {
 | 
						|
          max_freq = color_freq[i];
 | 
						|
          previous_forme = (byte)i;  // second most used color
 | 
						|
        }
 | 
						|
      }
 | 
						|
      GFX2_Log(GFX2_DEBUG, "Save_MOTO() most used color index %u, 2nd %u\n", previous_fond, previous_forme);
 | 
						|
 | 
						|
      if (target_machine == MACHINE_MO5)
 | 
						|
      {
 | 
						|
        /**
 | 
						|
         * For MO5 we use a different 40col algorithm
 | 
						|
         * to make sure the last pixel of a GPL and the first the next
 | 
						|
         * are both FORME or both FOND, else we get an ugly glitch on the
 | 
						|
         * EFGJ033 Gate Array MO5!
 | 
						|
         */
 | 
						|
        byte forme_byte = 0;
 | 
						|
        byte couleur_byte = 0x10;
 | 
						|
        GFX2_Log(GFX2_DEBUG, "Save_MOTO() 40col using MO5 algo\n");
 | 
						|
        for (y = 0; y < context->Height; y++)
 | 
						|
        {
 | 
						|
          for (bx = 0; bx < 40; bx++)
 | 
						|
          {
 | 
						|
            byte fond = 0xff, forme = 0xff;
 | 
						|
            forme_byte &= 1;  // Last bit of the previous FORME byte
 | 
						|
            x = bx*8;
 | 
						|
            if (forme_byte)
 | 
						|
              forme = Get_pixel(context, x, y);
 | 
						|
            else
 | 
						|
              fond = Get_pixel(context, x, y);
 | 
						|
            while (++x < bx * 8 + 8)
 | 
						|
            {
 | 
						|
              byte col = Get_pixel(context, x, y);
 | 
						|
              forme_byte <<= 1;
 | 
						|
              if (col == forme)
 | 
						|
                forme_byte |= 1;
 | 
						|
              else if (col != fond)
 | 
						|
              {
 | 
						|
                if (forme == 0xff)
 | 
						|
                {
 | 
						|
                  forme_byte |= 1;
 | 
						|
                  forme = col;
 | 
						|
                }
 | 
						|
                else if (fond == 0xff)
 | 
						|
                  fond = col;
 | 
						|
                else
 | 
						|
                {
 | 
						|
                  Warning_with_format("Constraint error at pixel (%d,%d)", x, y);
 | 
						|
                  goto error;
 | 
						|
                }
 | 
						|
              }
 | 
						|
            }
 | 
						|
            if (forme != 0xff)
 | 
						|
              couleur_byte = (forme << 4) | (couleur_byte & 0x0f);
 | 
						|
            if (fond != 0xff)
 | 
						|
              couleur_byte = (couleur_byte & 0xf0) | fond;
 | 
						|
            vram_forme[bx+y*40] = forme_byte;
 | 
						|
            vram_couleur[bx+y*40] = couleur_byte;
 | 
						|
          }
 | 
						|
        }
 | 
						|
      }
 | 
						|
      else
 | 
						|
      {
 | 
						|
        GFX2_Log(GFX2_DEBUG, "Save_MOTO() 40col using optimized algo\n");
 | 
						|
        // encoding of each 8x1 block
 | 
						|
        for (bx = 0; bx < 40; bx++)
 | 
						|
        {
 | 
						|
          for (y = 0; y < context->Height; y++)
 | 
						|
          {
 | 
						|
            byte forme_byte = 1;
 | 
						|
            byte col;
 | 
						|
            byte c1, c1_count = 1;
 | 
						|
            byte c2 = 0xff, c2_count = 0;
 | 
						|
            byte fond, forme;
 | 
						|
            x = bx * 8;
 | 
						|
            c1 = Get_pixel(context, x, y);
 | 
						|
            while (++x < bx * 8 + 8)
 | 
						|
            {
 | 
						|
              forme_byte <<= 1;
 | 
						|
              col = Get_pixel(context, x, y);
 | 
						|
              if (col > 15)
 | 
						|
              {
 | 
						|
                Warning_with_format("color %d > 15 at pixel (%d,%d)", col, x, y);
 | 
						|
                goto error;
 | 
						|
              }
 | 
						|
              if (col == c1)
 | 
						|
              {
 | 
						|
                forme_byte |= 1;
 | 
						|
                c1_count++;
 | 
						|
              }
 | 
						|
              else
 | 
						|
              {
 | 
						|
                c2_count++;
 | 
						|
                if (c2 == 0xff)
 | 
						|
                  c2 = col;
 | 
						|
                else if (col != c2)
 | 
						|
                {
 | 
						|
                  Warning_with_format("constraint error at pixel (%d,%d)", x, y);
 | 
						|
                  goto error;
 | 
						|
                }
 | 
						|
              }
 | 
						|
            }
 | 
						|
            if (c2 == 0xff)
 | 
						|
            {
 | 
						|
              // Only one color in the 8x1 block
 | 
						|
              if (c1 == previous_fond)
 | 
						|
                c2 = previous_forme;
 | 
						|
              else
 | 
						|
                c2 = previous_fond;
 | 
						|
            }
 | 
						|
            // select background color (fond)
 | 
						|
            // and foreground color (forme)
 | 
						|
            if (c1 == previous_fond)
 | 
						|
            {
 | 
						|
              fond = c1;
 | 
						|
              forme = c2;
 | 
						|
              forme_byte = ~forme_byte;
 | 
						|
            }
 | 
						|
            else if (c2 == previous_fond)
 | 
						|
            {
 | 
						|
              fond = c2;
 | 
						|
              forme = c1;
 | 
						|
            }
 | 
						|
            else if (c1 == most_used_color)
 | 
						|
            {
 | 
						|
              fond = c1;
 | 
						|
              forme = c2;
 | 
						|
              forme_byte = ~forme_byte;
 | 
						|
            }
 | 
						|
            else if (c2 == most_used_color)
 | 
						|
            {
 | 
						|
              fond = c2;
 | 
						|
              forme = c1;
 | 
						|
            }
 | 
						|
            else if (c1_count >= c2_count)
 | 
						|
            {
 | 
						|
              fond = c1;
 | 
						|
              forme = c2;
 | 
						|
              forme_byte = ~forme_byte;
 | 
						|
            }
 | 
						|
            else
 | 
						|
            {
 | 
						|
              fond = c2;
 | 
						|
              forme = c1;
 | 
						|
            }
 | 
						|
            // write to VRAM
 | 
						|
            vram_forme[bx+y*40] = forme_byte;
 | 
						|
            // transpose for TO7 compatibility
 | 
						|
            if (transpose)
 | 
						|
              vram_couleur[bx+y*40] = ((fond & 7) | ((fond & 8) << 4) | (forme << 3)) ^ 0xC0;
 | 
						|
            else
 | 
						|
              vram_couleur[bx+y*40] = fond | (forme << 4);
 | 
						|
            previous_fond = fond;
 | 
						|
            previous_forme = forme;
 | 
						|
          }
 | 
						|
          if (transpose)
 | 
						|
          {
 | 
						|
            previous_fond = (vram_couleur[bx] & 7) | (~vram_couleur[bx] & 0x80) >> 4;
 | 
						|
            previous_forme = ((vram_couleur[bx] & 0x78) >> 3) ^ 8;
 | 
						|
          }
 | 
						|
          else
 | 
						|
          {
 | 
						|
            previous_fond = vram_couleur[bx] & 15;
 | 
						|
            previous_forme = vram_couleur[bx] >> 4;
 | 
						|
          }
 | 
						|
        }
 | 
						|
      }
 | 
						|
    }
 | 
						|
    break;
 | 
						|
  case MOTO_MODE_80col:
 | 
						|
    for (bx = 0; bx < context->Width / 16; bx++)
 | 
						|
    {
 | 
						|
      for (y = 0; y < context->Height; y++)
 | 
						|
      {
 | 
						|
        byte val = 0;
 | 
						|
        for (x = bx * 16; x < bx*16 + 8; x++)
 | 
						|
          val = (val << 1) | Get_pixel(context, x, y);
 | 
						|
        vram_forme[y*(context->Width/16)+bx] = val;
 | 
						|
        for (; x < bx*16 + 16; x++)
 | 
						|
          val = (val << 1) | Get_pixel(context, x, y);
 | 
						|
        vram_couleur[y*(context->Width/16)+bx] = val;
 | 
						|
      }
 | 
						|
    }
 | 
						|
    break;
 | 
						|
  case MOTO_MODE_bm4:
 | 
						|
    for (y = 0; y < context->Height; y++)
 | 
						|
    {
 | 
						|
      for (bx = 0; bx < context->Width / 8; bx++)
 | 
						|
      {
 | 
						|
        byte val1 = 0, val2 = 0, pixel;
 | 
						|
        for (x = bx * 8; x < bx*8 + 8; x++)
 | 
						|
        {
 | 
						|
          pixel = Get_pixel(context, x, y);
 | 
						|
          if (pixel > 3)
 | 
						|
          {
 | 
						|
            Warning_with_format("color %d > 3 at pixel (%d,%d)", pixel, x, y);
 | 
						|
            goto error;
 | 
						|
          }
 | 
						|
          val1 = (val1 << 1) | (pixel >> 1);
 | 
						|
          val2 = (val2 << 1) | (pixel & 1);
 | 
						|
        }
 | 
						|
        vram_forme[y*(context->Width/8)+bx] = val1;
 | 
						|
        vram_couleur[y*(context->Width/8)+bx] = val2;
 | 
						|
      }
 | 
						|
    }
 | 
						|
    break;
 | 
						|
  case MOTO_MODE_bm16:
 | 
						|
    for (bx = 0; bx < context->Width / 4; bx++)
 | 
						|
    {
 | 
						|
      for (y = 0; y < context->Height; y++)
 | 
						|
      {
 | 
						|
         vram_forme[y*(context->Width/4)+bx] = (Get_pixel(context, bx*4, y) << 4) | Get_pixel(context, bx*4+1, y);
 | 
						|
         vram_couleur[y*(context->Width/4)+bx] = (Get_pixel(context, bx*4+2, y) << 4) | Get_pixel(context, bx*4+3, y);
 | 
						|
      }
 | 
						|
    }
 | 
						|
    break;
 | 
						|
  }
 | 
						|
  // palette
 | 
						|
  for (i = 0; i < 16; i++)
 | 
						|
  {
 | 
						|
    word to8color = MOTO_gamma_correct_RGB_to_MOTO(context->Palette + i);
 | 
						|
    vram_forme[8000+i*2] = to8color >> 8;
 | 
						|
    vram_forme[8000+i*2+1] = to8color & 0xFF;
 | 
						|
  }
 | 
						|
 | 
						|
  file = Open_file_write(context);
 | 
						|
  if (file == NULL)
 | 
						|
    goto error;
 | 
						|
 | 
						|
  if (format == 0)  // BIN
 | 
						|
  {
 | 
						|
    word chunk_length;
 | 
						|
 | 
						|
    if (target_machine == MACHINE_TO7 || target_machine == MACHINE_TO770 || target_machine == MACHINE_MO5)
 | 
						|
      chunk_length = 8000;  // Do not save palette
 | 
						|
    else
 | 
						|
    {
 | 
						|
      chunk_length = 8000 + 32 + 32;  // data + palette + comment
 | 
						|
      // Commentaire
 | 
						|
      if (context->Comment[0] != '\0')
 | 
						|
        strncpy((char *)vram_forme + 8032, context->Comment, 32);
 | 
						|
      else
 | 
						|
        snprintf((char *)vram_forme + 8032, 32, "GrafX2 %s.%s", Program_version, SVN_revision);
 | 
						|
      // also saves the video mode
 | 
						|
      vram_forme[8063] = '0' + mode;
 | 
						|
      memcpy(vram_couleur + 8000, vram_forme + 8000, 64);
 | 
						|
    }
 | 
						|
 | 
						|
    // Format BIN
 | 
						|
    // TO8/TO9 : set LGAMOD 0xE7DC  40col=0 bm4=0x21 80col=0x2a bm16=0x7b
 | 
						|
    if (!DECB_BIN_Add_Chunk(file, 1, reg_prc, &prc_value))
 | 
						|
      goto error;
 | 
						|
    if (!DECB_BIN_Add_Chunk(file, chunk_length, vram_address, vram_forme))
 | 
						|
      goto error;
 | 
						|
    prc_value &= 0xFE; // select color data
 | 
						|
    if (!DECB_BIN_Add_Chunk(file, 1, reg_prc, &prc_value))
 | 
						|
      goto error;
 | 
						|
    if (!DECB_BIN_Add_Chunk(file, chunk_length, vram_address, vram_couleur))
 | 
						|
      goto error;
 | 
						|
    if (!DECB_BIN_Add_End(file, 0x0000))
 | 
						|
      goto error;
 | 
						|
  }
 | 
						|
  else
 | 
						|
  {
 | 
						|
    // format MAP with TO-SNAP extensions
 | 
						|
    byte * unpacked_data;
 | 
						|
    byte * packed_data;
 | 
						|
 | 
						|
    unpacked_data = malloc(16*1024);
 | 
						|
    packed_data = malloc(16*1024);
 | 
						|
    if (packed_data == NULL || unpacked_data == NULL)
 | 
						|
    {
 | 
						|
      GFX2_Log(GFX2_ERROR, "Failed to allocate 2x16kB of memory\n");
 | 
						|
      free(packed_data);
 | 
						|
      free(unpacked_data);
 | 
						|
      goto error;
 | 
						|
    }
 | 
						|
    switch (mode)
 | 
						|
    {
 | 
						|
      case MOTO_MODE_40col:
 | 
						|
      case MOTO_MODE_bm4:
 | 
						|
        packed_data[0] = 0;  // mode
 | 
						|
        packed_data[1] = (context->Width / 8) - 1;
 | 
						|
        break;
 | 
						|
      case MOTO_MODE_80col:
 | 
						|
        packed_data[0] = 0x80; // mode
 | 
						|
        packed_data[1] = (context->Width / 8) - 1;
 | 
						|
        break;
 | 
						|
      case MOTO_MODE_bm16:
 | 
						|
        packed_data[0] = 0x40; // mode
 | 
						|
        packed_data[1] = (context->Width / 2) - 1;
 | 
						|
        break;
 | 
						|
    }
 | 
						|
    packed_data[2] = (context->Height / 8) - 1;
 | 
						|
    // 1st step : put data to pack in a linear buffer
 | 
						|
    // 2nd step : pack data
 | 
						|
    i = 0;
 | 
						|
    switch (mode)
 | 
						|
    {
 | 
						|
      case MOTO_MODE_40col:
 | 
						|
      case MOTO_MODE_bm4:
 | 
						|
        for (bx = 0; bx <= packed_data[1]; bx++)
 | 
						|
        {
 | 
						|
          for (y = 0; y < context->Height; y++)
 | 
						|
          {
 | 
						|
            unpacked_data[i] = vram_forme[bx + y*(packed_data[1]+1)];
 | 
						|
            unpacked_data[i+8192] = vram_couleur[bx + y*(packed_data[1]+1)];
 | 
						|
            i++;
 | 
						|
          }
 | 
						|
        }
 | 
						|
        i = 3;
 | 
						|
        i += MOTO_MAP_pack(packed_data+3, unpacked_data, context->Height * (packed_data[1]+1));
 | 
						|
        packed_data[i++] = 0; // ending of VRAM forme packing
 | 
						|
        packed_data[i++] = 0;
 | 
						|
        i += MOTO_MAP_pack(packed_data+i, unpacked_data + 8192, context->Height * (packed_data[1]+1));
 | 
						|
        packed_data[i++] = 0; // ending of VRAM couleur packing
 | 
						|
        packed_data[i++] = 0;
 | 
						|
        break;
 | 
						|
      case MOTO_MODE_80col:
 | 
						|
      case MOTO_MODE_bm16:
 | 
						|
        for (bx = 0; bx < (packed_data[1] + 1) / 2; bx++)
 | 
						|
        {
 | 
						|
          for (y = 0; y < context->Height; y++)
 | 
						|
            unpacked_data[i++] = vram_forme[bx + y*(packed_data[1]+1)/2];
 | 
						|
          for (y = 0; y < context->Height; y++)
 | 
						|
            unpacked_data[i++] = vram_couleur[bx + y*(packed_data[1]+1)/2];
 | 
						|
        }
 | 
						|
        i = 3;
 | 
						|
        i += MOTO_MAP_pack(packed_data+3, unpacked_data, context->Height * (packed_data[1]+1));
 | 
						|
        packed_data[i++] = 0; // ending of VRAM forme packing
 | 
						|
        packed_data[i++] = 0;
 | 
						|
        packed_data[i++] = 0; // ending of VRAM couleur packing
 | 
						|
        packed_data[i++] = 0;
 | 
						|
        break;
 | 
						|
    }
 | 
						|
    if (i&1)  // align
 | 
						|
      packed_data[i++] = 0;
 | 
						|
    if (target_machine != MACHINE_TO7 && target_machine != MACHINE_TO770 && target_machine != MACHINE_MO5)
 | 
						|
    {
 | 
						|
      // add TO-SNAP extension
 | 
						|
      // see http://collection.thomson.free.fr/code/articles/prehisto_bulletin/page.php?XI=0&XJ=13
 | 
						|
      // bytes 0-1 : Hardware video mode (value of SCRMOD 0x605F)
 | 
						|
      packed_data[i++] = 0;
 | 
						|
      switch (mode)
 | 
						|
      {
 | 
						|
        case MOTO_MODE_40col:
 | 
						|
          packed_data[i++] = 0;
 | 
						|
          break;
 | 
						|
        case MOTO_MODE_bm4:
 | 
						|
          packed_data[i++] = 0x01;
 | 
						|
          break;
 | 
						|
        case MOTO_MODE_80col:
 | 
						|
          packed_data[i++] = 0x80;
 | 
						|
          break;
 | 
						|
        case MOTO_MODE_bm16:
 | 
						|
          packed_data[i++] = 0x40;
 | 
						|
          break;
 | 
						|
      }
 | 
						|
      // bytes 2-3 : Border color
 | 
						|
      packed_data[i++] = 0;
 | 
						|
      packed_data[i++] = 0;
 | 
						|
      // bytes 4-5 : BASIC video mode (CONSOLE,,,,X)
 | 
						|
      packed_data[i++] = 0;
 | 
						|
      switch (mode)
 | 
						|
      {
 | 
						|
        case MOTO_MODE_40col:
 | 
						|
          packed_data[i++] = 0;
 | 
						|
          break;
 | 
						|
        case MOTO_MODE_bm4:
 | 
						|
          packed_data[i++] = 2;
 | 
						|
          break;
 | 
						|
        case MOTO_MODE_80col:
 | 
						|
          packed_data[i++] = 1;
 | 
						|
          break;
 | 
						|
        case MOTO_MODE_bm16:
 | 
						|
          packed_data[i++] = 3;
 | 
						|
          break;
 | 
						|
      }
 | 
						|
      // bytes 6-37 : BGR palette
 | 
						|
      for (x = 0; x < 16; x++)
 | 
						|
      {
 | 
						|
        word bgr = MOTO_gamma_correct_RGB_to_MOTO(context->Palette + x);
 | 
						|
        packed_data[i++] = bgr >> 8;
 | 
						|
        packed_data[i++] = bgr & 0xff;
 | 
						|
      }
 | 
						|
      // bytes 38-39 : TO-SNAP signature
 | 
						|
      packed_data[i++] = 0xA5;
 | 
						|
      packed_data[i++] = 0x5A;
 | 
						|
    }
 | 
						|
 | 
						|
    free(unpacked_data);
 | 
						|
 | 
						|
    if (!DECB_BIN_Add_Chunk(file, i, 0, packed_data) ||
 | 
						|
        !DECB_BIN_Add_End(file, 0x0000))
 | 
						|
    {
 | 
						|
      free(packed_data);
 | 
						|
      goto error;
 | 
						|
    }
 | 
						|
    free(packed_data);
 | 
						|
  }
 | 
						|
  fclose(file);
 | 
						|
  File_error = 0;
 | 
						|
  return;
 | 
						|
 | 
						|
error:
 | 
						|
  free(vram_forme);
 | 
						|
  free(vram_couleur);
 | 
						|
  if (file)
 | 
						|
    fclose(file);
 | 
						|
  File_error = 1;
 | 
						|
}
 | 
						|
 | 
						|
/////////////////////////////// Apple II Files //////////////////////////////
 | 
						|
 | 
						|
/**
 | 
						|
 * Test for an Apple II HGR or DHGR raw file
 | 
						|
 */
 | 
						|
void Test_HGR(T_IO_Context * context, FILE * file)
 | 
						|
{
 | 
						|
  long file_size;
 | 
						|
 | 
						|
  (void)context;
 | 
						|
  File_error = 1;
 | 
						|
 | 
						|
  file_size = File_length_file(file);
 | 
						|
  if (file_size == 8192)  // HGR
 | 
						|
    File_error = 0;
 | 
						|
  else if(file_size == 16384) // DHGR
 | 
						|
    File_error = 0;
 | 
						|
}
 | 
						|
 | 
						|
/**
 | 
						|
 * Load HGR (280x192) or DHGR (560x192) Apple II pictures
 | 
						|
 *
 | 
						|
 * Creates 2 layers :
 | 
						|
 * 1. Monochrome
 | 
						|
 * 2. Color
 | 
						|
 */
 | 
						|
void Load_HGR(T_IO_Context * context)
 | 
						|
{
 | 
						|
  unsigned long file_size;
 | 
						|
  FILE * file;
 | 
						|
  byte * vram[2];
 | 
						|
  int bank;
 | 
						|
  int x, y;
 | 
						|
  int is_dhgr = 0;
 | 
						|
 | 
						|
  file = Open_file_read(context);
 | 
						|
  if (file == NULL)
 | 
						|
  {
 | 
						|
    File_error = 1;
 | 
						|
    return;
 | 
						|
  }
 | 
						|
  file_size = File_length_file(file);
 | 
						|
  if (file_size == 16384)
 | 
						|
    is_dhgr = 1;
 | 
						|
 | 
						|
  vram[0] = malloc(8192);
 | 
						|
  Read_bytes(file, vram[0], 8192);
 | 
						|
  if (is_dhgr)
 | 
						|
  {
 | 
						|
    vram[1] = malloc(8192);
 | 
						|
    Read_bytes(file, vram[1], 8192);
 | 
						|
  }
 | 
						|
  else
 | 
						|
    vram[1] = NULL;
 | 
						|
  fclose(file);
 | 
						|
 | 
						|
  if (Config.Clear_palette)
 | 
						|
    memset(context->Palette,0,sizeof(T_Palette));
 | 
						|
  if (is_dhgr)
 | 
						|
  {
 | 
						|
    DHGR_set_palette(context->Palette);
 | 
						|
    Pre_load(context, 560, 192, file_size, FORMAT_HGR, PIXEL_TALL, 4);
 | 
						|
  }
 | 
						|
  else
 | 
						|
  {
 | 
						|
    HGR_set_palette(context->Palette);
 | 
						|
    Pre_load(context, 280, 192, file_size, FORMAT_HGR, PIXEL_SIMPLE, 2);
 | 
						|
  }
 | 
						|
  for (y = 0; y < 192; y++)
 | 
						|
  {
 | 
						|
    byte palette = 0, color = 0;
 | 
						|
    byte previous_palette = 0;  // palette for the previous pixel pair
 | 
						|
    int column, i;
 | 
						|
    int offset = ((y & 7) << 10) + ((y & 070) << 4) + ((y >> 6) * 40);
 | 
						|
    x = 0;
 | 
						|
    for (column = 0; column < 40; column++)
 | 
						|
    for (bank = 0; bank <= is_dhgr; bank++)
 | 
						|
    {
 | 
						|
      byte b = vram[bank][offset+column];
 | 
						|
      if (!is_dhgr)
 | 
						|
        palette = (b & 0x80) ? 4 : 0;
 | 
						|
      else
 | 
						|
        palette = (b & 0x80) ? 0 : 16;
 | 
						|
      for (i = 0; i < 7; i++)
 | 
						|
      {
 | 
						|
        if (context->Type == CONTEXT_MAIN_IMAGE)
 | 
						|
        {
 | 
						|
          // monochrome
 | 
						|
          Set_loading_layer(context, 0);
 | 
						|
          Set_pixel(context, x, y, ((b & 1) * (is_dhgr ? 15 : 3)) + palette);
 | 
						|
          Set_loading_layer(context, 1);
 | 
						|
        }
 | 
						|
        // color
 | 
						|
        color = (color << 1) | (b & 1);
 | 
						|
        if (is_dhgr)
 | 
						|
        {
 | 
						|
          if ((x & 3) == 0)
 | 
						|
            previous_palette = palette; // what is important is the value when the 1st bit was read...
 | 
						|
          /// emulate "chat mauve" DHGR mixed mode.
 | 
						|
          /// see http://boutillon.free.fr/Underground/Anim_Et_Graph/Extasie_Chat_Mauve_Reloaded/Extasie_Chat_Mauve_Reloaded.html
 | 
						|
          if (previous_palette) // BW
 | 
						|
            Set_pixel(context, x, y, ((b & 1) * 15) + palette);
 | 
						|
          else if ((x & 3) == 3)
 | 
						|
          {
 | 
						|
            Set_pixel(context, x - 3, y, (color & 15) + palette);
 | 
						|
            Set_pixel(context, x - 2, y, (color & 15) + palette);
 | 
						|
            Set_pixel(context, x - 1, y, (color & 15) + palette);
 | 
						|
            Set_pixel(context, x, y, (color & 15) + palette);
 | 
						|
          }
 | 
						|
        }
 | 
						|
        else
 | 
						|
        {
 | 
						|
          /// HGR emulation following the behaviour of a "Le Chat Mauve"
 | 
						|
          /// RVB adapter for the Apple //c.
 | 
						|
          /// Within the bit stream, the color of the middle pixel is :<br>
 | 
						|
          /// <tt>
 | 
						|
          /// 111 \          <br>
 | 
						|
          /// 110  }- white  <br>
 | 
						|
          /// 011 /          <br>
 | 
						|
          /// 010 \ _ color  <br>
 | 
						|
          /// 101 /          <br>
 | 
						|
          /// 000 \          <br>
 | 
						|
          /// 001  }- black  <br>
 | 
						|
          /// 100 /          <br>
 | 
						|
          /// </tt>
 | 
						|
          /// Color depends on the selected palette for the current byte
 | 
						|
          /// and the position of the pixel (odd or even).
 | 
						|
          if ((color & 3) == 3) // 11 => white
 | 
						|
          {
 | 
						|
            Set_pixel(context, x - 1, y, 3 + previous_palette);
 | 
						|
            Set_pixel(context, x, y, 3 + palette);
 | 
						|
          }
 | 
						|
          else if ((color & 1) == 0) // 0 => black
 | 
						|
            Set_pixel(context, x, y, palette);
 | 
						|
          else // 01 => color
 | 
						|
          {
 | 
						|
            if ((color & 7) == 5) // 101
 | 
						|
              Set_pixel(context, x - 1, y, 2 - (x & 1) + previous_palette);
 | 
						|
            Set_pixel(context, x, y, 2 - (x & 1) + palette);
 | 
						|
          }
 | 
						|
          previous_palette = palette;
 | 
						|
        }
 | 
						|
        b >>= 1;
 | 
						|
        x++;
 | 
						|
      }
 | 
						|
    }
 | 
						|
  }
 | 
						|
  // show hidden data in HOLES
 | 
						|
  for (y = 0; y < 64; y++)
 | 
						|
  for (bank = 0; bank < 1; bank++)
 | 
						|
  {
 | 
						|
    byte b = 0;
 | 
						|
    for (x = 0; x < 8; x++)
 | 
						|
      b |= vram[bank][x + (y << 7) + 120];
 | 
						|
    if (b != 0)
 | 
						|
      GFX2_LogHexDump(GFX2_DEBUG, bank ? "AUX " : "MAIN", vram[bank], (y << 7) + 120, 8);
 | 
						|
  }
 | 
						|
  free(vram[0]);
 | 
						|
  free(vram[1]);
 | 
						|
  File_error = 0;
 | 
						|
 | 
						|
  Set_image_mode(context, is_dhgr ? IMAGE_MODE_DHGR : IMAGE_MODE_HGR);
 | 
						|
}
 | 
						|
 | 
						|
/**
 | 
						|
 * Save HGR (280x192) or DHGR (560x192) Apple II pictures
 | 
						|
 *
 | 
						|
 * The data saved is the "monochrome" data from layer 1
 | 
						|
 */
 | 
						|
void Save_HGR(T_IO_Context * context)
 | 
						|
{
 | 
						|
  FILE * file;
 | 
						|
  byte * vram[2];
 | 
						|
  int bank;
 | 
						|
  int x, y;
 | 
						|
  int is_dhgr = 0;
 | 
						|
 | 
						|
  File_error = 1;
 | 
						|
  if (context->Height != 192 || (context->Width != 280 && context->Width != 560))
 | 
						|
  {
 | 
						|
    Warning_message("Picture must be 280x192 (HGR) or 560x192 (DHGR)");
 | 
						|
    return;
 | 
						|
  }
 | 
						|
  if (context->Width == 560)
 | 
						|
    is_dhgr = 1;
 | 
						|
  
 | 
						|
  file = Open_file_write(context);
 | 
						|
  if (file == NULL)
 | 
						|
    return;
 | 
						|
  vram[0] = calloc(8192, 1);
 | 
						|
  if (vram[0] == NULL)
 | 
						|
  {
 | 
						|
    fclose(file);
 | 
						|
    return;
 | 
						|
  }
 | 
						|
  if (is_dhgr)
 | 
						|
  {
 | 
						|
    vram[1] = calloc(8192, 1);
 | 
						|
    if (vram[1] == NULL)
 | 
						|
    {
 | 
						|
      free(vram[0]);
 | 
						|
      fclose(file);
 | 
						|
      return;
 | 
						|
    }
 | 
						|
  }
 | 
						|
  else
 | 
						|
    vram[1] = NULL;
 | 
						|
 | 
						|
  Set_saving_layer(context, 0); // "monochrome" layer
 | 
						|
  for (y = 0; y < 192; y++)
 | 
						|
  {
 | 
						|
    int i, column = 0;
 | 
						|
    int offset = ((y & 7) << 10) + ((y & 070) << 4) + ((y >> 6) * 40);
 | 
						|
    x = 0;
 | 
						|
    bank = 0;
 | 
						|
    while (x < context->Width)
 | 
						|
    {
 | 
						|
      byte b;
 | 
						|
      if (is_dhgr)
 | 
						|
        b = (Get_pixel(context, x, y) & 16) ? 0 : 0x80;
 | 
						|
      else
 | 
						|
        b = (Get_pixel(context, x, y) & 4) ? 0x80 : 0;
 | 
						|
      for (i = 0; i < 7; i++)
 | 
						|
      {
 | 
						|
        b = b | ((Get_pixel(context, x++, y) & 1) << i);
 | 
						|
      }
 | 
						|
      vram[bank][offset + column] = b;
 | 
						|
      if (is_dhgr)
 | 
						|
      {
 | 
						|
        if (++bank > 1)
 | 
						|
        {
 | 
						|
          bank = 0;
 | 
						|
          column++;
 | 
						|
        }
 | 
						|
      }
 | 
						|
      else
 | 
						|
        column++;
 | 
						|
    }
 | 
						|
  }
 | 
						|
 | 
						|
  if (Write_bytes(file, vram[0], 8192))
 | 
						|
  {
 | 
						|
    if (is_dhgr)
 | 
						|
    {
 | 
						|
      if (Write_bytes(file, vram[1], 8192))
 | 
						|
        File_error = 0;
 | 
						|
    }
 | 
						|
    else
 | 
						|
      File_error = 0;
 | 
						|
  }
 | 
						|
 | 
						|
  free(vram[0]);
 | 
						|
  free(vram[1]);
 | 
						|
  fclose(file);
 | 
						|
}
 |