#include #include #include #include #include #include "get_address.h" #include "xex.h" #ifndef VERSION #define VERSION "???" #endif #define SELF "blob2xex" #define DEFAULT_SIZE 0xffff static FILE *outfh = 0; /* get_offset() is patterned on get_address(), but isn't limited to 64K. TODO: it won't handle offsets >2GB. */ int get_offset(const char *arg) { unsigned int got; if(sscanf(arg, "0x%x", &got) != 1) if(sscanf(arg, "$%x", &got) != 1) if(sscanf(arg, "%d", &got) != 1) { fprintf(stderr, "Invalid offset '%s'\n", arg); return -1; } return (int)got; } int write_segment( const char *infile, const char *outfile, int loadaddr, int initaddr, int offset, int size) { FILE *infh; unsigned char buffer[65536], *p = buffer; xex_segment seg; int c; if(size < 1) { fprintf(stderr, SELF ": fatal: invalid size %d (must be >= 1).\n", size); return(0); } if(strcmp(infile, "-") == 0) { infh = stdin; infile = "(standard input)"; } else { infh = fopen(infile, "rb"); if(!infh) { fprintf(stderr, SELF ": %s: %s\n", infile, strerror(errno)); return(0); } } if(outfh) { seg.has_ff_header = 0; } else { seg.has_ff_header = 1; if(strcmp(outfile, "-") == 0) { outfh = stdout; } else { outfh = fopen(outfile, "wb"); if(!outfh) { fprintf(stderr, SELF ": fatal: %s: %s\n", outfile, strerror(errno)); return(0); } } } if(isatty(fileno(outfh))) { fprintf(stderr, SELF ": fatal: standard output is a terminal; not writing binary data.\n"); return 0; } seg.object = buffer; seg.len = 0; seg.start_addr = loadaddr; /* skip bytes in input. don't seek, input might be stdin. not very efficient to read 1 byte at a time, but we're dealing with very small files, by modern standards. */ while(offset) { c = getc(infh); if(c < 0) { fprintf(stderr, SELF ": fatal: offset extends past EOF on file %s\n", infile); return(0); } offset--; } /* make sure we don't wrap the Atari's address space. */ if(size + loadaddr > 0xffff) size = (size - loadaddr) + 1; /* read bytes, or until EOF (which is not an error) */ while(size) { c = getc(infh); if(c < 0) break; *p++ = c; seg.len++; size--; } if(seg.len == 0) { fprintf(stderr, SELF ": fatal: read 0 bytes from %s, xex files cannot contain empty segments.\n", infile); return(0); } seg.end_addr = seg.start_addr + seg.len - 1; /* if we start/end in ROM, warn, but it's not an error. */ if(seg.start_addr >= 0xc000) fprintf(stderr, SELF ": warning: %s: start address $%04x loads into ROM.\n", infile, seg.start_addr); if(seg.end_addr >= 0xc000) fprintf(stderr, SELF ": warning: %s: end address $%04x loads into ROM.\n", infile, seg.end_addr); xex_fwrite_seg(&seg, outfh); if(initaddr >= 0) { xex_init_seg(&seg, buffer, initaddr); xex_fwrite_seg(&seg, outfh); } fclose(infh); return(seg.len); } void usage() { printf(SELF ": Usage:\n " SELF " outfile [-v] [-r runaddr]\n" " [-l loadaddr ] [-i initaddr] " "[-o offset] [-s size] infile] ..." "\nSee man page for details.\n"); } int main(int argc, char **argv) { char *outfile = 0, *infile = 0; int i, loadaddr = -1, runaddr = -1, initaddr = -1, offset = 0, size = DEFAULT_SIZE, *param = 0; int bytes = 0, was_file = 0; int segcount = 0, incount = 0; outfile = argv[1]; if(!outfile || strcmp(outfile, "--help") == 0 || strcmp(outfile, "-h") == 0) { usage(); exit(0); } if(strcmp(outfile, "--version") == 0 || strcmp(outfile, "-V") == 0) { printf(SELF " " VERSION "\n"); exit(0); } if(outfile[0] == '-' && outfile[1] != '\0') { fprintf(stderr, SELF ": output file must come before any options.\n"); exit(1); } for(i = 2; i < argc; i++) { char *arg = argv[i]; if(!arg[0]) continue; /* skip empty args */ if(param) { /* previous option needs an argument */ was_file = 0; if(param == &offset) { if( (offset = get_offset(arg) ) < 0 ) exit(1); } else { if( (*param = get_address(SELF, arg) ) < 0 ) exit(1); } param = 0; } else if(arg[0] == '-' && arg[1] != '\0') { was_file = 0; infile = 0; switch(arg[1]) { case 'l': param = &loadaddr; break; case 'r': param = &runaddr; break; case 'i': param = &initaddr; break; case 'o': param = &offset; break; case 's': param = &size; break; case 'v': xex_verbose = 1; break; default: fprintf(stderr, SELF ": unknown option '-%c'\n", arg[1]); usage(); exit(1); break; } } else { was_file = 1; if(loadaddr == -1) { fprintf(stderr, SELF ": at least one load address (-l) is required.\n"); exit(1); } infile = arg; incount++; if( (bytes = write_segment(infile, outfile, loadaddr, initaddr, offset, size)) ) { segcount++; loadaddr += bytes; } else { segcount = 0; break; } infile = 0; initaddr = -1; offset = 0; size = DEFAULT_SIZE; } } if(incount) { if(segcount && ((param || !was_file || (initaddr >= 0)) || (offset != 0) || (size != DEFAULT_SIZE))) { fprintf(stderr, SELF ": " "warning: extra arguments after last input file ignored.\n"); } } else { fprintf(stderr, SELF ": no input files!\n"); } if(outfh) { if(runaddr >= 0) { xex_segment seg; unsigned char buf[10]; xex_run_seg(&seg, buf, runaddr); xex_fwrite_seg(&seg, outfh); } fclose(outfh); } if(segcount) { if(xex_verbose) fprintf(stderr, SELF ": read %d input files, wrote %d segments to %s.\n", incount, segcount, outfile); } else { unlink(outfile); return 1; } return 0; }