1 /* zxs.c: Routines for .zxs snapshots
2    Copyright (c) 1998,2003 Philip Kendall
3 
4    $Id: zxs.c 3698 2008-06-30 15:12:02Z pak21 $
5 
6    This program is free software; you can redistribute it and/or modify
7    it under the terms of the GNU General Public License as published by
8    the Free Software Foundation; either version 2 of the License, or
9    (at your option) any later version.
10 
11    This program is distributed in the hope that it will be useful,
12    but WITHOUT ANY WARRANTY; without even the implied warranty of
13    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
14    GNU General Public License for more details.
15 
16    You should have received a copy of the GNU General Public License along
17    with this program; if not, write to the Free Software Foundation, Inc.,
18    51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
19 
20    Author contact information:
21 
22    E-mail: philip-fuse@shadowmagic.org.uk
23 
24 */
25 
26 #include <config.h>
27 
28 #include <string.h>
29 
30 #ifdef HAVE_ZLIB_H
31 #include <zlib.h>
32 #endif				/* #ifdef HAVE_ZLIB_H */
33 
34 #include "internals.h"
35 
36 static libspectrum_error
37 read_chunk( libspectrum_snap *snap, const libspectrum_byte **buffer,
38 	    const libspectrum_byte *end );
39 
40 typedef libspectrum_error (*read_chunk_fn)( libspectrum_snap *snap,
41 					    int *compression,
42 					    const libspectrum_byte **buffer,
43 					    const libspectrum_byte *end,
44 					    size_t data_length,
45 					    int parameter );
46 
47 static libspectrum_error
inflate_block(libspectrum_byte ** uncompressed,size_t * uncompressed_length,const libspectrum_byte ** compressed,size_t compressed_length)48 inflate_block( libspectrum_byte **uncompressed, size_t *uncompressed_length,
49 	       const libspectrum_byte **compressed, size_t compressed_length )
50 {
51 
52 #ifdef HAVE_ZLIB_H
53 
54   libspectrum_dword header_length, expected_crc32, actual_crc32;
55   libspectrum_byte *zlib_buffer;
56   unsigned long actual_length;
57   int error;
58 
59   /* First, look at the compression header */
60   header_length = libspectrum_read_dword( compressed );
61   if( header_length != 12 ) {
62     libspectrum_print_error( LIBSPECTRUM_ERROR_UNKNOWN,
63 			     "zxs_inflate_block: unknown header length %lu",
64 			     (unsigned long)header_length );
65     return LIBSPECTRUM_ERROR_UNKNOWN;
66   }
67   compressed_length -= 12;
68 
69   expected_crc32 = libspectrum_read_dword( compressed );
70   *uncompressed_length = libspectrum_read_dword( compressed );
71 
72   /* Some space to put the zlib header for decompression */
73   zlib_buffer =
74     libspectrum_malloc( ( compressed_length + 6 ) * sizeof( *zlib_buffer ) );
75 
76   /* zlib's header */
77   zlib_buffer[0] = 0x78; zlib_buffer[1] = 0xda;
78 
79   memcpy( &zlib_buffer[2], *compressed, compressed_length );
80   *compressed += compressed_length;
81 
82   *uncompressed = libspectrum_malloc( *uncompressed_length * sizeof( **uncompressed ) );
83 
84   actual_length = *uncompressed_length;
85   error = uncompress( *uncompressed, &actual_length, zlib_buffer,
86 		      compressed_length + 6 );
87 
88   /* At this point, we expect to get a Z_DATA_ERROR, as we don't have
89      the Adler-32 checksum of the data. There is a 1 in 65521 chance
90      that the random bytes will match, so we might (very rarely) get
91      Z_OK */
92   if( error != Z_DATA_ERROR && error != Z_OK ) {
93     libspectrum_free( *uncompressed ); libspectrum_free( zlib_buffer );
94     libspectrum_print_error( LIBSPECTRUM_ERROR_CORRUPT,
95 			     "zxs_inflate_block: unexpected zlib error" );
96     return LIBSPECTRUM_ERROR_CORRUPT;
97   }
98 
99   if( *uncompressed_length != actual_length ) {
100     libspectrum_free( *uncompressed ); libspectrum_free( zlib_buffer );
101     libspectrum_print_error(
102       LIBSPECTRUM_ERROR_CORRUPT,
103       "zxs_inflate_block: block expanded to 0x%04lx, not the expected 0x%04lx bytes",
104       actual_length, (unsigned long)*uncompressed_length
105     );
106     return LIBSPECTRUM_ERROR_CORRUPT;
107   }
108 
109   libspectrum_free( zlib_buffer );
110 
111   actual_crc32 = crc32( 0, Z_NULL, 0 );
112   actual_crc32 = crc32( actual_crc32, *uncompressed, *uncompressed_length );
113 
114   if( actual_crc32 != expected_crc32 ) {
115     libspectrum_free( *uncompressed );
116     libspectrum_print_error(
117       LIBSPECTRUM_ERROR_CORRUPT,
118       "zxs_inflate_block: crc 0x%08x does not match expected 0x%08x",
119       actual_crc32, expected_crc32
120     );
121     return LIBSPECTRUM_ERROR_CORRUPT;
122   }
123 
124   return LIBSPECTRUM_ERROR_NONE;
125 
126 #else				/* #ifdef HAVE_ZLIB_H */
127 
128   /* No zlib, so can't inflate the block */
129   return LIBSPECTRUM_ERROR_UNKNOWN;
130 
131 #endif				/* #ifdef HAVE_ZLIB_H */
132 
133 }
134 
135 static libspectrum_error
read_riff_chunk(libspectrum_snap * snap,int * compression GCC_UNUSED,const libspectrum_byte ** buffer,const libspectrum_byte * end,size_t data_length GCC_UNUSED,int parameter GCC_UNUSED)136 read_riff_chunk( libspectrum_snap *snap, int *compression GCC_UNUSED,
137 		 const libspectrum_byte **buffer, const libspectrum_byte *end,
138 		 size_t data_length GCC_UNUSED, int parameter GCC_UNUSED)
139 {
140   char id[5];
141   libspectrum_error error;
142 
143   if( end - *buffer < 4 ) {
144     libspectrum_print_error(
145       LIBSPECTRUM_ERROR_CORRUPT,
146       "zxs_read_riff_chunk: not enough data for form type"
147     );
148     return LIBSPECTRUM_ERROR_CORRUPT;
149   }
150 
151   memcpy( id, *buffer, 4 ); id[4] = '\0'; *buffer += 4;
152 
153   if( strcmp( id, "SNAP" ) ) {
154     libspectrum_print_error( LIBSPECTRUM_ERROR_UNKNOWN,
155 			     "zxs_read_riff_chunk: unknown form type '%s'",
156 			     id );
157     return LIBSPECTRUM_ERROR_UNKNOWN;
158   }
159 
160   while( *buffer < end ) {
161     error = read_chunk( snap, buffer, end );
162     if( error ) return error;
163   }
164 
165   return LIBSPECTRUM_ERROR_NONE;
166 }
167 
168 static libspectrum_error
read_fmtz_chunk(libspectrum_snap * snap,int * compression,const libspectrum_byte ** buffer,const libspectrum_byte * end GCC_UNUSED,size_t data_length,int parameter GCC_UNUSED)169 read_fmtz_chunk( libspectrum_snap *snap, int *compression,
170 		 const libspectrum_byte **buffer,
171 		 const libspectrum_byte *end GCC_UNUSED,
172 		 size_t data_length, int parameter GCC_UNUSED )
173 {
174   libspectrum_word model;
175 
176   if( data_length != 8 ) {
177     libspectrum_print_error( LIBSPECTRUM_ERROR_UNKNOWN,
178 			     "zxs_read_fmtz_chunk: unknown length %lu",
179 			     (unsigned long)data_length );
180     return LIBSPECTRUM_ERROR_UNKNOWN;
181   }
182 
183   *buffer += 2;			/* Skip version number */
184 
185   model = libspectrum_read_word( buffer );
186 
187   switch( model ) {
188 
189   case 0x0010:  case 0x0020:
190     libspectrum_snap_set_machine( snap, LIBSPECTRUM_MACHINE_48 );
191     break;
192 
193   case 0x0030:
194     libspectrum_snap_set_machine( snap, LIBSPECTRUM_MACHINE_128 );
195     break;
196 
197   case 0x0040:
198     libspectrum_snap_set_machine( snap, LIBSPECTRUM_MACHINE_PLUS2 );
199     break;
200 
201   case 0x0050:
202     libspectrum_snap_set_machine( snap, LIBSPECTRUM_MACHINE_PLUS2A );
203     break;
204 
205   case 0x0060:
206     libspectrum_snap_set_machine( snap, LIBSPECTRUM_MACHINE_PLUS3 );
207     break;
208 
209   default:
210     libspectrum_print_error(
211       LIBSPECTRUM_ERROR_UNKNOWN,
212       "zxs_read_fmtz_chunk: unknown machine type 0x%04x", model
213     );
214     return LIBSPECTRUM_ERROR_UNKNOWN;
215   }
216 
217   *buffer += 2;			/* Skip hardware flags */
218 
219   *compression = libspectrum_read_word( buffer );
220 
221   switch( *compression ) {
222 
223   case 0x0008:			/* Deflation */
224     *compression = 1; break;
225 
226   case 0xffff:			/* Not compressed */
227     *compression = 0; break;
228 
229   default:
230     libspectrum_print_error(
231       LIBSPECTRUM_ERROR_UNKNOWN,
232       "zxs_read_fmtz_chunk: unknown compression type 0x%04x", *compression
233     );
234     return LIBSPECTRUM_ERROR_UNKNOWN;
235   }
236 
237   return LIBSPECTRUM_ERROR_NONE;
238 }
239 
240 static libspectrum_error
read_rz80_chunk(libspectrum_snap * snap,int * compression GCC_UNUSED,const libspectrum_byte ** buffer,const libspectrum_byte * end GCC_UNUSED,size_t data_length,int parameter GCC_UNUSED)241 read_rz80_chunk( libspectrum_snap *snap, int *compression GCC_UNUSED,
242 		 const libspectrum_byte **buffer,
243 		 const libspectrum_byte *end GCC_UNUSED,
244 		 size_t data_length, int parameter GCC_UNUSED )
245 {
246   if( data_length != 33 ) {
247     libspectrum_print_error( LIBSPECTRUM_ERROR_UNKNOWN,
248 			     "zxs_read_rZ80_chunk: unknown length %lu",
249 			     (unsigned long)data_length );
250     return LIBSPECTRUM_ERROR_UNKNOWN;
251   }
252 
253   libspectrum_snap_set_a   ( snap, **buffer ); (*buffer)++;
254   libspectrum_snap_set_f   ( snap, **buffer ); (*buffer)++;
255   libspectrum_snap_set_bc  ( snap, libspectrum_read_word( buffer ) );
256   libspectrum_snap_set_de  ( snap, libspectrum_read_word( buffer ) );
257   libspectrum_snap_set_hl  ( snap, libspectrum_read_word( buffer ) );
258 
259   libspectrum_snap_set_a_  ( snap, **buffer ); (*buffer)++;
260   libspectrum_snap_set_f_  ( snap, **buffer ); (*buffer)++;
261   libspectrum_snap_set_bc_ ( snap, libspectrum_read_word( buffer ) );
262   libspectrum_snap_set_de_ ( snap, libspectrum_read_word( buffer ) );
263   libspectrum_snap_set_hl_ ( snap, libspectrum_read_word( buffer ) );
264 
265   libspectrum_snap_set_ix  ( snap, libspectrum_read_word( buffer ) );
266   libspectrum_snap_set_iy  ( snap, libspectrum_read_word( buffer ) );
267   libspectrum_snap_set_pc  ( snap, libspectrum_read_word( buffer ) );
268   libspectrum_snap_set_sp  ( snap, libspectrum_read_word( buffer ) );
269 
270   libspectrum_snap_set_i   ( snap, **buffer ); (*buffer)++;
271   libspectrum_snap_set_r   ( snap, **buffer ); (*buffer)++;
272   libspectrum_snap_set_iff1( snap, **buffer ); (*buffer)++;
273   libspectrum_snap_set_iff2( snap, **buffer ); (*buffer)++;
274   libspectrum_snap_set_im  ( snap, **buffer ); (*buffer)++;
275 
276   libspectrum_snap_set_tstates( snap, libspectrum_read_dword( buffer ) );
277 
278   return LIBSPECTRUM_ERROR_NONE;
279 }
280 
281 static libspectrum_error
read_r048_chunk(libspectrum_snap * snap,int * compression GCC_UNUSED,const libspectrum_byte ** buffer,const libspectrum_byte * end GCC_UNUSED,size_t data_length,int parameter GCC_UNUSED)282 read_r048_chunk( libspectrum_snap *snap, int *compression GCC_UNUSED,
283 		 const libspectrum_byte **buffer,
284 		 const libspectrum_byte *end GCC_UNUSED,
285 		 size_t data_length, int parameter GCC_UNUSED )
286 {
287   if( data_length != 9 ) {
288     libspectrum_print_error(
289       LIBSPECTRUM_ERROR_UNKNOWN,
290       "zxs_read_r048_chunk: unknown length %lu", (unsigned long)data_length
291     );
292     return LIBSPECTRUM_ERROR_UNKNOWN;
293   }
294 
295   libspectrum_snap_set_out_ula( snap, **buffer ); (*buffer)++;
296 
297   *buffer += 8;			/* Skip key data */
298 
299   return LIBSPECTRUM_ERROR_NONE;
300 }
301 
302 static libspectrum_error
read_r128_chunk(libspectrum_snap * snap,int * compression GCC_UNUSED,const libspectrum_byte ** buffer,const libspectrum_byte * end GCC_UNUSED,size_t data_length,int parameter GCC_UNUSED)303 read_r128_chunk( libspectrum_snap *snap, int *compression GCC_UNUSED,
304 		 const libspectrum_byte **buffer,
305 		 const libspectrum_byte *end GCC_UNUSED,
306 		 size_t data_length, int parameter GCC_UNUSED )
307 {
308   size_t i;
309 
310   if( data_length != 18 ) {
311     libspectrum_print_error( LIBSPECTRUM_ERROR_UNKNOWN,
312 			     "zxs_read_r128_chunk: unknown length %lu",
313 			     (unsigned long)data_length );
314     return LIBSPECTRUM_ERROR_UNKNOWN;
315   }
316 
317   libspectrum_snap_set_out_128_memoryport( snap, **buffer ); (*buffer)++;
318   libspectrum_snap_set_out_ay_registerport( snap, **buffer ); (*buffer)++;
319 
320   for( i = 0; i < 16; i++ ) {
321     libspectrum_snap_set_ay_registers( snap, i, **buffer ); (*buffer)++;
322   }
323 
324   return LIBSPECTRUM_ERROR_NONE;
325 }
326 
327 static libspectrum_error
read_rplus3_chunk(libspectrum_snap * snap,int * compression GCC_UNUSED,const libspectrum_byte ** buffer,const libspectrum_byte * end GCC_UNUSED,size_t data_length,int parameter GCC_UNUSED)328 read_rplus3_chunk( libspectrum_snap *snap, int *compression GCC_UNUSED,
329 		   const libspectrum_byte **buffer,
330 		   const libspectrum_byte *end GCC_UNUSED, size_t data_length,
331 		   int parameter GCC_UNUSED )
332 {
333   if( data_length != 1 ) {
334     libspectrum_print_error( LIBSPECTRUM_ERROR_UNKNOWN,
335 			     "zxs_read_rplus3_chunk: unknown length %lu",
336 			     (unsigned long)data_length );
337     return LIBSPECTRUM_ERROR_UNKNOWN;
338   }
339 
340   libspectrum_snap_set_out_plus3_memoryport( snap, **buffer ); (*buffer)++;
341 
342   return LIBSPECTRUM_ERROR_NONE;
343 }
344 
345 static libspectrum_error
read_ram_chunk(libspectrum_snap * snap,int * compression,const libspectrum_byte ** buffer,const libspectrum_byte * end GCC_UNUSED,size_t data_length,int parameter)346 read_ram_chunk( libspectrum_snap *snap, int *compression,
347 		const libspectrum_byte **buffer,
348 		const libspectrum_byte *end GCC_UNUSED,
349 		size_t data_length, int parameter )
350 {
351   int page = parameter;
352   libspectrum_byte *buffer2; size_t uncompressed_length;
353   libspectrum_error error;
354 
355   if( *compression ) {
356 
357     error = inflate_block( &buffer2, &uncompressed_length,
358 			   buffer, data_length );
359     if( error ) return error;
360 
361     if( uncompressed_length != 0x4000 ) {
362       libspectrum_free( buffer2 );
363       libspectrum_print_error(
364         LIBSPECTRUM_ERROR_CORRUPT,
365 	"zxs_read_ram_chunk: page %d does not expand to 0x4000 bytes", page
366       );
367       return LIBSPECTRUM_ERROR_MEMORY;
368     }
369 
370   } else {			/* Uncompressed data */
371 
372     if( data_length != 0x4000 ) {
373       libspectrum_print_error(
374         LIBSPECTRUM_ERROR_UNKNOWN,
375 	"zxs_read_ram_chunk: page %d has unknown length %lu", page,
376 	(unsigned long)data_length
377       );
378       return LIBSPECTRUM_ERROR_UNKNOWN;
379     }
380 
381     buffer2 = libspectrum_malloc( 0x4000 * sizeof( *buffer2 ) );
382     memcpy( buffer2, buffer, 0x4000 ); *buffer += 0x4000;
383   }
384 
385   libspectrum_snap_set_pages( snap, page, buffer2 );
386 
387   return LIBSPECTRUM_ERROR_NONE;
388 }
389 
390 static libspectrum_error
skip_chunk(libspectrum_snap * snap GCC_UNUSED,int * compression GCC_UNUSED,const libspectrum_byte ** buffer,const libspectrum_byte * end GCC_UNUSED,size_t data_length,int parameter GCC_UNUSED)391 skip_chunk( libspectrum_snap *snap GCC_UNUSED, int *compression GCC_UNUSED,
392 	    const libspectrum_byte **buffer,
393 	    const libspectrum_byte *end GCC_UNUSED,
394 	    size_t data_length, int parameter GCC_UNUSED )
395 {
396   *buffer += data_length;
397   return LIBSPECTRUM_ERROR_NONE;
398 }
399 
400 struct read_chunk_t {
401 
402   const char *id;
403   read_chunk_fn function;
404   int parameter;
405 
406 };
407 
408 static struct read_chunk_t read_chunks[] = {
409 
410   { "RIFF", read_riff_chunk,   0 },
411   { "fmtz", read_fmtz_chunk,   0 },
412   { "rZ80", read_rz80_chunk,   0 },
413   { "r048", read_r048_chunk,   0 },
414   { "r128", read_r128_chunk,   0 },
415   { "r+3 ", read_rplus3_chunk, 0 },
416 
417   { "ram0", read_ram_chunk,    0 },
418   { "ram1", read_ram_chunk,    1 },
419   { "ram2", read_ram_chunk,    2 },
420   { "ram3", read_ram_chunk,    3 },
421   { "ram4", read_ram_chunk,    4 },
422   { "ram5", read_ram_chunk,    5 },
423   { "ram6", read_ram_chunk,    6 },
424   { "ram7", read_ram_chunk,    7 },
425 
426   { "LIST", skip_chunk,        0 },
427 
428 };
429 
430 static size_t read_chunks_count =
431   sizeof( read_chunks ) / sizeof( struct read_chunk_t );
432 
433 static libspectrum_error
read_chunk_header(char * id,libspectrum_dword * data_length,const libspectrum_byte ** buffer,const libspectrum_byte * end)434 read_chunk_header( char *id, libspectrum_dword *data_length,
435 		   const libspectrum_byte **buffer,
436 		   const libspectrum_byte *end )
437 {
438   if( end - *buffer < 8 ) {
439     libspectrum_print_error(
440       LIBSPECTRUM_ERROR_CORRUPT,
441       "zxs_read_chunk_header: not enough data for chunk header"
442     );
443     return LIBSPECTRUM_ERROR_CORRUPT;
444   }
445 
446   memcpy( id, *buffer, 4 ); id[4] = '\0'; *buffer += 4;
447   *data_length = libspectrum_read_dword( buffer );
448 
449   return LIBSPECTRUM_ERROR_NONE;
450 }
451 
452 static libspectrum_error
read_chunk(libspectrum_snap * snap,const libspectrum_byte ** buffer,const libspectrum_byte * end)453 read_chunk( libspectrum_snap *snap, const libspectrum_byte **buffer,
454 	    const libspectrum_byte *end )
455 {
456   char id[5];
457   libspectrum_dword data_length;
458   libspectrum_error error;
459   size_t i; int done;
460   int compression;
461 
462   error = read_chunk_header( id, &data_length, buffer, end );
463   if( error ) return error;
464 
465   if( *buffer + data_length > end ) {
466     libspectrum_print_error(
467       LIBSPECTRUM_ERROR_CORRUPT,
468       "zxs_read_chunk: chunk length goes beyond end of file"
469     );
470     return LIBSPECTRUM_ERROR_CORRUPT;
471   }
472 
473   done = 0;
474 
475   for( i = 0; !done && i < read_chunks_count; i++ ) {
476 
477     if( !strcmp( id, read_chunks[i].id ) ) {
478       error = read_chunks[i].function( snap, &compression, buffer, end,
479 				       data_length, read_chunks[i].parameter );
480       if( error ) return error;
481       done = 1;
482     }
483 
484   }
485 
486   if( !done ) {
487     libspectrum_print_error( LIBSPECTRUM_ERROR_UNKNOWN,
488 			     "zxs_read_chunk: unknown chunk id '%s'", id );
489     *buffer += data_length;
490   }
491 
492   if( data_length % 2 ) (*buffer)++;
493 
494   return LIBSPECTRUM_ERROR_NONE;
495 }
496 
497 libspectrum_error
libspectrum_zxs_read(libspectrum_snap * snap,const libspectrum_byte * buffer,size_t length)498 libspectrum_zxs_read( libspectrum_snap *snap, const libspectrum_byte *buffer,
499 		      size_t length )
500 {
501   libspectrum_error error;
502 
503   /* Set machine type in case it's not set later */
504   libspectrum_snap_set_machine( snap, LIBSPECTRUM_MACHINE_48 );
505 
506   error = read_chunk( snap, &buffer, buffer + length );
507   if( error ) {
508 
509     /* Tidy up any RAM pages we may have allocated */
510     size_t i;
511 
512     for( i = 0; i < 8; i++ ) {
513       libspectrum_byte *page = libspectrum_snap_pages( snap, i );
514       if( page ) {
515 	libspectrum_free( page );
516 	libspectrum_snap_set_pages( snap, i, NULL );
517       }
518     }
519 
520     return error;
521   }
522 
523   return LIBSPECTRUM_ERROR_NONE;
524 }
525