1 /* State save/load functions */
2 #include <stdio.h>
3 #include <stdlib.h>
4 #include <string.h>
5 #include <ctype.h>
6 #include <stdarg.h>
7 #include "osd_cpu.h"
8 #include "memory.h"
9 #include "osdepend.h"
10 #include "state.h"
11 #include "mame.h"
12
13
14 /* A forward linked list of the contents of a section */
15 typedef struct tag_state_var {
16 struct tag_state_var *next;
17 char *name;
18 unsigned size;
19 unsigned chunk;
20 void *data;
21 } state_var;
22
23 /* Our state handling structure */
24 typedef struct {
25 void *file;
26 const char *cur_module;
27 int cur_instance;
28 state_var *list;
29 } state_handle;
30
xtoul(char ** p,int * size)31 static INLINE unsigned xtoul(char **p, int *size)
32 {
33 unsigned val = 0, digit;
34
35 if (size) *size = 0;
36 while( isxdigit(**p) )
37 {
38 digit = toupper(**p) - '0';
39 if( digit > 9 ) digit -= 7;
40 val = (val << 4) | digit;
41 if( size ) (*size)++;
42 *p += 1;
43 }
44 while( isspace(**p) ) *p += 1;
45 if (size) (*size) >>= 1;
46 return val;
47 }
48
ultox(unsigned val,unsigned size)49 static INLINE char *ultox(unsigned val, unsigned size)
50 {
51 static char buffer[32+1];
52 static char digit[] = "0123456789ABCDEF";
53 char *p = &buffer[size];
54 *p-- = '\0';
55 while( size-- > 0 )
56 {
57 *p-- = digit[val & 15];
58 val >>= 4;
59 }
60 return buffer;
61 }
62
63 /**************************************************************************
64 * my_stricmp
65 * Compare strings case insensitive
66 **************************************************************************/
my_stricmp(const char * dst,const char * src)67 static INLINE int my_stricmp( const char *dst, const char *src)
68 {
69 while( *src && *dst )
70 {
71 if( tolower(*src) != tolower(*dst) ) return *dst - *src;
72 src++;
73 dst++;
74 }
75 return *dst - *src;
76 }
77
78 /* free a linked list of state_vars (aka section) */
state_free_section(void * s)79 void state_free_section( void *s )
80 {
81 state_handle *state = (state_handle *)s;
82 state_var *v1, *v2;
83 v2 = state->list;
84 while( v2 )
85 {
86 if( v2->name ) free( v2->name );
87 if( v2->data ) free( v2->data );
88 v1 = v2;
89 v2 = v2->next;
90 free( v1 );
91 }
92 state->list = NULL;
93 }
94
state_create(const char * name)95 void *state_create(const char *name)
96 {
97 state_handle *state;
98 state = (state_handle *) malloc( sizeof(state_handle) );
99 if( !state ) return NULL;
100 state->cur_module = NULL;
101 state->cur_instance = 0;
102 state->list = NULL;
103 state->file = osd_fopen( name, NULL, OSD_FILETYPE_STATE, 1 );
104 if( !state->file )
105 {
106 free(state);
107 return NULL;
108 }
109 return state;
110 }
111
state_open(const char * name)112 void *state_open(const char *name)
113 {
114 state_handle *state;
115 state = (state_handle *) malloc( sizeof(state_handle) );
116 if( !state ) return NULL;
117 state->cur_module = NULL;
118 state->cur_instance = 0;
119 state->list = NULL;
120 state->file = osd_fopen( name, NULL, OSD_FILETYPE_STATE, 0 );
121 if( !state->file )
122 {
123 free(state);
124 return NULL;
125 }
126 return state;
127 }
128
state_close(void * s)129 void state_close( void *s )
130 {
131 state_handle *state = (state_handle *)s;
132 if( !state ) return;
133 state_free_section( state );
134 if( state->file ) osd_fclose( state->file );
135 free( state );
136 }
137
138 /* Output a formatted string to the state file */
emit(void * s,const char * fmt,...)139 static void CLIB_DECL emit(void *s, const char *fmt, ...)
140 {
141 static char buffer[128+1];
142 state_handle *state = (state_handle *)s;
143 va_list arg;
144 int length;
145
146 va_start(arg,fmt);
147 length = vsprintf(buffer, fmt, arg);
148 va_end(arg);
149
150 if( osd_fwrite(state->file, buffer, length) != length )
151 {
152 logerror("emit: Error while saving state '%s'\n", buffer);
153 }
154 }
155
state_save_section(void * s,const char * module,int instance)156 void state_save_section( void *s, const char *module, int instance )
157 {
158 state_handle *state = (state_handle *)s;
159 if( !state->cur_module ||
160 (state->cur_module && my_stricmp(state->cur_module, module)) ||
161 state->cur_instance != instance )
162 {
163 if( state->cur_module )
164 emit(state,"\n");
165 state->cur_module = module;
166 state->cur_instance = instance;
167 emit(state,"[%s.%d]\n", module, instance);
168 }
169 }
170
state_save_UINT8(void * s,const char * module,int instance,const char * name,const UINT8 * val,unsigned size)171 void state_save_UINT8( void *s, const char *module,int instance,
172 const char *name, const UINT8 *val, unsigned size )
173 {
174 state_handle *state = (state_handle *)s;
175
176 state_save_section( state, module, instance );
177
178 /* If this is to much for a single line use the dump format */
179 if( size > 16 )
180 {
181 unsigned offs = 0;
182 while( size-- > 0 )
183 {
184 if( (offs & 15 ) == 0 )
185 emit( state, "%s.%s=", name, ultox(offs,4) );
186 emit( state, "%s", ultox(*val++,2) );
187 if( (++offs & 15) == 0)
188 emit( state, "\n" );
189 else
190 emit( state, " " );
191 }
192 if( offs & 15 ) emit( state, "\n" );
193 }
194 else
195 {
196 emit( state, "%s=", name );
197 while( size-- > 0 )
198 {
199 emit( state, "%s", ultox(*val++,2) );
200 if( size ) emit( state, " " );
201 }
202 emit( state, "\n" );
203 }
204 }
205
state_save_INT8(void * s,const char * module,int instance,const char * name,const INT8 * val,unsigned size)206 void state_save_INT8( void *s, const char *module,int instance,
207 const char *name, const INT8 *val, unsigned size )
208 {
209 state_save_UINT8( s, module, instance, name, (UINT8*)val, size );
210 }
211
state_save_UINT16(void * s,const char * module,int instance,const char * name,const UINT16 * val,unsigned size)212 void state_save_UINT16(void *s, const char *module,int instance,
213 const char *name, const UINT16 *val, unsigned size)
214 {
215 state_handle *state = (state_handle *)s;
216
217 state_save_section( state, module, instance );
218
219 /* If this is to much for a single line use the dump format */
220 if( size > 8 )
221 {
222 unsigned offs = 0;
223 while( size-- > 0 )
224 {
225 if( (offs & 7 ) == 0 )
226 emit( state, "%s.%s=", name, ultox(offs,4) );
227 emit( state, "%s", ultox(*val++,4) );
228 if( (++offs & 7) == 0)
229 emit( state, "\n" );
230 else
231 emit( state, " " );
232 }
233 if( offs & 7 ) emit( state, "\n" );
234 }
235 else
236 {
237 emit( state, "%s=", name );
238 while( size-- > 0 )
239 {
240 emit( state, "%s", ultox(*val++,4) );
241 if( size ) emit( state, " " );
242 }
243 emit( state, "\n" );
244 }
245 }
246
state_save_INT16(void * s,const char * module,int instance,const char * name,const INT16 * val,unsigned size)247 void state_save_INT16( void *s, const char *module,int instance,
248 const char *name, const INT16 *val, unsigned size )
249 {
250 state_save_UINT16( s, module, instance, name, (UINT16*)val, size );
251 }
252
state_save_UINT32(void * s,const char * module,int instance,const char * name,const UINT32 * val,unsigned size)253 void state_save_UINT32( void *s, const char *module,int instance,
254 const char *name, const UINT32 *val, unsigned size )
255 {
256 state_handle *state = (state_handle *)s;
257
258 state_save_section( state, module, instance );
259
260 /* If this is to much for a single line use the dump format */
261 if( size > 4 )
262 {
263 unsigned offs = 0;
264 while( size-- > 0 )
265 {
266 if( (offs & 3 ) == 0 )
267 emit( state, "%s.%s=", name, ultox(offs,4) );
268 emit( state, "%s", ultox(*val++,8) );
269 if( (++offs & 3) == 0)
270 emit( state, "\n" );
271 else
272 emit( state, " " );
273 }
274 if( offs & 3 ) emit( state, "\n" );
275 }
276 else
277 {
278 emit( state, "%s=", name );
279 while( size-- > 0 )
280 {
281 emit( state, "%s", ultox(*val++,8) );
282 if( size ) emit( state, " " );
283 }
284 emit( state, "\n" );
285 }
286 }
287
state_save_INT32(void * s,const char * module,int instance,const char * name,const INT32 * val,unsigned size)288 void state_save_INT32( void *s, const char *module,int instance,
289 const char *name, const INT32 *val, unsigned size )
290 {
291 state_save_UINT32( s, module, instance, name, (UINT32*)val, size );
292 }
293
294 /* load a linked list of state_vars (aka section) */
state_load_section(void * s,const char * module,int instance)295 void state_load_section( void *s, const char *module, int instance )
296 {
297 state_handle *state = (state_handle *)s;
298
299 /* Make the buffer twice as big as it was while saving
300 the state, so we should always catch a [section] */
301
302 static char buffer[256+1];
303 char section[128+1], *p, *d;
304 int length, element_size, rewind_file = 1;
305 unsigned offs, data;
306
307 if( state->cur_module &&
308 !my_stricmp(state->cur_module, module) &&
309 state->cur_instance == instance )
310 return; /* fine, we already got it */
311
312 if( !state->list )
313 state_free_section(state);
314
315 sprintf(section, "[%s.%d]", module, instance);
316
317 for( ; ; )
318 {
319 length = osd_fread(state->file, buffer, sizeof(buffer) - 1);
320 if( length <= 0 )
321 {
322 if( rewind_file )
323 {
324 logerror("state_load_section: Section '%s' not found\n", section);
325 return;
326 }
327
328 rewind_file = 0;
329 osd_fseek(state->file, 0, SEEK_SET);
330 length = osd_fread(state->file, buffer, sizeof(buffer) - 1);
331 if( length <= 0 )
332 {
333 logerror("state_load_section: Truncated state while loading state '%s'\n", section);
334 return;
335 }
336 }
337 buffer[ length ] = '\0';
338 p = strchr(buffer, '[');
339 if( p && !my_stricmp(p, section) )
340 {
341 /* skip CR, LF or both */
342 p += strlen(section);
343 if( *p == '\r' || *p == '\n' ) p++; /* skip CR or LF */
344 if( *p == '\r' || *p == '\n' ) p++; /* in any order */
345 state->cur_module = module;
346 state->cur_instance = instance;
347 /* now read all state_vars until the next section or end of state */
348 for( ; ; )
349 {
350 state_var *v;
351
352 /* seek back to the end of line state position */
353 osd_fseek( state->file, (int)(p - &buffer[length]), SEEK_CUR );
354
355 length = osd_fread( state->file, buffer, sizeof(buffer) - 1 );
356
357 if( length <= 0 )
358 return;
359 buffer[ length ] = '\0';
360
361 p = strchr(buffer, '\n');
362 if( !p ) p = strchr(buffer, '\r');
363 if( !p )
364 {
365 logerror("state_load_section: Line to long in section '%s'\n", section);
366 return;
367 }
368
369 *p = '\0'; /* cut buffer here */
370 p = strchr(buffer, '\n'); /* do we still have a CR? */
371 if( p ) *p = '\0';
372 p = strchr(buffer, '\r'); /* do we still have a LF? */
373 if( p ) *p = '\0';
374
375
376 if( *buffer == '[' ) /* next section ? */
377 return;
378
379 if( *buffer == '\0' || /* empty line or comment ? */
380 *buffer == '#' ||
381 *buffer == ';' )
382 continue;
383
384 /* find the state_var data */
385 p = strchr(buffer, '=');
386 if( !p )
387 {
388 logerror("state_load_section: Line contains no '=' character\n");
389 return;
390 }
391
392 /* buffer = state_var[.offs], p = data */
393 *p++ = '\0';
394
395 /* is there an offs defined ? */
396 d = strchr(buffer, '.');
397 if( d )
398 {
399 /* buffer = state_var, d = offs, p = data */
400 *d++ = '\0';
401 offs = xtoul(&d,NULL);
402 if( offs )
403 {
404 v = state->list;
405 while( v && my_stricmp(v->name, buffer) )
406 v = v->next;
407 if( !v )
408 {
409 logerror("state_load_section: Invalid variable continuation found '%s.%04X'\n", buffer, offs);
410 return;
411 }
412 }
413 }
414 else
415 {
416 offs = 0;
417 }
418
419 if( state->list )
420 {
421 /* next state_var */
422 v = state->list;
423 while( v->next ) v = v->next;
424 v->next = (struct tag_state_var*)malloc( sizeof(state_var) );
425 v = v->next;
426 }
427 else
428 {
429 /* first state_var */
430 state->list = (state_var*)malloc(sizeof(state_var));
431 v = state->list;
432 }
433 if( !v )
434 {
435 logerror("state_load_section: Out of memory while reading '%s'\n", section);
436 return;
437 }
438 v->name = (char*)malloc(strlen(buffer) + 1);
439 if( !v->name )
440 {
441 logerror("state_load_section: Out of memory while reading '%s'\n", section);
442 return;
443 }
444 strcpy(v->name, buffer);
445 v->size = 0;
446 v->data = NULL;
447
448 /* convert the line back into data */
449 data = xtoul( &p, &element_size );
450 do
451 {
452 v->size++;
453 /* need to allocate first/next chunk of memory? */
454 if( v->size * element_size >= v->chunk )
455 {
456 v->chunk += CHUNK_SIZE;
457 if( v->data )
458 v->data = realloc(v->data, v->chunk);
459 else
460 v->data = malloc(v->chunk);
461 }
462 /* check if the (re-)allocation failed */
463 if( !v->data )
464 {
465 logerror("state_load_section: Out of memory while reading '%s'\n", section);
466 return;
467 }
468 /* store element */
469 switch( element_size )
470 {
471 case 1: *((UINT8*)v->data + v->size) = data;
472 case 2: *((UINT16*)v->data + v->size) = data;
473 case 4: *((UINT32*)v->data + v->size) = data;
474 }
475 data = xtoul( &p, NULL );
476 } while( *p );
477 }
478 }
479 else
480 {
481 /* skip back a half buffer size */
482 osd_fseek( state->file, - (sizeof(buffer)-1) / 2, SEEK_CUR );
483 }
484 }
485 }
486
state_load_UINT8(void * s,const char * module,int instance,const char * name,UINT8 * val,unsigned size)487 void state_load_UINT8( void *s, const char *module, int instance,
488 const char *name, UINT8 *val, unsigned size )
489 {
490 state_handle *state = (state_handle *)s;
491 state_var *v;
492
493 state_load_section( state, module, instance );
494
495 v = state->list;
496 while( v && my_stricmp(v->name, name) ) v = v->next;
497
498 if( v )
499 {
500 unsigned offs;
501 for( offs = 0; offs < size && offs < v->size; offs++ )
502 *val++ = *((UINT8*)v->data + offs);
503 }
504 else
505 {
506 logerror("state_load_UINT8: variable '%s' not found in section [%s.%d]\n", name, module, instance);
507 memset(val, 0, size);
508 }
509 }
510
state_load_INT8(void * s,const char * module,int instance,const char * name,INT8 * val,unsigned size)511 void state_load_INT8( void *s, const char *module, int instance,
512 const char *name, INT8 *val, unsigned size )
513 {
514 state_handle *state = (state_handle *)s;
515 state_var *v;
516
517 state_load_section( state, module, instance );
518
519 v = state->list;
520 while( v && my_stricmp(v->name, name) ) v = v->next;
521
522 if( v )
523 {
524 unsigned offs;
525 for( offs = 0; offs < size && offs < v->size; offs++ )
526 *val++ = *((INT8*)v->data + offs);
527 }
528 else
529 {
530 logerror("state_load_INT8: variable '%s' not found in section [%s.%d]\n", name, module, instance);
531 memset(val, 0, size);
532 }
533 }
534
state_load_UINT16(void * s,const char * module,int instance,const char * name,UINT16 * val,unsigned size)535 void state_load_UINT16( void *s, const char *module, int instance,
536 const char *name, UINT16 *val, unsigned size )
537 {
538 state_handle *state = (state_handle *)s;
539 state_var *v;
540
541 state_load_section( state, module, instance );
542
543 v = state->list;
544 while( v && my_stricmp(v->name, name) ) v = v->next;
545
546 if( v )
547 {
548 unsigned offs;
549 for( offs = 0; offs < size && offs < v->size; offs++ )
550 *val++ = *((UINT16*)v->data + offs);
551 }
552 else
553 {
554 logerror("state_load_UINT16: variable '%s' not found in section [%s.%d]\n", name, module, instance);
555 memset(val, 0, size * 2);
556 }
557 }
558
state_load_INT16(void * s,const char * module,int instance,const char * name,INT16 * val,unsigned size)559 void state_load_INT16( void *s, const char *module, int instance,
560 const char *name, INT16 *val, unsigned size )
561 {
562 state_handle *state = (state_handle *)s;
563 state_var *v;
564
565 state_load_section( state, module, instance );
566
567 v = state->list;
568 while( v && my_stricmp(v->name, name) ) v = v->next;
569
570 if( v )
571 {
572 unsigned offs;
573 for( offs = 0; offs < size && offs < v->size; offs++ )
574 *val++ = *((INT16*)v->data + offs);
575 }
576 else
577 {
578 logerror("state_load_INT16: variable '%s' not found in section [%s.%d]\n", name, module, instance);
579 memset(val, 0, size * 2);
580 }
581 }
582
state_load_UINT32(void * s,const char * module,int instance,const char * name,UINT32 * val,unsigned size)583 void state_load_UINT32( void *s, const char *module, int instance,
584 const char *name, UINT32 *val, unsigned size )
585 {
586 state_handle *state = (state_handle *)s;
587 state_var *v;
588
589 state_load_section( state, module, instance );
590
591 v = state->list;
592 while( v && my_stricmp(v->name, name) ) v = v->next;
593
594 if( v )
595 {
596 unsigned offs;
597 for( offs = 0; offs < size && offs < v->size; offs++ )
598 *val++ = *((UINT32*)v->data + offs);
599 }
600 else
601 {
602 logerror("state_load_UINT32: variable'%s' not found in section [%s.%d]\n", name, module, instance);
603 memset(val, 0, size * 4);
604 }
605 }
606
state_load_INT32(void * s,const char * module,int instance,const char * name,INT32 * val,unsigned size)607 void state_load_INT32( void *s, const char *module, int instance,
608 const char *name, INT32 *val, unsigned size )
609 {
610 state_handle *state = (state_handle *)s;
611 state_var *v;
612
613 state_load_section( state, module, instance );
614
615 v = state->list;
616 while( v && my_stricmp(v->name, name) ) v = v->next;
617
618 if( v )
619 {
620 unsigned offs;
621 for( offs = 0; offs < size && offs < v->size; offs++ )
622 *val++ = *((INT32*)v->data + offs);
623 }
624 else
625 {
626 logerror("state_load_INT32: variable'%s' not found in section [%s.%d]\n", name, module, instance);
627 memset(val, 0, size * 4);
628 }
629 }
630
631
632
633