diff options
author | Bobby Bingham <koorogi@koorogi.info> | 2015-09-12 14:11:23 -0500 |
---|---|---|
committer | Bobby Bingham <koorogi@koorogi.info> | 2015-09-15 22:43:32 -0500 |
commit | f39b2b31fc8a604d7a3972cd15473bc8cab28684 (patch) | |
tree | 0480b2ca50965fb5c3c4863ff275850e4196a994 /src/satmkboot.c |
tool to create saturn boot sector
Diffstat (limited to 'src/satmkboot.c')
-rw-r--r-- | src/satmkboot.c | 218 |
1 files changed, 218 insertions, 0 deletions
diff --git a/src/satmkboot.c b/src/satmkboot.c new file mode 100644 index 0000000..6bd9823 --- /dev/null +++ b/src/satmkboot.c @@ -0,0 +1,218 @@ +#include <stdio.h> +#include <stdlib.h> +#include <unistd.h> +#include <string.h> + +#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 = "JTUBKAEL ", + .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 void usage(const char *progname) +{ + const int width = 20; + + fprintf(stderr, "usage: %s -h\n", progname); + fprintf(stderr, " %s -io\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"); +} + +static int process_args(int argc, char **argv) +{ + int fail = 0, help = 0; + int opt; + + extern char *optarg; + extern int optind, optopt; + + while ((opt = getopt(argc, argv, ":hi:o:")) != -1) { + switch (opt) { + case 'h': + help = 1; + break; + + case 'i': + if (ipfile) { + fprintf(stderr, "Duplicate -i parameter\n"); + fail = 1; + } + ipfile = optarg; + break; + + case 'o': + if (outfile) { + fprintf(stderr, "Duplicate -o parameter\n"); + fail = 1; + } + outfile = optarg; + break; + + case ':': + fprintf(stderr, "Option '%c' missing required parameter\n", optopt); + fail = 1; + break; + + default: + fprintf(stderr, "Unknown option '%c'\n", opt); + fail = 1; + } + } + + if (help) { + usage(argv[0]); + } else { + if (argv[optind]) { + fprintf(stderr, "Unknown extra arguments\n"); + fail = 1; + } + if (!ipfile || !outfile) { + fprintf(stderr, "Missing required option\n"); + fail = 1; + } + + if (fail) { + fprintf(stderr, "Use -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; + } + + 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; +} |