]> git.sesse.net Git - pistorm/blob - config_file/config_file.c
12ba4b4e99955384a09dc5f833f954bc1ec14c63
[pistorm] / config_file / config_file.c
1 // SPDX-License-Identifier: MIT
2
3 #include "platforms/platforms.h"
4 #include <stdio.h>
5 #include <stdlib.h>
6 #include <string.h>
7
8 #include "rominfo.h"
9
10 #define M68K_CPU_TYPES M68K_CPU_TYPE_SCC68070
11
12 const char *cpu_types[M68K_CPU_TYPES] = {
13   "68000",
14   "68010",
15   "68EC020",
16   "68020",
17   "68EC030",
18   "68030",
19   "68EC040",
20   "68LC040",
21   "68040",
22   "SCC68070",
23 };
24
25 const char *map_type_names[MAPTYPE_NUM] = {
26   "NONE",
27   "rom",
28   "ram",
29   "register",
30   "ram (no alloc)",
31 };
32
33 const char *config_item_names[CONFITEM_NUM] = {
34   "NONE",
35   "cpu",
36   "map",
37   "loopcycles",
38   "mouse",
39   "keyboard",
40   "platform",
41   "setvar",
42   "kbfile",
43 };
44
45 const char *mapcmd_names[MAPCMD_NUM] = {
46   "NONE",
47   "type",
48   "address",
49   "size",
50   "range",
51   "file",
52   "ovl",
53   "id",
54   "autodump_file",
55   "autodump_mem",
56 };
57
58 int get_config_item_type(char *cmd) {
59   for (int i = 0; i < CONFITEM_NUM; i++) {
60     if (strcmp(cmd, config_item_names[i]) == 0) {
61       return i;
62     }
63   }
64
65   return CONFITEM_NONE;
66 }
67
68 unsigned int get_m68k_cpu_type(char *name) {
69   for (int i = 0; i < M68K_CPU_TYPES; i++) {
70     if (strcmp(name, cpu_types[i]) == 0) {
71       printf("[CFG] Set CPU type to %s.\n", cpu_types[i]);
72       return i + 1;
73     }
74   }
75
76   printf("[CFG] Invalid CPU type %s specified, defaulting to 68000.\n", name);
77   return M68K_CPU_TYPE_68000;
78 }
79
80 unsigned int get_map_cmd(char *name) {
81   for (int i = 1; i < MAPCMD_NUM; i++) {
82     if (strcmp(name, mapcmd_names[i]) == 0) {
83       return i;
84     }
85   }
86
87   return MAPCMD_UNKNOWN;
88 }
89
90 unsigned int get_map_type(char *name) {
91   for (int i = 1; i < MAPTYPE_NUM; i++) {
92     if (strcmp(name, map_type_names[i]) == 0) {
93       return i;
94     }
95   }
96
97   return MAPTYPE_NONE;
98 }
99
100 void trim_whitespace(char *str) {
101   while (strlen(str) != 0 && (str[strlen(str) - 1] == ' ' || str[strlen(str) - 1] == '\t' || str[strlen(str) - 1] == 0x0A || str[strlen(str) - 1] == 0x0D)) {
102     str[strlen(str) - 1] = '\0';
103   }
104 }
105
106 unsigned int get_int(char *str) {
107   if (strlen(str) == 0)
108     return -1;
109
110   int ret_int = 0;
111
112   if (strlen(str) > 2 && str[0] == '0' && str[1] == 'x') {
113     for (int i = 2; i < (int)strlen(str); i++) {
114       if (str[i] >= '0' && str[i] <= '9') {
115         ret_int = (str[i] - '0') | (ret_int << 4);
116       }
117       else {
118         switch(str[i]) {
119           case 'A': ret_int = 0xA | (ret_int << 4); break;
120           case 'B': ret_int = 0xB | (ret_int << 4); break;
121           case 'C': ret_int = 0xC | (ret_int << 4); break;
122           case 'D': ret_int = 0xD | (ret_int << 4); break;
123           case 'E': ret_int = 0xE | (ret_int << 4); break;
124           case 'F': ret_int = 0xF | (ret_int << 4); break;
125           case 'K': ret_int = ret_int * SIZE_KILO; break;
126           case 'M': ret_int = ret_int * SIZE_MEGA; break;
127           case 'G': ret_int = ret_int * SIZE_GIGA; break;
128           default:
129             printf("[CFG] Unknown character %c in hex value.\n", str[i]);
130             break;
131         }
132       }
133     }
134     return ret_int;
135   }
136   else {
137     ret_int = atoi(str);
138     if (str[strlen(str) - 1] == 'K')
139       ret_int = ret_int * SIZE_KILO;
140     else if (str[strlen(str) - 1] == 'M')
141       ret_int = ret_int * SIZE_MEGA;
142     else if (str[strlen(str) - 1] == 'G')
143       ret_int = ret_int * SIZE_GIGA;
144
145     return ret_int;
146   }
147 }
148
149 void get_next_string(char *str, char *str_out, int *strpos, char separator) {
150   int str_pos = 0, out_pos = 0, startquote = 0, endstring = 0;
151
152   if (!str_out)
153     return;
154
155   if (strpos)
156     str_pos = *strpos;
157
158   while ((str[str_pos] == ' ' || str[str_pos] == '\t') && str_pos < (int)strlen(str)) {
159     str_pos++;
160   }
161
162   if (str[str_pos] == '\"') {
163     str_pos++;
164     startquote = 1;
165   }
166
167
168   for (int i = str_pos; i < (int)strlen(str); i++) {
169     str_out[out_pos] = str[i];
170
171     if (startquote) {
172       if (str[i] == '\"')
173         endstring = 1;
174     } else {
175       if ((separator == ' ' && (str[i] == ' ' || str[i] == '\t')) || str[i] == separator) {
176         endstring = 1;
177       }
178     }
179
180     if (endstring) {
181       str_out[out_pos] = '\0';
182       if (strpos) {
183         *strpos = i + 1;
184       }
185       break;
186     }
187
188     out_pos++;
189     if (i + 1 == (int)strlen(str) && strpos) {
190       *strpos = i + 1;
191       str_out[out_pos] = '\0';
192     }
193   }
194 }
195
196 void add_mapping(struct emulator_config *cfg, unsigned int type, unsigned int addr, unsigned int size, int mirr_addr, char *filename, char *map_id, unsigned int autodump) {
197   unsigned int index = 0, file_size = 0;
198   FILE *in = NULL;
199
200   while (index < MAX_NUM_MAPPED_ITEMS) {
201     if (cfg->map_type[index] == MAPTYPE_NONE)
202       break;
203     index++;
204   }
205   if (index == MAX_NUM_MAPPED_ITEMS) {
206     printf("[CFG] Unable to map item, only %d items can be mapped with current binary.\n", MAX_NUM_MAPPED_ITEMS);
207     return;
208   }
209
210   cfg->map_type[index] = type;
211   cfg->map_offset[index] = addr;
212   cfg->map_size[index] = size;
213   cfg->map_high[index] = addr + size;
214   cfg->map_mirror[index] = mirr_addr;
215   if (strlen(map_id)) {
216     cfg->map_id[index] = (char *)malloc(strlen(map_id) + 1);
217     strcpy(cfg->map_id[index], map_id);
218   }
219
220   switch(type) {
221     case MAPTYPE_RAM_NOALLOC:
222       printf("[CFG] Adding %d byte (%d MB) RAM mapping %s...\n", size, size / 1024 / 1024, map_id);
223       cfg->map_data[index] = (unsigned char *)filename;
224       break;
225     case MAPTYPE_RAM:
226       printf("[CFG] Allocating %d bytes for RAM mapping (%d MB)...\n", size, size / 1024 / 1024);
227       cfg->map_data[index] = (unsigned char *)malloc(size);
228       if (!cfg->map_data[index]) {
229         printf("[CFG] ERROR: Unable to allocate memory for mapped RAM!\n");
230         goto mapping_failed;
231       }
232       memset(cfg->map_data[index], 0x00, size);
233       break;
234     case MAPTYPE_ROM:
235       in = fopen(filename, "rb");
236       if (!in) {
237         if (!autodump) {
238           printf("[CFG] Failed to open file %s for ROM mapping. Using onboard ROM instead, if available.\n", filename);
239           goto mapping_failed;
240         } else if (autodump == MAPCMD_AUTODUMP_FILE) {
241           printf("[CFG] Could not open file %s for ROM mapping. Autodump flag is set, dumping to file.\n", filename);
242           dump_range_to_file(cfg->map_offset[index], cfg->map_size[index], filename);
243           in = fopen(filename, "rb");
244           if (in == NULL) {
245             printf("[CFG] Could not open dumped file for reading. Using onboard ROM instead, if available.\n");
246             goto mapping_failed;
247           }
248         } else if (autodump == MAPCMD_AUTODUMP_MEM) {
249           printf("[CFG] Could not open file %s for ROM mapping. Autodump flag is set, dumping to memory.\n", filename);
250           cfg->map_data[index] = dump_range_to_memory(cfg->map_offset[index], cfg->map_size[index]);
251           cfg->rom_size[index] = cfg->map_size[index];
252           if (cfg->map_data[index] == NULL) {
253             printf("[CFG] Could not dump range to memory. Using onboard ROM instead, if available.\n");
254             goto mapping_failed;
255           }
256           goto skip_file_ops;
257         }
258       }
259       fseek(in, 0, SEEK_END);
260       file_size = (int)ftell(in);
261       if (size == 0) {
262         cfg->map_size[index] = file_size;
263         cfg->map_high[index] = addr + cfg->map_size[index];
264       }
265       fseek(in, 0, SEEK_SET);
266       cfg->map_data[index] = (unsigned char *)calloc(1, cfg->map_size[index]);
267       cfg->rom_size[index] = (cfg->map_size[index] <= file_size) ? cfg->map_size[index] : file_size;
268       if (!cfg->map_data[index]) {
269         printf("[CFG] ERROR: Unable to allocate memory for mapped ROM!\n");
270         goto mapping_failed;
271       }
272       memset(cfg->map_data[index], 0x00, cfg->map_size[index]);
273       fread(cfg->map_data[index], cfg->rom_size[index], 1, in);
274       if (in)
275         fclose(in);
276 skip_file_ops:
277       displayRomInfo(cfg->map_data[index], cfg->rom_size[index]);
278       if (cfg->map_size[index] == cfg->rom_size[index])
279         m68k_add_rom_range(cfg->map_offset[index], cfg->map_high[index], cfg->map_data[index]);
280       break;
281     case MAPTYPE_REGISTER:
282     default:
283       break;
284   }
285
286   printf("[CFG] [MAP %d] Added %s mapping for range %.8lX-%.8lX ID: %s\n", index, map_type_names[type], cfg->map_offset[index], cfg->map_high[index] - 1, cfg->map_id[index] ? cfg->map_id[index] : "None");
287
288   return;
289
290   mapping_failed:;
291   cfg->map_type[index] = MAPTYPE_NONE;
292   if (in)
293     fclose(in);
294 }
295
296 void free_config_file(struct emulator_config *cfg) {
297   if (!cfg) {
298     printf("[CFG] Tried to free NULL config, aborting.\n");
299   }
300
301   if (cfg->platform) {
302     cfg->platform->shutdown(cfg);
303     free(cfg->platform);
304     cfg->platform = NULL;
305   }
306
307   for (int i = 0; i < MAX_NUM_MAPPED_ITEMS; i++) {
308     if (cfg->map_data[i]) {
309       if (cfg->map_type[i] != MAPTYPE_RAM_NOALLOC) {
310         free(cfg->map_data[i]);
311       }
312       cfg->map_data[i] = NULL;
313     }
314     if (cfg->map_id[i]) {
315       free(cfg->map_id[i]);
316       cfg->map_id[i] = NULL;
317     }
318   }
319
320   if (cfg->mouse_file) {
321     free(cfg->mouse_file);
322     cfg->mouse_file = NULL;
323   }
324   if (cfg->keyboard_file) {
325     free(cfg->keyboard_file);
326     cfg->keyboard_file = NULL;
327   }
328
329   m68k_clear_ranges();
330
331   printf("[CFG] Config file freed. Maybe.\n");
332 }
333
334 struct emulator_config *load_config_file(char *filename) {
335   FILE *in = fopen(filename, "rb");
336   if (in == NULL) {
337     printf("[CFG] Failed to open config file %s for reading.\n", filename);
338     return NULL;
339   }
340
341   char *parse_line = NULL;
342   char cur_cmd[128];
343   struct emulator_config *cfg = NULL;
344   int cur_line = 1;
345
346   parse_line = (char *)calloc(1, 512);
347   if (!parse_line) {
348     printf("[CFG] Failed to allocate memory for config file line buffer.\n");
349     return NULL;
350   }
351   cfg = (struct emulator_config *)calloc(1, sizeof(struct emulator_config));
352   if (!cfg) {
353     printf("[CFG] Failed to allocate memory for temporary emulator config.\n");
354     goto load_failed;
355   }
356
357   memset(cfg, 0x00, sizeof(struct emulator_config));
358   cfg->cpu_type = M68K_CPU_TYPE_68000;
359
360   while (!feof(in)) {
361     int str_pos = 0;
362     memset(parse_line, 0x00, 512);
363     fgets(parse_line, 512, in);
364
365     if (strlen(parse_line) <= 2 || parse_line[0] == '#' || parse_line[0] == '/')
366       goto skip_line;
367
368     trim_whitespace(parse_line);
369
370     get_next_string(parse_line, cur_cmd, &str_pos, ' ');
371
372     switch (get_config_item_type(cur_cmd)) {
373       case CONFITEM_CPUTYPE:
374         cfg->cpu_type = get_m68k_cpu_type(parse_line + str_pos);
375         break;
376       case CONFITEM_MAP: {
377         unsigned int maptype = 0, mapsize = 0, mapaddr = 0, autodump = 0;
378         unsigned int mirraddr = ((unsigned int)-1);
379         char mapfile[128], mapid[128];
380         memset(mapfile, 0x00, 128);
381         memset(mapid, 0x00, 128);
382
383         while (str_pos < (int)strlen(parse_line)) {
384           get_next_string(parse_line, cur_cmd, &str_pos, '=');
385           switch(get_map_cmd(cur_cmd)) {
386             case MAPCMD_TYPE:
387               get_next_string(parse_line, cur_cmd, &str_pos, ' ');
388               maptype = get_map_type(cur_cmd);
389               //printf("Type! %s\n", map_type_names[maptype]);
390               break;
391             case MAPCMD_ADDRESS:
392               get_next_string(parse_line, cur_cmd, &str_pos, ' ');
393               mapaddr = get_int(cur_cmd);
394               //printf("Address! %.8X\n", mapaddr);
395               break;
396             case MAPCMD_SIZE:
397               get_next_string(parse_line, cur_cmd, &str_pos, ' ');
398               mapsize = get_int(cur_cmd);
399               //printf("Size! %.8X\n", mapsize);
400               break;
401             case MAPCMD_RANGE:
402               get_next_string(parse_line, cur_cmd, &str_pos, '-');
403               mapaddr = get_int(cur_cmd);
404               get_next_string(parse_line, cur_cmd, &str_pos, ' ');
405               mapsize = get_int(cur_cmd) - 1 - mapaddr;
406               //printf("Range! %d-%d\n", mapaddr, mapaddr + mapsize);
407               break;
408             case MAPCMD_FILENAME:
409               get_next_string(parse_line, cur_cmd, &str_pos, ' ');
410               strcpy(mapfile, cur_cmd);
411               //printf("File! %s\n", mapfile);
412               break;
413             case MAPCMD_MAP_ID:
414               get_next_string(parse_line, cur_cmd, &str_pos, ' ');
415               strcpy(mapid, cur_cmd);
416               //printf("File! %s\n", mapfile);
417               break;
418             case MAPCMD_OVL_REMAP:
419               get_next_string(parse_line, cur_cmd, &str_pos, ' ');
420               mirraddr = get_int(cur_cmd);
421               break;
422             case MAPCMD_AUTODUMP_FILE:
423             case MAPCMD_AUTODUMP_MEM:
424               autodump = get_map_cmd(cur_cmd);
425               break;
426             default:
427               printf("[CFG] Unknown/unhandled map argument %s on line %d.\n", cur_cmd, cur_line);
428               break;
429           }
430         }
431         add_mapping(cfg, maptype, mapaddr, mapsize, mirraddr, mapfile, mapid, autodump);
432
433         break;
434       }
435       case CONFITEM_LOOPCYCLES:
436         cfg->loop_cycles = get_int(parse_line + str_pos);
437         printf("[CFG] Set CPU loop cycles to %d.\n", cfg->loop_cycles);
438         break;
439       case CONFITEM_MOUSE:
440         get_next_string(parse_line, cur_cmd, &str_pos, ' ');
441         cfg->mouse_file = (char *)calloc(1, strlen(cur_cmd) + 1);
442         strcpy(cfg->mouse_file, cur_cmd);
443         get_next_string(parse_line, cur_cmd, &str_pos, ' ');
444         cfg->mouse_toggle_key = cur_cmd[0];
445         get_next_string(parse_line, cur_cmd, &str_pos, ' ');
446         cfg->mouse_autoconnect = (strcmp(cur_cmd, "autoconnect") == 0) ? 1 : 0;
447         cfg->mouse_enabled = 1;
448         printf("[CFG] Enabled mouse event forwarding from file %s, toggle key %c.\n", cfg->mouse_file, cfg->mouse_toggle_key);
449         break;
450       case CONFITEM_KEYBOARD:
451         get_next_string(parse_line, cur_cmd, &str_pos, ' ');
452         cfg->keyboard_toggle_key = cur_cmd[0];
453         get_next_string(parse_line, cur_cmd, &str_pos, ' ');
454         cfg->keyboard_grab = (strcmp(cur_cmd, "grab") == 0) ? 1 : 0;
455         get_next_string(parse_line, cur_cmd, &str_pos, ' ');
456         cfg->keyboard_autoconnect = (strcmp(cur_cmd, "autoconnect") == 0) ? 1 : 0;
457         printf("[CFG] Enabled keyboard event forwarding, toggle key %c", cfg->keyboard_toggle_key);
458         if (cfg->keyboard_grab)
459           printf(", locking from host when connected");
460         if (cfg->keyboard_autoconnect)
461           printf(", connected to guest at startup");
462         printf(".\n");
463         break;
464       case CONFITEM_KBFILE:
465         get_next_string(parse_line, cur_cmd, &str_pos, ' ');
466         cfg->keyboard_file = (char *)calloc(1, strlen(cur_cmd) + 1);
467         strcpy(cfg->keyboard_file, cur_cmd);
468         printf("[CFG] Set keyboard event source file to %s.\n", cfg->keyboard_file);
469         break;
470       case CONFITEM_PLATFORM: {
471         char platform_name[128], platform_sub[128];
472         memset(platform_name, 0x00, 128);
473         memset(platform_sub, 0x00, 128);
474         get_next_string(parse_line, platform_name, &str_pos, ' ');
475         printf("[CFG] Setting platform to %s", platform_name);
476         get_next_string(parse_line, platform_sub, &str_pos, ' ');
477         if (strlen(platform_sub))
478           printf(" (sub: %s)", platform_sub);
479         printf("\n");
480         cfg->platform = make_platform_config(platform_name, platform_sub);
481         break;
482       }
483       case CONFITEM_SETVAR: {
484         if (!cfg->platform) {
485           printf("[CFG] Warning: setvar used in config file with no platform specified.\n");
486           break;
487         }
488
489         char var_name[128], var_value[128];
490         memset(var_name, 0x00, 128);
491         memset(var_value, 0x00, 128);
492         get_next_string(parse_line, var_name, &str_pos, ' ');
493         get_next_string(parse_line, var_value, &str_pos, ' ');
494         cfg->platform->setvar(cfg, var_name, var_value);
495
496         break;
497       }
498       case CONFITEM_NONE:
499       default:
500         printf("[CFG] Unknown config item %s on line %d.\n", cur_cmd, cur_line);
501         break;
502     }
503
504   skip_line:
505     cur_line++;
506   }
507   goto load_successful;
508
509   load_failed:;
510   if (cfg) {
511     for (int i = 0; i < MAX_NUM_MAPPED_ITEMS; i++) {
512       if (cfg->map_data[i])
513         free(cfg->map_data[i]);
514       cfg->map_data[i] = NULL;
515     }
516     free(cfg);
517     cfg = NULL;
518   }
519   load_successful:;
520   if (parse_line)
521     free(parse_line);
522
523   return cfg;
524 }
525
526 int get_named_mapped_item(struct emulator_config *cfg, char *name) {
527   if (strlen(name) == 0)
528     return -1;
529
530   for (int i = 0; i < MAX_NUM_MAPPED_ITEMS; i++) {
531     if (cfg->map_type[i] == MAPTYPE_NONE || !cfg->map_id[i])
532       continue;
533     if (strcmp(name, cfg->map_id[i]) == 0)
534       return i;
535   }
536
537   return -1;
538 }
539
540 int get_mapped_item_by_address(struct emulator_config *cfg, uint32_t address) {
541   for (int i = 0; i < MAX_NUM_MAPPED_ITEMS; i++) {
542     if (cfg->map_type[i] == MAPTYPE_NONE || !cfg->map_data[i])
543       continue;
544     else if (address >= cfg->map_offset[i] && address < cfg->map_high[i]) {
545       if (cfg->map_type[i] == MAPTYPE_RAM || cfg->map_type[i] == MAPTYPE_RAM_NOALLOC || cfg->map_type[i] == MAPTYPE_ROM)
546         return i;
547     }
548   }
549
550   return -1;
551 }
552
553 uint8_t *get_mapped_data_pointer_by_address(struct emulator_config *cfg, uint32_t address) {
554   for (int i = 0; i < MAX_NUM_MAPPED_ITEMS; i++) {
555     if (cfg->map_type[i] == MAPTYPE_NONE || !cfg->map_data[i])
556       continue;
557     else if (address >= cfg->map_offset[i] && address < cfg->map_high[i]) {
558       if (cfg->map_type[i] == MAPTYPE_RAM || cfg->map_type[i] == MAPTYPE_RAM_NOALLOC || cfg->map_type[i] == MAPTYPE_ROM)
559         return cfg->map_data[i] + (address - cfg->map_offset[i]);
560     }
561   }
562
563   return NULL;
564 }