From 5e78ad628272facd73d96be653a2bd5717704391 Mon Sep 17 00:00:00 2001 From: Thomas Bernard Date: Sat, 7 Dec 2019 19:24:08 +0100 Subject: [PATCH] Loading of atari ST TNY files --- src/const.h | 1 + src/fileformats.h | 5 + src/helpfile.h | 1 + src/loadsave.c | 3 +- src/stformats.c | 226 ++++++++++++++++++++++++++++++++++++++++++++++ 5 files changed, 235 insertions(+), 1 deletion(-) diff --git a/src/const.h b/src/const.h index 3b5b3963..16b0674c 100644 --- a/src/const.h +++ b/src/const.h @@ -140,6 +140,7 @@ enum FILE_FORMATS FORMAT_PC1, ///< Atari ST Degas Elite FORMAT_CEL, ///< Atari ST Cyber Paint Cell FORMAT_NEO, ///< Atari ST NeoChrome + FORMAT_TNY, ///< Atari ST Tiny Stuff FORMAT_C64, ///< Several C64 formats : Koala, FLI, BML, etc. FORMAT_PRG, ///< C64 autoload picture FORMAT_GPX, ///< pixcen C64 format : .gpx diff --git a/src/fileformats.h b/src/fileformats.h index 332dd6f4..c9808339 100644 --- a/src/fileformats.h +++ b/src/fileformats.h @@ -101,6 +101,11 @@ void Test_PC1(T_IO_Context *, FILE *); void Load_PC1(T_IO_Context *); void Save_PC1(T_IO_Context *); +// -- Tiny Stuff ------------------------------------------------------------ +void Test_TNY(T_IO_Context *, FILE *); +void Load_TNY(T_IO_Context *); +void Save_TNY(T_IO_Context *); + // -- NEO ------------------------------------------------------------------- void Test_NEO(T_IO_Context *, FILE *); void Load_NEO(T_IO_Context *); diff --git a/src/helpfile.h b/src/helpfile.h index f9a31514..3bedcba8 100644 --- a/src/helpfile.h +++ b/src/helpfile.h @@ -543,6 +543,7 @@ static const T_Help_table helptable_credits[] = HELP_TEXT (" SCx : Colorix (?)") HELP_TEXT (" SCR : Advanced OCP Art Studio + iMPdraw") HELP_TEXT (" TIFF : Aldus (now Adobe)") + HELP_TEXT (" TNY : David Mumper") HELP_TEXT (" XPM : C.Nahaboo and D.Dardailler / Bull") HELP_TEXT ("") HELP_TEXT ("") diff --git a/src/loadsave.c b/src/loadsave.c index 0da779dc..b3a65e1d 100644 --- a/src/loadsave.c +++ b/src/loadsave.c @@ -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;" + "pi1;pc1;pi2;pc2;pi3;pc3;neo;tny;tn1;tn2;tn3;tn4;" "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;" @@ -138,6 +138,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_TNY, " tny", Test_TNY, Load_TNY, NULL, 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"}, {FORMAT_KCF, " kcf", Test_KCF, Load_KCF, Save_KCF, 1, 0, 0, "kcf", "kcf"}, diff --git a/src/stformats.c b/src/stformats.c index 2e765fb4..dcb35bf0 100644 --- a/src/stformats.c +++ b/src/stformats.c @@ -50,6 +50,7 @@ * - PI1 : Degas * - PC1 : Degas elite compressed * - NEO : Neochrome + * - TNY : Tiny Stuff / Tiny View * * @{ */ @@ -1164,4 +1165,229 @@ error: Remove_file(context); } +void Test_TNY(T_IO_Context * context, FILE * file) +{ + unsigned long file_size; + unsigned long theorical_size; + byte res; // 0 = Low, 1 = midres, 2 = hires. +3 for color cycling + word control_bytes; + word data_words; + (void)context; + + File_error = 1; + file_size = File_length_file(file); + if (file_size > 32044) + return; + if (!Read_byte(file, &res)) + return; + if (res >= 6) + return; + if (res >= 3) + { + if (fseek(file, 4, SEEK_CUR) < 0) // skip color cycling info + return; + theorical_size = 41; + } + else + theorical_size = 37; + if (fseek(file, 16*2, SEEK_CUR) < 0) // skip palette + return; + if (!Read_word_be(file, &control_bytes) || !Read_word_be(file, &data_words)) + return; + theorical_size += control_bytes + data_words * 2; + if (theorical_size <= file_size && file_size < theorical_size + 512) + { + GFX2_Log(GFX2_DEBUG, "TNY res=%d %hu control bytes, %hu data words\n", + (int)res, control_bytes, data_words); + File_error = 0; + } +} + +void Load_TNY(T_IO_Context * context) +{ + FILE * file; + byte res; // 0 = Low, 1 = midres, 2 = hires. +3 for color cycling + word control_bytes; + byte * control; + word data_words; + byte * data; + byte buffer[32000 + 2]; + + File_error = 1; + file = Open_file_read(context); + if (file == NULL) + return; + if (Read_byte(file, &res) && (res < 6)) + { + enum PIXEL_RATIO ratio = PIXEL_SIMPLE; + word width = 640, height = 200; + byte bpp; + byte cycling_range = 0; + byte cycling_speed = 0; + word duration = 0; + + switch (res) + { + case 0: + case 3: + width = 320; + bpp = 4; + break; + case 1: + case 4: + bpp = 2; + ratio = PIXEL_TALL; + break; + case 2: + case 5: + bpp = 1; + height = 400; + break; + } + if (res >= 3) + { + if( !Read_byte(file, &cycling_range) + || !Read_byte(file, &cycling_speed) + || !Read_word_be(file, &duration)) + { + fclose(file); + return; + } + GFX2_Log(GFX2_DEBUG, "TNY Color Cycling : %02x speed=%02x duration=%d\n", + cycling_range, cycling_speed, duration); + } + // Read palette + if ( !Read_bytes(file, buffer, 32) + || !Read_word_be(file, &control_bytes) + || !Read_word_be(file, &data_words)) + { + fclose(file); + return; + } + control = GFX2_malloc(control_bytes); + data = GFX2_malloc(data_words * 2); + if (control == NULL || data == NULL) + { + fclose(file); + return; + } + if ( Read_bytes(file, control, control_bytes) + && Read_bytes(file, data, data_words * 2)) + { + byte cb; + word cc, count; + int src, dst; + int line; + + File_error = 0; + Pre_load(context, width, height, File_length_file(file), FORMAT_TNY, ratio, bpp); + // Set palette + if (Config.Clear_palette) + memset(context->Palette, 0, sizeof(T_Palette)); + PI1_decode_palette(buffer, context->Palette); + if (res >= 3) + { + context->Cycle_range[context->Color_cycles].Start = (cycling_range & 0xf0) >> 4; + context->Cycle_range[context->Color_cycles].End = (cycling_range & 0x0f); + if (cycling_speed & 0x80) + { + context->Cycle_range[context->Color_cycles].Inverse = 1; + cycling_speed = 256 - cycling_speed; + } + else + context->Cycle_range[context->Color_cycles].Inverse = 0; + context->Cycle_range[context->Color_cycles].Speed = 175 / cycling_speed; + context->Color_cycles++; + } + for (cc = 0, src = 0, dst = 0; cc < control_bytes && dst < 32000; ) + { + cb = control[cc++]; + if (cb & 0x80) + { + // copy + do { + buffer[dst++] = data[src++]; + buffer[dst++] = data[src++]; + } while(++cb != 0); + } + else switch(cb) + { + case 0: // repeat (word count) + count = control[cc++] << 8; + count += control[cc++]; + while (count-- > 0) + { + buffer[dst++] = data[src]; + buffer[dst++] = data[src+1]; + } + src += 2; + break; + case 1: // copy (word count) + count = control[cc++] << 8; + count += control[cc++]; + memcpy(buffer+dst, data+src, count * 2); + dst += count * 2; + src += count * 2; + break; + default: // repeat next data word + while(cb-- > 0) + { + buffer[dst++] = data[src]; + buffer[dst++] = data[src+1]; + } + src += 2; + } + } + GFX2_Log(GFX2_DEBUG, " %d %d ; %d %d ; %d\n", cc, control_bytes, src, data_words * 2, dst); + /** + * Tiny stuff packs the ST RAM in 4 planes of 8000 bytes + * Data is organized by column in each plane + * Warning : this is the same organization for all 3 ST Video modes + */ + for (line = 0; line < 200; line++) + { + int col; + for (col = 0; col < 20; col++) + { + byte planar[8]; + byte pixels[16]; + src = (line + col * 200) * 2; + for (dst = 0; dst < 8;) + { + planar[dst++] = buffer[src]; + planar[dst++] = buffer[src+1]; + src += 8000; + } + switch(res) + { + case 0: + case 3: + PI1_8b_to_16p(planar, pixels); + for (dst = 0 ; dst < 16; dst++) + Set_pixel(context, col * 16 + dst, line, pixels[dst]); + break; + case 1: + case 4: + PI2_8b_to_16p(planar, pixels); + for (dst = 0 ; dst < 16; dst++) + Set_pixel(context, col * 32 + dst, line, pixels[dst]); + PI2_8b_to_16p(planar + 4, pixels); + for (dst = 0 ; dst < 16; dst++) + Set_pixel(context, col * 32 + 16 + dst, line, pixels[dst]); + break; + case 2: + case 5: + for (dst = 0 ; dst < 64; dst++) + Set_pixel(context, (col % 10) * 64 + dst, line * 2 + (col / 10), + (planar[(dst >> 3)] >> (7 - (dst & 7))) & 1); + } + } + } + } + free(control); + free(data); + } + fclose(file); +} + /* @} */