Save (and Load) Autoloading TO8 pictures

This commit is contained in:
Thomas Bernard 2018-11-14 11:26:05 +01:00
parent 434428a47f
commit 2eb332411b
No known key found for this signature in database
GPG Key ID: 0FF11B67A5C0863C
3 changed files with 273 additions and 41 deletions

View File

@ -144,5 +144,6 @@ void Load_FLI(T_IO_Context *);
// -- Thomson MO/TO computer series pictures --------------------------------
void Test_MOTO(T_IO_Context *, FILE *);
void Load_MOTO(T_IO_Context *);
void Save_MOTO(T_IO_Context *);
#endif

View File

@ -120,7 +120,7 @@ const T_Format File_formats[] = {
{FORMAT_ICO, " ico", Test_ICO, Load_ICO, Save_ICO, 0, 0, 0, "ico", "ico;ic2;cur"},
{FORMAT_INFO," info",Test_INFO,Load_INFO,NULL, 0, 0, 0, "info", "info"},
{FORMAT_FLI, " flc", Test_FLI, Load_FLI, NULL, 0, 0, 0, "flc", "flc;fli;dat"},
{FORMAT_MOTO," moto",Test_MOTO,Load_MOTO,NULL, 0, 0, 0, "bin", "bin;map"},
{FORMAT_MOTO," moto",Test_MOTO,Load_MOTO,Save_MOTO,0, 1, 0, "bin", "bin;map"},
{FORMAT_MISC,"misc.",NULL, NULL, NULL, 0, 0, 0, "", "tga;pnm;xpm;xcf;jpg;jpeg;tif;tiff"},
};

View File

@ -53,6 +53,9 @@
#include "keycodes.h"
#include "fileformats.h"
extern char Program_version[]; // generated in pversion.c
extern const char SVN_revision[]; // generated in version.c
//////////////////////////////////// PAL ////////////////////////////////////
//
@ -4867,8 +4870,6 @@ void Load_FLI(T_IO_Context * context)
void Test_MOTO(T_IO_Context * context, FILE * file)
{
long file_size;
byte code;
word size, address;
(void)context;
file_size = File_length_file(file);
@ -4876,34 +4877,27 @@ void Test_MOTO(T_IO_Context * context, FILE * file)
File_error = 1;
if (file_size <= 10)
return;
switch (file_size)
switch (MOTO_Check_binary_file(file))
{
// Files in RAW formats (from TGA2teo)
case 8004: // 2 colors palette
case 8008: // 4 colors palette
case 8032: // 16 colors palette
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
File_error = 0;
return;
default:
break;
}
break;
case 2: // MAP file (SAVEP/LOADP)
case 3: // TO autoloading picture
case 4: // MO autoloading picture
File_error = 0;
return;
default:
break;
}
// Check for Thomson binary format
// last 5 bytes must be
// FF 00 00 xx xx (xx xx = start address when using LOADM,R)
if (fseek(file, -5, SEEK_END) < 0)
return;
if(!Read_byte(file, &code))
return;
if(code != 0xff)
return;
if(!Read_word_be(file, &size))
return;
if(size != 0)
return;
if(!Read_word_be(file, &address))
return;
File_error = 0;
}
/**
@ -4933,11 +4927,12 @@ void Load_MOTO(T_IO_Context * context)
{
// FORME / COULEUR
FILE * file;
byte * vram_forme;
byte * vram_couleur;
byte * vram_forme = NULL;
byte * vram_couleur = NULL;
long file_size;
int file_type;
int bx, x, y, i;
byte bpp;
byte bpp = 4;
byte code;
word length, address;
enum MOTO_mode { F_40col, F_80col, F_bm4, F_bm16 } mode = F_40col;
@ -4966,29 +4961,26 @@ void Load_MOTO(T_IO_Context * context)
// Load default palette
memcpy(context->Palette, mo5palette, sizeof(mo5palette));
// check for Thomson binary format
// last 5 bytes must be
// FF 00 00 xx xx (xx xx = start address when using LOADM,R)
if (fseek(file, -5, SEEK_END) < 0)
return;
if (!(Read_byte(file,&code) && Read_word_be(file,&length) && Read_word_be(file,&address)))
return;
file_type = MOTO_Check_binary_file(file);
if (fseek(file, 0, SEEK_SET) < 0)
{
fclose(file);
return;
if (code == 0xff && length == 0)
}
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;
GFX2_Log(GFX2_DEBUG, "Thomson binary file detected. run address=&H%04X\n", address);
if (!(Read_byte(file,&code) && Read_word_be(file,&length) && Read_word_be(file,&address)))
{
fclose(file);
return;
}
GFX2_Log(GFX2_DEBUG, "block &H%02X length=&H%04X address=&H%04X\n", code, length, address);
if (length < 5 || !(Read_byte(file,&map_mode) && Read_byte(file,&col_count) && Read_byte(file,&line_count)))
{
fclose(file);
@ -5150,6 +5142,53 @@ void Load_MOTO(T_IO_Context * context)
}
fclose(file);
}
else if(file_type == 3 || file_type == 4)
{
do
{
if (!(Read_byte(file,&code) && Read_word_be(file,&length) && Read_word_be(file,&address)))
{
if (vram_forme)
break;
fclose(file);
return;
}
// MO5 VRAM address is &H0000
// TO7/TO8/TO9 VRAM addres is &H4000
if (length >= 8000 && length <= 8192 && address == 0x4000) // TO7/TO8/TO9 VRAM address
{
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)
context->Palette[x].B = gamma[vram_couleur[8000+x*2] & 0x0F];
context->Palette[x].G = gamma[vram_couleur[8000+x*2+1] >> 4];
context->Palette[x].R = gamma[vram_couleur[8000+x*2+1] & 0x0F];
}
if (length >= 8064)
memcpy(context->Comment, vram_couleur + 8032, 32);
context->Comment[COMMENT_SIZE] = '\0';
}
length = 0;
}
}
if (length > 0)
fseek(file, length, SEEK_CUR);
} while(code == 0);
fclose(file);
}
else
{
char filename[MAX_PATH_CHARACTERS];
@ -5263,7 +5302,10 @@ void Load_MOTO(T_IO_Context * context)
byte forme, couleurs;
forme = vram_forme[i];
couleurs = vram_couleur[i];
if (vram_couleur)
couleurs = vram_couleur[i];
else
couleurs = (mode == F_40col) ? 0x01 : 0x00;
i++;
switch(mode)
{
@ -5318,3 +5360,192 @@ void Load_MOTO(T_IO_Context * context)
}
}
}
void Save_MOTO(T_IO_Context * context)
{
FILE * file;
byte * vram_forme;
byte * vram_couleur;
int i, x, y, bx;
word reg_prc = 0xE7C3; // TO8 : PRC
byte prc_value = 0x65;// Value to write to PRC to select VRAM bank
file = Open_file_write(context);
if (file == NULL )
{
File_error = 1;
return;
}
vram_forme = malloc(8192);
vram_couleur = malloc(8192);
{
// 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)
{
GFX2_Log(GFX2_WARNING, "Save_MOTO() color %u > 15 at pixel (%d,%d)\n", 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);
// 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 == c1)
{
forme_byte |= 1;
c1_count++;
}
else
{
c2_count++;
if (c2 == 0xff)
c2 = col;
else if (col != c2)
{
GFX2_Log(GFX2_WARNING, "Save_MOTO() constraint error (%d,%d)\n", 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;
vram_couleur[bx+y*40] = ((fond & 7) | ((fond & 8) << 4) | (forme << 3)) ^ 0xC0;
previous_fond = fond;
previous_forme = forme;
}
previous_fond = (vram_couleur[bx-1] & 7) | (~vram_couleur[bx-1] & 0x80) >> 4;
previous_forme = ((vram_couleur[bx-1] & 0x78) >> 3) ^ 8;
}
}
}
// 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;
}
// 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);
memcpy(vram_couleur + 8000, vram_forme + 8000, 64);
// Format BIN
if (!MOTO_BIN_Add_Chunk(file, 1, reg_prc, &prc_value))
goto error;
if (!MOTO_BIN_Add_Chunk(file, 8000+64, 0x4000, vram_forme))
goto error;
prc_value &= 0xFE; // select color data
if (!MOTO_BIN_Add_Chunk(file, 1, reg_prc, &prc_value))
goto error;
if (!MOTO_BIN_Add_Chunk(file, 8000+64, 0x4000, vram_couleur))
goto error;
if (!MOTO_BIN_Add_End(file, 0x0000))
goto error;
// TODO : format MAP
fclose(file);
File_error = 0;
return;
error:
free(vram_forme);
free(vram_couleur);
fclose(file);
File_error = 1;
}