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