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