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