Support loading PPH files (Amstrad CPC).

git-svn-id: svn://pulkomandy.tk/GrafX2/trunk@2162 416bcca6-2ee7-4201-b75f-2eb2f807beb1
This commit is contained in:
Adrien Destugues 2016-07-19 20:28:43 +00:00
parent 0c7d17304a
commit 702f1835da
4 changed files with 480 additions and 109 deletions

View File

@ -135,6 +135,7 @@ enum FILE_FORMATS
FORMAT_GPL,
FORMAT_SCR,
FORMAT_CM5,
FORMAT_PPH,
FORMAT_XPM,
FORMAT_MISC, ///< Must be last of enum: others formats recognized by SDL_image
};

View File

@ -136,6 +136,11 @@ void Test_CM5(T_IO_Context *);
void Load_CM5(T_IO_Context *);
void Save_CM5(T_IO_Context *);
// -- PPH (Amstrad CPC)
void Test_PPH(T_IO_Context *);
void Load_PPH(T_IO_Context *);
void Save_PPH(T_IO_Context *);
// -- XPM (X PixMap)
// Loading is done through SDL_Image
void Save_XPM(T_IO_Context*);
@ -153,7 +158,7 @@ void Load_SDL_Image(T_IO_Context *);
// ENUM Name TestFunc LoadFunc SaveFunc PalOnly Comment Layers Ext Exts
T_Format File_formats[] = {
{FORMAT_ALL_IMAGES, "(all)", NULL, NULL, NULL, 0, 0, 0, "", "gif;png;bmp;pcx;pkm;iff;lbm;ilbm;img;sci;scq;scf;scn;sco;pi1;pc1;cel;neo;c64;koa;koala;fli;bml;cdu;prg;tga;pnm;xpm;xcf;jpg;jpeg;tif;tiff;ico;cm5"},
{FORMAT_ALL_IMAGES, "(all)", NULL, NULL, NULL, 0, 0, 0, "", "gif;png;bmp;pcx;pkm;iff;lbm;ilbm;img;sci;scq;scf;scn;sco;pi1;pc1;cel;neo;c64;koa;koala;fli;bml;cdu;prg;tga;pnm;xpm;xcf;jpg;jpeg;tif;tiff;ico;cm5;pph"},
{FORMAT_ALL_PALETTES, "(all)", NULL, NULL, NULL, 1, 0, 0, "", "kcf;pal;gpl"},
{FORMAT_ALL_FILES, "(*.*)", NULL, NULL, NULL, 0, 0, 0, "", "*"},
{FORMAT_GIF, " gif", Test_GIF, Load_GIF, Save_GIF, 0, 1, 1, "gif", "gif"},
@ -177,6 +182,7 @@ T_Format File_formats[] = {
{FORMAT_C64, " c64", Test_C64, Load_C64, Save_C64, 0, 1, 0, "c64", "c64;koa;koala;fli;bml;cdu;prg"},
{FORMAT_SCR, " cpc", NULL, NULL, Save_SCR, 0, 0, 0, "cpc", "cpc;scr"},
{FORMAT_CM5, " cm5", Test_CM5, Load_CM5, Save_CM5, 0, 0, 1, "cm5", "cm5"},
{FORMAT_PPH, " pph", Test_PPH, Load_PPH, Save_PPH, 0, 0, 1, "pph", "pph"},
{FORMAT_XPM, " xpm", NULL, NULL, Save_XPM, 0, 0, 0, "xpm", "xpm"},
{FORMAT_MISC,"misc.",NULL, NULL, NULL, 0, 0, 0, "", "tga;pnm;xpm;xcf;jpg;jpeg;tif;tiff;ico"},
};

View File

@ -2916,11 +2916,11 @@ int Save_C64_multi(T_IO_Context *context, char *filename, byte saveWhat, byte lo
*/
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;
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...
// 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,count;
@ -2932,66 +2932,66 @@ int Save_C64_multi(T_IO_Context *context, char *filename, byte saveWhat, byte lo
count=0;
// Detect the ackground 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;
// Detect the ackground 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);
cols |= (1 << pixel);
}
// 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);
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 << pixel))
used++;
}
cand = 0;
used = 0;
// Count the number of used colors in the tile
for (n = 0; n<16; n++)
{
if (cols & (1 << pixel))
used++;
}
if (used>3)
{
// This is a tile that uses the background color (and 3 others)
if (used>3)
{
// 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 was not used in any other tile yet,
// but it could be the background one.
candidates |= 1 << n;
}
// 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 was not used in any other tile yet,
// but 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
invalids |= 1 << n;
}
if ((cols & 1 << n) == 0 ) {
// This color isn't used at all in this tile:
// Can't be the global
invalids |= 1 << n;
}
if (candidates & (1 << n)) {
// We have a candidate, mark it as such
cand++;
}
}
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 found");
return 1;
}
}
}
}
// After checking the constraints for this tile, do we have
// candidate background colors left ?
if (cand==0)
{
Warning_message("No possible global background color found");
return 1;
}
}
}
}
// Now just pick the first valid candidate
for (n = 0; n<16; n++)
@ -3302,6 +3302,8 @@ void Test_CM5(T_IO_Context * context)
File_error = 0;
}
fclose(file);
// TODO: check existence of a .SCR file with the same name
}
@ -3439,8 +3441,6 @@ void Load_CM5(T_IO_Context* context)
ext[1] = (idx & 2) ? 'f':'F';
ext[2] = (idx & 4) ? 'x':'X';
printf("trying to load %s...\n", filename);
file = fopen(filename, "rb");
} while(file == NULL);
}
@ -3538,3 +3538,364 @@ void Save_CM5(T_IO_Context* context)
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.
*/
void Test_PPH(T_IO_Context * context)
{
FILE *file;
unsigned char buffer[MAX_PATH_CHARACTERS];
long file_size;
int w;
int expected;
Get_full_filename(buffer, context->File_name, context->File_directory);
File_error = 1;
if ((file = fopen(buffer, "rb")))
{
// First check file size is large enough to hold the header
file_size = File_length_file(file);
if (file_size < 11) {
File_error = 1;
goto abort;
}
// File is large enough for the header, now check if the data makes some sense
fread(buffer, 1, 6, file);
if (buffer[0] > 5) {
// Unknown mode
File_error = 2;
goto abort;
}
w = buffer[1] | (buffer[2] << 8);
if (w < 2 || w > 384) {
// Invalid width
File_error = 3;
goto abort;
}
w = buffer[3] | (buffer[4] << 8);
if (w < 1 || w > 272) {
// Invalid height
File_error = 4;
goto abort;
}
if (buffer[5] < 1 || buffer[5] > 28)
{
// Invalid palettes count
File_error = 5;
goto abort;
}
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;
goto abort;
}
expected += 16;
break;
case 1:
case 5:
expected += buffer[5] * 5 - 1;
break;
case 2:
// Palete size should be 2 bytes
if (buffer[5] != 1) {
File_error = 7;
goto abort;
}
expected += 2;
break;
}
if (file_size != expected)
{
File_error = 6;
goto abort;
}
File_error = 0;
} else {
File_error = 8;
}
abort:
fclose(file);
// TODO: check existence of .ODD/.EVE files with the same name
}
void Load_PPH(T_IO_Context* context)
{
FILE *file;
FILE *feven;
char filename[MAX_PATH_CHARACTERS];
// 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;
char* ext;
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 }
};
Get_full_filename(filename, context->File_name, context->File_directory);
if (!(file = fopen(filename, "rb")))
{
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
= (CPCPAL[base[i & 3]].R + CPCPAL[base[i >> 2]].R) / 2;
context->Palette[i + 16*j].G
= (CPCPAL[base[i & 3]].G + CPCPAL[base[i >> 2]].G) / 2;
context->Palette[i + 16*j].B
= (CPCPAL[base[i & 3]].B + CPCPAL[base[i >> 2]].B) / 2;
}
// 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
= (CPCPAL[base[i & 15]].R + CPCPAL[base[i >> 4]].R) / 2;
context->Palette[i].G
= (CPCPAL[base[i & 15]].G + CPCPAL[base[i >> 4]].G) / 2;
context->Palette[i].B
= (CPCPAL[base[i & 15]].B + CPCPAL[base[i >> 4]].B) / 2;
}
}
break;
}
fclose(file);
// Load the picture data
// There are two pages, each storing bytes in the CPC vram format but lines in
// linear order.
ext = filename + strlen(filename) - 3;
ext[0] = 'O';
ext[1] = 'D';
ext[2] = 'D';
file = fopen(filename, "rb");
ext[0] = 'E';
ext[1] = 'V';
ext[2] = 'E';
feven = fopen(filename, "rb");
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)
{
// TODO
}

View File

@ -136,7 +136,10 @@ void Set_data_directory(const char * program_dir, char * data_dir)
// Haiku provides us with an API to find it.
#elif defined(__HAIKU__)
if (find_path(Set_data_directory, B_FIND_PATH_DATA_DIRECTORY, "grafx2/", data_dir, PATH_MAX) != B_OK)
strcat(data_dir,"../data/grafx2/");
{
// If the program is not installed, find_path will fail. Try from local dir then.
strcat(data_dir,"../share/grafx2/");
}
// All other targets, program is in a "bin" subdirectory
#else