1 /*
2 * resources.c - Resource (setting) handling for VICE.
3 *
4 * Written by
5 * Ettore Perazzoli <ettore@comm2000.it>
6 * Andreas Boose <viceteam@t-online.de>
7 *
8 * This file is part of VICE, the Versatile Commodore Emulator.
9 * See README for copyright notice.
10 *
11 * This program is free software; you can redistribute it and/or modify
12 * it under the terms of the GNU General Public License as published by
13 * the Free Software Foundation; either version 2 of the License, or
14 * (at your option) any later version.
15 *
16 * This program is distributed in the hope that it will be useful,
17 * but WITHOUT ANY WARRANTY; without even the implied warranty of
18 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
19 * GNU General Public License for more details.
20 *
21 * You should have received a copy of the GNU General Public License
22 * along with this program; if not, write to the Free Software
23 * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA
24 * 02111-1307 USA.
25 *
26 */
27
28 /* This implements simple facilities to handle the resources and command-line
29 options. All the resources for the emulators can be stored in a single
30 file, and they are separated by an `emulator identifier', i.e. the machine
31 name between brackets (e.g. ``[C64]''). All the resources are stored in
32 the form ``ResourceName=ResourceValue'', and separated by newline
33 characters. Leading and trailing spaces are removed from the
34 ResourceValue unless it is put between quotes ("). */
35
36 #include "vice.h"
37
38 /* #define VICE_DEBUG_RESOURCES */
39
40 #include <stdarg.h>
41 #include <stdio.h>
42 #include <stdlib.h>
43 #include <string.h>
44 #include <ctype.h>
45
46 #ifdef HAVE_STRINGS_H
47 #include <strings.h>
48 #endif
49
50 #include "archdep.h"
51 #include "ioutil.h"
52 #include "lib.h"
53 #include "log.h"
54 #include "network.h"
55 #include "resources.h"
56 #include "util.h"
57 #include "vice-event.h"
58
59 #ifdef VICE_DEBUG_RESOURCES
60 #define DBG(x) printf x
61 #else
62 #define DBG(x)
63 #endif
64
65 typedef struct resource_ram_s {
66 /* Resource name. */
67 char *name;
68
69 /* Type of resource. */
70 resource_type_t type;
71
72 /* Factory default value. */
73 resource_value_t factory_value;
74
75 /* Is the resource important for history recording or netplay? */
76 resource_event_relevant_t event_relevant;
77
78 /* Value that is needed for correct history recording and netplay. */
79 resource_value_t *event_strict_value;
80
81 /* Pointer to the value. This is only used for *reading* it. To change
82 it, use `set_func'. */
83 resource_value_t *value_ptr;
84
85 /* Function to call to set the integer value. */
86 resource_set_func_int_t *set_func_int;
87
88 /* Function to call to set the string value. */
89 resource_set_func_string_t *set_func_string;
90
91 /* Extra parameter to pass to `set_func'. */
92 void *param;
93
94 /* callback function vector chain */
95 struct resource_callback_desc_s *callback;
96
97 /* number of next entry in hash collision list */
98 int hash_next;
99 } resource_ram_t;
100
101 /* the type of the callback vector chain */
102 typedef struct resource_callback_desc_s {
103 resource_callback_func_t *func;
104 void *param;
105 struct resource_callback_desc_s *next;
106 } resource_callback_desc_t;
107
108
109 static unsigned int num_resources, num_allocated_resources;
110 static resource_ram_t *resources;
111 static char *machine_id = NULL;
112
113 static void write_resource_item(FILE *f, int num);
114 static char *string_resource_item(int num, const char *delim);
115
116 /* use a hash table with 1024 entries */
117 static const unsigned int logHashSize = 10;
118
119 static int *hashTable = NULL;
120
121 static resource_callback_desc_t *resource_modified_callback = NULL;
122
123 /* calculate the hash key */
resources_calc_hash_key(const char * name)124 static unsigned int resources_calc_hash_key(const char *name)
125 {
126 unsigned int key, i, shift;
127
128 DBG(("resources_calc_hash_key: '%s'\n", name ? name : "<empty/null>"));
129
130 key = 0; shift = 0;
131 for (i = 0; name[i] != '\0'; i++) {
132 /* resources are case-insensitive */
133 unsigned int sym = (unsigned int)tolower((int)name[i]);
134
135 if (shift >= logHashSize) {
136 shift = 0;
137 }
138
139 key ^= (sym << shift);
140 if (shift + 8 > logHashSize) {
141 key ^= (((unsigned int)sym) >> (logHashSize - shift));
142 }
143 shift++;
144 }
145 return (key & ((1 << logHashSize) - 1));
146 }
147
148
149 /* add a new callback function at the head of the vector chain */
resources_add_callback(resource_callback_desc_t ** where,resource_callback_func_t * callback,void * param)150 static void resources_add_callback(resource_callback_desc_t **where,
151 resource_callback_func_t *callback,
152 void *param)
153 {
154 if (callback != NULL) {
155 resource_callback_desc_t *cbd;
156
157 cbd = lib_malloc(sizeof(resource_callback_desc_t));
158 cbd->func = callback;
159 cbd->param = param;
160 cbd->next = *where;
161 *where = cbd;
162 }
163 }
164
165
166 /* execute a callback vector chain */
resources_exec_callback_chain(const resource_callback_desc_t * callbacks,const char * name)167 static void resources_exec_callback_chain(const resource_callback_desc_t
168 *callbacks, const char *name)
169 {
170 const resource_callback_desc_t *cbd = callbacks;
171
172 while (cbd != NULL) {
173 (*cbd->func)(name, cbd->param);
174 cbd = cbd->next;
175 }
176 }
177
178
179 /* issue callbacks for a modified resource */
resources_issue_callback(resource_ram_t * res,int global_callback)180 static void resources_issue_callback(resource_ram_t *res, int global_callback)
181 {
182 if (res->callback != NULL) {
183 resources_exec_callback_chain(res->callback, res->name);
184 }
185
186 if ((global_callback != 0) && (resource_modified_callback != NULL)) {
187 resources_exec_callback_chain(resource_modified_callback, res->name);
188 }
189 }
190
191
192 #if 0
193 /* for debugging (hash collisions, hash chains, ...) */
194 static void resources_check_hash_table(FILE *f)
195 {
196 int i, entries;
197
198 for (i = 0, entries = 0; i < (1 << logHashSize); i++) {
199 if (hashTable[i] >= 0) {
200 int next;
201
202 fprintf(f, "%d: %s", i, resources[hashTable[i]].name);
203 next = resources[hashTable[i]].hash_next;
204 while (next >= 0) {
205 fprintf(f, " -> %s", resources[next].name);
206 next = resources[next].hash_next;
207 }
208 fprintf(f, "\n");
209 entries++;
210 }
211 }
212 fprintf(f, "NUM %d, ENTIES %d\n", num_resources, entries);
213 }
214 #endif
215
lookup(const char * name)216 static resource_ram_t *lookup(const char *name)
217 {
218 resource_ram_t *res;
219 unsigned int hashkey;
220
221 if (name == NULL) {
222 return NULL;
223 }
224 hashkey = resources_calc_hash_key(name);
225 res = (hashTable[hashkey] >= 0) ? resources + hashTable[hashkey] : NULL;
226 while (res != NULL) {
227 if (strcasecmp(res->name, name) == 0) {
228 return res;
229 }
230 res = (res->hash_next >= 0) ? resources + res->hash_next : NULL;
231 }
232 return NULL;
233 }
234
235 /* Configuration filename set via -config */
236 char *vice_config_file = NULL;
237
238 /* ------------------------------------------------------------------------- */
239 /* register an array(!) of integer resources */
resources_register_int(const resource_int_t * r)240 int resources_register_int(const resource_int_t *r)
241 {
242 const resource_int_t *sp;
243 resource_ram_t *dp;
244
245 DBG(("resources_register_int name:'%s'\n", r->name ? r->name : "<empty/null>"));
246
247 sp = r;
248 dp = resources + num_resources;
249 while (sp->name != NULL) {
250 unsigned int hashkey;
251
252 if (sp->value_ptr == NULL || sp->set_func == NULL) {
253 archdep_startup_log_error(
254 "Inconsistent resource declaration '%s'.\n", sp->name);
255 return -1;
256 }
257
258 if (lookup(sp->name)) {
259 archdep_startup_log_error(
260 "Duplicated resource declaration '%s'.\n", sp->name);
261 return -1;
262 }
263
264 if (num_allocated_resources <= num_resources) {
265 num_allocated_resources *= 2;
266 resources = lib_realloc(resources, num_allocated_resources
267 * sizeof(resource_ram_t));
268 dp = resources + num_resources;
269 }
270
271 dp->name = lib_stralloc(sp->name);
272 dp->type = RES_INTEGER;
273 dp->factory_value = uint_to_void_ptr(sp->factory_value);
274 dp->value_ptr = (void *)(sp->value_ptr);
275 dp->event_relevant = sp->event_relevant;
276 dp->event_strict_value = sp->event_strict_value;
277 dp->set_func_int = sp->set_func;
278 dp->param = sp->param;
279 dp->callback = NULL;
280
281 hashkey = resources_calc_hash_key(sp->name);
282 dp->hash_next = hashTable[hashkey];
283 hashTable[hashkey] = (int)(dp - resources);
284
285 num_resources++, sp++, dp++;
286 }
287
288 return 0;
289 }
290
resources_register_string(const resource_string_t * r)291 int resources_register_string(const resource_string_t *r)
292 {
293 const resource_string_t *sp;
294 resource_ram_t *dp;
295
296 DBG(("resources_register_string name:'%s'\n", r->name ? r->name : "<empty/null>"));
297
298 sp = r;
299 dp = resources + num_resources;
300 while (sp->name != NULL) {
301 unsigned int hashkey;
302
303 if (sp->factory_value == NULL
304 || sp->value_ptr == NULL || sp->set_func == NULL) {
305 archdep_startup_log_error(
306 "Inconsistent resource declaration '%s'.\n", sp->name);
307 return -1;
308 }
309
310 if (lookup(sp->name)) {
311 archdep_startup_log_error(
312 "Duplicated resource declaration '%s'.\n", sp->name);
313 return -1;
314 }
315
316 if (num_allocated_resources <= num_resources) {
317 num_allocated_resources *= 2;
318 resources = lib_realloc(resources, num_allocated_resources
319 * sizeof(resource_ram_t));
320 dp = resources + num_resources;
321 }
322
323 dp->name = lib_stralloc(sp->name);
324 dp->type = RES_STRING;
325 dp->factory_value = (resource_value_t)(sp->factory_value);
326 dp->value_ptr = (void *)(sp->value_ptr);
327 dp->event_relevant = sp->event_relevant;
328 dp->event_strict_value = sp->event_strict_value;
329 dp->set_func_string = sp->set_func;
330 dp->param = sp->param;
331 dp->callback = NULL;
332
333 hashkey = resources_calc_hash_key(sp->name);
334 dp->hash_next = hashTable[hashkey];
335 hashTable[hashkey] = (int)(dp - resources);
336
337 num_resources++, sp++, dp++;
338 }
339
340 return 0;
341 }
342
343
resources_free(void)344 static void resources_free(void)
345 {
346 unsigned int i;
347
348 for (i = 0; i < num_resources; i++) {
349 lib_free((resources + i)->name);
350 }
351 }
352
353
354 /** \brief Shutown resources
355 */
resources_shutdown(void)356 void resources_shutdown(void)
357 {
358 resources_free();
359
360 lib_free(resources);
361 lib_free(hashTable);
362 lib_free(machine_id);
363 lib_free(vice_config_file);
364 }
365
resources_query_type(const char * name)366 resource_type_t resources_query_type(const char *name)
367 {
368 resource_ram_t *res;
369
370 if ((res = lookup(name)) != NULL) {
371 return res->type;
372 } else {
373 return (resource_type_t)-1;
374 }
375 }
376
resources_write_item_to_file(FILE * fp,const char * name)377 int resources_write_item_to_file(FILE *fp, const char *name)
378 {
379 resource_ram_t *res = lookup(name);
380
381 if (res != NULL) {
382 write_resource_item(fp, (int)(res - resources));
383 return 0;
384 }
385 log_warning(LOG_DEFAULT, "Trying to save unknown resource '%s'", name);
386
387 return -1;
388 }
389
resources_write_item_to_string(const char * name,const char * delim)390 char *resources_write_item_to_string(const char *name, const char *delim)
391 {
392 resource_ram_t *res = lookup(name);
393
394 if (res != NULL) {
395 return string_resource_item((int)(res - resources), delim);
396 }
397
398 log_warning(LOG_DEFAULT, "Trying to save unknown resource '%s'", name);
399
400 return NULL;
401 }
402
resource_create_event_data(char ** event_data,int * data_size,resource_ram_t * r,resource_value_t value)403 static void resource_create_event_data(char **event_data, int *data_size,
404 resource_ram_t *r,
405 resource_value_t value)
406 {
407 int name_size;
408 const char *name = r->name;
409
410 name_size = (int)strlen(name) + 1;
411
412 if (r->type == RES_INTEGER) {
413 *data_size = name_size + sizeof(uint32_t);
414 } else {
415 *data_size = name_size + (int)strlen((char *)value) + 1;
416 }
417
418 *event_data = lib_malloc(*data_size);
419 strcpy(*event_data, name);
420
421 if (r->type == RES_INTEGER) {
422 *(uint32_t *)(*event_data + name_size) = vice_ptr_to_uint(value);
423 } else {
424 strcpy(*event_data + name_size, (char *)value);
425 }
426 }
427
resource_record_event(resource_ram_t * r,resource_value_t value)428 static void resource_record_event(resource_ram_t *r,
429 resource_value_t value)
430 {
431 char *event_data;
432 int data_size;
433
434 resource_create_event_data(&event_data, &data_size, r, value);
435
436 network_event_record(EVENT_RESOURCE, event_data, data_size);
437
438 lib_free(event_data);
439 }
440
441 /* ------------------------------------------------------------------------- */
442
resources_init(const char * machine)443 int resources_init(const char *machine)
444 {
445 unsigned int i;
446
447 machine_id = lib_stralloc(machine);
448 num_allocated_resources = 100;
449 num_resources = 0;
450 resources = lib_malloc(num_allocated_resources * sizeof(resource_ram_t));
451
452 /* hash table maps hash keys to index in resources array rather than
453 pointers into the array because the array may be reallocated. */
454 hashTable = lib_malloc((1 << logHashSize) * sizeof(int));
455
456 for (i = 0; i < (unsigned int)(1 << logHashSize); i++) {
457 hashTable[i] = -1;
458 }
459
460 return 0;
461 }
462
resources_set_value_internal(resource_ram_t * r,resource_value_t value)463 static int resources_set_value_internal(resource_ram_t *r,
464 resource_value_t value)
465 {
466 int status = 0;
467
468 switch (r->type) {
469 case RES_INTEGER:
470 status = (*r->set_func_int)(vice_ptr_to_int(value), r->param);
471 break;
472 case RES_STRING:
473 status = (*r->set_func_string)((const char *)value, r->param);
474 break;
475 }
476
477 if (status != 0) {
478 resources_issue_callback(r, 1);
479 }
480
481 return status;
482 }
483
resources_set_value(const char * name,resource_value_t value)484 int resources_set_value(const char *name, resource_value_t value)
485 {
486 resource_ram_t *r = lookup(name);
487
488 if (r == NULL) {
489 log_warning(LOG_DEFAULT,
490 "Trying to assign value to unknown "
491 "resource `%s'.", name);
492 return -1;
493 }
494
495 if (r->event_relevant == RES_EVENT_STRICT
496 && network_get_mode() != NETWORK_IDLE) {
497 return -2;
498 }
499
500 if (r->event_relevant == RES_EVENT_SAME && network_connected()) {
501 resource_record_event(r, value);
502 return 0;
503 }
504
505 return resources_set_value_internal(r, value);
506 }
507
resources_set_internal_int(resource_ram_t * r,int value)508 static int resources_set_internal_int(resource_ram_t *r, int value)
509 {
510 int status = 0;
511
512 switch (r->type) {
513 case RES_INTEGER:
514 status = (*r->set_func_int)(value, r->param);
515 break;
516 default:
517 return -1;
518 }
519
520 if (status != 0) {
521 resources_issue_callback(r, 1);
522 }
523
524 return status;
525 }
526
resources_set_internal_string(resource_ram_t * r,const char * value)527 static int resources_set_internal_string(resource_ram_t *r,
528 const char *value)
529 {
530 int status = 0;
531
532 switch (r->type) {
533 case RES_STRING:
534 status = (*r->set_func_string)(value, r->param);
535 break;
536 default:
537 return -1;
538 }
539
540 if (status != 0) {
541 resources_issue_callback(r, 1);
542 }
543
544 return status;
545 }
546
resources_set_int(const char * name,int value)547 int resources_set_int(const char *name, int value)
548 {
549 resource_ram_t *r = lookup(name);
550
551 if (r == NULL) {
552 log_warning(LOG_DEFAULT,
553 "Trying to assign value to unknown "
554 "resource `%s'.", name);
555 return -1;
556 }
557
558 if (r->event_relevant == RES_EVENT_STRICT && network_get_mode() != NETWORK_IDLE) {
559 return -2;
560 }
561
562 if (r->event_relevant == RES_EVENT_SAME && network_connected()) {
563 resource_record_event(r, uint_to_void_ptr(value));
564 return 0;
565 }
566
567 return resources_set_internal_int(r, value);
568 }
569
resources_set_string(const char * name,const char * value)570 int resources_set_string(const char *name, const char *value)
571 {
572 resource_ram_t *r = lookup(name);
573
574 if (r == NULL) {
575 log_warning(LOG_DEFAULT,
576 "Trying to assign value to unknown "
577 "resource `%s'.", name);
578 return -1;
579 }
580
581 if (r->event_relevant == RES_EVENT_STRICT && network_get_mode() != NETWORK_IDLE) {
582 return -2;
583 }
584
585 if (r->event_relevant == RES_EVENT_SAME && network_connected()) {
586 resource_record_event(r, (resource_value_t)value);
587 return 0;
588 }
589
590 return resources_set_internal_string(r, value);
591 }
592
resources_set_value_event(void * data,int size)593 void resources_set_value_event(void *data, int size)
594 {
595 char *name;
596 char *valueptr;
597 resource_ram_t *r;
598
599 name = data;
600 valueptr = name + strlen(name) + 1;
601 r = lookup(name);
602 if (r->type == RES_INTEGER) {
603 resources_set_value_internal(r, (resource_value_t) uint_to_void_ptr(*(uint32_t *)valueptr));
604 } else {
605 resources_set_value_internal(r, (resource_value_t)valueptr);
606 }
607 }
608
resources_set_int_sprintf(const char * name,int value,...)609 int resources_set_int_sprintf(const char *name, int value, ...)
610 {
611 va_list args;
612 char *resname;
613 int result;
614
615 va_start(args, value);
616 resname = lib_mvsprintf(name, args);
617 va_end(args);
618
619 result = resources_set_int(resname, value);
620 lib_free(resname);
621
622 return result;
623 }
624
resources_set_string_sprintf(const char * name,const char * value,...)625 int resources_set_string_sprintf(const char *name, const char *value, ...)
626 {
627 va_list args;
628 char *resname;
629 int result;
630
631 va_start(args, value);
632 resname = lib_mvsprintf(name, args);
633 va_end(args);
634
635 result = resources_set_string(resname, value);
636 lib_free(resname);
637
638 return result;
639 }
640
resources_set_value_string(const char * name,const char * value)641 int resources_set_value_string(const char *name, const char *value)
642 {
643 resource_ram_t *r = lookup(name);
644 int status;
645
646 if (r == NULL) {
647 log_warning(LOG_DEFAULT,
648 "Trying to assign value to unknown "
649 "resource `%s'.", name);
650 return -1;
651 }
652
653 switch (r->type) {
654 case RES_INTEGER:
655 {
656 char *endptr;
657 int int_value;
658
659 int_value = (int)strtol(value, &endptr, 0);
660
661 if (*endptr == '\0') {
662 status = (*r->set_func_int)(int_value, r->param);
663 } else {
664 status = -1;
665 }
666 }
667 break;
668 case RES_STRING:
669 status = (*r->set_func_string)(value, r->param);
670 break;
671 default:
672 log_warning(LOG_DEFAULT, "Unknown resource type for `%s'", name);
673 status = -1;
674 break;
675 }
676
677 if (status != 0) {
678 resources_issue_callback(r, 1);
679 }
680
681 return status;
682 }
683
resources_get_value(const char * name,void * value_return)684 int resources_get_value(const char *name, void *value_return)
685 {
686 resource_ram_t *r = lookup(name);
687
688 if (r == NULL) {
689 log_warning(LOG_DEFAULT,
690 "Trying to read value from unknown "
691 "resource `%s'.", name);
692 return -1;
693 }
694
695 switch (r->type) {
696 case RES_INTEGER:
697 *(int *)value_return = *(int *)r->value_ptr;
698 break;
699 case RES_STRING:
700 *(char **)value_return = *(char **)r->value_ptr;
701 break;
702 default:
703 log_warning(LOG_DEFAULT, "Unknown resource type for `%s'", name);
704 return -1;
705 }
706
707 return 0;
708 }
709
resources_get_int(const char * name,int * value_return)710 int resources_get_int(const char *name, int *value_return)
711 {
712 resource_ram_t *r = lookup(name);
713
714 if (r == NULL) {
715 log_warning(LOG_DEFAULT,
716 "Trying to read value from unknown "
717 "resource `%s'.", name);
718 return -1;
719 }
720
721 switch (r->type) {
722 case RES_INTEGER:
723 *value_return = *(int *)r->value_ptr;
724 break;
725 default:
726 log_warning(LOG_DEFAULT, "Unknown resource type for `%s'", name);
727 return -1;
728 }
729
730 return 0;
731 }
732
resources_get_string(const char * name,const char ** value_return)733 int resources_get_string(const char *name, const char **value_return)
734 {
735 resource_ram_t *r = lookup(name);
736
737 if (r == NULL) {
738 log_warning(LOG_DEFAULT,
739 "Trying to read value from unknown "
740 "resource `%s'.", name);
741 return -1;
742 }
743
744 switch (r->type) {
745 case RES_STRING:
746 *value_return = *(const char **)r->value_ptr;
747 break;
748 default:
749 log_warning(LOG_DEFAULT, "Unknown resource type for `%s'", name);
750 return -1;
751 }
752
753 return 0;
754 }
755
resources_get_int_sprintf(const char * name,int * value_return,...)756 int resources_get_int_sprintf(const char *name, int *value_return, ...)
757 {
758 va_list args;
759 char *resname;
760 int result;
761
762 va_start(args, value_return);
763 resname = lib_mvsprintf(name, args);
764 va_end(args);
765
766 result = resources_get_int(resname, value_return);
767 lib_free(resname);
768
769 return result;
770 }
771
resources_get_string_sprintf(const char * name,const char ** value_return,...)772 int resources_get_string_sprintf(const char *name, const char **value_return,
773 ...)
774 {
775 va_list args;
776 char *resname;
777 int result;
778
779 va_start(args, value_return);
780 resname = lib_mvsprintf(name, args);
781 va_end(args);
782
783 result = resources_get_string(resname, value_return);
784 lib_free(resname);
785
786 return result;
787 }
788
resources_set_default_int(const char * name,int value)789 int resources_set_default_int(const char *name, int value)
790 {
791 resource_ram_t *r = lookup(name);
792
793 if (r == NULL) {
794 log_warning(LOG_DEFAULT,
795 "Trying to assign default to unknown "
796 "resource `%s'.", name);
797 return -1;
798 }
799
800 r->factory_value = uint_to_void_ptr(value);
801 return 0;
802 }
803
resources_set_default_string(const char * name,char * value)804 int resources_set_default_string(const char *name, char *value)
805 {
806 resource_ram_t *r = lookup(name);
807
808 if (r == NULL) {
809 log_warning(LOG_DEFAULT,
810 "Trying to assign default to unknown "
811 "resource `%s'.", name);
812 return -1;
813 }
814 /* since these pointers are usually static/not allocated, we just
815 assign it here and don't free() as one might expect */
816 r->factory_value = value;
817 return 0;
818 }
819
resources_get_default_value(const char * name,void * value_return)820 int resources_get_default_value(const char *name, void *value_return)
821 {
822 resource_ram_t *r = lookup(name);
823
824 if (r == NULL) {
825 log_warning(LOG_DEFAULT,
826 "Trying to read value from unknown "
827 "resource `%s'.", name);
828 return -1;
829 }
830
831 switch (r->type) {
832 case RES_INTEGER:
833 *(int *)value_return = vice_ptr_to_int(r->factory_value);
834 break;
835 case RES_STRING:
836 *(char **)value_return = (char *)(r->factory_value);
837 break;
838 default:
839 log_warning(LOG_DEFAULT, "Unknown resource type for `%s'", name);
840 return -1;
841 }
842
843 return 0;
844 }
845
846 /* FIXME: make event safe */
resources_set_defaults(void)847 int resources_set_defaults(void)
848 {
849 unsigned int i;
850
851 for (i = 0; i < num_resources; i++) {
852 switch (resources[i].type) {
853 case RES_INTEGER:
854 if ((*resources[i].set_func_int)(vice_ptr_to_int(resources[i].factory_value),
855 resources[i].param) < 0) {
856 log_verbose("Cannot set resource %s", resources[i].name);
857 return -1;
858 }
859 break;
860 case RES_STRING:
861 if ((*resources[i].set_func_string)((const char *)(resources[i].factory_value),
862 resources[i].param) < 0) {
863 log_verbose("Cannot set resource %s", resources[i].name);
864 return -1;
865 }
866 break;
867 }
868
869 resources_issue_callback(resources + i, 0);
870 }
871
872 if (resource_modified_callback != NULL) {
873 resources_exec_callback_chain(resource_modified_callback, NULL);
874 }
875
876 return 0;
877 }
878
resources_set_event_safe(void)879 int resources_set_event_safe(void)
880 {
881 unsigned int i;
882
883 for (i = 0; i < num_resources; i++) {
884 switch (resources[i].type) {
885 case RES_INTEGER:
886 if (resources[i].event_relevant == RES_EVENT_STRICT) {
887 if ((*resources[i].set_func_int)(vice_ptr_to_int(resources[i].event_strict_value),
888 resources[i].param) < 0) {
889 return -1;
890 }
891 }
892 break;
893 case RES_STRING:
894 if (resources[i].event_relevant == RES_EVENT_STRICT) {
895 if ((*resources[i].set_func_string)((const char *)(resources[i].event_strict_value),
896 resources[i].param) < 0) {
897 return -1;
898 }
899 }
900 break;
901 }
902 resources_issue_callback(resources + i, 0);
903 }
904
905 if (resource_modified_callback != NULL) {
906 resources_exec_callback_chain(resource_modified_callback, NULL);
907 }
908
909 return 0;
910 }
911
resources_get_event_safe_list(event_list_state_t * list)912 void resources_get_event_safe_list(event_list_state_t *list)
913 {
914 unsigned int i;
915 char *event_data;
916 int data_size;
917
918 for (i = 0; i < num_resources; i++) {
919 if (resources[i].event_relevant == RES_EVENT_SAME) {
920 resource_create_event_data(&event_data, &data_size,
921 &resources[i],
922 *(resources[i].value_ptr));
923 event_record_in_list(list, EVENT_RESOURCE,
924 event_data, data_size);
925 lib_free(event_data);
926 }
927 }
928 event_record_in_list(list, EVENT_LIST_END, NULL, 0);
929 }
930
resources_toggle(const char * name,int * new_value_return)931 int resources_toggle(const char *name, int *new_value_return)
932 {
933 resource_ram_t *r = lookup(name);
934 int value;
935
936 if (r == NULL) {
937 log_warning(LOG_DEFAULT,
938 "Trying to toggle boolean value of unknown "
939 "resource `%s'.", name);
940 return -1;
941 }
942
943 value = !(*(int *)r->value_ptr);
944
945 if (r->event_relevant == RES_EVENT_STRICT && network_get_mode() != NETWORK_IDLE) {
946 return -2;
947 }
948
949 if (new_value_return != NULL) {
950 *new_value_return = value;
951 }
952
953 if (r->event_relevant == RES_EVENT_SAME && network_connected()) {
954 resource_record_event(r, uint_to_void_ptr(value));
955 return 0;
956 }
957
958 return resources_set_internal_int(r, value);
959 }
960
resources_touch(const char * name)961 int resources_touch(const char *name)
962 {
963 void *tmp;
964
965 if (resources_get_value(name, (resource_value_t *)&tmp) < 0) {
966 return -1;
967 }
968
969 return resources_set_value(name, (resource_value_t)tmp);
970 }
971
972 /* ------------------------------------------------------------------------- */
973
974 /* Check whether `buf' is the emulator ID for the machine we are emulating. */
check_emu_id(const char * buf)975 static int check_emu_id(const char *buf)
976 {
977 size_t machine_id_len, buf_len;
978
979 buf_len = strlen(buf);
980 if (*buf != '[' || *(buf + buf_len - 1) != ']') {
981 return 0;
982 }
983
984 if (machine_id == NULL) {
985 return 1;
986 }
987
988 machine_id_len = strlen(machine_id);
989 if (machine_id_len != buf_len - 2) {
990 return 0;
991 }
992
993 if (strncmp(buf + 1, machine_id, machine_id_len) == 0) {
994 return 1;
995 } else {
996 return 0;
997 }
998 }
999
1000 /* ------------------------------------------------------------------------- */
1001
1002 /* Read one resource line from the file descriptor `f'.
1003 Returns:
1004 1 on success,
1005 0 on EOF or end of emulator section.
1006 -1 on general error
1007 RESERR_TYPE_INVALID on parse/type error
1008 RESERR_UNKNOWN_RESOURCE on unknown resource error
1009 */
1010 /* FIXME: make event safe */
resources_read_item_from_file(FILE * f)1011 int resources_read_item_from_file(FILE *f)
1012 {
1013 char buf[1024];
1014 char *arg_ptr;
1015 int line_len, resname_len;
1016 size_t arg_len;
1017 resource_ram_t *r;
1018
1019 line_len = util_get_line(buf, 1024, f);
1020
1021 if (line_len < 0) {
1022 return 0;
1023 }
1024
1025 /* Ignore empty lines. */
1026 if (*buf == '\0') {
1027 return 1;
1028 }
1029
1030 if (*buf == '[') {
1031 /* End of emulator-specific section. */
1032 return 0;
1033 }
1034
1035 arg_ptr = strchr(buf, '=');
1036 if (!arg_ptr) {
1037 return -1;
1038 }
1039
1040 resname_len = (int)(arg_ptr - buf);
1041 arg_ptr++;
1042 arg_len = strlen(arg_ptr);
1043
1044 /* If the value is between quotes, remove them. */
1045 if (*arg_ptr == '"' && *(arg_ptr + arg_len - 1) == '"') {
1046 *(arg_ptr + arg_len - 1) = '\0';
1047 arg_ptr++;
1048 }
1049
1050 *(buf + resname_len) = '\0';
1051
1052 {
1053 int result;
1054
1055 r = lookup(buf);
1056 if (r == NULL) {
1057 log_error(LOG_DEFAULT, "Unknown resource `%s'.", buf);
1058 return RESERR_UNKNOWN_RESOURCE;
1059 }
1060
1061 switch (r->type) {
1062 case RES_INTEGER:
1063 result = (*r->set_func_int)(atoi(arg_ptr), r->param);
1064 break;
1065 case RES_STRING:
1066 result = (*r->set_func_string)(arg_ptr, r->param);
1067 break;
1068 default:
1069 log_error(LOG_DEFAULT, "Unknown resource type for `%s'.",
1070 r->name);
1071 result = RESERR_TYPE_INVALID;
1072 break;
1073 }
1074
1075 if (result < 0) {
1076 switch (r->type) {
1077 case RES_INTEGER:
1078 case RES_STRING:
1079 log_error(LOG_DEFAULT, "Cannot assign value `%s' to resource `%s'.", arg_ptr, r->name);
1080 break;
1081 default:
1082 log_error(LOG_DEFAULT, "Cannot assign value to resource `%s'.", r->name);
1083 break;
1084 }
1085 return -1;
1086 }
1087
1088 resources_issue_callback(r, 0);
1089
1090 return 1;
1091 }
1092 }
1093
1094 /* Load the resources from file `fname'. If `fname' is NULL, load them from
1095 the default resource file. */
resources_load(const char * fname)1096 int resources_load(const char *fname)
1097 {
1098 FILE *f;
1099 int retval;
1100 int line_num;
1101 int err = 0;
1102 char *default_name = NULL;
1103
1104 if (fname == NULL) {
1105 if (vice_config_file == NULL) {
1106 default_name = archdep_default_resource_file_name();
1107 } else {
1108 default_name = lib_stralloc(vice_config_file);
1109 }
1110 fname = default_name;
1111 }
1112
1113 f = fopen(fname, MODE_READ_TEXT);
1114
1115 if (f == NULL) {
1116 lib_free(default_name);
1117 return RESERR_FILE_NOT_FOUND;
1118 }
1119
1120 log_message(LOG_DEFAULT, "Reading configuration file `%s'.", fname);
1121
1122 /* Find the start of the configuration section for this emulator. */
1123 for (line_num = 1;; line_num++) {
1124 char buf[1024];
1125
1126 if (util_get_line(buf, 1024, f) < 0) {
1127 lib_free(default_name);
1128 fclose(f);
1129 return RESERR_READ_ERROR;
1130 }
1131
1132 if (check_emu_id(buf)) {
1133 line_num++;
1134 break;
1135 }
1136 }
1137
1138 do {
1139 retval = resources_read_item_from_file(f);
1140 switch (retval) {
1141 case RESERR_TYPE_INVALID:
1142 log_error(LOG_DEFAULT,
1143 "%s: Invalid resource specification at line %d.",
1144 fname, line_num);
1145 err = 1;
1146 break;
1147 case RESERR_UNKNOWN_RESOURCE:
1148 log_warning(LOG_DEFAULT,
1149 "%s: Unknown resource specification at line %d.",
1150 fname, line_num);
1151 break;
1152 }
1153 line_num++;
1154 } while (retval != 0);
1155
1156 fclose(f);
1157 lib_free(default_name);
1158
1159 if (resource_modified_callback != NULL) {
1160 resources_exec_callback_chain(resource_modified_callback, NULL);
1161 }
1162
1163 return err ? RESERR_FILE_INVALID : 0;
1164 }
1165
1166 #ifdef __LIBRETRO__
1167 #include "cmdline.h"
1168 extern cmdline_option_ram_t *options;
1169 static char* disabled_resources[] =
1170 {
1171 /* Core options */
1172 "Mouse", "AutostartPrgMode", "AutostartDelayRandom", "VirtualDevices", "CrtcFilter", "CrtcStretchVertical",
1173 "VICExternalPalette", "VICPaletteFile", "TEDExternalPalette", "TEDPaletteFile",
1174 "CrtcExternalPalette", "CrtcPaletteFile", "VICIIExternalPalette", "VICIIPaletteFile",
1175 "VICColorGamma", "VICColorSaturation", "VICColorContrast", "VICColorBrightness", "VICColorTint",
1176 "TEDColorGamma", "TEDColorSaturation", "TEDColorContrast", "TEDColorBrightness", "TEDColorTint",
1177 "VICIIColorGamma", "VICIIColorSaturation", "VICIIColorContrast", "VICIIColorBrightness", "VICIIColorTint",
1178 "AutostartWarp", "AttachDevice8Readonly", "EasyFlashWriteCRT", "UserportJoy", "UserportJoyType",
1179 "DriveTrueEmulation", "DriveSoundEmulation", "DriveSoundEmulationVolume",
1180 "VICIIAudioLeak", "VICAudioLeak", "TEDAudioLeak", "SidStereo", "SidStereoAddressStart",
1181 "SidEngine", "SidModel", "SidResidSampling", "SidResidPassband", "SidResidGain", "SidResidFilterBias",
1182 "SidResid8580Passband", "SidResid8580Gain", "SidResid8580FilterBias", "SFXSoundExpander", "SFXSoundExpanderChip",
1183 "Go64Mode", "C128ColumnKey", "RAMBlock0", "RAMBlock1", "RAMBlock2", "RAMBlock3", "RAMBlock5", "REU", "REUsize",
1184 "Drive8Type", "WarpMode", "KeymapSymFile", "KeymapPosFile", "KeymapIndex",
1185
1186 /* Frontend resources */
1187 "SDLStatusbar", "ExitScreenshotName", "ExitScreenshotName1", "RefreshRate", "SoundRecordDeviceName", "SoundRecordDeviceArg",
1188 "SoundDeviceName", "Sound", "SoundSampleRate", "SoundBufferSize", "SoundFragmentSize", "SoundDeviceArg",
1189 "SoundSuspendTime", "SoundSpeedAdjustment", "SoundVolume", "SoundOutput", "MachineVideoStandard",
1190 "VICIIVideoCache", "VICIIDoubleScan", "VICIIHwScale", "VICIIDoubleSize", "VICIIBorderMode",
1191 "VICIIPALScanLineShade", "VICIIPALBlur", "VICIIPALOddLinePhase", "VICIIPALOddLineOffset", "VICIIFilter",
1192 "EventSnapshotDir", "EventStartSnapshot", "EventEndSnapshot", "EventStartMode", "EventImageInclude",
1193
1194 /* Stubbed resources */
1195 "DebugCartEnable", "CPMCart", "MonitorServerAddress", "MonitorServer"
1196 };
1197 static int disabled_resources_num;
resources_get_description(const char * name)1198 static char *resources_get_description(const char *name)
1199 {
1200 for (int i = 0; i < num_resources; i++)
1201 {
1202 if (options[i].resource_name == NULL)
1203 continue;
1204 if (!strcmp(options[i].resource_name, name))
1205 return (char *)options[i].description;
1206 }
1207 return "No description";
1208 }
1209
string_resource_item(int num,const char * delim)1210 static char *string_resource_item(int num, const char *delim)
1211 {
1212 /* Skip core optionized & frontend resources */
1213 for (int d = 0; d < disabled_resources_num; d++)
1214 {
1215 if (!strcmp(resources[num].name, disabled_resources[d]))
1216 return NULL;
1217 }
1218
1219 char *line = NULL;
1220 resource_value_t v;
1221
1222 switch (resources[num].type) {
1223 case RES_INTEGER:
1224 v = (resource_value_t) uint_to_void_ptr(*(int *)resources[num].value_ptr);
1225 line = lib_msprintf("%s=%d ### %s%s", resources[num].name, vice_ptr_to_int(v), resources_get_description(resources[num].name), delim);
1226 break;
1227 case RES_STRING:
1228 v = *resources[num].value_ptr;
1229 if ((char *)v != NULL) {
1230 line = lib_msprintf("%s=\"%s\" ### %s%s", resources[num].name, (char *)v,
1231 resources_get_description(resources[num].name), delim);
1232 } else {
1233 line = lib_msprintf("%s= ### %s%s", resources[num].name, resources_get_description(resources[num].name), delim);
1234 }
1235 break;
1236 default:
1237 log_error(LOG_DEFAULT, "Unknown value type for resource `%s'.",
1238 resources[num].name);
1239 break;
1240 }
1241 return line;
1242 }
1243 #else
string_resource_item(int num,const char * delim)1244 static char *string_resource_item(int num, const char *delim)
1245 {
1246 char *line = NULL;
1247 resource_value_t v;
1248
1249 switch (resources[num].type) {
1250 case RES_INTEGER:
1251 v = (resource_value_t) uint_to_void_ptr(*(int *)resources[num].value_ptr);
1252 line = lib_msprintf("%s=%d%s", resources[num].name, vice_ptr_to_int(v), delim);
1253 break;
1254 case RES_STRING:
1255 v = *resources[num].value_ptr;
1256 if ((char *)v != NULL) {
1257 line = lib_msprintf("%s=\"%s\"%s", resources[num].name, (char *)v,
1258 delim);
1259 } else {
1260 line = lib_msprintf("%s=%s", resources[num].name, delim);
1261 }
1262 break;
1263 default:
1264 log_error(LOG_DEFAULT, "Unknown value type for resource `%s'.",
1265 resources[num].name);
1266 break;
1267 }
1268 return line;
1269 }
1270 #endif
1271
1272 /* Write the resource specification for resource number `num' to file
1273 descriptor `f'. */
write_resource_item(FILE * f,int num)1274 static void write_resource_item(FILE *f, int num)
1275 {
1276 char *line;
1277
1278 line = string_resource_item(num, "\n");
1279
1280 if (line != NULL) {
1281 fputs(line, f);
1282 lib_free(line);
1283 }
1284 }
1285
1286 /* check if a resource contains its default value */
resource_item_isdefault(int num)1287 static int resource_item_isdefault(int num)
1288 {
1289 int i1, i2;
1290 char *s1, *s2;
1291 resource_value_t v;
1292
1293 switch (resources[num].type) {
1294 case RES_INTEGER:
1295 v = (resource_value_t) uint_to_void_ptr(*(int *)resources[num].value_ptr);
1296 i1 = vice_ptr_to_int(v);
1297 i2 = vice_ptr_to_int(resources[num].factory_value);
1298 if (i1 == i2) {
1299 return 1;
1300 }
1301 DBG(("%s = (int) default: \"%d\" is: \"%d\"\n", resources[num].name, i2, i1));
1302 break;
1303 case RES_STRING:
1304 v = *resources[num].value_ptr;
1305 s1 = (char *)v == NULL ? "" : (char *)v;
1306 s2 = (char *)resources[num].factory_value == NULL ? "" : (char *)resources[num].factory_value;
1307 if (!strcmp(s1, s2)) {
1308 return 1;
1309 }
1310 DBG(("%s = (string) default: \"%s\" is: \"%s\"\n", resources[num].name, s2, s1));
1311 break;
1312 default:
1313 log_error(LOG_DEFAULT, "Unknown value type for resource `%s'.", resources[num].name);
1314 break;
1315 }
1316 return 0;
1317 }
1318
1319 /* Save all the resources into file `fname'. If `fname' is NULL, save them
1320 in the default resource file. Writing the resources does not destroy the
1321 resources for the other emulators. */
resources_save(const char * fname)1322 int resources_save(const char *fname)
1323 {
1324 char *backup_name = NULL;
1325 FILE *in_file = NULL, *out_file;
1326 unsigned int i;
1327 char *default_name = NULL;
1328
1329 /* get name for config file */
1330 if (fname == NULL) {
1331 if (vice_config_file == NULL) {
1332 /* get default filename. this also creates the .vice directory if not present */
1333 default_name = archdep_default_resource_file_name();
1334 } else {
1335 default_name = lib_stralloc(vice_config_file);
1336 }
1337 fname = default_name;
1338 }
1339
1340 /* make a backup of an existing config, open it */
1341 if (util_file_exists(fname) != 0) {
1342 /* try to open it */
1343 if (ioutil_access(fname, IOUTIL_ACCESS_W_OK) != 0) {
1344 lib_free(default_name);
1345 return RESERR_WRITE_PROTECTED;
1346 }
1347 /* get backup name */
1348 backup_name = archdep_make_backup_filename(fname);
1349 /* if backup exists, remove it */
1350 if (util_file_exists(backup_name) != 0) {
1351 if (ioutil_access(backup_name, IOUTIL_ACCESS_W_OK) != 0) {
1352 lib_free(backup_name);
1353 lib_free(default_name);
1354 return RESERR_WRITE_PROTECTED;
1355 }
1356 if (ioutil_remove(backup_name) != 0) {
1357 lib_free(backup_name);
1358 lib_free(default_name);
1359 return RESERR_CANNOT_REMOVE_BACKUP;
1360 }
1361 }
1362 /* move existing config to backup */
1363 if (ioutil_rename(fname, backup_name) != 0) {
1364 lib_free(backup_name);
1365 lib_free(default_name);
1366 return RESERR_CANNOT_RENAME_FILE;
1367 }
1368 /* open the old config */
1369 in_file = fopen(backup_name, MODE_READ_TEXT);
1370 if (!in_file) {
1371 lib_free(backup_name);
1372 return RESERR_READ_ERROR;
1373 }
1374 }
1375
1376 log_message(LOG_DEFAULT, "Writing configuration file `%s'.", fname);
1377
1378 out_file = fopen(fname, MODE_WRITE_TEXT);
1379
1380 if (!out_file) {
1381 if (in_file != NULL) {
1382 fclose(in_file);
1383 }
1384 lib_free(backup_name);
1385 lib_free(default_name);
1386 return RESERR_CANNOT_CREATE_FILE;
1387 }
1388
1389 setbuf(out_file, NULL);
1390
1391 /* Copy the configuration for the other emulators. */
1392 if (in_file != NULL) {
1393 while (1) {
1394 char buf[1024];
1395
1396 if (util_get_line(buf, 1024, in_file) < 0) {
1397 break;
1398 }
1399
1400 if (check_emu_id(buf)) {
1401 break;
1402 }
1403
1404 fprintf(out_file, "%s\n", buf);
1405 }
1406 }
1407
1408 /* Write our current configuration. */
1409 fprintf(out_file, "[%s]\n", machine_id);
1410 for (i = 0; i < num_resources; i++) {
1411 /* only dump into the file what is different to the default config */
1412 if (!resource_item_isdefault(i)) {
1413 write_resource_item(out_file, i);
1414 }
1415 }
1416 fprintf(out_file, "\n");
1417
1418 if (in_file != NULL) {
1419 char buf[1024];
1420
1421 /* Skip the old configuration for this emulator. */
1422 while (1) {
1423 if (util_get_line(buf, 1024, in_file) < 0) {
1424 break;
1425 }
1426
1427 /* Check if another emulation section starts. */
1428 if (*buf == '[') {
1429 fprintf(out_file, "%s\n", buf);
1430 break;
1431 }
1432 }
1433
1434 if (!feof(in_file)) {
1435 /* Copy the configuration for the other emulators. */
1436 while (util_get_line(buf, 1024, in_file) >= 0) {
1437 fprintf(out_file, "%s\n", buf);
1438 }
1439 }
1440 fclose(in_file);
1441 /* remove the backup */
1442 ioutil_remove(backup_name);
1443 }
1444
1445 fclose(out_file);
1446 lib_free(backup_name);
1447 lib_free(default_name);
1448 return 0;
1449 }
1450
1451 /* dump ALL resources of the current machine into a file */
resources_dump(const char * fname)1452 int resources_dump(const char *fname)
1453 {
1454 FILE *out_file;
1455 unsigned int i;
1456
1457 #ifdef __LIBRETRO__
1458 disabled_resources_num = sizeof(disabled_resources) / sizeof(disabled_resources[0]);
1459 log_message(LOG_DEFAULT, "Dumping resources to file `%s'.", fname);
1460 #else
1461 log_message(LOG_DEFAULT, "Dumping %d resources to file `%s'.", num_resources, fname);
1462 #endif
1463
1464 out_file = fopen(fname, MODE_WRITE_TEXT);
1465 if (!out_file) {
1466 return RESERR_CANNOT_CREATE_FILE;
1467 }
1468
1469 setbuf(out_file, NULL);
1470
1471 /* Write our current configuration. */
1472 fprintf(out_file, "[%s]\n", machine_id);
1473 for (i = 0; i < num_resources; i++) {
1474 write_resource_item(out_file, i);
1475 }
1476 fprintf(out_file, "\n");
1477
1478 fclose(out_file);
1479 return 0;
1480 }
1481
resources_register_callback(const char * name,resource_callback_func_t * callback,void * callback_param)1482 int resources_register_callback(const char *name,
1483 resource_callback_func_t *callback,
1484 void *callback_param)
1485 {
1486 if (name == NULL) {
1487 resources_add_callback(&resource_modified_callback, callback,
1488 callback_param);
1489 return 0;
1490 } else {
1491 resource_ram_t *res = lookup(name);
1492
1493 if (res != NULL) {
1494 resources_add_callback(&(res->callback), callback, callback_param);
1495 return 0;
1496 }
1497 }
1498 return -1;
1499 }
1500