/* vim:expandtab:ts=2 sw=2:
*/
/* Grafx2 - The Ultimate 256-color bitmap paint program
Copyright owned by various GrafX2 authors, see COPYRIGHT.txt for details.
Grafx2 is free software; you can redistribute it and/or
modify it under the terms of the GNU General Public License
as published by the Free Software Foundation; version 2
of the License.
Grafx2 is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with Grafx2; if not, see
*/
///@file 2gsformats.c
/// Formats for the Apple II GS
#include
#include
#include
#include "global.h"
#include "fileformats.h"
#include "loadsavefuncs.h"
#include "io.h"
#include "gfx2mem.h"
#include "gfx2log.h"
/**
* Test for an Apple 2 GS picture file
*/
void Test_2GS(T_IO_Context * context, FILE * file)
{
unsigned long file_size;
dword chunksize;
byte header[5];
(void)context;
File_error = 1;
file_size = File_length_file(file);
if (!Read_dword_le(file, &chunksize))
return;
if (!Read_bytes(file, header, sizeof(header)))
return;
if (0 != memcmp(header, "\x04MAIN", 5))
return;
if (file_size < chunksize)
return;
File_error = 0;
}
/**
* Load a 16 entry Apple II GS palette from file
*/
static int Load_2GS_Palette(T_Components * palette, FILE * file)
{
unsigned int i;
for (i = 0; i < 16; i++)
{
word palentry;
if (!Read_word_le(file, &palentry))
return 0;
palette[i].R = ((palentry >> 8) & 0x0f) * 0x11;
palette[i].G = ((palentry >> 4) & 0x0f) * 0x11;
palette[i].B = (palentry & 0x0f) * 0x11;
}
return 1;
}
#define COMPONENTS(x) linepal[x].R, linepal[x].G, linepal[x].B
/**
* set the (2 or 4) pixels for the byte.
*/
#define WRITE2GS_PIXELS \
if (multipalcount > 0) \
{ \
if (linemode & 0x80) \
{ \
Set_pixel_24b(context, x++, y, COMPONENTS(8 + (pixel >> 6))); \
Set_pixel_24b(context, x++, y, COMPONENTS(12 + ((pixel >> 4) & 3))); \
Set_pixel_24b(context, x++, y, COMPONENTS((pixel >> 2) & 3)); \
Set_pixel_24b(context, x++, y, COMPONENTS(4 + (pixel & 3))); \
} \
else \
{ \
Set_pixel_24b(context, x++, y, COMPONENTS(pixel >> 4)); \
Set_pixel_24b(context, x++, y, COMPONENTS(pixel & 15)); \
} \
} \
else \
{ \
if (linemode & 0x80) \
{ \
Set_pixel(context, x++, y, (linemode << 4) | 8 | (pixel >> 6)); \
Set_pixel(context, x++, y, (linemode << 4) | 12 | ((pixel >> 4) & 3)); \
Set_pixel(context, x++, y, (linemode << 4) | ((pixel >> 2) & 3)); \
Set_pixel(context, x++, y, (linemode << 4) | 4 | (pixel & 3)); \
} \
else \
{ \
Set_pixel(context, x++, y, (linemode << 4) | (pixel >> 4)); \
Set_pixel(context, x++, y, (linemode << 4) | (pixel & 15)); \
} \
}
void Load_2GS(T_IO_Context * context)
{
FILE * file;
unsigned long file_size;
dword chunksize;
byte header[5];
word mode;
word width;
word height;
word palcount;
word palindex;
long lineoffset;
long dataoffset;
word y;
byte bpp = 2;
word multipalcount = 0;
long multipaloffset = -1;
long offset;
File_error = 1;
file = Open_file_read(context);
if (file == NULL)
return;
file_size = File_length_file(file);
if (!Read_dword_le(file, &chunksize))
return;
if (!Read_bytes(file, header, sizeof(header)))
goto error;
if (0 != memcmp(header, "\x04MAIN", 5))
goto error;
if (!Read_word_le(file, &mode) || !Read_word_le(file, &width) || !Read_word_le(file, &palcount))
goto error;
if (Config.Clear_palette)
memset(context->Palette, 0, sizeof(T_Palette));
for (palindex = 0; palindex < palcount; palindex++)
{
if (!Load_2GS_Palette(context->Palette + (palindex * 16), file))
goto error;
}
if (!Read_word_le(file, &height))
goto error;
lineoffset = ftell(file);
dataoffset = lineoffset + 4 * (long)height;
GFX2_Log(GFX2_DEBUG, " mode %02x %ux%u %u palettes\n", mode, (unsigned)width, (unsigned)height, (unsigned)palcount);
// read other chunks before decoding the picture
GFX2_Log(GFX2_DEBUG, "file_size : %06lx, chunksize : %06lx, current offset : %06lx\n", file_size, chunksize, dataoffset);
offset = chunksize;
while (offset < (long)file_size)
{
char * p;
byte c;
fseek(file, offset, SEEK_SET);
if (!Read_dword_le(file, &chunksize) || !Read_byte(file, &c))
goto error;
p = GFX2_malloc(c + 1);
if (p)
{
fread(p, 1, c, file);
p[c] = '\0';
GFX2_Log(GFX2_DEBUG, "%06lx: %04x %s\n", offset, chunksize, p);
if (c == 4 && 0 == memcmp(p, "NOTE", 4))
{
word len;
if (Read_word_le(file, &len))
{
free(p);
p = GFX2_malloc(len + 1);
if (p)
{
fread(p, 1, len, file);
p[len] = '\0';
GFX2_Log(GFX2_DEBUG, "%s\n", p);
}
}
}
else if (c == 12 && 0 == memcmp(p, "SuperConvert", 12))
{
dword len = chunksize - 4 - 13;
free(p);
p = GFX2_malloc(len);
if (p)
{
fread(p, 1, len, file);
GFX2_LogHexDump(GFX2_DEBUG, "", p, 0 /*offset + 4 + 13*/, (long)len);
}
}
else if (c == 8 && 0 == memcmp(p, "MULTIPAL", 8))
{
if (Read_word_le(file, &multipalcount))
{
// all palettes are there...
multipaloffset = ftell(file);
}
}
free(p);
}
offset += chunksize;
}
if (multipalcount > 0)
{
bpp = 12;
}
else
{
while ((1 << bpp) < ((mode & 0x80) ? 4 : 16) * palcount)
bpp++;
}
File_error = 0;
Pre_load(context, width, height, file_size, FORMAT_2GS, (mode & 0x80) ? PIXEL_TALL : PIXEL_SIMPLE, bpp);
if (File_error)
goto error;
for (y = 0; y < height; y++)
{
word linebytes;
byte linemode;
word x;
T_Components linepal[16];
if (multipalcount > y)
{
fseek(file, multipaloffset + 32 * y, SEEK_SET);
if (!Load_2GS_Palette(linepal, file))
goto error;
}
fseek(file, lineoffset, SEEK_SET);
if (!Read_word_le(file, &linebytes) || !Read_byte(file, &linemode))
goto error;
//GFX2_Log(GFX2_DEBUG, "Line #%03u mode/pal=%02x %u bytes\n", (unsigned)y, linemode, (unsigned)linebytes);
lineoffset += 4;
fseek(file, dataoffset, SEEK_SET);
if (multipalcount > 0)
{
linemode = 0;
}
for (x = 0; x < width; )
{
byte code, pixel;
unsigned count;
if (!Read_byte(file, &code))
goto error;
count = (code & 0x3f) + 1;
switch (code >> 6)
{
case 0: // unpacked
while (count-- > 0)
{
if (!Read_byte(file, &pixel))
goto error;
WRITE2GS_PIXELS
}
break;
case 3: // byte packed x 4
count <<= 2;
case 1: // byte packed
if (!Read_byte(file, &pixel))
goto error;
while (count-- > 0)
{
WRITE2GS_PIXELS
}
break;
case 2: // dword packed
{
byte pixels[4];
if (!Read_bytes(file, pixels, 4))
goto error;
while (count-- > 0)
{
int i;
for (i = 0; i < 4; i++)
{
pixel = pixels[i];
WRITE2GS_PIXELS
}
}
}
break;
}
}
dataoffset += linebytes;
}
fclose(file);
return;
error:
File_error = 1;
fclose(file);
}