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