#include #include #include #include #include "bootinfo.h" #include "symbols.h" static struct systemid sysid = { .title = "TEST TITLE ", .maker = "SEGA TP T-000 ", .product = "T-000000G ", .version = "V0.001", .reldate = "20000101", .device = "CD-1/1 ", .regions = " ", .peripherals = "J ", .bootsize = 0xe00, .stack_master = 0, .stack_slave = 0, .load_addr = 0x06004000, .load_size = 0, }; static char ipbuf[0x7200]; static char *ipfile, *outfile; static char *serialize_region_code(char *out, const struct symbolname *region) { size_t namelen = strlen(region->name); char *tmp = out; char *end = out+32; memcpy(tmp, "\xa0\x0e\x00\x09For ", 8); tmp += 8; memcpy(tmp, region->name, namelen); tmp += namelen; *tmp++ = '.'; memset(tmp, ' ', end - tmp); return end; } #define WRITE(f,p,s) fwrite(p,s,1,f) #define WRITE32(f,v) fwrite((char[]){(v)>>24,(v)>>16,(v)>>8,(v)},4,1,f) static int write_output(FILE *fp) { static const unsigned char zeros[16]; extern const unsigned char securitycode[]; extern const size_t securitycode_size; /* system id - 0x100 bytes */ WRITE (fp, "SEGA SEGASATURN ", 16); WRITE (fp, sysid.maker, 16); WRITE (fp, sysid.product, 10); WRITE (fp, sysid.version, 6); WRITE (fp, sysid.reldate, 8); WRITE (fp, sysid.device, 8); WRITE (fp, sysid.regions, 10); WRITE (fp, " ", 6); WRITE (fp, sysid.peripherals, 16); WRITE (fp, sysid.title, 112); WRITE (fp, zeros, 16); /* reserved bytes */ WRITE32(fp, sysid.bootsize); WRITE (fp, zeros, 4); /* reserved bytes */ WRITE32(fp, sysid.stack_master); WRITE32(fp, sysid.stack_slave); WRITE32(fp, sysid.load_addr); WRITE32(fp, sysid.load_size); WRITE (fp, zeros, 8); /* reserved bytes */ /* security code */ WRITE(fp, securitycode, securitycode_size); /* initial program */ WRITE(fp, ipbuf, sysid.bootsize - 0xe00); return ferror(fp) ? -1 : 0; } static size_t load_ip(char *filename, char *out, size_t maxsize) { FILE *fp; size_t size; const char *errprefix = "Error loading initial program"; if (!(fp = fopen(filename, "rb"))) { goto fail_perror; } if ((size = fread(out, 1, maxsize, fp)) < maxsize) { /* read fewer than requested amount. determine if we hit error or eof */ if (ferror(fp)) { goto fail_perror; } } else { /* successfully read the requested amount. verify we reached eof */ if (fgetc(fp) != EOF) { fprintf(stderr, "%s: exceeds maximum size\n", errprefix); goto fail; } } fclose(fp); return size; fail_perror: perror(errprefix); fail: if (fp) fclose(fp); return -1; } static int append_symbol(char *list, size_t size, char symbol) { for (size_t i = 0; i < size; i++) { if (list[i] == ' ') list[i] = symbol; if (list[i] == symbol) return 0; } return -1; } static void print_symbols(FILE *fp, int width, const struct symbolname *symbols) { for (; symbols->symbol; symbols++) { fprintf(fp, "\t%*s%c: %s\n", width+4, "", symbols->symbol, symbols->name); } } static void usage(const char *progname) { const int width = 20; fprintf(stderr, "usage: %s -h\n", progname); fprintf(stderr, " %s -io[r]\n\n", progname); fprintf(stderr, "\t%-*s%s\n", width, "-h", "Show this help text"); fprintf(stderr, "\t%-*s%s\n", width, "-i ip.bin", "Initial program code file"); fprintf(stderr, "\t%-*s%s\n", width, "-o output", "Output file"); fprintf(stderr, "\t%-*s%s\n", width, "-r regions", "Geographical regions (default all):"); print_symbols(stderr, width, regiondefs); } #define ERR_UNKNOWN_OPTION 0 #define ERR_MISSING_OPTION 1 #define ERR_DUPLICATE_INPUT 2 #define ERR_DUPLICATE_OUTPUT 3 #define ERR_REGION_OVERFLOW 4 static const char *errmsgs[] = { "Unknown extra argument", "Missing required option", "Duplicate -i parameter", "Duplicate -o parameter", "Too many regions specified (max 10)", }; static int show_arg_error(const char *progname, int prev_errors, int error) { if (!(prev_errors & 1 << error)) fprintf(stderr, "%s: %s\n", progname, errmsgs[error]); return prev_errors | 1 << error; } static int process_args(int argc, char **argv) { int errors = 0, fail = 0, help = 0; int opt; extern char *optarg; extern int optind, optopt; while ((opt = getopt(argc, argv, "hi:o:r:")) != -1) { switch (opt) { case 'h': help = 1; break; case 'i': if (ipfile) errors = show_arg_error(argv[0], errors, ERR_DUPLICATE_INPUT); else ipfile = optarg; break; case 'o': if (outfile) errors = show_arg_error(argv[0], errors, ERR_DUPLICATE_OUTPUT); else outfile = optarg; break; case 'r': for (; *optarg; optarg++) { if (append_symbol(sysid.regions, sizeof sysid.regions, *optarg)) { errors = show_arg_error(argv[0], errors, ERR_REGION_OVERFLOW); break; } } break; default: fail = 1; } } if (help) { usage(argv[0]); } else if (!fail) { if (argv[optind]) errors = show_arg_error(argv[0], errors, ERR_UNKNOWN_OPTION); if (!ipfile || !outfile) errors = show_arg_error(argv[0], errors, ERR_MISSING_OPTION); fail = errors; } if (fail) { fprintf(stderr, "\nUse -h option for help on correct usage.\n"); } return fail | help; } int main(int argc, char **argv) { FILE *outfp = NULL; char *ipout = ipbuf; size_t ipsize; if (process_args(argc, argv)) { return 1; } if (sysid.regions[0] == ' ') strcpy(sysid.regions, "JTUBKAEL"); for (int i = 0; i < 10 && sysid.regions[i] != ' '; i++) { const struct symbolname *region = find_symbol(regiondefs, sysid.regions[i]); if (region) ipout = serialize_region_code(ipout, region); } if ((ipsize = load_ip(ipfile, ipout, sizeof ipbuf - (ipout - ipbuf))) == -1) { goto fail; } sysid.bootsize += ipsize; if (!(outfp = fopen(outfile, "wb"))) { perror("Error opening output file"); goto fail; } if (write_output(outfp)) { perror("Error writing output"); goto fail; } fclose(outfp); return 0; fail: if (outfp) fclose(outfp); return 1; }