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