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