main
blastem.c
1/*
2 Copyright 2013-2016 Michael Pavone
3 This file is part of BlastEm.
4 BlastEm is free software distributed under the terms of the GNU General Public License version 3 or greater. See COPYING for full license text.
5*/
6#include <stdio.h>
7#include <stdlib.h>
8#include <string.h>
9#include <ctype.h>
10
11#include "system.h"
12#include "68kinst.h"
13#include "m68k_core.h"
14#include "z80_to_x86.h"
15#include "mem.h"
16#include "vdp.h"
17#include "render.h"
18#include "genesis.h"
19#include "gdb_remote.h"
20#include "gst.h"
21#include "util.h"
22#include "romdb.h"
23#include "terminal.h"
24#include "arena.h"
25#include "config.h"
26#include "bindings.h"
27#include "menu.h"
28#include "zip.h"
29#define BLASTEM_VERSION "0.6.2"
30
31int headless = 0;
32int exit_after = 0;
33int z80_enabled = 1;
34int frame_limit = 0;
35uint8_t use_native_states = 1;
36
37tern_node * config;
38
39#define SMD_HEADER_SIZE 512
40#define SMD_MAGIC1 0x03
41#define SMD_MAGIC2 0xAA
42#define SMD_MAGIC3 0xBB
43#define SMD_BLOCK_SIZE 0x4000
44
45#include <zlib.h>
46#define ROMFILE gzFile
47#define romopen gzopen
48#define romread gzfread
49#define romseek gzseek
50#define romgetc gzgetc
51#define romclose gzclose
52
53uint16_t *process_smd_block(uint16_t *dst, uint8_t *src, size_t bytes)
54{
55 for (uint8_t *low = src, *high = (src+bytes/2), *end = src+bytes; high < end; high++, low++) {
56 *(dst++) = *low << 8 | *high;
57 }
58 return dst;
59}
60
61int load_smd_rom(ROMFILE f, void **buffer)
62{
63 uint8_t block[SMD_BLOCK_SIZE];
64 romseek(f, SMD_HEADER_SIZE, SEEK_SET);
65
66 size_t filesize = 512 * 1024;
67 size_t readsize = 0;
68 uint16_t *dst, *buf;
69 dst = buf = malloc(filesize);
70
71
72 size_t read;
73 do {
74 if ((readsize + SMD_BLOCK_SIZE > filesize)) {
75 filesize *= 2;
76 buf = realloc(buf, filesize);
77 dst = buf + readsize/sizeof(uint16_t);
78 }
79 read = romread(block, 1, SMD_BLOCK_SIZE, f);
80 if (read > 0) {
81 dst = process_smd_block(dst, block, read);
82 readsize += read;
83 }
84 } while(read > 0);
85 romclose(f);
86
87 *buffer = buf;
88
89 return readsize;
90}
91
92uint8_t is_smd_format(const char *filename, uint8_t *header)
93{
94 if (header[1] == SMD_MAGIC1 && header[8] == SMD_MAGIC2 && header[9] == SMD_MAGIC3) {
95 int i;
96 for (i = 3; i < 8; i++) {
97 if (header[i] != 0) {
98 return 0;
99 }
100 }
101 if (i == 8) {
102 if (header[2]) {
103 fatal_error("%s is a split SMD ROM which is not currently supported", filename);
104 }
105 return 1;
106 }
107 }
108 return 0;
109}
110
111uint32_t load_rom_zip(const char *filename, void **dst)
112{
113 static const char *valid_exts[] = {"bin", "md", "gen", "sms", "rom", "smd"};
114 const uint32_t num_exts = sizeof(valid_exts)/sizeof(*valid_exts);
115 zip_file *z = zip_open(filename);
116 if (!z) {
117 return 0;
118 }
119
120 for (uint32_t i = 0; i < z->num_entries; i++)
121 {
122 char *ext = path_extension(z->entries[i].name);
123 if (!ext) {
124 continue;
125 }
126 for (uint32_t j = 0; j < num_exts; j++)
127 {
128 if (!strcasecmp(ext, valid_exts[j])) {
129 size_t out_size = nearest_pow2(z->entries[i].size);
130 *dst = zip_read(z, i, &out_size);
131 if (*dst) {
132 if (is_smd_format(z->entries[i].name, *dst)) {
133 size_t offset;
134 for (offset = 0; offset + SMD_BLOCK_SIZE + SMD_HEADER_SIZE <= out_size; offset += SMD_BLOCK_SIZE)
135 {
136 uint8_t tmp[SMD_BLOCK_SIZE];
137 memcpy(tmp, *dst + offset + SMD_HEADER_SIZE, SMD_BLOCK_SIZE);
138 process_smd_block(*dst + offset, tmp, SMD_BLOCK_SIZE);
139 }
140 out_size = offset;
141 }
142 free(ext);
143 zip_close(z);
144 return out_size;
145 }
146 }
147 }
148 free(ext);
149 }
150 zip_close(z);
151 return 0;
152}
153
154uint32_t load_rom(const char * filename, void **dst, system_type *stype)
155{
156 uint8_t header[10];
157 char *ext = path_extension(filename);
158 if (ext && !strcasecmp(ext, "zip")) {
159 free(ext);
160 return load_rom_zip(filename, dst);
161 }
162 free(ext);
163 ROMFILE f = romopen(filename, "rb");
164 if (!f) {
165 return 0;
166 }
167 if (sizeof(header) != romread(header, 1, sizeof(header), f)) {
168 fatal_error("Error reading from %s\n", filename);
169 }
170
171 if (is_smd_format(filename, header)) {
172 if (stype) {
173 *stype = SYSTEM_GENESIS;
174 }
175 return load_smd_rom(f, dst);
176 }
177
178 size_t filesize = 512 * 1024;
179 size_t readsize = sizeof(header);
180
181 char *buf = malloc(filesize);
182 memcpy(buf, header, readsize);
183
184 size_t read;
185 do {
186 read = romread(buf + readsize, 1, filesize - readsize, f);
187 if (read > 0) {
188 readsize += read;
189 if (readsize == filesize) {
190 int one_more = romgetc(f);
191 if (one_more >= 0) {
192 filesize *= 2;
193 buf = realloc(buf, filesize);
194 buf[readsize++] = one_more;
195 } else {
196 read = 0;
197 }
198 }
199 }
200 } while (read > 0);
201
202 *dst = buf;
203
204 romclose(f);
205 return readsize;
206}
207
208
209
210int break_on_sync = 0;
211char *save_state_path;
212
213
214
215
216
217char * save_filename;
218system_header *current_system;
219system_header *menu_system;
220system_header *game_system;
221void persist_save()
222{
223 if (!game_system) {
224 return;
225 }
226 game_system->persist_save(game_system);
227}
228
229char *title;
230void update_title(char *rom_name)
231{
232 if (title) {
233 free(title);
234 title = NULL;
235 }
236 title = alloc_concat(rom_name, " - BlastEm");
237 render_update_caption(title);
238}
239
240static char *get_save_dir(system_media *media)
241{
242 char *savedir_template = tern_find_path(config, "ui\0save_path\0", TVAL_PTR).ptrval;
243 if (!savedir_template) {
244 savedir_template = "$USERDATA/blastem/$ROMNAME";
245 }
246 tern_node *vars = tern_insert_ptr(NULL, "ROMNAME", media->name);
247 vars = tern_insert_ptr(vars, "HOME", get_home_dir());
248 vars = tern_insert_ptr(vars, "EXEDIR", get_exe_dir());
249 vars = tern_insert_ptr(vars, "USERDATA", (char *)get_userdata_dir());
250 char *save_dir = replace_vars(savedir_template, vars, 1);
251 tern_free(vars);
252 if (!ensure_dir_exists(save_dir)) {
253 warning("Failed to create save directory %s\n", save_dir);
254 }
255 return save_dir;
256}
257
258void setup_saves(system_media *media, system_header *context)
259{
260 static uint8_t persist_save_registered;
261 rom_info *info = &context->info;
262 char *save_dir = get_save_dir(info->is_save_lock_on ? media->chain : media);
263 char const *parts[] = {save_dir, PATH_SEP, info->save_type == SAVE_I2C ? "save.eeprom" : info->save_type == SAVE_NOR ? "save.nor" : "save.sram"};
264 free(save_filename);
265 save_filename = alloc_concat_m(3, parts);
266 if (info->is_save_lock_on) {
267 //initial save dir was calculated based on lock-on cartridge because that's where the save device is
268 //save directory used for save states should still be located in the normal place
269 free(save_dir);
270 parts[0] = save_dir = get_save_dir(media);
271 }
272 if (use_native_states || context->type != SYSTEM_GENESIS) {
273 parts[2] = "quicksave.state";
274 } else {
275 parts[2] = "quicksave.gst";
276 }
277 free(save_state_path);
278 save_state_path = alloc_concat_m(3, parts);
279 context->save_dir = save_dir;
280 if (info->save_type != SAVE_NONE) {
281 context->load_save(context);
282 if (!persist_save_registered) {
283 atexit(persist_save);
284 persist_save_registered = 1;
285 }
286 }
287}
288
289void apply_updated_config(void)
290{
291 render_config_updated();
292 if (current_system && current_system->config_updated) {
293 current_system->config_updated(current_system);
294 }
295}
296
297static system_media cart, lock_on;
298void reload_media(void)
299{
300 if (!current_system) {
301 return;
302 }
303 if (current_system->next_rom) {
304 free(current_system->next_rom);
305 }
306 char const *parts[] = {
307 cart.dir, PATH_SEP, cart.name, ".", cart.extension
308 };
309 char const **start = parts[0] ? parts : parts + 2;
310 int num_parts = parts[0] ? 5 : 3;
311 if (!parts[4]) {
312 num_parts--;
313 }
314 current_system->next_rom = alloc_concat_m(num_parts, start);
315 current_system->request_exit(current_system);
316}
317
318void lockon_media(char *lock_on_path)
319{
320 reload_media();
321 cart.chain = &lock_on;
322 free(lock_on.dir);
323 free(lock_on.name);
324 free(lock_on.extension);
325 lock_on.dir = path_dirname(lock_on_path);
326 lock_on.name = basename_no_extension(lock_on_path);
327 lock_on.extension = path_extension(lock_on_path);
328 lock_on.size = load_rom(lock_on_path, &lock_on.buffer, NULL);
329}
330
331static uint32_t opts = 0;
332static uint8_t force_region = 0;
333void init_system_with_media(const char *path, system_type force_stype)
334{
335 if (game_system) {
336 game_system->persist_save(game_system);
337 //swap to game context arena and mark all allocated pages in it free
338 if (current_system == menu_system) {
339 current_system->arena = set_current_arena(game_system->arena);
340 }
341 mark_all_free();
342 game_system->free_context(game_system);
343 } else if(current_system) {
344 //start a new arena and save old one in suspended system context
345 current_system->arena = start_new_arena();
346 }
347 system_type stype = SYSTEM_UNKNOWN;
348 if (!(cart.size = load_rom(path, &cart.buffer, &stype))) {
349 fatal_error("Failed to open %s for reading\n", path);
350 }
351 free(cart.dir);
352 free(cart.name);
353 free(cart.extension);
354 cart.dir = path_dirname(path);
355 cart.name = basename_no_extension(path);
356 cart.extension = path_extension(path);
357 if (force_stype != SYSTEM_UNKNOWN) {
358 stype = force_stype;
359 }
360 if (stype == SYSTEM_UNKNOWN) {
361 stype = detect_system_type(&cart);
362 }
363 if (stype == SYSTEM_UNKNOWN) {
364 fatal_error("Failed to detect system type for %s\n", path);
365 }
366 //allocate new system context
367 game_system = alloc_config_system(stype, &cart, opts, force_region);
368 if (!game_system) {
369 fatal_error("Failed to configure emulated machine for %s\n", path);
370 }
371 if (menu_system) {
372 menu_system->next_context = game_system;
373 }
374 game_system->next_context = menu_system;
375 setup_saves(&cart, game_system);
376 update_title(game_system->info.name);
377}
378
379int main(int argc, char ** argv)
380{
381 set_exe_str(argv[0]);
382 config = load_config();
383 int width = -1;
384 int height = -1;
385 int loaded = 0;
386 system_type stype = SYSTEM_UNKNOWN, force_stype = SYSTEM_UNKNOWN;
387 char * romfname = NULL;
388 char * statefile = NULL;
389 debugger_type dtype = DEBUGGER_NATIVE;
390 uint8_t start_in_debugger = 0;
391 uint8_t debug_target = 0;
392 for (int i = 1; i < argc; i++) {
393 if (argv[i][0] == '-') {
394 switch(argv[i][1]) {
395 case 'b':
396 i++;
397 if (i >= argc) {
398 fatal_error("-b must be followed by a frame count\n");
399 }
400 headless = 1;
401 exit_after = atoi(argv[i]);
402 break;
403 case 'd':
404 start_in_debugger = 1;
405 //allow debugging the menu
406 if (argv[i][2] == 'm') {
407 debug_target = 1;
408 }
409 break;
410 case 'D':
411 gdb_remote_init();
412 dtype = DEBUGGER_GDB;
413 start_in_debugger = 1;
414 break;
415 case 'l':
416 opts |= OPT_ADDRESS_LOG;
417 break;
418 case 'v':
419 info_message("blastem %s\n", BLASTEM_VERSION);
420 return 0;
421 break;
422 case 'n':
423 z80_enabled = 0;
424 break;
425 case 'r':
426 i++;
427 if (i >= argc) {
428 fatal_error("-r must be followed by region (J, U or E)\n");
429 }
430 force_region = translate_region_char(toupper(argv[i][0]));
431 if (!force_region) {
432 fatal_error("'%c' is not a valid region character for the -r option\n", argv[i][0]);
433 }
434 break;
435 case 'm':
436 i++;
437 if (i >= argc) {
438 fatal_error("-r must be followed by a machine type (sms, gen or jag)\n");
439 }
440 if (!strcmp("sms", argv[i])) {
441 stype = force_stype = SYSTEM_SMS;
442 } else if (!strcmp("gen", argv[i])) {
443 stype = force_stype = SYSTEM_GENESIS;
444 } else {
445 fatal_error("Unrecognized machine type %s\n", argv[i]);
446 }
447 break;
448 case 's':
449 i++;
450 if (i >= argc) {
451 fatal_error("-s must be followed by a savestate filename\n");
452 }
453 statefile = argv[i];
454 break;
455 case 't':
456 force_no_terminal();
457 break;
458 case 'y':
459 opts |= YM_OPT_WAVE_LOG;
460 break;
461 case 'o': {
462 i++;
463 if (i >= argc) {
464 fatal_error("-o must be followed by a lock on cartridge filename\n");
465 }
466 lock_on.size = load_rom(argv[i], &lock_on.buffer, NULL);
467 if (!lock_on.size) {
468 fatal_error("Failed to load lock on cartridge %s\n", argv[i]);
469 }
470 lock_on.name = basename_no_extension(argv[i]);
471 lock_on.extension = path_extension(argv[i]);
472 cart.chain = &lock_on;
473 break;
474 }
475 case 'h':
476 info_message(
477 "Usage: blastem [OPTIONS] ROMFILE [WIDTH] [HEIGHT]\n"
478 "Options:\n"
479 " -h Print this help text\n"
480 " -r (J|U|E) Force region to Japan, US or Europe respectively\n"
481 " -m MACHINE Force emulated machine type to MACHINE. Valid values are:\n"
482 " sms - Sega Master System/Mark III\n"
483 " gen - Sega Genesis/Megadrive\n"
484 " -s FILE Load a GST format savestate from FILE\n"
485 " -o FILE Load FILE as a lock-on cartridge\n"
486 " -d Enter debugger on startup\n"
487 " -n Disable Z80\n"
488 " -v Display version number and exit\n"
489 " -l Log 68K code addresses (useful for assemblers)\n"
490 " -y Log individual YM-2612 channels to WAVE files\n"
491 );
492 return 0;
493 default:
494 fatal_error("Unrecognized switch %s\n", argv[i]);
495 }
496 } else if (!loaded) {
497 if (!(cart.size = load_rom(argv[i], &cart.buffer, stype == SYSTEM_UNKNOWN ? &stype : NULL))) {
498 fatal_error("Failed to open %s for reading\n", argv[i]);
499 }
500 cart.dir = path_dirname(argv[i]);
501 cart.name = basename_no_extension(argv[i]);
502 cart.extension = path_extension(argv[i]);
503 romfname = argv[i];
504 loaded = 1;
505 } else if (width < 0) {
506 width = atoi(argv[i]);
507 } else if (height < 0) {
508 height = atoi(argv[i]);
509 }
510 }
511
512 int def_width = 0, def_height = 0;
513 char *config_width = tern_find_path(config, "video\0width\0", TVAL_PTR).ptrval;
514 if (config_width) {
515 def_width = atoi(config_width);
516 }
517 if (!def_width) {
518 def_width = 640;
519 }
520 char *config_height = tern_find_path(config, "video\0height\0", TVAL_PTR).ptrval;
521 if (config_height) {
522 def_height = atoi(config_height);
523 }
524 if (!def_height) {
525 def_height = -1;
526 }
527 width = width < 1 ? def_width : width;
528 height = height < 1 ? def_height : height;
529
530 if (!headless) {
531 render_init(width, height, "BlastEm");
532 }
533 set_bindings();
534
535 uint8_t menu = !loaded;
536 if (!loaded) {
537 //load menu
538 romfname = tern_find_path(config, "ui\0rom\0", TVAL_PTR).ptrval;
539 if (!romfname) {
540 romfname = "menu.bin";
541 }
542 if (is_absolute_path(romfname)) {
543 if (!(cart.size = load_rom(romfname, &cart.buffer, &stype))) {
544 fatal_error("Failed to open UI ROM %s for reading", romfname);
545 }
546 } else {
547 cart.buffer = (uint16_t *)read_bundled_file(romfname, &cart.size);
548 if (!cart.buffer) {
549 fatal_error("Failed to open UI ROM %s for reading", romfname);
550 }
551 uint32_t rom_size = nearest_pow2(cart.size);
552 if (rom_size > cart.size) {
553 cart.buffer = realloc(cart.buffer, rom_size);
554 cart.size = rom_size;
555 }
556 }
557 //force system detection, value on command line is only for games not the menu
558 stype = detect_system_type(&cart);
559 cart.dir = path_dirname(romfname);
560 cart.name = basename_no_extension(romfname);
561 cart.extension = path_extension(romfname);
562 loaded = 1;
563 }
564 char *state_format = tern_find_path(config, "ui\0state_format\0", TVAL_PTR).ptrval;
565 if (state_format && !strcmp(state_format, "gst")) {
566 use_native_states = 0;
567 } else if (state_format && strcmp(state_format, "native")) {
568 warning("%s is not a valid value for the ui.state_format setting. Valid values are gst and native\n", state_format);
569 }
570
571 if (loaded) {
572 if (stype == SYSTEM_UNKNOWN) {
573 stype = detect_system_type(&cart);
574 }
575 if (stype == SYSTEM_UNKNOWN) {
576 fatal_error("Failed to detect system type for %s\n", romfname);
577 }
578 current_system = alloc_config_system(stype, &cart, menu ? 0 : opts, force_region);
579 if (!current_system) {
580 fatal_error("Failed to configure emulated machine for %s\n", romfname);
581 }
582
583 setup_saves(&cart, current_system);
584 update_title(current_system->info.name);
585 if (menu) {
586 menu_system = current_system;
587 } else {
588 game_system = current_system;
589 }
590 }
591
592 current_system->debugger_type = dtype;
593 current_system->enter_debugger = start_in_debugger && menu == debug_target;
594 current_system->start_context(current_system, menu ? NULL : statefile);
595 for(;;)
596 {
597 if (current_system->should_exit) {
598 break;
599 }
600 if (current_system->next_rom) {
601 char *next_rom = current_system->next_rom;
602 current_system->next_rom = NULL;
603 init_system_with_media(next_rom, force_stype);
604 free(next_rom);
605 menu = 0;
606 current_system = game_system;
607 current_system->debugger_type = dtype;
608 current_system->enter_debugger = start_in_debugger && menu == debug_target;
609 current_system->start_context(current_system, statefile);
610 } else if (menu && game_system) {
611 current_system->arena = set_current_arena(game_system->arena);
612 current_system = game_system;
613 menu = 0;
614 current_system->resume_context(current_system);
615 } else if (!menu && menu_system) {
616 current_system->arena = set_current_arena(menu_system->arena);
617 current_system = menu_system;
618 menu = 1;
619 if (!current_system->next_rom) {
620 current_system->resume_context(current_system);
621 }
622 } else {
623 break;
624 }
625 }
626
627 return 0;
628}