From 9eba486c476b138b42fd3bd7df4c9f847be23ac6 Mon Sep 17 00:00:00 2001 From: Thomas Bernard Date: Sun, 8 Dec 2019 10:45:10 +0100 Subject: [PATCH] Saving of atari ST TNY format --- src/loadsave.c | 2 +- src/stformats.c | 124 ++++++++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 125 insertions(+), 1 deletion(-) diff --git a/src/loadsave.c b/src/loadsave.c index b3a65e1d..c8c3fe97 100644 --- a/src/loadsave.c +++ b/src/loadsave.c @@ -138,7 +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_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"}, {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 dcb35bf0..ec3758d7 100644 --- a/src/stformats.c +++ b/src/stformats.c @@ -1390,4 +1390,128 @@ void Load_TNY(T_IO_Context * context) fclose(file); } +void Save_TNY(T_IO_Context * context) +{ + byte res = 0; + FILE * file; + byte buffer[32000]; + byte control[16000]; + byte data[32000]; + word cc = 0, dc = 0; // control count, data count + int line, dst, src; + + File_error = 1; + file = Open_file_write(context); + if (file == NULL) + return; + + // TODO : detect color cycling + if (!Write_byte(file, res)) // resolution +3 with color cycling + goto error; + + // palette + PI1_code_palette(context->Palette, buffer); + if (!Write_bytes(file, buffer, 16*2)) + goto error; + + /** + * fill the buffer with the special Tiny Stuff organization + * @see Load_TNY + */ + for (line = 0; line < 200; line++) + { + int col; + for (col = 0; col < 20; col++) + { + byte planar[8]; + + // Low res + PI1_16p_to_8b(context->Target_address + line * context->Pitch + col*16, planar); + dst = (line + col * 200) * 2; + for (src = 0; src < 8;) + { + buffer[dst] = planar[src++]; + buffer[dst+1] = planar[src++]; + dst += 8000; + } + } + } + +#define WORDS_EQU(p1, p2) (((p1)[0] == (p2)[0]) && ((p1)[1] == (p2)[1])) + + // now the compression + for (src = 0; src < 32000; ) + { + word count; + + // count repeat + count = 0; + while ((src + count * 2) < 32000 && WORDS_EQU(buffer + src, buffer + src + count * 2)) + count++; + if (count > 127) + { + GFX2_Log(GFX2_DEBUG, "%5d REPEAT %d %02x%02x\n", src, count, buffer[src+1], buffer[src]); + control[cc++] = 0; // repeat word + control[cc++] = count >> 8; + control[cc++] = count & 0xff; + data[dc * 2] = buffer[src]; + data[dc * 2 + 1] = buffer[src+1]; + dc++; + src += count * 2; + count = 1; + } + else if (count > 1) + { + // TODO: merge a repeat count of 2 between 2 copy count ? + GFX2_Log(GFX2_DEBUG, "%5d REPEAT %d %02x%02x\n", src, count, buffer[src+1], buffer[src]); + control[cc++] = (byte)count; + data[dc * 2] = buffer[src]; + data[dc * 2 + 1] = buffer[src+1]; + dc++; + src += count * 2; + count = 1; + } + if (src >= 32000) + break; + // count copy + while ((src + count * 2) < 32000 && !WORDS_EQU(buffer + src + (count - 1) * 2, buffer + src + count * 2)) + count++; + + if ((src + count * 2) < 32000) + count--; + if (count > 128) + { + GFX2_Log(GFX2_DEBUG, "%5d COPY %d %02x%02x %02x%02x...\n", + src, count, buffer[src+1], buffer[src], buffer[src+3], buffer[src+2]); + control[cc++] = 1; // copy word + control[cc++] = count >> 8; + control[cc++] = count & 0xff; + memcpy(data + dc * 2, buffer + src, count * 2); + dc += count; + src += count * 2; + } + else if (count > 0) + { + GFX2_Log(GFX2_DEBUG, "%5d COPY %d %02x%02x ...\n", + src, count, buffer[src+1], buffer[src]); + control[cc++] = (byte)(256 - count); // copy byte + memcpy(data + dc * 2, buffer + src, count * 2); + dc += count; + src += count * 2; + } + } + + if (!Write_word_be(file, cc) || !Write_word_be(file, dc)) + goto error; + if (!Write_bytes(file, control, cc) || !Write_bytes(file, data, dc * 2)) + goto error; + fclose(file); + File_error = 0; + return; +error: + if (file != NULL) + fclose(file); + Remove_file(context); +} + /* @} */