addrom.c (4722B)
1 #include <sys/types.h> 2 #include <sys/stat.h> 3 4 #include <fcntl.h> 5 #include <inttypes.h> 6 #include <libgen.h> 7 #include <stdarg.h> 8 #include <stdint.h> 9 #include <stdio.h> 10 #include <stdlib.h> 11 #include <string.h> 12 #include <unistd.h> 13 14 #include "arg.h" 15 16 #define HEADERSZSZ 4 17 #define GAMECNTSZ 4 18 #define HEADERSZ 16 19 #define CHTITLESZ 64 20 #define ENTITLESZ 32 21 #define ROMNAMESZ 16 22 #define MAGICSZ 8 23 #define ENTRYSZ (CHTITLESZ + ENTITLESZ + ROMNAMESZ + MAGICSZ) 24 25 #define MAGIC 0x0000000000000000 26 27 typedef struct { 28 char *chtitle; 29 char *entitle; 30 char *romname; 31 } Rom; 32 33 char *argv0; 34 static char *in, *out; 35 static intmax_t place; 36 37 static void 38 die(const char *errstr, ...) 39 { 40 va_list ap; 41 42 va_start(ap, errstr); 43 vfprintf(stderr, errstr, ap); 44 va_end(ap); 45 46 exit(1); 47 } 48 49 static void 50 addrom(char *datfile, const Rom *rom) 51 { 52 ssize_t pos = 0; 53 54 if (rom->chtitle) 55 strncpy(datfile + pos, rom->chtitle, CHTITLESZ); 56 pos += CHTITLESZ; 57 if (rom->entitle) 58 strncpy(datfile + pos, rom->entitle, ENTITLESZ); 59 pos += ENTITLESZ; 60 if (rom->romname) 61 strncpy(datfile + pos, rom->romname, ROMNAMESZ); 62 pos += ROMNAMESZ; 63 *(uint64_t *)(datfile + pos) = MAGIC; 64 } 65 66 static uint32_t 67 getbingamecount(uint64_t n) 68 { 69 return (n << 32 << 6) / 0x88888888; 70 } 71 72 static void 73 ereadinfile(char *datbuf, const ssize_t size, FILE *infile) 74 { 75 if (fread(datbuf, 1, size, infile) != size) 76 die("Couldn't read %zu bytes of input file %s\n", size, in); 77 } 78 79 static void * 80 ecalloc(const ssize_t size) 81 { 82 char *buf; 83 84 if (!(buf = calloc(size, 1))) 85 die("Can't allocate enough memory for buffer\n"); 86 87 return buf; 88 } 89 90 static void 91 usage(void) 92 { 93 die("usage: %s [-c title] [-e title] [-i file] [-n position] -o file -r name\n" 94 " %s [-c title] [-e title] -i file [-n position] [-o file] -r name\n", 95 basename(argv0), basename(argv0)); 96 } 97 98 int 99 main(int argc, char *argv[]) 100 { 101 struct stat st; 102 Rom rom = { 0 }; 103 off_t infilesize; 104 size_t outbufsize; 105 ssize_t offset; 106 FILE *infile, *outfile; 107 char *outbuf; 108 int infd; 109 110 ARGBEGIN { 111 case 'c': 112 rom.chtitle = EARGF(usage()); 113 if (strlen(rom.chtitle) > CHTITLESZ) 114 die("Chinese title must be less then than %zu bytes: " 115 "%s\n", CHTITLESZ, rom.chtitle); 116 break; 117 case 'e': 118 rom.entitle = EARGF(usage()); 119 if (strlen(rom.entitle) > ENTITLESZ) 120 die("English title must be less then than %zu bytes: " 121 "%s\n", ENTITLESZ, rom.entitle); 122 break; 123 case 'i': 124 in = EARGF(usage()); 125 break; 126 case 'n': 127 place = strtoimax(EARGF(usage()), NULL, 10); 128 if (place < 1) 129 die("Place must be at least 1: %d\n", place); 130 break; 131 case 'o': 132 out = EARGF(usage()); 133 break; 134 case 'r': 135 rom.romname = EARGF(usage()); 136 if (strlen(rom.romname) > ROMNAMESZ) 137 die("Rom name must be less then than %zu bytes: %s\n", 138 ROMNAMESZ, rom.romname); 139 break; 140 default: 141 usage(); 142 break; 143 } ARGEND 144 145 if (!(in || out) || !rom.romname) 146 usage(); 147 148 if (out && access(out, F_OK) != -1) 149 die("Output file %s exists and will not be overwritten\n", 150 out); 151 152 /* create and populate buffer */ 153 if (in) { /* from existing file */ 154 if ((infd = open(in, out ? O_RDONLY : O_RDWR)) == -1) 155 die("Can't open input file %s\n", in); 156 157 if (fstat(infd, &st) == -1 || !S_ISREG(st.st_mode)) 158 die("Input file %s is not a regular file\n", in); 159 160 if ((infilesize = st.st_size) < HEADERSZ) 161 die("Corrupted input file %s\n", in); 162 163 if (!(infile = fdopen(infd, out ? "r" : "r+"))) 164 die("Can't open input file %s\n", in); 165 166 outbufsize = infilesize + ENTRYSZ; 167 168 outbuf = ecalloc(outbufsize); 169 170 if (place == 0 || place * ENTRYSZ + HEADERSZ > infilesize) 171 offset = infilesize; 172 else 173 offset = HEADERSZ + (place - 1) * ENTRYSZ; 174 175 ereadinfile(outbuf, offset, infile); 176 177 addrom(outbuf + offset, &rom); 178 179 ereadinfile(outbuf + offset + ENTRYSZ, infilesize - offset, 180 infile); 181 } else { /* from scratch, position is ignored */ 182 outbufsize = HEADERSZ + ENTRYSZ; 183 184 outbuf = ecalloc(outbufsize); 185 186 offset = HEADERSZ; 187 188 addrom(outbuf + offset, &rom); 189 } 190 191 *(uint32_t *)(outbuf) = HEADERSZ; 192 *(uint32_t *)(outbuf + HEADERSZSZ) = 193 getbingamecount((outbufsize - HEADERSZ) / ENTRYSZ); 194 195 /* write out buffer */ 196 if (out && !(outfile = fopen(out, "w"))) 197 die("Can't open output file %s\n", out); 198 else if (!out && fseeko(infile, 0, SEEK_SET) == -1) 199 die("Couldn't seek input file %s\n", in); 200 201 if (fwrite(outbuf, 1, outbufsize, out ? outfile : infile) != 202 outbufsize) 203 die("Couldn't write all data to file %s\n", out ? out : in); 204 205 if (in && fclose(infile) == EOF) 206 fprintf(stderr, "There was an error while closing input file " 207 "%s\n", in); 208 209 if (out && fclose(outfile) == EOF) 210 fprintf(stderr, "There was an error while closing output file " 211 "%s\n", out); 212 213 free(outbuf); 214 215 return(0); 216 }