From 2eb332411bc724da841defde1861e376e52f4047 Mon Sep 17 00:00:00 2001 From: Thomas Bernard Date: Wed, 14 Nov 2018 11:26:05 +0100 Subject: [PATCH] Save (and Load) Autoloading TO8 pictures --- src/fileformats.h | 1 + src/loadsave.c | 2 +- src/miscfileformats.c | 311 ++++++++++++++++++++++++++++++++++++------ 3 files changed, 273 insertions(+), 41 deletions(-) diff --git a/src/fileformats.h b/src/fileformats.h index 13db5727..38975a92 100644 --- a/src/fileformats.h +++ b/src/fileformats.h @@ -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 diff --git a/src/loadsave.c b/src/loadsave.c index ff87ae63..a8f10146 100644 --- a/src/loadsave.c +++ b/src/loadsave.c @@ -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"}, }; diff --git a/src/miscfileformats.c b/src/miscfileformats.c index e99d72e3..6f392b8c 100644 --- a/src/miscfileformats.c +++ b/src/miscfileformats.c @@ -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; +}