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