1 /* vim:expandtab:ts=2 sw=2:
2 */
3 /*  Grafx2 - The Ultimate 256-color bitmap paint program
4 
5 	Copyright owned by various GrafX2 authors, see COPYRIGHT.txt for details.
6 
7     Grafx2 is free software; you can redistribute it and/or
8     modify it under the terms of the GNU General Public License
9     as published by the Free Software Foundation; version 2
10     of the License.
11 
12     Grafx2 is distributed in the hope that it will be useful,
13     but WITHOUT ANY WARRANTY; without even the implied warranty of
14     MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
15     GNU General Public License for more details.
16 
17     You should have received a copy of the GNU General Public License
18     along with Grafx2; if not, see <http://www.gnu.org/licenses/>
19 */
20 
21 ///@file cpcformats.c
22 /// Formats for the Amstrad CPC / CPC Plus computers
23 
24 #include <stdlib.h>
25 #include <string.h>
26 #include <ctype.h>
27 #include "global.h"
28 #include "fileformats.h"
29 #include "io.h"
30 #include "loadsavefuncs.h"
31 #include "libraw2crtc.h"
32 #include "oldies.h"
33 #include "windows.h"
34 #include "misc.h"
35 #include "gfx2mem.h"
36 #include "gfx2log.h"
37 
38 #ifndef MIN
39 #define MIN(a,b) (((a)<(b)) ? (a) : (b))
40 #endif
41 #ifndef MAX
42 #define MAX(a,b) (((a)>(b)) ? (a) : (b))
43 #endif
44 
45 /**
46  * @defgroup cpcformats Amstrad CPC/CPC+ picture formats
47  * @ingroup loadsaveformats
48  *
49  * Support for Amstrad CPC/CPC+ picture formats. The Amstrad CPC has
50  * 3 video modes :
51  * - mode 0 : 160x200 16 colors
52  * - mode 1 : 320x200 4 colors
53  * - mode 2 : 640x200 2 colors
54  *
55  * Supported formats :
56  * - GO1/GO2 : GraphOS
57  * - SCR : OCP Art Studio / iMPdraw v2 / etc.
58  * - CM5 : Mode 5 Viewer
59  * - PPH : Perfect Pix
60  * - SGX : SymbOS graphic files
61  *
62  * @{
63  */
64 
65 /**
66  * Test for SGX file (SymbOS)
67  *
68  * http://www.cpcwiki.eu/index.php/Format:SGX_(SymbOS_graphic_files)
69  */
Test_SGX(T_IO_Context * context,FILE * file)70 void Test_SGX(T_IO_Context * context, FILE * file)
71 {
72   byte header[8];
73   (void)context;
74 
75   if (Read_bytes(file, header, 8))
76   {
77     unsigned long file_size = File_length_file(file);
78     if (header[0] > 0 && header[0] < 64
79         && ((header[1] + 3) >> 2) == header[0]
80         && file_size >= (3 + (unsigned long)header[0] * (unsigned long)header[2]))
81     {
82       // Simple 4 colour graphic
83       File_error = 0;
84     }
85     else if (header[0] == 64 && (header[1] == 0 || header[1] == 5))
86     {
87       // extended
88       word byte_width = header[2] | ((word)header[3] << 8);
89       word pixel_width = header[4] | ((word)header[5] << 8);
90       word height = header[6] | ((word)header[7] << 8);
91       if (height == 0 || byte_width == 0 || byte_width > 255) return;
92       if (header[1] == 0 && byte_width != ((pixel_width + 3) >> 2)) return;
93       if (header[1] == 5 && byte_width != ((pixel_width + 1) >> 1)) return;
94       File_error = 0;
95     }
96   }
97 }
98 
Set_SGX_Palette(T_IO_Context * context)99 static void Set_SGX_Palette(T_IO_Context * context)
100 {
101   static const byte sgx_palette[] = {
102     0xff, 0xff, 0x80, // 0
103     0x00, 0x00, 0x00,
104     0xff, 0x80, 0x00,
105     0x80, 0x00, 0x00,
106     0x00, 0xff, 0xff, // 4
107     0x00, 0x00, 0x80,
108     0x80, 0x80, 0xff,
109     0x80, 0x00, 0xff,
110     0xff, 0xff, 0xff, // 8
111     0x00, 0x80, 0x00,
112     0x00, 0xff, 0x00,
113     0xff, 0x00, 0xff,
114     0xff, 0xff, 0x00, // 12
115     0x80, 0x80, 0x80,
116     0xff, 0x80, 0x80,
117     0xff, 0x00, 0x00
118   };
119   if (Config.Clear_palette)
120     memset(context->Palette,0,sizeof(T_Palette));
121   memcpy(context->Palette, sgx_palette, sizeof(sgx_palette));
122 }
123 
124 /**
125  * Structure for current information about loading SGX file
126  */
127 struct sgx_data {
128   T_IO_Context * context;
129   FILE * file;
130   unsigned long file_size;
131   word width, height;
132   byte ncolors;
133 };
134 
135 /**
136  * Callback function for Read_SGX()
137  */
138 typedef int (*Read_SGX_Callback)(struct sgx_data *, word, word, word, word, word, byte);
139 
140 /**
141  * Read a SGX file structure.
142  *
143  * @return 0 for error
144  */
Read_SGX(struct sgx_data * data,Read_SGX_Callback cb)145 static int Read_SGX(struct sgx_data * data, Read_SGX_Callback cb)
146 {
147   word posx = 0;
148   word posy = 0;
149   byte b;
150 
151   data->width = 0;
152   data->height = 0;
153   while (Read_byte(data->file, &b))
154   {
155     if (b == 0)
156     {
157       // EOF
158       GFX2_Log(GFX2_DEBUG, "SGX EOF\n");
159       return 1;
160     }
161     else if (b == 255)
162     {
163       GFX2_Log(GFX2_DEBUG, "SGX LF\n");
164       // skip 2 bytes
165       if (!(Read_byte(data->file, &b) && Read_byte(data->file, &b)))
166         return 0;
167       posx = 0;
168       posy = data->height;
169     }
170     else if (b < 64)
171     {
172       byte byte_width, width, height;
173       byte_width = b;
174       if (!(Read_byte(data->file, &width) && Read_byte(data->file, &height)))
175         return 0;
176       GFX2_Log(GFX2_DEBUG, "SGX Simple 4c : %dx%d\n", (int)width, (int)height);
177       if (!cb(data, posx, posy, width, height, byte_width, 4))
178         return 0;
179       posx += width;
180       data->width = MAX(data->width, posx);
181       data->height = MAX(data->height, posy + height);
182       if (data->ncolors == 0)
183         data->ncolors = 4;
184     }
185     else if (b == 64)
186     {
187       byte type;
188       word byte_width, width, height;
189       if (!(Read_byte(data->file, &type) && Read_word_le(data->file, &byte_width)
190             && Read_word_le(data->file, &width) && Read_word_le(data->file, &height)))
191         return 0;
192       GFX2_Log(GFX2_DEBUG, "SGX Extended %dc : %dx%d\n", type == 0 ? 4 : 16, (int)width, (int)height);
193       if (!cb(data, posx, posy, width, height, byte_width, type == 0 ? 4 : 16))
194         return 0;
195       posx += width;
196       data->width = MAX(data->width, posx);
197       data->height = MAX(data->height, posy + height);
198       data->ncolors = MAX(data->ncolors, type == 0 ? 4 : 16);
199     }
200     else
201     {
202       GFX2_Log(GFX2_WARNING, "SGX : unrecognized chunk. Byte 0x%02x at offset 0x%06x\n", b, ftell(data->file) - 1);
203     }
204   }
205   return 1; // it is OK if the file does finish with a EOF mark
206 }
207 
208 /**
209  * Only skip bytes
210  */
SGX_Get_dimensions(struct sgx_data * data,word posx,word posy,word width,word height,word byte_width,byte ncolors)211 int SGX_Get_dimensions(struct sgx_data * data, word posx, word posy, word width, word height, word byte_width, byte ncolors)
212 {
213   (void)posx;
214   (void)posy;
215   (void)width;
216   (void)ncolors;
217   if (fseek(data->file, (long)byte_width * height, SEEK_CUR) < 0)
218     return 0;
219   return 1;
220 }
221 
222 /**
223  * Set the pixels
224  * @return 0 in case of error
225  */
SGX_Load_Picture(struct sgx_data * data,word posx,word posy,word width,word height,word byte_width,byte ncolors)226 int SGX_Load_Picture(struct sgx_data * data, word posx, word posy, word width, word height, word byte_width, byte ncolors)
227 {
228   word y;
229 
230   for (y = posy; y < (posy + height); y++)
231   {
232     word i, x;
233     for (i = 0, x = posx; i < byte_width; i++)
234     {
235       byte b;
236       if (!Read_byte(data->file, &b))
237         return 0;
238       if (ncolors == 4)
239       {
240         do {
241           // upper nibble is 4 lower color bits, lower nibble is 4 upper color bits
242           Set_pixel(data->context, x++, y, (b & 0x80) >> 7 | (b & 0x08) >> 2);
243           b <<= 1;
244         }
245         while (((x ^ posx) & 3) && (x < (posx + width)));
246       }
247       else
248       {
249         Set_pixel(data->context, x++, y, b >> 4);
250         if (x < (posx + width))
251           Set_pixel(data->context, x++, y, b & 0x0f);
252       }
253     }
254   }
255   return 1;
256 }
257 
Load_SGX(T_IO_Context * context)258 void Load_SGX(T_IO_Context * context)
259 {
260   struct sgx_data data = { context, NULL, 0, 0, 0, 0 };
261 
262   File_error = 1;
263   data.file = Open_file_read(context);
264   if (data.file == NULL) return;
265   data.file_size = File_length_file(data.file);
266   if (Read_SGX(&data, SGX_Get_dimensions))
267   {
268     GFX2_Log(GFX2_DEBUG, "SGX total dimensions : %ux%u, %d colors\n",
269              (unsigned)data.width, (unsigned)data.height, (int)data.ncolors);
270     File_error = 0;
271     Pre_load(context, data.width, data.height, data.file_size, FORMAT_SGX, PIXEL_SIMPLE, data.ncolors == 16 ? 4 : 2);
272     if (File_error == 0)
273     {
274       if (fseek(data.file, 0, SEEK_SET) < 0 || !Read_SGX(&data, SGX_Load_Picture))
275         File_error = 1;
276       else
277         Set_SGX_Palette(context);
278     }
279   }
280   fclose(data.file);
281 }
282 
283 /**
284  * Write SGX file format as specified here :
285  * http://www.cpcwiki.eu/index.php/Format:SGX_(SymbOS_graphic_files)
286  *
287  * We are using only simple chunks for 4 colors pictures.
288  * Chunk dimentions are limited to the ones of simple chunks (252x255),
289  * we could go up to 508x65535 with extended chunks.
290  *
291  * @return 0 for error
292  */
Save_SGX_Sub(T_IO_Context * context,FILE * file,word n_colors)293 static int Save_SGX_Sub(T_IO_Context * context, FILE * file, word n_colors)
294 {
295   word posy = 0;
296 
297   while (posy < context->Height)
298   {
299     word posx = 0;
300     word height = MIN(context->Height - posy, 255);
301     if (posy != 0)
302     {
303       // Write Line Feed
304       if (!Write_byte(file, 255)
305           || !Write_byte(file, 255) || !Write_byte(file, 255))
306         return 0;
307     }
308     while (posx < context->Width)
309     {
310       word width = MIN(context->Width - posx, 252);
311       GFX2_Log(GFX2_DEBUG, "Save_SGX : (%hu,%hu) %hux%hu %huc\n",
312                posx, posy, width, height, n_colors);
313       if (n_colors == 4)
314       {
315         word y;
316         byte byte_width = (width + 3) >> 2;
317         if (!Write_byte(file, byte_width)
318             || !Write_byte(file, (byte)width) || !Write_byte(file, (byte)height))
319           return 0;
320         for (y = 0; y < height; y++)
321         {
322           byte i;
323           word x;
324           for (i = 0, x = 0; i < byte_width; i++)
325           {
326             byte b = 0;
327             do
328             {
329               byte c = Get_pixel(context, posx + x++, posy + y);
330               b <<= 1;
331               b |= (c & 1) << 4;
332               b |= (c & 2) >> 1;
333             } while (x & 3);
334             if (!Write_byte(file, b))
335               return 0;
336           }
337         }
338       }
339       else
340       {
341         word y;
342         word byte_width = (width + 1) >> 1;
343         if (!Write_byte(file, 64) || !Write_byte(file, 5)
344             || !Write_word_le(file, byte_width) || !Write_word_le(file, width) || !Write_word_le(file, height))
345           return 0;
346         for (y = 0; y < height; y++)
347         {
348           word i, x;
349           for (i = 0, x = 0; i < byte_width; i++)
350           {
351             byte b = Get_pixel(context, posx + x++, posy + y) << 4;
352             b |= Get_pixel(context, posx + x++, posy + y) & 0x0f;
353             if (!Write_byte(file, b))
354               return 0;
355           }
356         }
357       }
358       posx += width;
359     }
360     posy += height;
361   }
362   // Write EOF marker
363   for (posy = 0; posy < 3; posy++)
364     Write_byte(file, 0);
365   return 1;
366 }
367 
Save_SGX(T_IO_Context * context)368 void Save_SGX(T_IO_Context * context)
369 {
370   FILE * file;
371   dword color_usage[256];
372   word n_colors;
373   int i;
374 
375   File_error =  1;
376   n_colors = Current_layer_count_used_colors(context, color_usage);
377   for (i = 16; i < 256; i++)
378   {
379     if (color_usage[i] != 0)
380     {
381       Warning_message("SGX format is limited to 16 colors");
382       return;
383     }
384   }
385   // 4 colors mode if sufficient, otherwise 16 colors
386   n_colors = 4;
387   for (i = 4; i < 16; i++)
388   {
389     if (color_usage[i] != 0)
390     {
391       n_colors = 16;
392       break;
393     }
394   }
395   file = Open_file_write(context);
396   if (file == NULL)
397     return;
398   if (Save_SGX_Sub(context, file, n_colors))
399     File_error = 0;
400   fclose(file);
401 }
402 
403 /**
404  * Test for SCR file (Amstrad CPC)
405  *
406  * SCR file format is originally from "Advanced OCP Art Studio" :
407  * http://www.cpcwiki.eu/index.php/Format:Advanced_OCP_Art_Studio_File_Formats
408  *
409  * .WIN "window" format is also supported.
410  *
411  * SCR files are normally just a dump of the 16K of video memory. So they are
412  * essentially 16 kilobytes of pixel data without any header. To make things
413  * more fun, there is an optional compression. This all makes detection a bit
414  * fuzzy. However there are various things we can still check:
415  *
416  * - Presence of a valid PAL file. If the PAL file is not there the pixel data
417  *   may still be valid. The PAL file size depends on the screen mode (number
418  *   of colors).
419  * - An AMSDOS header is a good indication but in some cases it may not
420  *   be there.
421  * - Some tools embed the palette and mode (and usually some kind of loader
422  *   code) in the SCR file, we can also detect these.
423  */
Test_SCR(T_IO_Context * context,FILE * file)424 void Test_SCR(T_IO_Context * context, FILE * file)
425 {
426   // http://orgams.wikidot.com/le-format-impdraw-v2
427   // http://orgams.wikidot.com/les-fichiers-win-compatibles-ocp-art-studio
428   FILE * pal_file;
429   unsigned long pal_size, file_size;
430   byte mode, color_anim_flag;
431   word loading_address = 0;
432   word exec_address = 0;
433 
434   File_error = 1;
435 
436   if (CPC_check_AMSDOS(file, &loading_address, &exec_address, &file_size))
437   {
438     if (loading_address == 0x170) // iMPdraw v2
439     {
440       byte buffer[0x90];
441       fseek(file, 128, SEEK_SET); // right after AMSDOS header
442       Read_bytes(file, buffer, 0x90);
443       GFX2_LogHexDump(GFX2_DEBUG, "", buffer, 0, 0x90);
444       File_error = 0;
445       return;
446     }
447     else if (loading_address == 0x200 && exec_address == 0x811 && file_size > 16000)
448     {
449       // convimgcpc
450       File_error = 0;
451       return;
452     }
453     else if (loading_address == 0xc000 && file_size > 16000)
454     {
455       File_error = 0;
456       return;
457     }
458     else if (loading_address == 0x0040 && exec_address == 0x8000)
459     {
460       File_error = 0;
461       return;
462     }
463     else if (loading_address == 0xc000 || loading_address == 0x0200)
464     {
465       byte buffer[4];
466       fseek(file, 128, SEEK_SET); // right after AMSDOS header
467       Read_bytes(file, buffer, 4);
468       // ConvImgCPC "LZW" packed pictures. Signatures :
469       // PKSL -> 320x200 STD
470       // PKS3 -> 320x200 Mode 3
471       // PKSP -> 320x200 PLUS
472       // PKVL -> Overscan STD
473       // PKVP -> Overscan PLUS
474       if (buffer[0] == 'P' && buffer[1] == 'K'
475           && (buffer[2] == 'S' || buffer[2] == 'V')
476           && (buffer[3] == 'L' || buffer[3] == 'P'))
477       {
478         File_error = 0;
479         return;
480       }
481     }
482   }
483   else
484     file_size = File_length_file(file);
485 
486   if (file_size > 16384*2)
487     return;
488 
489   // requires the PAL file
490   pal_file = Open_file_read_with_alternate_ext(context, "pal");
491   if (pal_file == NULL)
492     return;
493   /** @todo the palette data can be hidden in the 48 "empty" bytes
494    * every 2048 bytes of a standard resolution SCR file.
495    * So we should detect the hidden Z80 code and load them.
496    * Load address of file is C000. Z80 code :<br>
497    * <tt>C7D0: 3a d0 d7 cd 1c bd 21 d1 d7 46 48 cd 38 bc af 21 | :.....!..FH.8..!</tt><br>
498    * <tt>C7E0: d1 d7 46 48 f5 e5 cd 32 bc e1 f1 23 3c fe 10 20 | ..FH...2...#<.. </tt><br>
499    * <tt>C7F0: f1 c3 18 bb 00 00 00 00 00 00 00 00 00 00 00 00 | ................</tt><br>
500    * mode and palette :<br>
501    * <tt>D7D0: 00 1a 00 0c 03 0b 01 0d 17 10 02 0f 09 19 06 00 | ................</tt><br>
502    * https://gitlab.com/GrafX2/grafX2/merge_requests/121#note_119964168
503    */
504 
505 
506   if (CPC_check_AMSDOS(pal_file, NULL, NULL, &pal_size))
507     fseek(pal_file, 128, SEEK_SET); // right after AMSDOS header
508   else
509   {
510     pal_size = File_length_file(pal_file);
511     fseek(pal_file, 0, SEEK_SET);
512   }
513 
514   if (pal_size != 239)
515   {
516     fclose(pal_file);
517     return;
518   }
519 
520   if (!Read_byte(pal_file, &mode) || !Read_byte(pal_file, &color_anim_flag))
521   {
522     fclose(pal_file);
523     return;
524   }
525   GFX2_Log(GFX2_DEBUG, "Test_SCR() mode=%d color animation flag %02X\n", mode, color_anim_flag);
526   if (mode <= 2 && (color_anim_flag == 0 || color_anim_flag == 0xff))
527     File_error = 0;
528   fclose(pal_file);
529 }
530 
531 /**
532  * Unpack LZ streams from CPCconvImg v0.x (Demoniak/iMPACT!)
533  * @param dst destination buffer
534  * @param file input
535  * @return unpacked length or -1 for error
536  */
Depack_CPC_LZW(byte * dst,FILE * file)537 static int Depack_CPC_LZW(byte * dst, FILE * file)
538 {
539   int bitcount = 0;
540   byte control_byte = 0;
541   int count = 0;
542 
543   for (;;)
544   {
545     if (bitcount == 0)
546     {
547       if (!Read_byte(file, &control_byte))
548         return -1;
549       bitcount = 8;
550     }
551 
552     if (!(control_byte & 1))
553     {
554       if (!Read_byte(file, &dst[count++]))
555         return -1;
556     }
557     else
558     {
559       byte code, code2;
560       word delta;
561       word len;
562       if (!Read_byte(file, &code))
563         return -1;
564       if (code == 0)
565       {
566         return count; // EOF
567       }
568       if (code & 0x80)
569       {
570         // 1LLL DDDD DDDD DDDD
571         if (!Read_byte(file, &code2))
572           return -1;
573         len = 3 + ((code >> 4) & 7);
574         delta = ((word)(code & 15) << 8) + (word)code2 + 1;
575       }
576       else if (code & 0x40)
577       {
578         // 01DD DDDD
579         len = 2;
580         delta = (code & 0x3f) + 1;
581       }
582       else if (code & 0x20)
583       {
584         // 001L LLLL DDDD DDDD
585         len = 2 + (code & 31);
586         if (!Read_byte(file, &code2))
587           return -1;
588         delta = (word)code2 + 1;
589       }
590       else if (code & 0x10)
591       {
592         // 0001 DDDD DDDD DDDD LLLL LLLL
593         if (!Read_byte(file, &code2))
594           return -1;
595         delta = ((word)(code & 15) << 8) + (word)code2 + 1;
596         if (!Read_byte(file, &code2))
597           return -1;
598         len = (word)code2 + 1;
599       }
600       else if (code == 0x0f)
601       {
602         // 0000 1111 LDLD LDLD
603         if (!Read_byte(file, &code2))
604           return -1;
605         delta = len = (word)code2 + 1;
606       }
607       else if (code > 1)
608       {
609         // 0000 LDLD   1 < len = delta < 15
610         delta = len = (word)code;
611       }
612       else
613       {
614         // 0000 0001
615         delta = len = 256;
616       }
617       while (len--)
618       {
619         dst[count] = dst[count - delta];
620         count++;
621       }
622     }
623     bitcount--;
624     control_byte >>= 1;
625   }
626 }
627 
628 
629 /**
630  * Load Advanced OCP Art Studio files (Amstrad CPC)
631  *
632  * Standard resolution files (Mode 0 160x200, mode 1 320x200 and
633  * mode 2 640x200) are supported. The .PAL file is loaded if available.
634  * "MJH" RLE packing is supported.
635  * Embedded CRTC registers and palette data from various tools is also
636  * supported.
637  *
638  * .WIN "window" format is also loaded here and allows different picture sizes.
639  */
Load_SCR(T_IO_Context * context)640 void Load_SCR(T_IO_Context * context)
641 {
642     // The Amstrad CPC screen memory is mapped in a weird mode, somewhere
643     // between bitmap and textmode. Basically the only way to decode this is to
644     // emulate the video chip and read the bytes as needed...
645     // Moreover, the hardware allows the screen to have any size from 8x1 to
646     // 800x273 pixels, and there is no indication of that in the file besides
647     // its size. It can also use any of the 3 screen modes. Fortunately this
648     // last bit of information is stored in the palette file.
649     // Oh, and BTW, the picture can be offset, and it's even usual to do it,
650     // because letting 128 pixels unused at the beginning of the file make it a
651     // lot easier to handle screens using more than 16K of VRam.
652     // The pixel encoding change with the video mode so we have to know that
653     // before attempting to load anything...
654     // As if this wasn't enough, Advanced OCP Art Studio, the reference tool on
655     // Amstrad, can use RLE packing when saving files, meaning we also have to
656     // handle that.
657 
658     // All this mess enforces us to load (and unpack if needed) the file to a
659     // temporary 32k buffer before actually decoding it.
660   FILE * pal_file, * file;
661   unsigned long real_file_size, file_size, amsdos_file_size = 0;
662   word addr;
663   word load_address = 0x4000; // default for OCP Art studio
664   word exec_address = 0;
665   word display_start = 0x4000;
666   byte mode, color_anim_flag, color_anim_delay;
667   byte pal_data[236]; // 12 palettes of 16+1 colors + 16 excluded inks + 16 protected inks
668   word width, height = 200;
669   byte bpp;
670   enum PIXEL_RATIO ratio;
671   byte * cpc_ram;
672   word x, y;
673   int i;
674   byte sig[3];
675   word block_length;
676   word win_width, win_height;
677   int linear = 0;
678   int is_win = 0;
679   int columns = 80;
680   int cpc_plus = 0;
681   const byte * cpc_plus_pal = NULL;
682 
683   File_error = 1;
684   // requires the PAL file for OCP Art studio files
685   pal_file = Open_file_read_with_alternate_ext(context, "pal");
686   if (pal_file != NULL)
687   {
688     file_size = File_length_file(pal_file);
689     if (CPC_check_AMSDOS(pal_file, NULL, NULL, &file_size))
690       fseek(pal_file, 128, SEEK_SET); // right after AMSDOS header
691     else
692       fseek(pal_file, 0, SEEK_SET);
693     if (!Read_byte(pal_file, &mode) || !Read_byte(pal_file, &color_anim_flag)
694           || !Read_byte(pal_file, &color_anim_delay) || !Read_bytes(pal_file, pal_data, 236))
695     {
696       GFX2_Log(GFX2_WARNING, "Load_SCR() failed to load .PAL file\n");
697       fclose(pal_file);
698       return;
699     }
700     fclose(pal_file);
701     GFX2_Log(GFX2_DEBUG, "Load_SCR() mode=%d color animation flag=%02X delay=%u\n",
702              mode, color_anim_flag, color_anim_delay);
703   }
704   else
705   {
706     memset(pal_data, 0, sizeof(pal_data));
707   }
708 
709   file = Open_file_read(context);
710   if (file == NULL)
711     return;
712   file_size = File_length_file(file);
713   real_file_size = file_size;
714   if (CPC_check_AMSDOS(file, &load_address, &exec_address, &amsdos_file_size))
715   {
716     display_start = load_address;
717     if (file_size < (amsdos_file_size + 128))
718     {
719       GFX2_Log(GFX2_ERROR, "Load_SCR() mismatch in file size. AMSDOS file size %lu, should be %lu\n", amsdos_file_size, file_size - 128);
720       fclose(file);
721       return;
722     }
723     else if (file_size > (amsdos_file_size + 128))
724       GFX2_Log(GFX2_INFO, "Load_SCR() %lu extra bytes at end of file\n", file_size - 128 - amsdos_file_size);
725     fseek(file, 128, SEEK_SET); // right after AMSDOS header
726     snprintf(context->Comment, COMMENT_SIZE, "AMSDOS load@ &%04X exec@ &%04X",
727              load_address, exec_address);
728     file_size = amsdos_file_size;
729   }
730   else
731     fseek(file, 0, SEEK_SET);
732 
733   if (!Read_bytes(file, sig, 3) || !Read_word_le(file, &block_length))
734   {
735     fclose(file);
736     return;
737   }
738   fseek(file, -5, SEEK_CUR);
739 
740   cpc_ram = GFX2_malloc(64*1024);
741   memset(cpc_ram, 0, 64*1024);
742 
743   if (0 == memcmp(sig, "PKV", 3))
744   {
745     // PKVL / PKVP => Overscan
746     fseek(file, 4, SEEK_CUR);
747     i = Depack_CPC_LZW(cpc_ram + load_address, file);
748     if (i < 0)
749     {
750       File_error = 1;
751       fclose(file);
752       free(cpc_ram);
753       return;
754     }
755     GFX2_Log(GFX2_DEBUG, "%c%c%c%c count=%d\n", sig[0], sig[1], sig[2], block_length & 255, i);
756     cpc_plus = (block_length & 255) == 'P';
757     if (cpc_plus)
758       cpc_plus_pal = cpc_ram + 0x801;
759     else
760     {
761       int j;
762       for (j = 0; j < 16; j++)
763         pal_data[12*j] = CPC_Firmware_to_Hardware_color(cpc_ram[0x801 + j]);
764     }
765     context->Comment[COMMENT_SIZE-2] = ' ';
766     context->Comment[COMMENT_SIZE-1] = 'P';
767     context->Comment[COMMENT_SIZE] = '\0';
768   }
769   else if (0 == memcmp(sig, "PKS", 3))
770   {
771     // PKSL = CPC "old"
772     // PKSP = CPC +
773     fseek(file, 4 + 1, SEEK_CUR);
774     cpc_plus = (block_length & 255) == 'P';
775     if (!Read_bytes(file, cpc_ram, cpc_plus ? 32 : 16))
776     {
777       File_error = 1;
778       fclose(file);
779       free(cpc_ram);
780       return;
781     }
782     if (!cpc_plus)
783     {
784       for (i = 0; i < 16; i++)
785         pal_data[12*i] = CPC_Firmware_to_Hardware_color(cpc_ram[i]);
786     }
787     else
788       cpc_plus_pal = cpc_ram;
789     i = Depack_CPC_LZW(cpc_ram + load_address, file);
790     if (i < 0)
791     {
792       File_error = 1;
793       fclose(file);
794       free(cpc_ram);
795       return;
796     }
797     mode = block_length >> 8;
798     GFX2_Log(GFX2_DEBUG, "%c%c%c%c mode %d count=%d\n", sig[0], sig[1], sig[2], block_length & 255, mode, i);
799     linear = 1;
800     display_start = load_address;
801     i = 0;
802     height = 200;
803     columns = 80;
804     context->Comment[COMMENT_SIZE-2] = ' ';
805     context->Comment[COMMENT_SIZE-1] = 'P';
806     context->Comment[COMMENT_SIZE] = '\0';
807   }
808   else if (0 != memcmp(sig, "MJH", 3) || block_length > 16384)
809   {
810     // raw data
811     Read_bytes(file, cpc_ram + load_address, file_size);
812     i = file_size;
813   }
814   else
815   {
816     // MJH packed format
817     i = 0;
818     do
819     {
820       if (!Read_bytes(file, sig, 3) || !Read_word_le(file, &block_length))
821         break;
822       if (0 != memcmp(sig, "MJH", 3))
823         break;
824       GFX2_Log(GFX2_DEBUG, "  %.3s block %u\n", sig, block_length);
825       file_size -= 5;
826       while (block_length > 0)
827       {
828         byte code;
829         if (!Read_byte(file, &code))
830           break;
831         file_size--;
832         if (code == 1)
833         {
834           byte repeat, value;
835           if (!Read_byte(file, &repeat) || !Read_byte(file, &value))
836             break;
837           file_size -= 2;
838           do
839           {
840             cpc_ram[load_address + i++] = value;
841             block_length--;
842           }
843           while(--repeat != 0);
844         }
845         else
846         {
847           cpc_ram[load_address + i++] = code;
848           block_length--;
849         }
850       }
851       GFX2_Log(GFX2_DEBUG, "  unpacked %d bytes. remaining bytes in file=%lu\n",
852                i, file_size);
853     }
854     while(file_size > 0 && i < 16384);
855   }
856   fclose(file);
857 
858   if (i > 5)
859   {
860     win_width = cpc_ram[load_address + i - 4] + (cpc_ram[load_address + i - 3] << 8);  // in bits
861     win_height = cpc_ram[load_address + i - 2];
862     if (((win_width + 7) >> 3) * win_height + 5 == i) // that's a WIN file !
863     {
864       width = win_width >> (2 - mode);
865       height = win_height;
866       is_win = 1;
867       columns = (win_width + 7) >> 3;
868       GFX2_Log(GFX2_DEBUG, ".WIN file detected len=%d (%d,%d) %dcols %02X %02X %02X %02X %02X\n",
869           i, width, height, columns,
870           cpc_ram[load_address + i - 5], cpc_ram[load_address + i - 4], cpc_ram[load_address + i - 3],
871           cpc_ram[load_address + i - 2], cpc_ram[load_address + i - 1]);
872     }
873     else
874     {
875       GFX2_Log(GFX2_DEBUG, ".SCR file. Data length %d\n", i);
876       if (load_address == 0x170)
877       {
878         // fichier iMPdraw v2
879         // http://orgams.wikidot.com/le-format-impdraw-v2
880         GFX2_Log(GFX2_DEBUG, "Detected \"%s\"\n", cpc_ram + load_address + 6);
881         mode = cpc_ram[load_address + 0x14] - 0x0e;
882         cpc_plus = cpc_ram[load_address + 0x3c];
883         GFX2_Log(GFX2_DEBUG, "Mode %d CPC %d\n", (int)mode, (int)cpc_plus);
884         for (addr = load_address + 0x1d; cpc_ram[addr] < 16; addr += 2)
885         {
886           GFX2_Log(GFX2_DEBUG, " R%d = &H%02x = %d\n", cpc_ram[addr], cpc_ram[addr+1], cpc_ram[addr+1]);
887           // see http://www.cpcwiki.eu/index.php/CRTC#The_6845_Registers
888           switch(cpc_ram[addr])
889           {
890             case 1:
891               columns = cpc_ram[addr+1] * 2;
892               break;
893             case 6:
894               height = cpc_ram[addr+1] * 8;
895               break;
896             case 12:
897               display_start = ((cpc_ram[addr+1] & 0x30) << 10) | ((cpc_ram[addr+1] & 0x03) << 9);
898               GFX2_Log(GFX2_DEBUG, "  display_start &H%04X\n", display_start);
899            }
900         }
901         snprintf(context->Comment, COMMENT_SIZE, "%s mode %d %s",
902                  cpc_ram + load_address + 7, mode, cpc_plus ? "CPC+" : "");
903         if (cpc_plus)
904         {
905           // palette at 0x801 (mode at 0x800 ?)
906           GFX2_LogHexDump(GFX2_DEBUG, "", cpc_ram, 0x800, 0x21);
907           cpc_plus_pal = cpc_ram + 0x801;
908         }
909         else
910         {
911           int j;
912           // palette at 0x7f00
913           GFX2_LogHexDump(GFX2_DEBUG, "", cpc_ram, 0x7f00, 16);
914           for (j = 0; j < 16; j++)
915             pal_data[12*j] = cpc_ram[0x7f00 + j];
916         }
917       }
918       else if (load_address == 0x200 && exec_address == 0x811)
919       {
920         /* from HARLEY.SCR :
921         0800  00 = mode
922         0801-0810 palette (Firmware colors)
923         0811  21 47 08      LD HL,0847  ; OVERSCAN_REG_VALUES
924         0814  cd 36 08      CALL 0836 ; LOAD_CRTC_REGS
925         0817  3a 00 08      LD A,(0800) ; MODE
926         081a  cd 1c bd      CALL BD1C ; Set screen mode
927         081d  21 01 08      LD HL,0801  ; PALETTE
928         0820  af            XOR A
929             LOOP:
930         0821  4e            LD C,(HL)
931         0822  41            LD B,C
932         0823  f5            PUSH AF
933         0824  e5            PUSH HL
934         0825  cd 32 bc      CALL BC32   ; SET ink A to color B,C
935         0828  e1            POP HL
936         0829  f1            POP AF
937         082a  23            INC HL
938         082b  3c            INC A
939         082c  fe 10         CMP 10
940         082e  20 f1         JR NZ,0821  ; LOOP
941         0830  cd 18 bb      CALL BB18 ; Wait key press
942         0833  21 55 08      LD HL,0855  ; STANDARD_REG_VALUES
943             LOAD_CRTC_REGS:
944         0836  01 00 bc      LD BC,BC00
945             LOOP_CRTC:
946         0839  7e            LD A,(HL)
947         083a  a7            AND A
948         083b  c8            RET Z
949         083c  ed 79         OUT (C),A
950         083e  04            INC B
951         083f  23            INC HL
952         0840  7e            LD A,(HL)
953         0841  ed 79         OUT (C),A
954         0843  23            INC HL
955         0844  05            DEC B
956         0845  18 f2         JR 0839 ; LOOP_CRTC
957             OVERSCAN_REG_VALUES:
958         0847  01 30  02 32  06 22  07 23  0c 0d  0d 00  00 00
959             STANDARD_REG_VALUES:
960         0855  01 28  02 2e  06 19  07 1e  0c 30  00 00
961         */
962         int j;
963         mode = cpc_ram[0x800];
964         for (j = 0; j < 16; j++)
965           pal_data[12*j] = CPC_Firmware_to_Hardware_color(cpc_ram[0x801 + j]);
966         addr = 0x847;
967         if (cpc_ram[0x80bb] == 1)
968           addr = 0x80bb;
969         for (; cpc_ram[addr] > 0 && cpc_ram[addr] < 16; addr += 2)
970         {
971           GFX2_Log(GFX2_DEBUG, " R%d = &H%02x = %d\n", cpc_ram[addr], cpc_ram[addr+1], cpc_ram[addr+1]);
972           // see http://www.cpcwiki.eu/index.php/CRTC#The_6845_Registers
973           switch(cpc_ram[addr])
974           {
975             case 1:
976               columns = cpc_ram[addr+1] * 2;
977               break;
978             case 6:
979               height = cpc_ram[addr+1] * 8;
980               break;
981             case 12:
982               display_start = (display_start & 0x01ff) | ((cpc_ram[addr+1] & 0x30) << 10) | ((cpc_ram[addr+1] & 0x03) << 9);
983               break;
984             case 13:
985               display_start = (display_start & 0xfe00) | (cpc_ram[addr+1] << 1);
986           }
987         }
988         snprintf(context->Comment, COMMENT_SIZE, "ConvImgCPC mode %d", mode);
989       }
990       else if (load_address == 0xC000 && cpc_ram[0xc7d0] != 0)
991       {
992         mode = cpc_ram[0xD7D0];
993         if ((mode & 0xc0) == 0x80)
994         {
995           // value sent to gate array :
996           // bits 7 & 6 = function :
997           //              10 => select mode, ROM conf and interrupt control
998           //              bit 4 : interrupt control
999           //              bit 3 : disable upper ROM (C000 = Basic)
1000           //              bit 2 : disable lower ROM (0000 = Firmware)
1001           //              bits 1&0 : screen mode
1002           mode &= 3;
1003           cpc_plus_pal = cpc_ram + 0xD7D1;
1004         }
1005         else
1006         {
1007           int j;
1008           for (j = 0; j < 16; j++)
1009             pal_data[12*j] = CPC_Firmware_to_Hardware_color(cpc_ram[0xD7D1 + j]);
1010         }
1011         snprintf(context->Comment, COMMENT_SIZE, "loader@ &%04X mode %d %s", exec_address, mode, cpc_plus_pal ? "CPC+" : "");
1012       }
1013       else if (load_address == 0x0040 && exec_address == 0x8000)
1014       {
1015         mode = cpc_ram[0x8008] & 3;
1016         memset(cpc_ram, cpc_ram[0x40], 0x40);
1017         for (addr = 0x8094; cpc_ram[addr] < 16 && cpc_ram[addr] != 0; addr += 2)
1018         {
1019           GFX2_Log(GFX2_DEBUG, " R%d = &H%02x = %d\n", cpc_ram[addr], cpc_ram[addr+1], cpc_ram[addr+1]);
1020           // see http://www.cpcwiki.eu/index.php/CRTC#The_6845_Registers
1021           switch(cpc_ram[addr])
1022           {
1023             case 1:
1024               columns = cpc_ram[addr+1] * 2;
1025               break;
1026             case 6:
1027               height = cpc_ram[addr+1] * 8;
1028               break;
1029             case 12:
1030               display_start = (display_start & 0x01ff) | ((cpc_ram[addr+1] & 0x30) << 10) | ((cpc_ram[addr+1] & 0x03) << 9);
1031               break;
1032             case 13:
1033               display_start = (display_start & 0xfe00) | (cpc_ram[addr+1] << 1);
1034            }
1035         }
1036         GFX2_Log(GFX2_DEBUG, "  display_start &H%04X\n", display_start);
1037         cpc_plus_pal = cpc_ram + 0x80a0;
1038       }
1039       if (i >= 30000)
1040       {
1041         height = 272; columns = 96;
1042       }
1043     }
1044   }
1045 
1046   switch (mode)
1047   {
1048     case 0:
1049       width = columns * 2;
1050       bpp = 4;
1051       ratio = PIXEL_WIDE;
1052       break;
1053     case 1:
1054       width = columns * 4;
1055       bpp = 2;
1056       ratio = PIXEL_SIMPLE;
1057       break;
1058     case 2:
1059       width = columns * 8;
1060       bpp = 1;
1061       ratio = PIXEL_TALL;
1062       break;
1063     default:
1064       return; // unsupported
1065   }
1066 
1067   if (Config.Clear_palette)
1068     memset(context->Palette,0,sizeof(T_Palette));
1069   // Setup the palette (amstrad hardware palette)
1070   CPC_set_HW_palette(context->Palette + 0x40);
1071 
1072   // Set the palette for this picture
1073   if (cpc_plus_pal)
1074   {
1075     for (i = 0; i < 16; i++)
1076     {
1077       context->Palette[i].G = cpc_plus_pal[i*2 + 1] * 0x11;
1078       context->Palette[i].R = (cpc_plus_pal[i*2] >> 4) * 0x11;
1079       context->Palette[i].B = (cpc_plus_pal[i*2] & 15) * 0x11;
1080     }
1081   }
1082   else
1083   {
1084     int ocp_plus_palette = 1;
1085     for (i = 0; i < 16 && ocp_plus_palette; i++)
1086     {
1087       byte inkr, inkb, inkg;
1088       int j;
1089       // OCP+ palettes use the 3 first slots and have the 9 remaining set to 0
1090       for (j = 3; j < 12; j++)
1091       {
1092         if (pal_data[12*i+j] != 0)
1093         {
1094           ocp_plus_palette = 0;
1095           break;
1096         }
1097       }
1098       inkr = (context->Palette[pal_data[12*i]].G / 86) * 9 + (context->Palette[pal_data[12*i]].R / 86) * 3 + (context->Palette[pal_data[12*i]].B / 86);
1099       inkb = (context->Palette[pal_data[12*i+1]].G / 86) * 9 + (context->Palette[pal_data[12*i+1]].R / 86) * 3 + (context->Palette[pal_data[12*i+1]].B / 86);
1100       inkg = (context->Palette[pal_data[12*i+2]].G / 86) * 9 + (context->Palette[pal_data[12*i+2]].R / 86) * 3 + (context->Palette[pal_data[12*i+2]].B / 86);
1101       if (inkr < 11 || inkb < 11 || inkg < 11)
1102           ocp_plus_palette = 0;
1103     }
1104     if (ocp_plus_palette)
1105     {
1106       GFX2_Log(GFX2_DEBUG, "OCP+ Palette detected\n");
1107       for (i = 0; i < 16; i++)
1108       {
1109         byte inkr, inkb, inkg;
1110         inkr = (context->Palette[pal_data[12*i]].G / 86) * 9 + (context->Palette[pal_data[12*i]].R / 86) * 3 + (context->Palette[pal_data[12*i]].B / 86);
1111         inkb = (context->Palette[pal_data[12*i+1]].G / 86) * 9 + (context->Palette[pal_data[12*i+1]].R / 86) * 3 + (context->Palette[pal_data[12*i+1]].B / 86);
1112         inkg = (context->Palette[pal_data[12*i+2]].G / 86) * 9 + (context->Palette[pal_data[12*i+2]].R / 86) * 3 + (context->Palette[pal_data[12*i+2]].B / 86);
1113         context->Palette[i].R = (26 - inkr) * 0x11;
1114         context->Palette[i].G = (26 - inkg) * 0x11;
1115         context->Palette[i].B = (26 - inkb) * 0x11;
1116       }
1117     }
1118     else
1119     {
1120       // Standard OCP palette
1121       for (i = 0; i < 16; i++)
1122         context->Palette[i] = context->Palette[pal_data[12*i]];
1123     }
1124   }
1125 
1126   File_error = 0;
1127   Pre_load(context, width, height, real_file_size, FORMAT_SCR, ratio, bpp);
1128 
1129   if (!is_win && !linear)
1130   {
1131     // Standard resolution files have the 200 lines stored in block
1132     // of 25 lines of 80 bytes = 2000 bytes every 2048 bytes.
1133     // so there are 48 bytes unused every 2048 bytes...
1134     for (y = 0; y < 8; y++)
1135     {
1136       addr = display_start + 0x800 * y;
1137       if (y > 0 && (display_start & 0x7ff))
1138       {
1139         if (!GFX2_is_mem_filled_with(cpc_ram + (addr & 0xf800), 0, display_start & 0x7ff))
1140           GFX2_LogHexDump(GFX2_DEBUG, "SCR1 ", cpc_ram,
1141                           addr & 0xf800, display_start & 0x7ff);
1142       }
1143       addr += (height >> 3) * columns;
1144       block_length = (height >> 3) * columns + (display_start & 0x7ff);
1145       if (block_length <= 0x800)
1146       {
1147         block_length = 0x800 - block_length;
1148         if (!GFX2_is_mem_filled_with(cpc_ram + addr, 0, block_length))
1149           GFX2_LogHexDump(GFX2_DEBUG, "SCR2 ", cpc_ram,
1150                           addr, block_length);
1151       }
1152       else
1153       {
1154         block_length = 0x1000 - block_length;
1155         if (!GFX2_is_mem_filled_with(cpc_ram + addr + 0x4000, 0, block_length))
1156           GFX2_LogHexDump(GFX2_DEBUG, "SCR2 ", cpc_ram,
1157                           addr + 0x4000, block_length);
1158       }
1159     }
1160     //for (j = 0; j < i; j += 2048)
1161     //  GFX2_LogHexDump(GFX2_DEBUG, "SCR ", cpc_ram, load_address + j + 2000, 48);
1162   }
1163 
1164   GFX2_Log(GFX2_DEBUG, "  display_start &H%04X  columns=%u\n", display_start, columns);
1165   for (y = 0; y < height; y++)
1166   {
1167     x = 0;
1168     for (i = 0; i < columns; i++)
1169     {
1170       byte pixels;
1171       if (is_win)
1172         addr = display_start + y * columns + i;
1173       else if (linear)
1174         addr = display_start + y + i * height;
1175       else
1176       {
1177         addr = display_start + ((y >> 3) * columns) + i;
1178         addr = (addr & 0xC7FF) + ((addr & 0x800) << 3);
1179         addr += (y & 7) << 11;
1180       }
1181       pixels = cpc_ram[addr];
1182       switch (mode)
1183       {
1184         case 0:
1185           Set_pixel(context, x++, y, (pixels & 0x80) >> 7 | (pixels & 0x08) >> 2 | (pixels & 0x20) >> 3 | (pixels & 0x02) << 2);
1186           Set_pixel(context, x++, y, (pixels & 0x40) >> 6 | (pixels & 0x04) >> 1 | (pixels & 0x10) >> 2 | (pixels & 0x01) << 3);
1187           break;
1188         case 1:
1189           do {
1190             // upper nibble is 4 lower color bits, lower nibble is 4 upper color bits
1191             Set_pixel(context, x++, y, (pixels & 0x80) >> 7 | (pixels & 0x08) >> 2);
1192             pixels <<= 1;
1193           }
1194           while ((x & 3) != 0);
1195           break;
1196         case 2:
1197           do {
1198             Set_pixel(context, x++, y, (pixels & 0x80) >> 7);
1199             pixels <<= 1;
1200           }
1201           while ((x & 7) != 0);
1202       }
1203     }
1204   }
1205 
1206   free(cpc_ram);
1207 }
1208 
1209 #include "impdraw_loader.h"
1210 #include "cpc_scr_simple_loader.h"
1211 
1212 /**
1213  * Save Amstrad SCR file
1214  *
1215  * guess mode from aspect ratio :
1216  * - normal pixels are mode 1
1217  * - wide pixels are mode 0
1218  * - tall pixels are mode 2
1219  *
1220  * Mode and palette are stored in a .PAL file for compatibility
1221  * with OCP Art studio.
1222  *
1223  * The picture color index should be 0-15,
1224  * The CPC Hardware palette is expected to be set (indexes 64 to 95)
1225  *
1226  * If the picture is using overscan (more than 16384 bytes)
1227  * we produce a iMPdraw v2 format autoloading file.
1228  * see http://orgams.wikidot.com/le-format-impdraw-v2
1229  *
1230  * If the picture is not using overscan (standard resolutions,
1231  * < 16384 screen buffer) a BASIC loader is saved.
1232  *
1233  * @todo Add possibility to set R9 value
1234  * @todo Add OCP packing support
1235  */
Save_SCR(T_IO_Context * context)1236 void Save_SCR(T_IO_Context * context)
1237 {
1238   int i, j;
1239   unsigned char* output;
1240   unsigned long outsize = 0;
1241   unsigned char r1 = 0; // Horizontal Displayed. Standard value is 40 (=80 bytes)
1242   int cpc_mode;
1243   FILE* file;
1244   int cpc_plus_pal = 0;
1245   unsigned short load_address = 0xC000; // Standard CPC screen address
1246   unsigned short exec_address = 0xC7D0;
1247   int overscan;
1248   byte cpc_hw_pal[16];
1249   byte r12 = 0x0C | 0x30; // set Display Start Address at C000
1250   byte r13 = 0;
1251 
1252   switch(context->Ratio)
1253   {
1254     case PIXEL_WIDE:
1255     case PIXEL_WIDE2:
1256       cpc_mode = 0; // 16 colors, 2 pixels per byte
1257       overscan = (context->Width * context->Height) > (16384 * 2);
1258       break;
1259     case PIXEL_TALL:
1260     case PIXEL_TALL2:
1261     case PIXEL_TALL3:
1262       cpc_mode = 2; // 2 colors, 8 pixels per byte
1263       overscan = (context->Width * context->Height) > (16384 * 8);
1264       break;
1265     default:
1266       cpc_mode = 1; // 4 colors, 4 pixels per byte
1267       overscan = (context->Width * context->Height) > (16384 * 4);
1268       break;
1269   }
1270   if (overscan)
1271   {
1272     // format iMP v2
1273     load_address = 0x170; // BASIC program at 0x170
1274     // picture at 0x200
1275     r12 = 0x0C | (0x200 >> 9);
1276     r13 = (0x200 >> 1) & 0xff;
1277     exec_address = 0; // BASIC program
1278   }
1279 
1280   CPC_set_HW_palette(context->Palette + 0x40);
1281   for (i = 0; i < 16; i++)
1282   {
1283     // search for the color in the HW palette (0x40-0x5F)
1284     byte index = 0x40;
1285     while ((index < 0x60) &&
1286         !CPC_compare_colors(context->Palette + i, context->Palette + index))
1287       index++;
1288     if (index >= 0x60)
1289     {
1290       GFX2_Log(GFX2_WARNING, "Save_SCR() color #%i not found in CPC HW palette.\n", i);
1291       cpc_plus_pal = 1;
1292       index = 0x54 - i; // default
1293     }
1294     cpc_hw_pal[i] = index;
1295     if (!CPC_is_CPC_old_color(context->Palette + i))
1296       cpc_plus_pal = 1;
1297   }
1298 
1299   file = Open_file_write_with_alternate_ext(context, "pal");
1300   if (file == NULL)
1301     return;
1302   CPC_write_AMSDOS_header(file, context->File_name, "pal", 2, 0x8809, 0x8809, 239);
1303   if (!Write_byte(file, cpc_mode) || !Write_byte(file, 0) || !Write_byte(file, 0))
1304   {
1305     fclose(file);
1306     return;
1307   }
1308   for (i = 0; i < 16; i++)
1309   {
1310     for (j = 0; j < 12; j++)  // write the same color for the 12 frames
1311     {
1312       Write_byte(file, cpc_hw_pal[i]);
1313     }
1314   }
1315   // border
1316   for (j = 0; j < 12; j++)
1317   {
1318     Write_byte(file, 0x54); // black
1319   }
1320   // excluded inks
1321   for (i = 0; i < 16; i++)
1322   {
1323     Write_byte(file, 0);
1324   }
1325   // protected inks
1326   for (i = 0; i < 16; i++)
1327   {
1328     Write_byte(file, 0);
1329   }
1330   fclose(file);
1331 
1332   output = raw2crtc(context, cpc_mode, 7, &outsize, &r1, r12, r13);
1333   GFX2_Log(GFX2_DEBUG, "Save_SCR() output=%p outsize=%lu r1=$%02X\n", output, outsize, r1);
1334 
1335   if (output == NULL)
1336     return;
1337 
1338   file = Open_file_write(context);
1339   if (file == NULL)
1340     File_error = 1;
1341   else
1342   {
1343     File_error = 0;
1344     CPC_write_AMSDOS_header(file, context->File_name, "SCR", overscan ? 0 : 2, load_address, exec_address, overscan ? 32400 : outsize);
1345     if (overscan)
1346     {
1347       // write iMPdraw loader
1348       byte buffer[0x90];
1349       memcpy(buffer, impdraw_loader, 0x90);
1350       buffer[0x184 - 0x170] = 0x0e + cpc_mode;
1351       buffer[0x18e - 0x170] = r1;
1352       //buffer[0x190 - 0x170] = // R2 ?
1353       buffer[0x192 - 0x170] = context->Height / 8;  // r6
1354       //buffer[0x194 - 0x170] = // r7 ?
1355       buffer[0x196 - 0x170] = r12;
1356       buffer[0x198 - 0x170] = r13;
1357       buffer[0x1ac - 0x170] = cpc_plus_pal;
1358       if (!Write_bytes(file, buffer, 0x90))
1359         File_error = 1;
1360       output = realloc(output, 32400 - 0x90);
1361       memset(output + outsize, 0, 32400 - 0x90 - outsize);
1362       outsize = 32400 - 0x90;
1363       memcpy(output + 0x7F00 - 0x200, cpc_hw_pal, 16);
1364       for (i = 0; i < 16; i++)
1365       {
1366         output[0x601 + i*2] = (context->Palette[i].R & 0xf0) | (context->Palette[i].B >> 4);
1367         output[0x602 + i*2] = context->Palette[i].G >> 4;
1368       }
1369     }
1370     else
1371     {
1372       memcpy(output + exec_address - load_address, cpc_scr_simple_loader, sizeof(cpc_scr_simple_loader));
1373       output[0xd7d0 - load_address] = cpc_mode;
1374       //memcpy(output + 0xd7d1 - load_address, cpc_hw_pal, 16);
1375       for (i = 0; i < 16; i++)
1376       {
1377         output[0xd7d1 - load_address + i] = (context->Palette[i].G / 86) * 9 + (context->Palette[i].R / 86) * 3 + (context->Palette[i].B / 86);
1378       }
1379     }
1380     if (!Write_bytes(file, output, outsize))
1381       File_error = 1;
1382     fclose(file);
1383     if (!overscan)
1384     {
1385       file = Open_file_write_with_alternate_ext(context, "BAS");
1386       if (file != NULL)
1387       {
1388         byte buffer[128];
1389         memset(buffer, 0, sizeof(buffer));
1390         buffer[2] = 10;  // basic line number
1391         buffer[4] = 0xad; // MODE
1392         buffer[5] = ' ';
1393         buffer[6] = 0x0e + cpc_mode;
1394         buffer[7] = 0x01; // :
1395         buffer[8] = 0xa8; // LOAD
1396         buffer[9] = '"';
1397         for (i = 0; i < 8; i++)
1398         {
1399           if (context->File_name[i] == '\0' || context->File_name[i] == '.')
1400             break;
1401           buffer[10+i] = (byte)toupper(context->File_name[i]);
1402         }
1403         memcpy(buffer + 10 + i, ".SCR\"", 5);
1404         i += 15;
1405         buffer[i++] = 0x01; // :
1406         buffer[i++] = 0x83; // CALL
1407         buffer[i++] = 0x1c; // &
1408         buffer[i++] = 0xd0;
1409         buffer[i++] = 0xc7;
1410         buffer[i++] = 0x00;
1411         buffer[0] = (byte)i; // length of line data
1412         CPC_write_AMSDOS_header(file, context->File_name, "BAS", 0, 0x170, 0, i + 2);
1413         if (!Write_bytes(file, buffer, 128))
1414           File_error = 1;
1415         fclose(file);
1416       }
1417     }
1418   }
1419   free (output);
1420 }
1421 
1422 /**
1423  * Test for GO1/GO2/KIT - Amstrad Plus Graphos
1424  *
1425  * This format is made of 3 files
1426  * .KIT hold the palette in "Kit4096" format. There are 16 colors each stored
1427  * as 12 bit RGB in RB0G order.
1428  * .GO1 and GO2 hold each half of the picture (top and bottom)
1429  * The file always cover the whole display of the Plus (192*272 mode 0 pixels)
1430  * Only mode 0 is possible.
1431  */
Test_GOS(T_IO_Context * context,FILE * file)1432 void Test_GOS(T_IO_Context * context, FILE * file)
1433 {
1434   FILE *file_oddeve;
1435   unsigned long file_size = 0;
1436 
1437   if (!CPC_check_AMSDOS(file, NULL, NULL, &file_size))
1438     file_size = File_length_file(file);
1439   if (file_size < 16383 || file_size > 16384) {
1440     File_error = 1;
1441     return;
1442   }
1443 
1444   file_oddeve = Open_file_read_with_alternate_ext(context, "GO2");
1445   if (file_oddeve == NULL) {
1446     File_error = 2;
1447     return;
1448   }
1449   if (!CPC_check_AMSDOS(file_oddeve, NULL, NULL, &file_size))
1450     file_size = File_length_file(file_oddeve);
1451   fclose(file_oddeve);
1452   if (file_size < 16383 || file_size > 16384) {
1453     File_error = 3;
1454     return;
1455   }
1456 
1457   File_error = 0;
1458 }
1459 
1460 
1461 /**
1462  * Load GO1/GO2/KIT - Amstrad CPC Plus Graphos
1463  */
Load_GOS(T_IO_Context * context)1464 void Load_GOS(T_IO_Context* context)
1465 {
1466   FILE *file;
1467   unsigned long file_size;
1468   int i;
1469   int x, y;
1470   byte * pixel_data;
1471 
1472   if (!(file = Open_file_read(context)))
1473   {
1474       File_error = 1;
1475       return;
1476   }
1477 
1478   if (CPC_check_AMSDOS(file, NULL, NULL, &file_size))
1479     fseek(file, 128, SEEK_SET); // right after AMSDOS header
1480   else
1481     file_size = File_length_file(file);
1482 
1483   Pre_load(context, 192, 272, file_size, FORMAT_GOS, PIXEL_WIDE, 4);
1484 
1485   // load pixels
1486   pixel_data = GFX2_malloc(16384);
1487   memset(pixel_data, 0, 16384);
1488   Read_bytes(file, pixel_data, file_size);
1489 
1490   i = 0;
1491   for (y = 0; y < 168; y++) {
1492     x = 0;
1493     while (x < 192) {
1494       byte pixels = pixel_data[i];
1495       Set_pixel(context, x++, y, (pixels & 0x80) >> 7 | (pixels & 0x08) >> 2 | (pixels & 0x20) >> 3 | (pixels & 0x02) << 2);
1496       Set_pixel(context, x++, y, (pixels & 0x40) >> 6 | (pixels & 0x04) >> 1 | (pixels & 0x10) >> 2 | (pixels & 0x01) << 3);
1497       i++;
1498     }
1499 
1500     i += 0x800;
1501     if (i > 0x3FFF) {
1502       i -= 0x4000;
1503     } else {
1504       i -= 192 / 2;
1505     }
1506   }
1507 
1508   fclose(file);
1509 
1510   // load pixels from GO2
1511   file = Open_file_read_with_alternate_ext(context, "GO2");
1512   if (CPC_check_AMSDOS(file, NULL, NULL, &file_size))
1513     fseek(file, 128, SEEK_SET); // right after AMSDOS header
1514 
1515   Read_bytes(file, pixel_data, file_size);
1516   i = 0;
1517   for (y = 168; y < 272; y++) {
1518     x = 0;
1519     while (x < 192) {
1520       byte pixels = pixel_data[i];
1521       Set_pixel(context, x++, y, (pixels & 0x80) >> 7 | (pixels & 0x08) >> 2 | (pixels & 0x20) >> 3 | (pixels & 0x02) << 2);
1522       Set_pixel(context, x++, y, (pixels & 0x40) >> 6 | (pixels & 0x04) >> 1 | (pixels & 0x10) >> 2 | (pixels & 0x01) << 3);
1523       i++;
1524     }
1525 
1526     i += 0x800;
1527     if (i > 0x3FFF) {
1528       i -= 0x4000;
1529     } else {
1530       i -= 192 / 2;
1531     }
1532   }
1533 
1534   fclose(file);
1535   free(pixel_data);
1536 
1537   file = Open_file_read_with_alternate_ext(context, "KIT");
1538   if (file == NULL) {
1539     // There is no palette, but that's fine, we can still load the pixels
1540     // Setup a default grayscale palette
1541     for (i = 0; i < 16; i++)
1542     {
1543       context->Palette[i].R = i * 0x11;
1544       context->Palette[i].G = i * 0x11;
1545       context->Palette[i].B = i * 0x11;
1546     }
1547     return;
1548   }
1549 
1550   if (CPC_check_AMSDOS(file, NULL, NULL, &file_size)) {
1551     fseek(file, 128, SEEK_SET); // right after AMSDOS header
1552   } else {
1553     file_size = File_length_file(file);
1554   }
1555 
1556   if (Config.Clear_palette)
1557     memset(context->Palette,0,sizeof(T_Palette));
1558 
1559   File_error = 0;
1560 
1561   if (file_size == 32)
1562   {
1563     for (i = 0; i < 16; i++)
1564     {
1565       uint16_t word;
1566       if (!Read_word_le(file, &word))
1567       {
1568         File_error = 2;
1569         break;
1570       }
1571 
1572       context->Palette[i].R = ((word >>  4) & 0xF) * 0x11;
1573       context->Palette[i].G = ((word >>  8) & 0xF) * 0x11;
1574       context->Palette[i].B = ((word >>  0) & 0xF) * 0x11;
1575     }
1576   }
1577   else
1578   {
1579     // Setup the palette (amstrad hardware palette)
1580     CPC_set_HW_palette(context->Palette + 0x40);
1581     for (i = 0; i < 16; i++)
1582     {
1583       byte ink;
1584       if (!Read_byte(file, &ink))
1585       {
1586         File_error = 2;
1587         break;
1588       }
1589       context->Palette[i] = context->Palette[ink];
1590     }
1591   }
1592 
1593   fclose(file);
1594 }
1595 
1596 
Save_GOS(T_IO_Context * context)1597 void Save_GOS(T_IO_Context* context)
1598 {
1599   FILE* file;
1600   int i;
1601   unsigned char* output;
1602   unsigned long outsize = 0;
1603   unsigned char r1 = 0;
1604 
1605   // TODO check picture dimensions (GOS is a fixed resolution format)
1606   // For now, force the size
1607   context->Width = 192;
1608   context->Height = 168; // Convert the first half
1609 
1610   // convert and save page 1
1611   file = Open_file_write(context);
1612   if (file == NULL)
1613     return;
1614   output = raw2crtc(context, 0, 7, &outsize, &r1, 0, 0);
1615   File_error = 0;
1616   if (!Write_bytes(file, output, outsize))
1617     File_error = 1;
1618   // Pad to expected size
1619   GFX2_Log(GFX2_DEBUG, "written %lu bytes, padding to 16384\n", outsize);
1620   if (outsize >= 8192)
1621   {
1622     memset(output, 0, 16384 - outsize);
1623     if (!Write_bytes(file, output, 16384 - outsize))
1624       File_error = 1;
1625   }
1626   free(output);
1627   fclose(file);
1628   if (File_error)
1629     return;
1630 
1631   // convert and save page 2
1632   // Advance context to second half of picture
1633   context->Target_address += context->Pitch * 168;
1634   context->Height = 104;
1635   file = Open_file_write_with_alternate_ext(context, "GO2");
1636   if (file == NULL)
1637   {
1638     File_error = 1;
1639     return;
1640   }
1641   output = raw2crtc(context, 0, 7, &outsize, &r1, 0, 0);
1642   File_error = 0;
1643   if (!Write_bytes(file, output, outsize))
1644     File_error = 1;
1645   // Pad to expected size
1646   GFX2_Log(GFX2_DEBUG, "written %lu bytes, padding to 16384\n", outsize);
1647   if (outsize >= 8192)
1648   {
1649     memset(output, 0, 16384 - outsize);
1650     if (!Write_bytes(file, output, 16384 - outsize))
1651       File_error = 1;
1652   }
1653   free(output);
1654   fclose(file);
1655   if (File_error)
1656     return;
1657 
1658   file = Open_file_write_with_alternate_ext(context, "KIT");
1659   for (i = 0; i < 16 && File_error == 0; i++)
1660   {
1661     uint16_t word;
1662     word = (context->Palette[i].R & 0xF0)
1663            | ((context->Palette[i].G & 0xF0) << 4)
1664            | (context->Palette[i].B >> 4);
1665     if (!Write_word_le(file, word))
1666       File_error = 2;
1667   }
1668   fclose(file);
1669 }
1670 
1671 
1672 /**
1673  * Test for CM5 - Amstrad CPC "Mode 5" picture
1674  *
1675  * This is a format designed by SyX.
1676  * There is one .GFX file in the usual amstrad format
1677  * and a .CM5 file with the palette, which varies over time.
1678  *
1679  * CM5 file is 2049 bytes, GFX is 18432 bytes.
1680  *
1681  * @todo check CM5 contains only valid values [0x40-0x5f]
1682  */
Test_CM5(T_IO_Context * context,FILE * file)1683 void Test_CM5(T_IO_Context * context, FILE * file)
1684 {
1685   // check cm5 file size == 2049 bytes
1686   FILE *file_gfx;
1687   unsigned long file_size;
1688 
1689   File_error = 1;
1690 
1691   if (!CPC_check_AMSDOS(file, NULL, NULL, &file_size))
1692     file_size = File_length_file(file);
1693   if (file_size != 2049)
1694     return;
1695 
1696   // check existence of a .GFX file with the same name
1697   file_gfx = Open_file_read_with_alternate_ext(context, "gfx");
1698   if (file_gfx == NULL)
1699     return;
1700   if (!CPC_check_AMSDOS(file_gfx, NULL, NULL, &file_size))
1701     file_size = File_length_file(file_gfx);
1702   fclose(file_gfx);
1703   if (file_size != 18432)
1704     return;
1705 
1706   File_error = 0;
1707 }
1708 
1709 
1710 /**
1711  * Load Amstrad CPC "Mode 5" picture
1712  *
1713  * Only support 288x256 resolution as the Mode 5 Viewer app only handles this
1714  * single resolution.
1715  * @see https://www.cpc-power.com/index.php?page=detail&num=12905
1716  * @see https://github.com/cpcsdk/cpctools/blob/master/resources/mode5_viewer/fx_mode5.s
1717  */
Load_CM5(T_IO_Context * context)1718 void Load_CM5(T_IO_Context* context)
1719 {
1720   // Ensure "8bit" constraint mode is switched on
1721   // Set palette to the CPC hardware colors
1722   // Load the palette data to the 4 colorlayers
1723   FILE *file;
1724   byte value = 0;
1725   int mod=0;
1726   short line = 0;
1727   int tx, ty;
1728   // for preview :
1729   byte ink0;
1730   byte ink1[256];
1731   byte ink2[256];
1732   byte ink3[256*6];
1733 
1734   if (!(file = Open_file_read(context)))
1735   {
1736       File_error = 1;
1737       return;
1738   }
1739 
1740   Pre_load(context, 48*6, 256, 2049, FORMAT_CM5, PIXEL_SIMPLE, 5);
1741 
1742   if (Config.Clear_palette)
1743   {
1744     memset(context->Palette,0,sizeof(T_Palette));
1745     // setup colors 0,1,2,3 to see something in the thumbnail preview of layer 5
1746     context->Palette[1].R = 60;
1747     context->Palette[2].B = 60;
1748     context->Palette[3].G = 60;
1749   }
1750 
1751   // Setup the palette (amstrad hardware palette)
1752   CPC_set_HW_palette(context->Palette + 0x40);
1753 
1754   First_color_in_palette = 64;
1755 
1756   if (CPC_check_AMSDOS(file, NULL, NULL, NULL))
1757     fseek(file, 128, SEEK_SET); // seek after AMSDOS header
1758   if (!Read_byte(file, &ink0))
1759     File_error = 2;
1760 
1761   // This forces the creation of 5 layers total :
1762   // Needed because the "pixel" functions will seek layer 4
1763   Set_loading_layer(context, 4);
1764   // Now select layer 1 again
1765   Set_loading_layer(context, 0);
1766 
1767   if (context->Type == CONTEXT_MAIN_IMAGE)
1768   {
1769     Set_image_mode(context, IMAGE_MODE_MODE5);
1770 
1771     // Fill layer with color we just read (Layer 1 - INK 0)
1772     for(ty=0; ty<context->Height; ty++)
1773       for(tx=0; tx<context->Width; tx++)
1774         Set_pixel(context, tx, ty, ink0);
1775   }
1776 
1777   for (line = 0; line < 256; )
1778   {
1779     if (!Read_byte(file, &value))
1780     {
1781       File_error = 1;
1782       break;
1783     }
1784     switch(mod)
1785     {
1786       case 0:
1787         // This is color for layer 2 - INK 1
1788         Set_loading_layer(context, 1);
1789         for(tx=0; tx<context->Width; tx++)
1790           Set_pixel(context, tx, line, value);
1791         ink1[line] = value;
1792         break;
1793       case 1:
1794         // This is color for layer 3 - INK 2
1795         Set_loading_layer(context, 2);
1796         for(tx=0; tx<context->Width; tx++)
1797           Set_pixel(context, tx, line, value);
1798         ink2[line] = value;
1799         break;
1800       default:
1801         // This is color for a block in layer 4 - INK 3
1802         Set_loading_layer(context, 3);
1803         for(tx=(mod-2)*48; tx<(mod-1)*48; tx++)
1804           Set_pixel(context, tx, line, value);
1805         ink3[line*6+(mod-2)] = value;
1806         break;
1807     }
1808     mod++;
1809     if (mod > 7)
1810     {
1811       mod = 0;
1812       line++;
1813     }
1814   }
1815 
1816   fclose(file);
1817 
1818   // Load the pixeldata to the 5th layer
1819   file = Open_file_read_with_alternate_ext(context, "gfx");
1820   if (file == NULL)
1821   {
1822     File_error = 1;
1823     return;
1824   }
1825   if (CPC_check_AMSDOS(file, NULL, NULL, NULL))
1826     fseek(file, 128, SEEK_SET); // seek after AMSDOS header
1827   Set_loading_layer(context, 4);
1828 
1829   if (context->Type == CONTEXT_PREVIEW)
1830     for (ty = 0; ty < 256; ty++)
1831       for (tx = 0; tx < 48*6; )
1832       {
1833         Read_byte(file, &value);
1834         for (mod = 0; mod < 4; mod++, tx++, value <<= 1)
1835         {
1836           switch(3 ^ (((value&0x80) >> 7) | ((value&0x8)>>2)))  // INK
1837           {
1838             case 0:
1839               Set_pixel(context, tx, ty, ink0);
1840               break;
1841             case 1:
1842               Set_pixel(context, tx, ty, ink1[ty]);
1843               break;
1844             case 2:
1845               Set_pixel(context, tx, ty, ink2[ty]);
1846               break;
1847             default:
1848               Set_pixel(context, tx, ty, ink3[ty*6+(tx/48)]);
1849           }
1850         }
1851       }
1852   else
1853     for (ty = 0; ty < 256; ty++)
1854       for (tx = 0; tx < 48*6; )
1855       {
1856         Read_byte(file, &value);
1857         Set_pixel(context, tx++, ty, 3 ^ (((value&0x80) >> 7) | ((value&0x8)>>2)));
1858         Set_pixel(context, tx++, ty, 3 ^ (((value&0x40) >> 6) | ((value&0x4)>>1)));
1859         Set_pixel(context, tx++, ty, 3 ^ (((value&0x20) >> 5) | ((value&0x2)>>0)));
1860         Set_pixel(context, tx++, ty, 3 ^ (((value&0x10) >> 4) | ((value&0x1)<<1)));
1861       }
1862 
1863   fclose(file);
1864 
1865 }
1866 
1867 /**
1868  * Save a CPC Mode 5 picture.
1869  * Resolution is fixed to 288x256.
1870  * The pictures uses 5 layers. 4 for defining the "inks"
1871  * the 5th to select one of the 4 inks.
1872  *
1873  * - Layer 1 : 1 color Only
1874  * - Layer 2 and 3 : 1 color/line
1875  * - Layer 4 : 1 color / 48x1 block
1876  * - Layer 5 : CPC mode 2 288x256 picture.
1877  *
1878  * The .CM5 file contains the inks from layers 1-4,
1879  * the .GFX file contains the pixel data in linear fashion
1880  * @todo Check picture has 5 layers
1881  * @todo Check the constraints on the layers
1882  * @see https://www.cpc-power.com/index.php?page=detail&num=12905
1883  */
Save_CM5(T_IO_Context * context)1884 void Save_CM5(T_IO_Context* context)
1885 {
1886   FILE* file;
1887   int tx, ty;
1888 
1889   if (!(file = Open_file_write(context)))
1890   {
1891     File_error = 1;
1892     return;
1893   }
1894 
1895   // Write layer 0
1896   Set_saving_layer(context, 0);
1897   Write_byte(file, Get_pixel(context, 0, 0));
1898   for(ty = 0; ty < 256; ty++)
1899   {
1900     Set_saving_layer(context, 1);
1901     Write_byte(file, Get_pixel(context, 0, ty));
1902     Set_saving_layer(context, 2);
1903     Write_byte(file, Get_pixel(context, 0, ty));
1904     Set_saving_layer(context, 3);
1905     for(tx = 0; tx < 6; tx++)
1906     {
1907       Write_byte(file, Get_pixel(context, tx*48, ty));
1908     }
1909   }
1910 
1911   fclose(file);
1912 
1913   // Now the pixeldata
1914   if (!(file = Open_file_write_with_alternate_ext(context, "gfx")))
1915   {
1916     File_error = 2;
1917     return;
1918   }
1919 
1920   Set_saving_layer(context, 4);
1921 
1922   for (ty = 0; ty < 256; ty++)
1923   {
1924     for (tx = 0; tx < 48*6; tx+=4)
1925     {
1926       byte code = 0;
1927       byte pixel;
1928 
1929       pixel = 3-Get_pixel(context, tx+3, ty);
1930       code |= (pixel&2)>>1 | ((pixel & 1)<<4);
1931       pixel = 3-Get_pixel(context, tx+2, ty);
1932       code |= ((pixel&2)<<0) | ((pixel & 1)<<5);
1933       pixel = 3-Get_pixel(context, tx+1, ty);
1934       code |= ((pixel&2)<<1) | ((pixel & 1)<<6);
1935       pixel = 3-Get_pixel(context, tx, ty);
1936       code |= ((pixel&2)<<2) | ((pixel & 1)<<7);
1937       Write_byte(file, code);
1938     }
1939   }
1940 
1941   fclose(file);
1942   File_error = 0;
1943 
1944 }
1945 
1946 
1947 /**
1948  * Amstrad CPC 'PPH' for Perfect Pix.
1949  * This is a format designed by Rhino.
1950  * There are 3 modes:
1951  * - Mode 'R': 1:1 pixels, 16 colors from the CPC 27 color palette.
1952  *   (this is implemented on CPC as two pictures with wide pixels, the "odd" one
1953  *   being shifted half a pixel to the right), and flipping)
1954  * - Mode 'B0': wide pixels, up to 126 out of 378 colors.
1955  *   (this is implemented as two pictures with wide pixels, sharing the same 16
1956  *   color palette, and flipping)
1957  * - Mode 'B1': 1:1 pixels, 1 fixed color, up to 34 palettes of 9 colors
1958  *   (actually 4 colors + flipping)
1959  *
1960  * - The standard CPC formats can also be encapsulated into a PPH file.
1961  *
1962  * @see http://www.pouet.net/prod.php?which=67770#c766959
1963  */
Test_PPH(T_IO_Context * context,FILE * file)1964 void Test_PPH(T_IO_Context * context, FILE * file)
1965 {
1966   FILE *file_oddeve;
1967   byte buffer[6];
1968   unsigned long file_size;
1969   unsigned int w, h;
1970   unsigned int expected;
1971 
1972   File_error = 1;
1973 
1974   // First check file size is large enough to hold the header
1975   file_size = File_length_file(file);
1976   if (file_size < 11) {
1977     File_error = 1;
1978     return;
1979   }
1980 
1981   // File is large enough for the header, now check if the data makes some sense
1982   if (!Read_bytes(file, buffer, 6))
1983     return;
1984   if (buffer[0] > 5) {
1985     // Unknown mode
1986     File_error = 2;
1987     return;
1988   }
1989 
1990   w = buffer[1] | (buffer[2] << 8);
1991   if (w < 2 || w > 384) {
1992     // Invalid width
1993     File_error = 3;
1994     return;
1995   }
1996 
1997   h = buffer[3] | (buffer[4] << 8);
1998   if (h < 1 || h > 272) {
1999     // Invalid height
2000     File_error = 4;
2001     return;
2002   }
2003 
2004   if (buffer[5] < 1 || buffer[5] > 28)
2005   {
2006     // Invalid palettes count
2007     File_error = 5;
2008     return;
2009   }
2010   expected = 6; // Size of header
2011   switch(buffer[0])
2012   {
2013     case 0:
2014     case 3:
2015     case 4:
2016       // Palette size should be 16 bytes, only 1 palette.
2017       if (buffer[5] != 1) {
2018         File_error = 7;
2019         return;
2020       }
2021       expected += 16;
2022       break;
2023 
2024     case 1:
2025     case 5:
2026       expected += buffer[5] * 5 - 1;
2027       break;
2028 
2029     case 2:
2030       // Palette size should be 2 bytes
2031       if (buffer[5] != 1) {
2032         File_error = 7;
2033         return;
2034       }
2035       expected += 2;
2036       break;
2037   }
2038 
2039   if (file_size != expected)
2040   {
2041     File_error = 6;
2042     return;
2043   }
2044 
2045   // check existence of .ODD/.EVE files with the same name
2046   // and the right size
2047   expected = w * h / 4;
2048   file_oddeve = Open_file_read_with_alternate_ext(context, "odd");
2049   if (file_oddeve == NULL)
2050     return;
2051   file_size = File_length_file(file_oddeve);
2052   fclose (file_oddeve);
2053   if (file_size != expected)
2054   {
2055     File_error = 8;
2056     return;
2057   }
2058   file_oddeve = Open_file_read_with_alternate_ext(context, "eve");
2059   if (file_oddeve == NULL)
2060     return;
2061   file_size = File_length_file(file_oddeve);
2062   fclose(file_oddeve);
2063   if (file_size != expected)
2064   {
2065     File_error = 8;
2066     return;
2067   }
2068   File_error = 0;
2069 }
2070 
2071 
pph_blend(uint8_t a,uint8_t b)2072 static uint8_t pph_blend(uint8_t a, uint8_t b)
2073 {
2074 	uint32_t h,l;
2075 	if (a > b) { h = a; l = b; }
2076 	else       { h = b; l = a; }
2077 
2078 	return (23 * h + 9 * l) / 32;
2079 }
2080 
2081 
Load_PPH(T_IO_Context * context)2082 void Load_PPH(T_IO_Context* context)
2083 {
2084   FILE *file;
2085   FILE *feven;
2086 
2087   // Read in the header
2088   uint8_t mode;
2089   uint16_t width;
2090   uint16_t height;
2091   uint8_t npal;
2092   int i,j;
2093   uint8_t a,b,c,d;
2094   int file_size;
2095   uint8_t pl[16];
2096 
2097   static const T_Components CPCPAL[27] =
2098   {
2099       { 0x00, 0x02, 0x01 }, { 0x00, 0x02, 0x6B }, { 0x0C, 0x02, 0xF4 },
2100       { 0x6C, 0x02, 0x01 }, { 0x69, 0x02, 0x68 }, { 0x6C, 0x02, 0xF2 },
2101       { 0xF3, 0x05, 0x06 }, { 0xF0, 0x02, 0x68 }, { 0xF3, 0x02, 0xF4 },
2102       { 0x02, 0x78, 0x01 }, { 0x00, 0x78, 0x68 }, { 0x0C, 0x7B, 0xF4 },
2103       { 0x6E, 0x7B, 0x01 }, { 0x6E, 0x7D, 0x6B }, { 0x6E, 0x7B, 0xF6 },
2104       { 0xF3, 0x7D, 0x0D }, { 0xF3, 0x7D, 0x6B }, { 0xFA, 0x80, 0xF9 },
2105       { 0x02, 0xF0, 0x01 }, { 0x00, 0xF3, 0x6B }, { 0x0F, 0xF3, 0xF2 },
2106       { 0x71, 0xF5, 0x04 }, { 0x71, 0xF3, 0x6B }, { 0x71, 0xF3, 0xF4 },
2107       { 0xF3, 0xF3, 0x0D }, { 0xF3, 0xF3, 0x6D }, { 0xFF, 0xF3, 0xF9 }
2108   };
2109 
2110   if (!(file = Open_file_read(context)))
2111   {
2112       File_error = 1;
2113       return;
2114   }
2115 
2116   file_size=File_length_file(file);
2117 
2118   Read_byte(file, &mode);
2119   Read_word_le(file, &width);
2120   Read_word_le(file, &height);
2121   Read_byte(file, &npal);
2122 
2123   if (npal > 16)
2124       npal = 16;
2125 
2126   // Switch to the proper aspect ratio
2127   switch (mode)
2128   {
2129       case 0:
2130       case 4:
2131         context->Ratio = PIXEL_WIDE;
2132         width /= 2;
2133         break;
2134 
2135       case 2:
2136         context->Ratio = PIXEL_TALL;
2137         break;
2138 
2139       case 1:
2140       case 5:
2141       case 3:
2142         context->Ratio = PIXEL_SIMPLE;
2143         break;
2144   }
2145 
2146   Pre_load(context, width, height, file_size, FORMAT_PPH, context->Ratio, 0);
2147 
2148   // First of all, detect the mode
2149   // 0, 1, 2 > Load as with SCR files?
2150   // R(3)    > Load as single layer, square pixels, 16 colors
2151   // B0(4)   > Load as single layer, wide pixels, expand palette with colorcycling
2152   // B1(5)   > Load as ???
2153   //           Maybe special mode similar to mode5, with 2 layers + auto-flicker?
2154 
2155   switch (mode)
2156   {
2157       case 0:
2158       case 3: // R
2159           // 16-color palette
2160           for (i = 0; i < 16; i++)
2161           {
2162               uint8_t color;
2163               Read_byte(file, &color);
2164               context->Palette[i] = CPCPAL[color];
2165           }
2166           break;
2167 
2168       case 1:
2169       case 5: // B1
2170       {
2171           // Single or multiple 4-color palettes
2172           uint8_t base[4];
2173           for (j = 0; j < npal; j++)
2174           {
2175             for (i = 0; i < 4; i++)
2176             {
2177               Read_byte(file,&base[i]);
2178             }
2179             for (i = 0; i < 16; i++)
2180             {
2181               context->Palette[i + 16*j].R = pph_blend(
2182                   CPCPAL[base[i & 3]].R, CPCPAL[base[i >> 2]].R);
2183               context->Palette[i + 16*j].G = pph_blend(
2184                   CPCPAL[base[i & 3]].G, CPCPAL[base[i >> 2]].G);
2185               context->Palette[i + 16*j].B = pph_blend(
2186                   CPCPAL[base[i & 3]].B, CPCPAL[base[i >> 2]].B);
2187             }
2188             // TODO this byte marks where this palette stops being used and the
2189             // next starts. We must handle this!
2190             Read_byte(file,&pl[j]);
2191           }
2192           pl[npal - 1] = 255;
2193           break;
2194       }
2195 
2196       case 2:
2197           // Single 2-color palette
2198           break;
2199 
2200       case 4: // B0
2201       {
2202           // Single 16-color palette + flipping, need to expand palette and
2203           // setup colorcycling ranges.
2204           uint8_t base[16];
2205           for (i = 0; i < 16; i++)
2206           {
2207               Read_byte(file,&base[i]);
2208           }
2209 
2210           for (i = 0; i < 256; i++)
2211           {
2212               context->Palette[i].R = pph_blend(
2213                   CPCPAL[base[i & 15]].R, CPCPAL[base[i >> 4]].R);
2214               context->Palette[i].G = pph_blend(
2215                   CPCPAL[base[i & 15]].G, CPCPAL[base[i >> 4]].G);
2216               context->Palette[i].B = pph_blend(
2217                   CPCPAL[base[i & 15]].B, CPCPAL[base[i >> 4]].B);
2218           }
2219       }
2220       break;
2221   }
2222 
2223   fclose(file);
2224 
2225   // Load the picture data
2226   // There are two pages, each storing bytes in the CPC vram format but lines in
2227   // linear order.
2228   file = Open_file_read_with_alternate_ext(context, "odd");
2229   if (file == NULL)
2230   {
2231     File_error = 3;
2232     return;
2233   }
2234   feven = Open_file_read_with_alternate_ext(context, "eve");
2235   if (feven == NULL)
2236   {
2237     File_error = 4;
2238     fclose(file);
2239     return;
2240   }
2241 
2242   c = 0;
2243   d = 0;
2244 
2245   for (j = 0; j < height; j++)
2246   {
2247       for (i = 0; i < width;)
2248       {
2249           uint8_t even, odd;
2250           Read_byte(feven, &even);
2251           Read_byte(file, &odd);
2252 
2253           switch (mode)
2254           {
2255               case 4:
2256                 a = ((even & 0x02) << 2) | ((even & 0x08) >> 2)
2257                   | ((even & 0x20) >> 3) | ((even & 0x80) >> 7);
2258                 a <<= 4;
2259                 a |= ((odd & 0x02) << 2) | (( odd & 0x08) >> 2)
2260                   | (( odd & 0x20) >> 3) | (( odd & 0x80) >> 7);
2261 
2262                 b = ((even & 0x01) << 3) | ((even & 0x04) >> 1)
2263                   | ((even & 0x10) >> 2) | ((even & 0x40) >> 6);
2264                 b <<= 4;
2265                 b |= ((odd & 0x01) << 3) | (( odd & 0x04) >> 1)
2266                   | (( odd & 0x10) >> 2) | (( odd & 0x40) >> 6);
2267 
2268                 Set_pixel(context, i++, j, a);
2269                 Set_pixel(context, i++, j, b);
2270                 break;
2271 
2272               case 3:
2273                 a = ((even & 0x02) << 2) | ((even & 0x08) >> 2)
2274                   | ((even & 0x20) >> 3) | ((even & 0x80) >> 7);
2275                 b = (( odd & 0x02) << 2) | (( odd & 0x08) >> 2)
2276                   | (( odd & 0x20) >> 3) | (( odd & 0x80) >> 7);
2277                 c = ((even & 0x01) << 3) | ((even & 0x04) >> 1)
2278                   | ((even & 0x10) >> 2) | ((even & 0x40) >> 6);
2279                 d = (( odd & 0x01) << 3) | (( odd & 0x04) >> 1)
2280                   | (( odd & 0x10) >> 2) | (( odd & 0x40) >> 6);
2281                 Set_pixel(context, i++, j, j & 1 ? b : a);
2282                 Set_pixel(context, i++, j, j & 1 ? a : b);
2283                 Set_pixel(context, i++, j, j & 1 ? d : c);
2284                 Set_pixel(context, i++, j, j & 1 ? c : d);
2285                 break;
2286 
2287               case 5:
2288                 if (d >= pl[c])
2289                 {
2290                     d = 0;
2291                     c++;
2292                 }
2293                 a = ((even & 0x80) >> 6) | ((even & 0x08) >> 3);
2294                 b = (( odd & 0x80) >> 6) | (( odd & 0x08) >> 3);
2295                 Set_pixel(context, i++, j, a + (b << 2) + c * 16);
2296                 a = ((even & 0x40) >> 5) | ((even & 0x04) >> 2);
2297                 b = (( odd & 0x40) >> 5) | (( odd & 0x04) >> 2);
2298                 Set_pixel(context, i++, j, a + (b << 2) + c * 16);
2299                 a = ((even & 0x20) >> 4) | ((even & 0x02) >> 1);
2300                 b = (( odd & 0x20) >> 4) | (( odd & 0x02) >> 1);
2301                 Set_pixel(context, i++, j, a + (b << 2) + c * 16);
2302                 a = ((even & 0x10) >> 3) | ((even & 0x01) >> 0);
2303                 b = (( odd & 0x10) >> 3) | (( odd & 0x01) >> 0);
2304                 Set_pixel(context, i++, j, a + (b << 2) + c * 16);
2305 
2306                 break;
2307 
2308               default:
2309                 File_error = 2;
2310                 return;
2311           }
2312 
2313       }
2314       d++;
2315   }
2316   fclose(file);
2317   fclose(feven);
2318 
2319   File_error = 0;
2320 }
2321 
2322 /**
2323  * Not yet implemented
2324  */
Save_PPH(T_IO_Context * context)2325 void Save_PPH(T_IO_Context* context)
2326 {
2327   (void)context; // unused
2328     // TODO
2329 
2330     // Detect mode
2331     // Wide pixels => B0 (4)
2332     // Square pixels:
2333     // - 16 colors used => R
2334     // - more colors used => B1 (if <16 colors per line)
2335 
2336     // Check palette
2337     // B0: use diagonal: 0, 17, 34, ... (assume the other are mixes)
2338     // R: use 16 used colors (or 16 first?)
2339     // B1: find the 16 colors used in a line? Or assume they are in-order already?
2340 }
2341 
2342 /** @} */
2343