support Atari ST CA1 format (Crack Art)

This commit is contained in:
Thomas Bernard 2020-01-18 02:32:02 +01:00
parent 893f4531c9
commit 1f601d06ae
No known key found for this signature in database
GPG Key ID: 0FF11B67A5C0863C
6 changed files with 428 additions and 2 deletions

View File

@ -138,6 +138,7 @@ enum FILE_FORMATS
FORMAT_SCx, ///< ColoRIX
FORMAT_PI1, ///< Atari ST Degas
FORMAT_PC1, ///< Atari ST Degas Elite
FORMAT_CA1, ///< Atari ST CrackArt
FORMAT_CEL, ///< Atari ST Cyber Paint Cell
FORMAT_NEO, ///< Atari ST NeoChrome
FORMAT_TNY, ///< Atari ST Tiny Stuff

View File

@ -101,6 +101,11 @@ void Test_PC1(T_IO_Context *, FILE *);
void Load_PC1(T_IO_Context *);
void Save_PC1(T_IO_Context *);
// -- CA1 -------------------------------------------------------------------
void Test_CA1(T_IO_Context *, FILE *);
void Load_CA1(T_IO_Context *);
void Save_CA1(T_IO_Context *);
// -- Tiny Stuff ------------------------------------------------------------
void Test_TNY(T_IO_Context *, FILE *);
void Load_TNY(T_IO_Context *);

View File

@ -520,6 +520,7 @@ static const T_Help_table helptable_credits[] =
HELP_TITLE(" FILE FORMATS CREDITS")
HELP_TEXT ("")
HELP_TEXT (" BMP,ICO : Microsoft")
HELP_TEXT (" CA1 : Crack Art (Jaybee and Roy)")
HELP_TEXT (" CEL,KCF : K.O.S. (KISekae Set system)")
HELP_TEXT (" CM5 : SyX")
HELP_TEXT (" EGX : Targhan & Supersly / Cargosoft")

View File

@ -115,7 +115,7 @@ static void Save_ClipBoard_Image(T_IO_Context *);
const T_Format File_formats[] = {
{FORMAT_ALL_IMAGES, "(all)", NULL, NULL, NULL, 0, 0, 0, "",
"gif;png;bmp;2bp;pcx;pkm;iff;lbm;ilbm;sham;ham;ham6;ham8;acbm;pic;anim;img;sci;scq;scf;scn;sco;cel;"
"pi1;pc1;pi2;pc2;pi3;pc3;neo;tny;tn1;tn2;tn3;tn4;"
"pi1;pc1;pi2;pc2;pi3;pc3;neo;tny;tn1;tn2;tn3;tn4;ca1;ca2;ca3;"
"c64;p64;a64;pi;rp;aas;art;dd;iph;ipt;hpc;ocp;koa;koala;fli;bml;cdu;prg;pmg;rpm;"
"gpx;"
"cpc;scr;win;pph;cm5;go1;"
@ -139,6 +139,7 @@ const T_Format File_formats[] = {
{FORMAT_SCx, " sc?", Test_SCx, Load_SCx, Save_SCx, 0, 0, 0, "sc?", "sci;scq;scf;scn;sco"},
{FORMAT_PI1, " pi1", Test_PI1, Load_PI1, Save_PI1, 0, 0, 0, "pi1", "pi1;pi2;pi3"},
{FORMAT_PC1, " pc1", Test_PC1, Load_PC1, Save_PC1, 0, 0, 0, "pc1", "pc1;pc2;pc3"},
{FORMAT_CA1, " ca1", Test_CA1, Load_CA1, Save_CA1, 0, 0, 0, "ca1", "ca1;ca2;ca3"},
{FORMAT_TNY, " tny", Test_TNY, Load_TNY, Save_TNY, 0, 0, 0, "tny", "tny;tn1;tn2;tn3;tn4"},
{FORMAT_CEL, " cel", Test_CEL, Load_CEL, Save_CEL, 0, 0, 0, "cel", "cel"},
{FORMAT_NEO, " neo", Test_NEO, Load_NEO, Save_NEO, 0, 0, 0, "neo", "neo"},

View File

@ -100,7 +100,7 @@ void PI2_8b_to_16p(const byte * src,byte * dest)
//// CODAGE d'une partie d'IMAGE ////
void PI1_16p_to_8b(byte * src,byte * dest)
void PI1_16p_to_8b(const byte * src, byte * dest)
{
int i; // index du pixel à calculer
word byte_mask; // Masque de codage
@ -1513,4 +1513,421 @@ error:
Remove_file(context);
}
/**
* test for CrackArt format.
*
* Test that the files starts with the "CA" signature,
* then 1 byte for the compressed flag (0 or 1),
* then 1 byte for the resolution (0=low, 1=med, 2=high)
*/
void Test_CA1(T_IO_Context * context, FILE * file)
{
byte header[4];
(void)context;
File_error = 1;
if (!Read_bytes(file, header, 4))
return;
if (header[0] == 'C' && header[1] == 'A')
{
if ((header[2] & 0xfe) == 0 && (header[3] < 3))
File_error = 0;
}
}
void Load_CA1(T_IO_Context * context)
{
FILE * file;
byte sig[2];
byte compressed;
byte res;
byte * buffer;
File_error = 1;
buffer = GFX2_malloc(32000);
if (buffer == NULL)
return;
file = Open_file_read(context);
if (file == NULL)
{
free(buffer);
return;
}
if (Read_bytes(file, sig, 2) && Read_byte(file, &compressed)
&& Read_byte(file, &res))
{
unsigned long file_size;
short width = 640, height = 200;
enum PIXEL_RATIO ratio = PIXEL_SIMPLE;
byte bpp = 4;
file_size = File_length_file(file);
GFX2_Log(GFX2_DEBUG, "Signature : '%c%c' %s res=%d\n",
sig[0], sig[1], compressed ? "compressed" : "", res);
switch (res)
{
case 0:
width = 320;
break;
case 1:
ratio = PIXEL_TALL;
bpp = 2;
break;
case 2:
height = 400;
bpp = 1;
}
File_error = 0;
Pre_load(context, width, height, file_size, FORMAT_CA1, ratio, bpp);
if (File_error == 0)
{
if (Config.Clear_palette)
memset(context->Palette,0,sizeof(T_Palette));
memset(buffer, 0, 32);
if (res == 2)
{
// black & white
buffer[0] = 0xff;
buffer[1] = 0xff;
}
else
{
if (!Read_bytes(file, buffer, 1 << (bpp + 1)))
File_error = 1;
}
PI1_decode_palette(buffer, context->Palette);
if (compressed)
{
byte escape, delta;
word offset;
if (!(Read_byte(file, &escape) && Read_byte(file, &delta) && Read_word_be(file, &offset)))
File_error = 1;
else if(offset != 0)
{
int i = 0, c = 0;
GFX2_Log(GFX2_DEBUG, " escape=%02X delta=%02X offset=%hu\n", escape, delta, offset);
memset(buffer, delta, 32000);
while (c < 32000 && File_error == 0)
{
byte cmd, data;
word repeat;
repeat = 0;
data = delta;
if (!Read_byte(file, &cmd))
File_error = 1;
if (cmd == escape)
{
if (!Read_byte(file, &cmd))
File_error = 1;
if (cmd == 0)
{
// byte count repeat
if (!Read_byte(file, &cmd) || !Read_byte(file, &data))
File_error = 1;
repeat = cmd;
GFX2_Log(GFX2_DEBUG, "byte count repeat : 0x%02x,0x%02x,%hu,0x%02x\n", escape, 0, repeat, data);
}
else if (cmd == 1)
{
// word count repeat
if (!Read_word_be(file, &repeat) || !Read_byte(file, &data))
File_error = 1;
GFX2_Log(GFX2_DEBUG, "word count repeat : 0x%02x,0x%02x,%hu,0x%02x\n", escape, cmd, repeat, data);
}
else if (cmd == 2)
{
if (!Read_byte(file, &cmd))
File_error = 1;
else if (cmd == 0)
{
// ESC,02,00 => STOP code
GFX2_Log(GFX2_DEBUG, "STOP : 0x%02x,0x02,%02x\n", escape, cmd);
break;
}
else
{
// ESC,02,a,b => repeat (a << 8 + b + 1) x byte "delta"
repeat = cmd << 8;
if (!Read_byte(file, &cmd))
File_error = 1;
repeat += cmd;
GFX2_Log(GFX2_DEBUG, "delta repeat : 0x%02x,%hu\n", escape, repeat);
}
}
else if (cmd == escape)
{
// ESC,ESC => 1 x byte "ESC"
data = cmd;
}
else
{
// ESC,a,b => repeat (a + 1) x byte b
repeat = cmd;
if (!Read_byte(file, &data))
File_error = 1;
}
}
else
{
data = cmd;
}
// output bytes
do
{
buffer[i] = data;
i += offset;
c++;
if (i >= 32000)
i -= (32000 - 1);
}
while (repeat-- > 0);
}
GFX2_Log(GFX2_DEBUG, "finished : i=%d c=%d\n", i, c);
}
}
else
{
// not compressed
if (!Read_bytes(file, buffer, 32000))
File_error = 1;
}
if (File_error == 0)
{
int line;
const byte * ptr = buffer;
int ncols;
ncols = (res == 0) ? 20 : 40;
for (line = 0; line < height; line++)
{
byte pixels[16];
int col, x;
for (col = 0; col < ncols; col++)
{
switch (res)
{
case 0:
PI1_8b_to_16p(ptr, pixels);
ptr += 8;
for (x = 0; x < 16; x++)
Set_pixel(context, col * 16 + x, line, pixels[x]);
break;
case 1:
PI2_8b_to_16p(ptr, pixels);
ptr += 4;
for (x = 0; x < 16; x++)
Set_pixel(context, col * 16 + x, line, pixels[x]);
break;
case 2:
for (x = 0 ; x < 16; x++)
Set_pixel(context, col * 16 + x, line, (ptr[(x >> 3)] >> (7 - (x & 7))) & 1);
ptr += 2;
}
}
}
}
}
}
fclose(file);
free(buffer);
}
/**
* Save a 320x200 16c picture in CrackArt format
*
* @todo support medium and high resolution
*/
void Save_CA1(T_IO_Context * context)
{
FILE * file;
byte * buffer;
byte res = 0; // 0 = low, 1 = med, 2 = high
byte compressed = 1; // 0 or 1
int height = 200;
File_error = 1;
buffer = GFX2_malloc(32000);
if (buffer == NULL)
return;
file = Open_file_write(context);
if (file == NULL)
{
free(buffer);
return;
}
if (Write_bytes(file, "CA", 2) && Write_byte(file, compressed) && Write_byte(file, res))
{
PI1_code_palette(context->Palette, buffer);
if (Write_bytes(file, buffer, 32))
{
int line;
byte * ptr = buffer;
for (line = 0; line < height; line++)
{
byte pixels[16];
int col, x;
for (col = 0; col < 20; col++)
{
for (x = 0; x < 16; x++)
pixels[x] = Get_pixel(context, col * 16 + x, line);
PI1_16p_to_8b(pixels, ptr);
ptr += 8;
}
}
if (compressed)
{
word freq[256];
word max, min;
byte max_index, min_index;
byte escape;
word offset;
int i;
memset(freq, 0, sizeof(freq));
for (i = 0; i < 32000; i++)
freq[buffer[i]]++;
min = 65535;
min_index = 0;
max = 0;
max_index = 0;
for (i = 0; i < 256; i++)
{
if (freq[i] <= min && i > 2)
{
min = freq[i];
min_index = (byte)i;
}
if (freq[i] > max)
{
max = freq[i];
max_index = (byte)i;
}
}
GFX2_Log(GFX2_DEBUG, " 0x%02X (%hu times) 0x%02X (%hu times)\n", min_index, min, max_index, max);
escape = min_index;
offset = 160; // 80 in high res
if (Write_byte(file, escape) && Write_byte(file, max_index) && Write_word_be(file, offset))
{
int c = 1;
byte current;
word count = 0;
File_error = 0;
i = offset;
current = buffer[0];
while (c < 32000 && File_error == 0)
{
if (buffer[i] == current)
count++;
else
{
if (count < 3)
{
// Litteral
do
{
if (!Write_byte(file, current))
File_error = 1;
if (current == escape)
Write_byte(file, current);
}
while (count-- && File_error == 0);
}
else
{
GFX2_Log(GFX2_DEBUG, "byte %02X x %hu\n", current, count);
if (count < 256)
{
// ESC,a,b => repeat (a + 1) x byte b
// with a > 2
if (!(Write_byte(file, escape)
&& Write_byte(file, count)
&& Write_byte(file, current)))
File_error = 1;
}
else if (current == max_index)
{
// ESC,02,word count => repeat (count + 1) x byte "delta"
if (!(Write_byte(file, escape)
&& Write_byte(file, 2)
&& Write_word_be(file, count)))
File_error = 1;
}
else
{
// ESC,01,word count,data
if (!(Write_byte(file, escape)
&& Write_byte(file, 1)
&& Write_word_be(file, count)
&& Write_byte(file, current)))
File_error = 1;
}
}
current = buffer[i];
count = 0;
}
i += offset;
c++;
if (i >= 32000)
i -= (32000 - 1);
}
GFX2_Log(GFX2_DEBUG, "end: byte %02X x %hu\n", current, count);
if (count < 3)
{
do
{
Write_byte(file, current);
if (current == escape)
Write_byte(file, current);
}
while (count--);
}
else if (current == max_index)
{
// STOP code
if (!(Write_byte(file, escape)
&& Write_byte(file, 2)
&& Write_byte(file, 0)))
File_error = 1;
}
else if (count < 256)
{
// ESC,a,b => repeat (a + 1) x byte b
// with a > 2
if (!(Write_byte(file, escape)
&& Write_byte(file, count)
&& Write_byte(file, current)))
File_error = 1;
}
else
{
// ESC,01,word count,data
if (!(Write_byte(file, escape)
&& Write_byte(file, 1)
&& Write_word_be(file, count)
&& Write_byte(file, current)))
File_error = 1;
}
}
}
else
{
// uncompressed
if (Write_bytes(file, buffer, 32000))
File_error = 0;
}
}
}
fclose(file);
free(buffer);
}
/* @} */

View File

@ -70,6 +70,7 @@ static const struct {
TESTFMTF(NEO, "atari_st/ATARIART.NEO", FLAG_16C)
TESTFMTF(PC1, "atari_st/eunmiisa.pc1", FLAG_16C)
TESTFMTF(PI1, "atari_st/evolutn.pi1", FLAG_16C)
TESTFMTF(CA1, "atari_st/GIANTS.CA1", FLAG_16C)
TESTFMTF(TNY, "atari_st/rose.tny", FLAG_16C)
TESTFMTL(FLI, "autodesk_FLI_FLC/2noppaa.fli")
TESTFMT(BMP, "bmp/test8.bmp")