1 /* pzx_read.c: Routines for reading .pzx files
2 Copyright (c) 2001, 2002 Philip Kendall, Darren Salt
3 Copyright (c) 2011-2015 Fredrick Meunier
4
5 This program is free software; you can redistribute it and/or modify
6 it under the terms of the GNU General Public License as published by
7 the Free Software Foundation; either version 2 of the License, or
8 (at your option) any later version.
9
10 This program is distributed in the hope that it will be useful,
11 but WITHOUT ANY WARRANTY; without even the implied warranty of
12 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13 GNU General Public License for more details.
14
15 You should have received a copy of the GNU General Public License along
16 with this program; if not, write to the Free Software Foundation, Inc.,
17 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
18
19 Author contact information:
20
21 E-mail: philip-fuse@shadowmagic.org.uk
22
23 */
24
25 #include "config.h"
26
27 #include <stddef.h>
28 #include <stdio.h>
29 #include <string.h>
30
31 #include "internals.h"
32
33 /* Used for passing internal data around */
34
35 typedef struct pzx_context {
36
37 libspectrum_word version;
38
39 } pzx_context;
40
41 /* Constants etc for each block type */
42
43 #define PZX_HEADER "PZXT"
44 struct info_t {
45
46 const char *id;
47 int archive_info_id;
48
49 };
50
51 /* Needs to be in strcmp order */
52 static struct info_t info_ids[] = {
53
54 { "Author", 0x02 },
55 { "Comment", 0xff },
56 { "Language", 0x04 },
57 { "Origin", 0x08 },
58 { "Price", 0x06 },
59 { "Protection", 0x07 },
60 { "Publisher", 0x01 },
61 { "Type", 0x05 },
62 { "Year", 0x03 },
63
64 };
65
66 #define PZX_PULSE "PULS"
67
68 #define PZX_DATA "DATA"
69
70 #define PZX_PAUSE "PAUS"
71
72 #define PZX_BROWSE "BRWS"
73
74 #define PZX_STOP "STOP"
75 static const libspectrum_byte PZXF_STOP48 = 1;
76
77 /* TODO: an extension to be similar to the TZX Custom Block Picture type */
78 #define PZX_INLAY "inly"
79
80 static const char * const signature = PZX_HEADER;
81 static const size_t signature_length = 4;
82
83 static libspectrum_error
84 read_block( libspectrum_tape *tape, const libspectrum_byte **buffer,
85 const libspectrum_byte *end, pzx_context *ctx );
86
87 typedef libspectrum_error (*read_block_fn)( libspectrum_tape *tape,
88 const libspectrum_byte **buffer,
89 const libspectrum_byte *end,
90 size_t data_length,
91 pzx_context *ctx );
92
93 static libspectrum_error
94 pzx_read_data( const libspectrum_byte **ptr, const libspectrum_byte *end,
95 size_t length, libspectrum_byte **data );
96
97 static libspectrum_error
98 pzx_read_string( const libspectrum_byte **ptr, const libspectrum_byte *end,
99 char **dest );
100
101 static int
info_t_compar(const void * a,const void * b)102 info_t_compar(const void *a, const void *b)
103 {
104 const char *key = a;
105 const struct info_t *test = b;
106 return strcmp( key, test->id );
107 }
108
109 static int
get_id_byte(char * info_tag)110 get_id_byte( char *info_tag ) {
111 struct info_t *info =
112 (struct info_t*)bsearch( info_tag, info_ids, ARRAY_SIZE( info_ids ),
113 sizeof( struct info_t ), info_t_compar );
114 return info == NULL ? -1 : info->archive_info_id;
115 }
116
117 static libspectrum_error
read_pzxt_block(libspectrum_tape * tape,const libspectrum_byte ** buffer,const libspectrum_byte * end,size_t data_length,pzx_context * ctx)118 read_pzxt_block( libspectrum_tape *tape, const libspectrum_byte **buffer,
119 const libspectrum_byte *end, size_t data_length,
120 pzx_context *ctx )
121 {
122 libspectrum_error error;
123
124 size_t i = 0;
125 size_t count = 0;
126 int id;
127 int *ids = NULL;
128 char *info_tag = NULL;
129 char *string;
130 char **strings = NULL;
131 const libspectrum_byte *block_end = *buffer + data_length;
132
133 if( data_length < 2 ) {
134 libspectrum_print_error( LIBSPECTRUM_ERROR_CORRUPT,
135 "read_pzxt_block: length %lu too short",
136 (unsigned long)data_length );
137 return LIBSPECTRUM_ERROR_CORRUPT;
138 }
139
140 ctx->version = (**buffer) << 8; (*buffer)++;
141 ctx->version |= **buffer; (*buffer)++;
142
143 if( ctx->version < 0x0100 || ctx->version >= 0x0200 ) {
144 libspectrum_print_error( LIBSPECTRUM_ERROR_UNKNOWN,
145 "read_pzxt_block: only version 1 pzx files are "
146 "supported" );
147 return LIBSPECTRUM_ERROR_UNKNOWN;
148 }
149
150 if( *buffer < block_end ) {
151 ids = libspectrum_new( int, 1 );
152 strings = libspectrum_new( char *, 1 );
153 count = 1;
154 i = 0;
155
156 ids[0] = 0x00;
157
158 /* Read in the title string itself */
159 error = pzx_read_string( buffer, block_end, &strings[0] );
160 if( error ) {
161 libspectrum_free( strings[0] );
162 return error;
163 }
164 }
165
166 while( *buffer < block_end ) {
167 error = pzx_read_string( buffer, block_end, &info_tag );
168 if( error ) {
169 size_t j;
170 for( j = 0; j < i; j++ ) libspectrum_free( strings[j] );
171 libspectrum_free( strings ); libspectrum_free( ids );
172 return error;
173 }
174
175 /* Get the ID byte */
176 id = get_id_byte( info_tag );
177
178 /* Read in the string itself */
179 error = pzx_read_string( buffer, block_end, &string );
180 if( error ) {
181 size_t j;
182 for( j = 0; j < i; j++ ) libspectrum_free( strings[j] );
183 libspectrum_free( strings ); libspectrum_free( ids );
184 return error;
185 }
186
187 i = count++;
188 ids = libspectrum_renew( int, ids, count );
189 strings = libspectrum_renew( char *, strings, count );
190
191 if( id == -1 ) {
192 size_t new_len = strlen( info_tag ) + strlen( string ) +
193 strlen( ": " ) + 1;
194 char *comment = libspectrum_new( char, new_len );
195 snprintf( comment, new_len, "%s: %s", info_tag, string );
196 libspectrum_free( string );
197 ids[i] = 0xff;
198 strings[i] = comment;
199 } else {
200 ids[i] = id;
201 strings[i] = string;
202 }
203
204 libspectrum_free( info_tag );
205 }
206
207 if( count ) {
208 libspectrum_tape_block* block =
209 libspectrum_tape_block_alloc( LIBSPECTRUM_TAPE_BLOCK_ARCHIVE_INFO );
210
211 libspectrum_tape_block_set_count( block, count );
212 libspectrum_tape_block_set_ids( block, ids );
213 libspectrum_tape_block_set_texts( block, strings );
214
215 libspectrum_tape_append_block( tape, block );
216 }
217
218 return LIBSPECTRUM_ERROR_NONE;
219 }
220
221
222 static libspectrum_error
read_data_block(libspectrum_tape * tape,const libspectrum_byte ** buffer,const libspectrum_byte * end,size_t data_length,pzx_context * ctx)223 read_data_block( libspectrum_tape *tape, const libspectrum_byte **buffer,
224 const libspectrum_byte *end, size_t data_length,
225 pzx_context *ctx )
226 {
227 const libspectrum_byte *block_end = *buffer + data_length;
228 libspectrum_tape_block* block;
229 libspectrum_byte *data;
230
231 libspectrum_error error;
232 libspectrum_dword count;
233 int initial_level;
234 size_t count_bytes;
235 size_t bits_in_last_byte;
236 libspectrum_word tail;
237 libspectrum_byte p0_count;
238 libspectrum_byte p1_count;
239 libspectrum_word *p0_pulses;
240 libspectrum_word *p1_pulses;
241
242 /* Check there's enough left in the buffer for all the metadata */
243 if( data_length < 8 ) {
244 libspectrum_print_error(
245 LIBSPECTRUM_ERROR_CORRUPT,
246 "read_data_block: not enough data in buffer"
247 );
248 return LIBSPECTRUM_ERROR_CORRUPT;
249 }
250
251 /* Get the metadata */
252 count = libspectrum_read_dword( buffer );
253 initial_level = !!(count & 0x80000000);
254 count &= 0x7fffffff;
255 count_bytes = libspectrum_bits_to_bytes( count );
256 bits_in_last_byte =
257 count % LIBSPECTRUM_BITS_IN_BYTE ?
258 count % LIBSPECTRUM_BITS_IN_BYTE : LIBSPECTRUM_BITS_IN_BYTE;
259 tail = libspectrum_read_word( buffer );
260 p0_count = **buffer; (*buffer)++;
261 p1_count = **buffer; (*buffer)++;
262
263 /* need to confirm that we have enough length left for the pulse definitions
264 */
265 if( data_length < 8 + 2*(p0_count + p1_count) ) {
266 libspectrum_print_error(
267 LIBSPECTRUM_ERROR_CORRUPT,
268 "read_data_block: not enough data in buffer"
269 );
270 return LIBSPECTRUM_ERROR_CORRUPT;
271 }
272
273 error = pzx_read_data( buffer, block_end,
274 p0_count * sizeof( libspectrum_word ),
275 (libspectrum_byte**)&p0_pulses );
276 if( error ) return error;
277
278 error = pzx_read_data( buffer, block_end,
279 p1_count * sizeof( libspectrum_word ),
280 (libspectrum_byte**)&p1_pulses );
281 if( error ) { libspectrum_free( p0_pulses ); return error; }
282
283 /* And the actual data */
284 error = pzx_read_data( buffer, block_end, count_bytes, &data );
285 if( error ) {
286 libspectrum_free( p0_pulses );
287 libspectrum_free( p1_pulses );
288 return error;
289 }
290
291 block = libspectrum_tape_block_alloc( LIBSPECTRUM_TAPE_BLOCK_DATA_BLOCK );
292
293 libspectrum_tape_block_set_count( block, count );
294 libspectrum_tape_block_set_tail_length( block, tail );
295 libspectrum_tape_block_set_level( block, initial_level );
296 libspectrum_tape_block_set_bit0_pulse_count( block, p0_count );
297 libspectrum_tape_block_set_bit0_pulses( block, p0_pulses );
298 libspectrum_tape_block_set_bit1_pulse_count( block, p1_count );
299 libspectrum_tape_block_set_bit1_pulses( block, p1_pulses );
300 libspectrum_tape_block_set_data_length( block, count_bytes );
301 libspectrum_tape_block_set_bits_in_last_byte( block, bits_in_last_byte );
302 libspectrum_tape_block_set_data( block, data );
303
304 libspectrum_tape_append_block( tape, block );
305
306 return LIBSPECTRUM_ERROR_NONE;
307 }
308
309 static libspectrum_error
read_next_pulse(const libspectrum_byte ** buffer,const libspectrum_byte * end,size_t * pulse_repeats,libspectrum_dword * length)310 read_next_pulse( const libspectrum_byte **buffer, const libspectrum_byte *end,
311 size_t *pulse_repeats, libspectrum_dword *length )
312 {
313 /* While we have at least one int 16 left try to extract the next pulse */
314 if( ( end - (*buffer) ) < (ptrdiff_t)2 ) goto pzx_corrupt;
315
316 *pulse_repeats = 1;
317 *length = libspectrum_read_word( buffer );
318 if( *length > 0x8000 ) {
319 if( ( end - (*buffer) ) < (ptrdiff_t)2 ) goto pzx_corrupt;
320 *pulse_repeats = *length & 0x7fff;
321 *length = libspectrum_read_word( buffer );
322 }
323 if( *length >= 0x8000 ) {
324 if( ( end - (*buffer) ) < (ptrdiff_t)2 ) goto pzx_corrupt;
325 *length &= 0x7fff;
326 *length <<= 16;
327 *length |= libspectrum_read_word( buffer );
328 }
329
330 /* And return */
331 return LIBSPECTRUM_ERROR_NONE;
332
333 pzx_corrupt:
334 libspectrum_print_error( LIBSPECTRUM_ERROR_CORRUPT,
335 "read_next_pulse: not enough data in buffer" );
336 return LIBSPECTRUM_ERROR_CORRUPT;
337 }
338
339 static libspectrum_error
read_puls_block(libspectrum_tape * tape,const libspectrum_byte ** buffer,const libspectrum_byte * end,size_t data_length,pzx_context * ctx)340 read_puls_block( libspectrum_tape *tape, const libspectrum_byte **buffer,
341 const libspectrum_byte *end, size_t data_length,
342 pzx_context *ctx )
343 {
344 size_t count = 0;
345 size_t pulse_repeats;
346 libspectrum_dword length;
347 libspectrum_tape_block *block;
348 libspectrum_error error;
349 size_t buffer_sizes = 64;
350 size_t *pulse_repeats_buffer =
351 libspectrum_new( size_t, buffer_sizes );
352 libspectrum_dword *lengths_buffer =
353 libspectrum_new( libspectrum_dword, buffer_sizes );
354 const libspectrum_byte *block_end = *buffer + data_length;
355
356 while( ( block_end - (*buffer) ) > (ptrdiff_t)0 ) {
357 error = read_next_pulse( buffer, block_end, &pulse_repeats, &length );
358 if( error ) {
359 libspectrum_free( pulse_repeats_buffer );
360 libspectrum_free( lengths_buffer );
361 return error;
362 }
363 pulse_repeats_buffer[ count ] = pulse_repeats;
364 lengths_buffer[ count ] = length;
365 count++;
366 if( buffer_sizes == count ) {
367 buffer_sizes *= 2;
368 pulse_repeats_buffer =
369 libspectrum_renew( size_t, pulse_repeats_buffer, buffer_sizes );
370 lengths_buffer =
371 libspectrum_renew( libspectrum_dword, lengths_buffer, buffer_sizes );
372 }
373 }
374
375 if( count == 0 ) {
376 libspectrum_print_error( LIBSPECTRUM_ERROR_CORRUPT,
377 "read_puls_block: no pulses found in pulse block" );
378 return LIBSPECTRUM_ERROR_CORRUPT;
379 }
380
381 if( buffer_sizes != count ) {
382 pulse_repeats_buffer =
383 libspectrum_renew( size_t, pulse_repeats_buffer, count );
384 lengths_buffer =
385 libspectrum_renew( libspectrum_dword, lengths_buffer, count );
386 }
387
388 block = libspectrum_tape_block_alloc( LIBSPECTRUM_TAPE_BLOCK_PULSE_SEQUENCE );
389
390 libspectrum_tape_block_set_count( block, count );
391 libspectrum_tape_block_set_pulse_lengths( block, lengths_buffer );
392 libspectrum_tape_block_set_pulse_repeats( block, pulse_repeats_buffer );
393
394 libspectrum_tape_append_block( tape, block );
395
396 /* And return */
397 return LIBSPECTRUM_ERROR_NONE;
398 }
399
400 static libspectrum_error
read_paus_block(libspectrum_tape * tape,const libspectrum_byte ** buffer,const libspectrum_byte * end,size_t data_length,pzx_context * ctx)401 read_paus_block( libspectrum_tape *tape, const libspectrum_byte **buffer,
402 const libspectrum_byte *end, size_t data_length,
403 pzx_context *ctx )
404 {
405 libspectrum_tape_block *block;
406 libspectrum_dword pause_tstates;
407 int initial_level;
408
409 /* Check the pause actually exists */
410 if( data_length < 2 ) {
411 libspectrum_print_error( LIBSPECTRUM_ERROR_CORRUPT,
412 "read_paus_block: not enough data in buffer" );
413 return LIBSPECTRUM_ERROR_CORRUPT;
414 }
415
416 block = libspectrum_tape_block_alloc( LIBSPECTRUM_TAPE_BLOCK_PAUSE );
417
418 pause_tstates = libspectrum_read_dword( buffer );
419 initial_level = !!(pause_tstates & 0x80000000);
420 pause_tstates &= 0x7fffffff;
421
422 /* Set the pause length */
423 libspectrum_set_pause_tstates( block, pause_tstates );
424 libspectrum_tape_block_set_level( block, initial_level );
425
426 libspectrum_tape_append_block( tape, block );
427
428 /* And return */
429 return LIBSPECTRUM_ERROR_NONE;
430 }
431
432 static libspectrum_error
read_brws_block(libspectrum_tape * tape,const libspectrum_byte ** buffer,const libspectrum_byte * end,size_t data_length,pzx_context * ctx)433 read_brws_block( libspectrum_tape *tape, const libspectrum_byte **buffer,
434 const libspectrum_byte *end, size_t data_length,
435 pzx_context *ctx )
436 {
437 libspectrum_tape_block* block;
438 char *text;
439 libspectrum_error error;
440
441 block = libspectrum_tape_block_alloc( LIBSPECTRUM_TAPE_BLOCK_COMMENT );
442
443 /* Get the actual comment */
444 error = pzx_read_string( buffer, *buffer + data_length, &text );
445 if( error ) { libspectrum_free( block ); return error; }
446 libspectrum_tape_block_set_text( block, text );
447
448 libspectrum_tape_append_block( tape, block );
449
450 return LIBSPECTRUM_ERROR_NONE;
451 }
452
453 static libspectrum_error
read_stop_block(libspectrum_tape * tape,const libspectrum_byte ** buffer,const libspectrum_byte * end,size_t data_length,pzx_context * ctx)454 read_stop_block( libspectrum_tape *tape, const libspectrum_byte **buffer,
455 const libspectrum_byte *end, size_t data_length,
456 pzx_context *ctx )
457 {
458 libspectrum_tape_block *block;
459 libspectrum_word flags;
460
461 if( data_length < 2 ) {
462 libspectrum_print_error( LIBSPECTRUM_ERROR_CORRUPT,
463 "tzx_read_stop: not enough data in buffer" );
464 return LIBSPECTRUM_ERROR_CORRUPT;
465 }
466
467 flags = libspectrum_read_word( buffer );
468
469 if( flags == PZXF_STOP48 ) {
470 block = libspectrum_tape_block_alloc( LIBSPECTRUM_TAPE_BLOCK_STOP48 );
471 } else {
472 /* General stop is a 0 duration pause */
473 block = libspectrum_tape_block_alloc( LIBSPECTRUM_TAPE_BLOCK_PAUSE );
474 libspectrum_tape_block_set_pause( block, 0 );
475 }
476
477 libspectrum_tape_append_block( tape, block );
478
479 return LIBSPECTRUM_ERROR_NONE;
480 }
481
482 static libspectrum_error
skip_block(libspectrum_tape * tape GCC_UNUSED,const libspectrum_byte ** buffer,const libspectrum_byte * end GCC_UNUSED,size_t data_length,pzx_context * ctx GCC_UNUSED)483 skip_block( libspectrum_tape *tape GCC_UNUSED,
484 const libspectrum_byte **buffer,
485 const libspectrum_byte *end GCC_UNUSED,
486 size_t data_length,
487 pzx_context *ctx GCC_UNUSED )
488 {
489 *buffer += data_length;
490 return LIBSPECTRUM_ERROR_NONE;
491 }
492
493 struct read_block_t {
494
495 const char *id;
496 read_block_fn function;
497
498 };
499
500 static struct read_block_t read_blocks[] = {
501
502 { PZX_HEADER, read_pzxt_block },
503 { PZX_PULSE, read_puls_block },
504 { PZX_DATA, read_data_block },
505 { PZX_PAUSE, read_paus_block },
506 { PZX_BROWSE, read_brws_block },
507 { PZX_STOP, read_stop_block },
508 { PZX_INLAY, skip_block },
509
510 };
511
512 static libspectrum_error
read_block_header(char * id,libspectrum_dword * data_length,const libspectrum_byte ** buffer,const libspectrum_byte * end)513 read_block_header( char *id, libspectrum_dword *data_length,
514 const libspectrum_byte **buffer,
515 const libspectrum_byte *end )
516 {
517 if( end - *buffer < 8 ) {
518 libspectrum_print_error(
519 LIBSPECTRUM_ERROR_CORRUPT,
520 "read_block_header: not enough data for block header"
521 );
522 return LIBSPECTRUM_ERROR_CORRUPT;
523 }
524
525 memcpy( id, *buffer, 4 ); id[4] = '\0'; *buffer += 4;
526 *data_length = libspectrum_read_dword( buffer );
527
528 return LIBSPECTRUM_ERROR_NONE;
529 }
530
531 static libspectrum_error
read_block(libspectrum_tape * tape,const libspectrum_byte ** buffer,const libspectrum_byte * end,pzx_context * ctx)532 read_block( libspectrum_tape *tape, const libspectrum_byte **buffer,
533 const libspectrum_byte *end, pzx_context *ctx )
534 {
535 char id[5];
536 libspectrum_dword data_length;
537 libspectrum_error error;
538 size_t i; int done;
539
540 error = read_block_header( id, &data_length, buffer, end );
541 if( error ) return error;
542
543 if( end - *buffer < data_length ) {
544 libspectrum_print_error(
545 LIBSPECTRUM_ERROR_CORRUPT,
546 "read_block: block length goes beyond end of file"
547 );
548 return LIBSPECTRUM_ERROR_CORRUPT;
549 }
550
551 done = 0;
552
553 for( i = 0; !done && i < ARRAY_SIZE( read_blocks ); i++ ) {
554
555 if( !memcmp( id, read_blocks[i].id, 4 ) ) {
556 error = read_blocks[i].function( tape, buffer, end, data_length, ctx );
557 if( error ) return error;
558 done = 1;
559 }
560
561 }
562
563 if( !done ) {
564 libspectrum_print_error( LIBSPECTRUM_ERROR_UNKNOWN,
565 "read_block: unknown block id '%s'", id );
566 *buffer += data_length;
567 }
568
569 return LIBSPECTRUM_ERROR_NONE;
570 }
571
572 /* The main load function */
573
574 libspectrum_error
internal_pzx_read(libspectrum_tape * tape,const libspectrum_byte * buffer,size_t length)575 internal_pzx_read( libspectrum_tape *tape, const libspectrum_byte *buffer,
576 size_t length )
577 {
578 libspectrum_error error;
579 const libspectrum_byte *end = buffer + length;
580 pzx_context *ctx;
581
582 if( end - buffer < 8 ) {
583 libspectrum_print_error(
584 LIBSPECTRUM_ERROR_CORRUPT,
585 "internal_pzx_read: not enough data for PZX header"
586 );
587 return LIBSPECTRUM_ERROR_CORRUPT;
588 }
589
590 if( memcmp( buffer, signature, signature_length ) ) {
591 libspectrum_print_error(
592 LIBSPECTRUM_ERROR_SIGNATURE,
593 "internal_pzx_read: wrong signature"
594 );
595 return LIBSPECTRUM_ERROR_SIGNATURE;
596 }
597
598 ctx = libspectrum_new( pzx_context, 1 );
599 ctx->version = 0;
600
601 while( buffer < end ) {
602 error = read_block( tape, &buffer, end, ctx );
603 if( error ) {
604 libspectrum_free( ctx );
605 return error;
606 }
607 }
608
609 libspectrum_free( ctx );
610 return LIBSPECTRUM_ERROR_NONE;
611 }
612
613 static libspectrum_error
pzx_read_data(const libspectrum_byte ** ptr,const libspectrum_byte * end,size_t length,libspectrum_byte ** data)614 pzx_read_data( const libspectrum_byte **ptr, const libspectrum_byte *end,
615 size_t length, libspectrum_byte **data )
616 {
617 /* Have we got enough bytes left in buffer? */
618 if( ( end - (*ptr) ) < (ptrdiff_t)(length) ) {
619 libspectrum_print_error( LIBSPECTRUM_ERROR_CORRUPT,
620 "pzx_read_data: not enough data in buffer" );
621 return LIBSPECTRUM_ERROR_CORRUPT;
622 }
623
624 /* Allocate memory for the data; the check for *length is to avoid
625 the implementation-defined behaviour of malloc( 0 ) */
626 if( length ) {
627 *data = libspectrum_new( libspectrum_byte, length );
628 /* Copy the block data across, and move along */
629 memcpy( *data, *ptr, length ); *ptr += length;
630 } else {
631 *data = NULL;
632 }
633
634 return LIBSPECTRUM_ERROR_NONE;
635 }
636
637 static libspectrum_error
pzx_read_string(const libspectrum_byte ** ptr,const libspectrum_byte * end,char ** dest)638 pzx_read_string( const libspectrum_byte **ptr, const libspectrum_byte *end,
639 char **dest )
640 {
641 size_t length = 0;
642 char *ptr2;
643 size_t buffer_size = 64;
644 char *buffer = libspectrum_new( char, buffer_size );
645
646 while( **ptr != '\0' && *ptr < end ) {
647 if( length == buffer_size ) {
648 buffer_size *= 2;
649 buffer = libspectrum_renew( char, buffer, buffer_size );
650 }
651 *(buffer + length++) = **ptr; (*ptr)++;
652 }
653
654 /* Advance past the null terminator discarding any garbage */
655 *ptr = end;
656
657 *dest = libspectrum_new( char, (length + 1) );
658
659 strncpy( *dest, buffer, length );
660
661 /* Null terminate the string */
662 (*dest)[length] = '\0';
663
664 /* Translate line endings */
665 for( ptr2 = (*dest); *ptr2; ptr2++ ) if( *ptr2 == '\r' ) *ptr2 = '\n';
666
667 libspectrum_free( buffer );
668
669 return LIBSPECTRUM_ERROR_NONE;
670 }
671