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