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