1477 lines
		
	
	
		
			41 KiB
		
	
	
	
		
			C
		
	
	
	
	
	
			
		
		
	
	
			1477 lines
		
	
	
		
			41 KiB
		
	
	
	
		
			C
		
	
	
	
	
	
/* vim:expandtab:ts=2 sw=2:
 | 
						|
*/
 | 
						|
/*  Grafx2 - The Ultimate 256-color bitmap paint program
 | 
						|
 | 
						|
    Copyright 2018-2019 Thomas Bernard
 | 
						|
    Copyright 2011 Pawel Góralski
 | 
						|
    Copyright 2009 Petter Lindquist
 | 
						|
    Copyright 2008 Yves Rizoud
 | 
						|
    Copyright 2008 Franck Charlet
 | 
						|
    Copyright 2007-2011 Adrien Destugues
 | 
						|
    Copyright 1996-2001 Sunset Design (Guillaume Dorme & Karl Maritaud)
 | 
						|
 | 
						|
    Grafx2 is free software; you can redistribute it and/or
 | 
						|
    modify it under the terms of the GNU General Public License
 | 
						|
    as published by the Free Software Foundation; version 2
 | 
						|
    of the License.
 | 
						|
 | 
						|
    Grafx2 is distributed in the hope that it will be useful,
 | 
						|
    but WITHOUT ANY WARRANTY; without even the implied warranty of
 | 
						|
    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 | 
						|
    GNU General Public License for more details.
 | 
						|
 | 
						|
    You should have received a copy of the GNU General Public License
 | 
						|
    along with Grafx2; if not, see <http://www.gnu.org/licenses/>
 | 
						|
*/
 | 
						|
 | 
						|
///@file cpcformats.c
 | 
						|
/// Formats for the Amstrad CPC / CPC Plus computers
 | 
						|
 | 
						|
#include <stdlib.h>
 | 
						|
#include <string.h>
 | 
						|
#include "global.h"
 | 
						|
#include "fileformats.h"
 | 
						|
#include "io.h"
 | 
						|
#include "loadsavefuncs.h"
 | 
						|
#include "libraw2crtc.h"
 | 
						|
#include "oldies.h"
 | 
						|
#include "gfx2mem.h"
 | 
						|
#include "gfx2log.h"
 | 
						|
 | 
						|
/**
 | 
						|
 * 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)
 | 
						|
{
 | 
						|
  // http://orgams.wikidot.com/le-format-impdraw-v2
 | 
						|
  // http://orgams.wikidot.com/les-fichiers-win-compatibles-ocp-art-studio
 | 
						|
  FILE * pal_file;
 | 
						|
  unsigned long pal_size, file_size;
 | 
						|
  byte mode, color_anim_flag;
 | 
						|
  word loading_address = 0;
 | 
						|
 | 
						|
  File_error = 1;
 | 
						|
 | 
						|
  if (CPC_check_AMSDOS(file, &loading_address, &file_size))
 | 
						|
  {
 | 
						|
    if (loading_address == 0x170) // iMPdraw v2
 | 
						|
    {
 | 
						|
      byte buffer[0x90];
 | 
						|
      fseek(file, 128, SEEK_SET); // right after AMSDOS header
 | 
						|
      Read_bytes(file, buffer, 0x90);
 | 
						|
      GFX2_LogHexDump(GFX2_DEBUG, "", buffer, 0, 0x90);
 | 
						|
      File_error = 0;
 | 
						|
      return;
 | 
						|
    }
 | 
						|
    else if ((loading_address == 0x200 || loading_address == 0xc000) && file_size > 16000)
 | 
						|
    {
 | 
						|
      File_error = 0;
 | 
						|
      return;
 | 
						|
    }
 | 
						|
  }
 | 
						|
  else
 | 
						|
    file_size = File_length_file(file);
 | 
						|
 | 
						|
  if (file_size > 16384*2)
 | 
						|
    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
 | 
						|
   */
 | 
						|
 | 
						|
 | 
						|
  if (CPC_check_AMSDOS(pal_file, NULL, &pal_size))
 | 
						|
    fseek(pal_file, 128, SEEK_SET); // right after AMSDOS header
 | 
						|
  else
 | 
						|
  {
 | 
						|
    pal_size = File_length_file(pal_file);
 | 
						|
    fseek(pal_file, 0, SEEK_SET);
 | 
						|
  }
 | 
						|
 | 
						|
  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;
 | 
						|
  word addr;
 | 
						|
  word load_address = 0x4000; // default for OCP Art studio
 | 
						|
  word display_start = 0x4000;
 | 
						|
  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 * cpc_ram;
 | 
						|
  word x, y;
 | 
						|
  int i;
 | 
						|
  byte sig[3];
 | 
						|
  word block_length;
 | 
						|
  word win_width, win_height;
 | 
						|
  int is_win = 0;
 | 
						|
  int columns = 80;
 | 
						|
  int cpc_plus = 0;
 | 
						|
  const byte * cpc_plus_pal = NULL;
 | 
						|
 | 
						|
  File_error = 1;
 | 
						|
  // requires the PAL file for OCP Art studio files
 | 
						|
  pal_file = Open_file_read_with_alternate_ext(context, "pal");
 | 
						|
  if (pal_file != NULL)
 | 
						|
  {
 | 
						|
    file_size = File_length_file(pal_file);
 | 
						|
    if (CPC_check_AMSDOS(pal_file, NULL, &file_size))
 | 
						|
      fseek(pal_file, 128, SEEK_SET); // right after AMSDOS header
 | 
						|
    else
 | 
						|
      fseek(pal_file, 0, SEEK_SET);
 | 
						|
    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);
 | 
						|
  }
 | 
						|
 | 
						|
  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, &load_address, &amsdos_file_size))
 | 
						|
  {
 | 
						|
    display_start = load_address;
 | 
						|
    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 (!Read_bytes(file, sig, 3) || !Read_word_le(file, &block_length))
 | 
						|
  {
 | 
						|
    fclose(file);
 | 
						|
    return;
 | 
						|
  }
 | 
						|
  fseek(file, -5, SEEK_CUR);
 | 
						|
 | 
						|
  cpc_ram = GFX2_malloc(64*1024);
 | 
						|
  memset(cpc_ram, 0, 64*1024);
 | 
						|
 | 
						|
  if (0 != memcmp(sig, "MJH", 3) || block_length > 16384)
 | 
						|
  {
 | 
						|
    // raw data
 | 
						|
    Read_bytes(file, cpc_ram + load_address, 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
 | 
						|
          {
 | 
						|
            cpc_ram[load_address + i++] = value;
 | 
						|
            block_length--;
 | 
						|
          }
 | 
						|
          while(--repeat != 0);
 | 
						|
        }
 | 
						|
        else
 | 
						|
        {
 | 
						|
          cpc_ram[load_address + 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 = cpc_ram[load_address + i - 4] + (cpc_ram[load_address + i - 3] << 8);  // in bits
 | 
						|
    win_height = cpc_ram[load_address + 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,
 | 
						|
          cpc_ram[load_address + i - 5], cpc_ram[load_address + i - 4], cpc_ram[load_address + i - 3],
 | 
						|
          cpc_ram[load_address + i - 2], cpc_ram[load_address + i - 1]);
 | 
						|
    }
 | 
						|
    else
 | 
						|
    {
 | 
						|
      GFX2_Log(GFX2_DEBUG, ".SCR file. Data length %d\n", i);
 | 
						|
      if (load_address == 0x170)
 | 
						|
      {
 | 
						|
        // fichier iMPdraw v2
 | 
						|
        // http://orgams.wikidot.com/le-format-impdraw-v2
 | 
						|
        GFX2_Log(GFX2_DEBUG, "Detected \"%s\"\n", cpc_ram + load_address + 6);
 | 
						|
        mode = cpc_ram[load_address + 0x14] - 0x0e;
 | 
						|
        cpc_plus = cpc_ram[load_address + 0x3c];
 | 
						|
        GFX2_Log(GFX2_DEBUG, "Mode %d CPC %d\n", (int)mode, (int)cpc_plus);
 | 
						|
        for (addr = load_address + 0x1d; cpc_ram[addr] < 16; addr += 2)
 | 
						|
        {
 | 
						|
          GFX2_Log(GFX2_DEBUG, " R%d = &H%02x = %d\n", cpc_ram[addr], cpc_ram[addr+1], cpc_ram[addr+1]);
 | 
						|
          // see http://www.cpcwiki.eu/index.php/CRTC#The_6845_Registers
 | 
						|
          switch(cpc_ram[addr])
 | 
						|
          {
 | 
						|
            case 1:
 | 
						|
              columns = cpc_ram[addr+1] * 2;
 | 
						|
              break;
 | 
						|
            case 6:
 | 
						|
              height = cpc_ram[addr+1] * 8;
 | 
						|
              break;
 | 
						|
            case 12:
 | 
						|
              display_start = ((cpc_ram[addr+1] & 0x30) << 10) | ((cpc_ram[addr+1] & 0x03) << 9);
 | 
						|
              GFX2_Log(GFX2_DEBUG, "  display_start &H%04X\n", display_start);
 | 
						|
           }
 | 
						|
        }
 | 
						|
        snprintf(context->Comment, COMMENT_SIZE, "%s mode %d %s",
 | 
						|
                 cpc_ram + load_address + 7, mode, cpc_plus ? "CPC+" : "");
 | 
						|
        if (cpc_plus)
 | 
						|
        {
 | 
						|
          // palette at 0x801 (mode at 0x800 ?)
 | 
						|
          GFX2_LogHexDump(GFX2_DEBUG, "", cpc_ram, 0x800, 0x21);
 | 
						|
          cpc_plus_pal = cpc_ram + 0x801;
 | 
						|
        }
 | 
						|
        else
 | 
						|
        {
 | 
						|
          int j;
 | 
						|
          // palette at 0x7f00
 | 
						|
          GFX2_LogHexDump(GFX2_DEBUG, "", cpc_ram, 0x7f00, 16);
 | 
						|
          for (j = 0; j < 16; j++)
 | 
						|
            pal_data[12*j] = cpc_ram[0x7f00 + j];
 | 
						|
        }
 | 
						|
      }
 | 
						|
      else if (load_address == 0x200)
 | 
						|
      {
 | 
						|
        /* from HARLEY.SCR :
 | 
						|
        0800  00 = mode
 | 
						|
        0801-0810 palette (Firmware colors)
 | 
						|
        0811  21 47 08      LD HL,0847  ; OVERSCAN_REG_VALUES
 | 
						|
        0814  cd 36 08      CALL 0836 ; LOAD_CRTC_REGS
 | 
						|
        0817  3a 00 08      LD A,(0800) ; MODE
 | 
						|
        081a  cd 1c bd      CALL BD1C ; Set screen mode
 | 
						|
        081d  21 01 08      LD HL,0801  ; PALETTE
 | 
						|
        0820  af            XOR A
 | 
						|
            LOOP:
 | 
						|
        0821  4e            LD C,(HL)
 | 
						|
        0822  41            LD B,C
 | 
						|
        0823  f5            PUSH AF
 | 
						|
        0824  e5            PUSH HL
 | 
						|
        0825  cd 32 bc      CALL BC32   ; SET ink A to color B,C
 | 
						|
        0828  e1            POP HL
 | 
						|
        0829  f1            POP AF
 | 
						|
        082a  23            INC HL
 | 
						|
        082b  3c            INC A
 | 
						|
        082c  fe 10         CMP 10
 | 
						|
        082e  20 f1         JR NZ,0821  ; LOOP
 | 
						|
        0830  cd 18 bb      CALL BB18 ; Wait key press
 | 
						|
        0833  21 55 08      LD HL,0855  ; STANDARD_REG_VALUES
 | 
						|
            LOAD_CRTC_REGS:
 | 
						|
        0836  01 00 bc      LD BC,BC00
 | 
						|
            LOOP_CRTC:
 | 
						|
        0839  7e            LD A,(HL)
 | 
						|
        083a  a7            AND A
 | 
						|
        083b  c8            RET Z
 | 
						|
        083c  ed 79         OUT (C),A
 | 
						|
        083e  04            INC B
 | 
						|
        083f  23            INC HL
 | 
						|
        0840  7e            LD A,(HL)
 | 
						|
        0841  ed 79         OUT (C),A
 | 
						|
        0843  23            INC HL
 | 
						|
        0844  05            DEC B
 | 
						|
        0845  18 f2         JR 0839 ; LOOP_CRTC
 | 
						|
            OVERSCAN_REG_VALUES:
 | 
						|
        0847  01 30  02 32  06 22  07 23  0c 0d  0d 00  00 00
 | 
						|
            STANDARD_REG_VALUES:
 | 
						|
        0855  01 28  02 2e  06 19  07 1e  0c 30  00 00
 | 
						|
        */
 | 
						|
        int j;
 | 
						|
        static const byte CPC_Firmware_Colors[] = {
 | 
						|
          0x54, 0x44, 0x55, 0x5c, 0x58, 0x5d, 0x4c, 0x45, 0x4d,
 | 
						|
          0x56, 0x46, 0x57, 0x5e, 0x40, 0x5f, 0x4e, 0x47, 0x4f,
 | 
						|
          0x52, 0x42, 0x53, 0x5a, 0x59, 0x5b, 0x4a, 0x43, 0x4b };
 | 
						|
        mode = cpc_ram[0x800];
 | 
						|
        for (j = 0; j < 16; j++)
 | 
						|
          pal_data[12*j] = CPC_Firmware_Colors[cpc_ram[0x801 + j]];
 | 
						|
        addr = 0x847;
 | 
						|
        if (cpc_ram[0x80bb] == 1)
 | 
						|
          addr = 0x80bb;
 | 
						|
        for (; cpc_ram[addr] > 0 && cpc_ram[addr] < 16; addr += 2)
 | 
						|
        {
 | 
						|
          GFX2_Log(GFX2_DEBUG, " R%d = &H%02x = %d\n", cpc_ram[addr], cpc_ram[addr+1], cpc_ram[addr+1]);
 | 
						|
          // see http://www.cpcwiki.eu/index.php/CRTC#The_6845_Registers
 | 
						|
          switch(cpc_ram[addr])
 | 
						|
          {
 | 
						|
            case 1:
 | 
						|
              columns = cpc_ram[addr+1] * 2;
 | 
						|
              break;
 | 
						|
            case 6:
 | 
						|
              height = cpc_ram[addr+1] * 8;
 | 
						|
              break;
 | 
						|
            case 12:
 | 
						|
              display_start = (display_start & 0x00ff) | ((cpc_ram[addr+1] & 0x30) << 10) | ((cpc_ram[addr+1] & 0x03) << 9);
 | 
						|
              break;
 | 
						|
            case 13:
 | 
						|
              display_start = (display_start & 0xff00) | cpc_ram[addr+1];
 | 
						|
           }
 | 
						|
         }
 | 
						|
      }
 | 
						|
      if (i >= 30000)
 | 
						|
      {
 | 
						|
        height = 272; columns = 96;
 | 
						|
      }
 | 
						|
    }
 | 
						|
  }
 | 
						|
 | 
						|
  switch (mode)
 | 
						|
  {
 | 
						|
    case 0:
 | 
						|
      width = columns * 2;
 | 
						|
      bpp = 4;
 | 
						|
      ratio = PIXEL_WIDE;
 | 
						|
      break;
 | 
						|
    case 1:
 | 
						|
      width = columns * 4;
 | 
						|
      bpp = 2;
 | 
						|
      ratio = PIXEL_SIMPLE;
 | 
						|
      break;
 | 
						|
    case 2:
 | 
						|
      width = columns * 8;
 | 
						|
      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
 | 
						|
  if (cpc_plus_pal)
 | 
						|
  {
 | 
						|
    for (i = 0; i < 16; i++)
 | 
						|
    {
 | 
						|
      context->Palette[i].G = cpc_plus_pal[i*2 + 1] * 0x11;
 | 
						|
      context->Palette[i].R = (cpc_plus_pal[i*2] >> 4) * 0x11;
 | 
						|
      context->Palette[i].B = (cpc_plus_pal[i*2] & 15) * 0x11;
 | 
						|
    }
 | 
						|
  }
 | 
						|
  else
 | 
						|
  {
 | 
						|
    for (i = 0; i < 16; i++)
 | 
						|
      context->Palette[i] = context->Palette[pal_data[12*i]];
 | 
						|
  }
 | 
						|
 | 
						|
  File_error = 0;
 | 
						|
  Pre_load(context, width, height, real_file_size, FORMAT_SCR, ratio, bpp);
 | 
						|
 | 
						|
  if (!is_win)
 | 
						|
  {
 | 
						|
    // 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 (y = 0; y < 8; y++)
 | 
						|
    {
 | 
						|
      addr = display_start + 0x800 * y;
 | 
						|
      if (y > 0 && (display_start & 0x7ff))
 | 
						|
      {
 | 
						|
        if (!GFX2_is_mem_filled_with(cpc_ram + (addr & 0xf800), 0, display_start & 0x7ff))
 | 
						|
          GFX2_LogHexDump(GFX2_DEBUG, "SCR1 ", cpc_ram,
 | 
						|
                          addr & 0xf800, display_start & 0x7ff);
 | 
						|
      }
 | 
						|
      addr += (height >> 3) * columns;
 | 
						|
      block_length = (height >> 3) * columns + (display_start & 0x7ff);
 | 
						|
      if (block_length <= 0x800)
 | 
						|
      {
 | 
						|
        block_length = 0x800 - block_length;
 | 
						|
        if (!GFX2_is_mem_filled_with(cpc_ram + addr, 0, block_length))
 | 
						|
          GFX2_LogHexDump(GFX2_DEBUG, "SCR2 ", cpc_ram,
 | 
						|
                          addr, block_length);
 | 
						|
      }
 | 
						|
      else
 | 
						|
      {
 | 
						|
        block_length = 0x1000 - block_length;
 | 
						|
        if (!GFX2_is_mem_filled_with(cpc_ram + addr + 0x4000, 0, block_length))
 | 
						|
          GFX2_LogHexDump(GFX2_DEBUG, "SCR2 ", cpc_ram,
 | 
						|
                          addr + 0x4000, block_length);
 | 
						|
      }
 | 
						|
    }
 | 
						|
    //for (j = 0; j < i; j += 2048)
 | 
						|
    //  GFX2_LogHexDump(GFX2_DEBUG, "SCR ", cpc_ram, load_address + j + 2000, 48);
 | 
						|
  }
 | 
						|
 | 
						|
  GFX2_Log(GFX2_DEBUG, "  display_start &H%04X\n", display_start);
 | 
						|
  for (y = 0; y < height; y++)
 | 
						|
  {
 | 
						|
    const byte * line;
 | 
						|
 | 
						|
    if (is_win)
 | 
						|
      addr = display_start + y * columns;
 | 
						|
    else
 | 
						|
    {
 | 
						|
      addr = display_start + ((y >> 3) * columns);
 | 
						|
      addr = (addr & 0xC7FF) | ((addr & 0x800) << 3);
 | 
						|
      addr += (y & 7) << 11;
 | 
						|
    }
 | 
						|
    //GFX2_Log(GFX2_DEBUG, "line#%d &H%04X\n", y, addr);
 | 
						|
    line = cpc_ram + addr;
 | 
						|
    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(cpc_ram);
 | 
						|
}
 | 
						|
 | 
						|
/**
 | 
						|
 * 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 GO1/GO2/KIT - Amstrad Plus Graphos
 | 
						|
 *
 | 
						|
 * This format is made of 3 files
 | 
						|
 * .KIT hold the palette in "Kit4096" format. There are 16 colors each stored
 | 
						|
 * as 12 bit RGB in RB0G order.
 | 
						|
 * .GO1 and GO2 hold each half of the picture (top and bottom)
 | 
						|
 * The file always cover the whole display of the Plus (196*272 or so)
 | 
						|
 */
 | 
						|
void Test_GOS(T_IO_Context * context, FILE * file)
 | 
						|
{
 | 
						|
  FILE *file_oddeve;
 | 
						|
  unsigned long file_size = 0;
 | 
						|
 | 
						|
  if (!CPC_check_AMSDOS(file, NULL, &file_size))
 | 
						|
    file_size = File_length_file(file);
 | 
						|
  if (file_size < 16383 || file_size > 16384) {
 | 
						|
    File_error = 1;
 | 
						|
    return;
 | 
						|
  }
 | 
						|
 | 
						|
  file_oddeve = Open_file_read_with_alternate_ext(context, "GO2");
 | 
						|
  if (file_oddeve == NULL) {
 | 
						|
    File_error = 2;
 | 
						|
    return;
 | 
						|
  }
 | 
						|
  if (!CPC_check_AMSDOS(file_oddeve, NULL, &file_size))
 | 
						|
    file_size = File_length_file(file_oddeve);
 | 
						|
  fclose(file_oddeve);
 | 
						|
  if (file_size < 16383 || file_size > 16384) {
 | 
						|
    File_error = 3;
 | 
						|
    return;
 | 
						|
  }
 | 
						|
 | 
						|
  File_error = 0;
 | 
						|
}
 | 
						|
 | 
						|
 | 
						|
/**
 | 
						|
 * Load GO1/GO2/KIT - Amstrad CPC Plus Graphos
 | 
						|
 */
 | 
						|
void Load_GOS(T_IO_Context* context)
 | 
						|
{
 | 
						|
  FILE *file;
 | 
						|
  unsigned long file_size;
 | 
						|
  int i;
 | 
						|
  int x, y;
 | 
						|
  byte * pixel_data;
 | 
						|
 | 
						|
  if (!(file = Open_file_read(context)))
 | 
						|
  {
 | 
						|
      File_error = 1;
 | 
						|
      return;
 | 
						|
  }
 | 
						|
 | 
						|
  if (CPC_check_AMSDOS(file, NULL, &file_size))
 | 
						|
    fseek(file, 128, SEEK_SET); // right after AMSDOS header
 | 
						|
  else
 | 
						|
    file_size = File_length_file(file);
 | 
						|
 | 
						|
  context->Ratio = PIXEL_WIDE;
 | 
						|
  Pre_load(context, 192, 272, file_size, FORMAT_GOS, context->Ratio, 0);
 | 
						|
  context->Width = 192;
 | 
						|
  context->Height = 272;
 | 
						|
 | 
						|
  // load pixels
 | 
						|
  pixel_data = GFX2_malloc(16384);
 | 
						|
  memset(pixel_data, 0, 16384);
 | 
						|
  Read_bytes(file, pixel_data, file_size);
 | 
						|
 | 
						|
  i = 0;
 | 
						|
  for (y = 0; y < 168; y++) {
 | 
						|
    x = 0;
 | 
						|
    while (x < 192) {
 | 
						|
      byte pixels = pixel_data[i];
 | 
						|
      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);
 | 
						|
      i++;
 | 
						|
    }
 | 
						|
 | 
						|
    i += 0x800;
 | 
						|
    if (i > 0x3FFF) {
 | 
						|
      i -= 0x4000;
 | 
						|
    } else {
 | 
						|
      i -= 192 / 2;
 | 
						|
    }
 | 
						|
  }
 | 
						|
 | 
						|
  fclose(file);
 | 
						|
 | 
						|
  // load pixels from GO2
 | 
						|
  file = Open_file_read_with_alternate_ext(context, "GO2");
 | 
						|
  if (CPC_check_AMSDOS(file, NULL, &file_size))
 | 
						|
    fseek(file, 128, SEEK_SET); // right after AMSDOS header
 | 
						|
 | 
						|
  Read_bytes(file, pixel_data, file_size);
 | 
						|
  i = 0;
 | 
						|
  for (y = 168; y < 272; y++) {
 | 
						|
    x = 0;
 | 
						|
    while (x < 192) {
 | 
						|
      byte pixels = pixel_data[i];
 | 
						|
      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);
 | 
						|
      i++;
 | 
						|
    }
 | 
						|
 | 
						|
    i += 0x800;
 | 
						|
    if (i > 0x3FFF) {
 | 
						|
      i -= 0x4000;
 | 
						|
    } else {
 | 
						|
      i -= 192 / 2;
 | 
						|
    }
 | 
						|
  }
 | 
						|
 | 
						|
  fclose(file);
 | 
						|
 | 
						|
  file = Open_file_read_with_alternate_ext(context, "KIT");
 | 
						|
  if (file == NULL) {
 | 
						|
    // There is no palette, but that's fine, we can still load the pixels
 | 
						|
    return;
 | 
						|
  }
 | 
						|
 | 
						|
  if (CPC_check_AMSDOS(file, NULL, &file_size)) {
 | 
						|
    fseek(file, 128, SEEK_SET); // right after AMSDOS header
 | 
						|
  } else {
 | 
						|
    file_size = File_length_file(file);
 | 
						|
  }
 | 
						|
 | 
						|
  if (Config.Clear_palette)
 | 
						|
    memset(context->Palette,0,sizeof(T_Palette));
 | 
						|
 | 
						|
  File_error = 0;
 | 
						|
 | 
						|
  if (file_size == 32)
 | 
						|
  {
 | 
						|
    for (i = 0; i < 16; i++)
 | 
						|
    {
 | 
						|
      uint16_t word;
 | 
						|
      if (!Read_word_le(file, &word))
 | 
						|
      {
 | 
						|
        File_error = 2;
 | 
						|
        return;
 | 
						|
      }
 | 
						|
 | 
						|
      context->Palette[i].R = ((word >>  4) & 0xF) * 0x11;
 | 
						|
      context->Palette[i].G = ((word >>  8) & 0xF) * 0x11;
 | 
						|
      context->Palette[i].B = ((word >>  0) & 0xF) * 0x11;
 | 
						|
    }
 | 
						|
  }
 | 
						|
  else
 | 
						|
  {
 | 
						|
    // Setup the palette (amstrad hardware palette)
 | 
						|
    CPC_set_HW_palette(context->Palette + 0x40);
 | 
						|
    for (i = 0; i < 16; i++)
 | 
						|
    {
 | 
						|
      byte ink;
 | 
						|
      if (!Read_byte(file, &ink))
 | 
						|
      {
 | 
						|
        File_error = 2;
 | 
						|
        return;
 | 
						|
      }
 | 
						|
      context->Palette[i] = context->Palette[ink];
 | 
						|
    }
 | 
						|
  }
 | 
						|
 | 
						|
  fclose(file);
 | 
						|
}
 | 
						|
 | 
						|
/**
 | 
						|
 * 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?
 | 
						|
}
 |