1 /*
2 * gbsplay is a Gameboy sound player
3 *
4 * 2003-2006,2013 (C) by Tobias Diedrich <ranma+gbsplay@tdiedrich.de>
5 * Christian Garbs <mitch@cgarbs.de>
6 * Licensed under GNU GPL v1 or, at your option, any later version.
7 */
8
9 #include <stdio.h>
10 #include <stdlib.h>
11 #include <sys/types.h>
12 #include <sys/stat.h>
13 #include <unistd.h>
14 #include <fcntl.h>
15 #include <errno.h>
16 #include <string.h>
17 #include <ctype.h>
18
19 #include "common.h"
20 #include "gbhw.h"
21 #include "gbcpu.h"
22 #include "gbs.h"
23 #include "crc32.h"
24
25 #define GBS_MAGIC "GBS"
26 #define GBS_EXTHDR_MAGIC "GBSX"
27 #define GBR_MAGIC "GBRF"
28
29 const char *boot_rom_file = ".dmg_rom.bin";
30
gbs_init(struct gbs * gbs,long subsong)31 regparm long gbs_init(struct gbs *gbs, long subsong)
32 {
33 gbhw_init(gbs->rom, gbs->romsize);
34
35 if (subsong == -1) subsong = gbs->defaultsong - 1;
36 if (subsong >= gbs->songs) {
37 fprintf(stderr, _("Subsong number out of range (min=0, max=%d).\n"), (int)gbs->songs - 1);
38 return 0;
39 }
40
41 if (gbs->defaultbank != 1) {
42 gbcpu_mem_put(0x2000, gbs->defaultbank);
43 }
44 gbhw_io_put(0xff06, gbs->tma);
45 gbhw_io_put(0xff07, gbs->tac);
46 gbhw_io_put(0xffff, 0x05);
47
48 REGS16_W(gbcpu_regs, SP, gbs->stack);
49
50 /* put halt breakpoint PC on stack */
51 gbcpu_halt_at_pc = 0xffff;
52 REGS16_W(gbcpu_regs, PC, 0xff80);
53 REGS16_W(gbcpu_regs, HL, gbcpu_halt_at_pc);
54 gbcpu_mem_put(0xff80, 0xe5); /* push hl */
55 gbcpu_step();
56 /* clear regs/memory touched by stack etup */
57 REGS16_W(gbcpu_regs, HL, 0x0000);
58 gbcpu_mem_put(0xff80, 0x00);
59
60 REGS16_W(gbcpu_regs, PC, gbs->init);
61 gbcpu_regs.rn.a = subsong;
62
63 gbs->ticks = 0;
64 gbs->subsong = subsong;
65
66 return 1;
67 }
68
gbs_set_nextsubsong_cb(struct gbs * gbs,gbs_nextsubsong_cb cb,void * priv)69 regparm void gbs_set_nextsubsong_cb(struct gbs *gbs, gbs_nextsubsong_cb cb, void *priv)
70 {
71 gbs->nextsubsong_cb = cb;
72 gbs->nextsubsong_cb_priv = priv;
73 }
74
gbs_nextsubsong(struct gbs * gbs)75 static regparm long gbs_nextsubsong(struct gbs *gbs)
76 {
77 if (gbs->nextsubsong_cb != NULL) {
78 return gbs->nextsubsong_cb(gbs, gbs->nextsubsong_cb_priv);
79 } else {
80 gbs->subsong++;
81 if (gbs->subsong >= gbs->songs)
82 return false;
83 gbs_init(gbs, gbs->subsong);
84 }
85 return true;
86 }
87
gbs_step(struct gbs * gbs,long time_to_work)88 regparm long gbs_step(struct gbs *gbs, long time_to_work)
89 {
90 long cycles = gbhw_step(time_to_work);
91 long time;
92
93 if (cycles < 0) {
94 return false;
95 }
96
97 gbs->ticks += cycles;
98
99 gbhw_getminmax(&gbs->lmin, &gbs->lmax, &gbs->rmin, &gbs->rmax);
100 gbs->lvol = -gbs->lmin > gbs->lmax ? -gbs->lmin : gbs->lmax;
101 gbs->rvol = -gbs->rmin > gbs->rmax ? -gbs->rmin : gbs->rmax;
102
103 time = gbs->ticks / GBHW_CLOCK;
104 if (gbs->silence_timeout) {
105 if (gbs->lmin == gbs->lmax && gbs->rmin == gbs->rmax) {
106 if (gbs->silence_start == 0)
107 gbs->silence_start = gbs->ticks;
108 } else gbs->silence_start = 0;
109 }
110
111 if (gbs->fadeout && gbs->subsong_timeout &&
112 time >= gbs->subsong_timeout - gbs->fadeout - gbs->gap)
113 gbhw_master_fade(128/gbs->fadeout, 0);
114 if (gbs->subsong_timeout &&
115 time >= gbs->subsong_timeout - gbs->gap)
116 gbhw_master_fade(128*16, 0);
117
118 if (gbs->silence_start &&
119 (gbs->ticks - gbs->silence_start) / GBHW_CLOCK >= gbs->silence_timeout) {
120 if (gbs->subsong_info[gbs->subsong].len == 0) {
121 gbs->subsong_info[gbs->subsong].len = gbs->ticks * GBS_LEN_DIV / GBHW_CLOCK;
122 }
123 return gbs_nextsubsong(gbs);
124 }
125 if (gbs->subsong_timeout && time >= gbs->subsong_timeout)
126 return gbs_nextsubsong(gbs);
127
128 return true;
129 }
130
gbs_printinfo(struct gbs * gbs,long verbose)131 regparm void gbs_printinfo(struct gbs *gbs, long verbose)
132 {
133 printf(_("GBSVersion: %u\n"
134 "Title: \"%s\"\n"
135 "Author: \"%s\"\n"
136 "Copyright: \"%s\"\n"
137 "Load address: 0x%04x\n"
138 "Init address: 0x%04x\n"
139 "Play address: 0x%04x\n"
140 "Stack pointer: 0x%04x\n"
141 "File size: 0x%08x\n"
142 "ROM size: 0x%08lx (%ld banks)\n"
143 "Subsongs: %u\n"
144 "Default subsong: %u\n"),
145 gbs->version,
146 gbs->title,
147 gbs->author,
148 gbs->copyright,
149 gbs->load,
150 gbs->init,
151 gbs->play,
152 gbs->stack,
153 (unsigned int)gbs->filesize,
154 gbs->romsize,
155 gbs->romsize/0x4000,
156 gbs->songs,
157 gbs->defaultsong);
158 if (gbs->tac & 0x04) {
159 long timertc = (256-gbs->tma) * (16 << (((gbs->tac+3) & 3) << 1));
160 if (gbs->tac & 0x80)
161 timertc /= 2;
162 printf(_("Timing: %2.2fHz timer%s\n"),
163 GBHW_CLOCK / (float)timertc,
164 (gbs->tac & 0x78) == 0x40 ? _(" + VBlank (ugetab)") : "");
165 } else {
166 printf(_("Timing: %s\n"),
167 _("59.7Hz VBlank\n"));
168 }
169 if (gbs->defaultbank != 1) {
170 printf(_("Bank @0x4000: %d\n"), gbs->defaultbank);
171 }
172 if (gbs->version == 2) {
173 printf(_("CRC32: 0x%08lx/0x%08lx (%s)\n"),
174 (unsigned long)gbs->crc, (unsigned long)gbs->crcnow,
175 gbs->crc == gbs->crcnow ? _("OK") : _("Failed"));
176 } else {
177 printf(_("CRC32: 0x%08lx\n"),
178 (unsigned long)gbs->crcnow);
179 }
180 if (verbose && gbs->version == 2) {
181 long i;
182 for (i=0; i<gbs->songs; i++) {
183 printf(_("Subsong %03ld: "), i);
184 if (gbs->subsong_info[i].title) {
185 printf("\"%s\" ", gbs->subsong_info[i].title);
186 } else {
187 printf("%s ", _("untitled"));
188 }
189 if (gbs->subsong_info[i].len) {
190 printf(_("(%ld seconds)\n"),
191 (long)(gbs->subsong_info[i].len >> GBS_LEN_SHIFT));
192 } else {
193 printf("%s\n", _("no time limit"));
194 }
195 }
196 }
197 }
198
gbs_free(struct gbs * gbs)199 static regparm void gbs_free(struct gbs *gbs)
200 {
201 if (gbs->buf)
202 free(gbs->buf);
203 if (gbs->subsong_info)
204 free(gbs->subsong_info);
205 free(gbs);
206 }
207
gbs_close(struct gbs * gbs)208 regparm void gbs_close(struct gbs *gbs)
209 {
210 gbs_free(gbs);
211 }
212
writeint(char * buf,uint32_t val,long bytes)213 static regparm void writeint(char *buf, uint32_t val, long bytes)
214 {
215 long shift = 0;
216 long i;
217
218 for (i=0; i<bytes; i++) {
219 buf[i] = (val >> shift) & 0xff;
220 shift += 8;
221 }
222 }
223
readint(char * buf,long bytes)224 static regparm uint32_t readint(char *buf, long bytes)
225 {
226 long i;
227 long shift = 0;
228 uint32_t res = 0;
229
230 for (i=0; i<bytes; i++) {
231 res |= (unsigned char)buf[i] << shift;
232 shift += 8;
233 }
234
235 return res;
236 }
237
gbs_write(struct gbs * gbs,char * name,long version)238 regparm long gbs_write(struct gbs *gbs, char *name, long version)
239 {
240 long fd;
241 long codelen = (gbs->codelen + 15) >> 4;
242 char pad[16];
243 char strings[65536];
244 long stringofs = 0;
245 long newlen = gbs->filesize;
246 long namelen = strlen(name);
247 char *tmpname = malloc(namelen + sizeof(".tmp\0"));
248
249 memcpy(tmpname, name, namelen);
250 sprintf(&tmpname[namelen], ".tmp");
251 memset(pad, 0xff, sizeof(pad));
252
253 if ((fd = open(tmpname, O_WRONLY|O_CREAT|O_TRUNC, 0644)) == -1) {
254 fprintf(stderr, _("Could not open %s: %s\n"), name, strerror(errno));
255 return 0;
256 }
257
258 if (version == 2) {
259 long len,i;
260 long ehdrlen = 32 + 8*gbs->songs;
261 uint32_t hdrcrc;
262
263 newlen = 0x70 + codelen*16 + ehdrlen;
264 gbs->buf[3] = 1;
265 gbs->buf = realloc(gbs->buf, newlen + 65536);
266 gbs->code = gbs->buf + 0x70;
267 gbs->exthdr = gbs->code + codelen*16;
268 writeint(gbs->buf + 0x6e, codelen, 2);
269 memset(&gbs->code[gbs->codelen], 0x00, codelen*16 - gbs->codelen);
270 memset(gbs->exthdr, 0x00, ehdrlen + 65536);
271 memcpy(gbs->exthdr, GBS_EXTHDR_MAGIC, strlen(GBS_EXTHDR_MAGIC));
272 gbs->exthdr[0x1c] = gbs->songs;
273 if ((len = strlen(gbs->title)) > 32) {
274 memcpy(strings+stringofs, gbs->title, len+1);
275 writeint(&gbs->exthdr[0x14], stringofs, 2);
276 stringofs += len+1;
277 } else writeint(&gbs->exthdr[0x14], 0xffff, 2);
278 if ((len = strlen(gbs->author)) > 32) {
279 memcpy(strings+stringofs, gbs->author, len+1);
280 writeint(&gbs->exthdr[0x16], stringofs, 2);
281 stringofs += len+1;
282 } else writeint(&gbs->exthdr[0x16], 0xffff, 2);
283 if ((len = strlen(gbs->copyright)) > 30) {
284 memcpy(strings+stringofs, gbs->copyright, len+1);
285 writeint(&gbs->exthdr[0x18], stringofs, 2);
286 stringofs += len+1;
287 } else writeint(&gbs->exthdr[0x18], 0xffff, 2);
288
289 for (i=0; i<gbs->songs; i++) {
290 writeint(&gbs->exthdr[0x20+8*i],
291 gbs->subsong_info[i].len, 4);
292 if (gbs->subsong_info[i].title &&
293 strcmp(gbs->subsong_info[i].title, "") != 0) {
294 len = strlen(gbs->subsong_info[i].title)+1;
295 memcpy(strings+stringofs, gbs->subsong_info[i].title, len);
296 writeint(&gbs->exthdr[0x20+8*i+4],
297 stringofs, 2);
298 stringofs += len;
299 } else writeint(&gbs->exthdr[0x20+8*i+4], 0xffff, 2);
300 }
301 memcpy(gbs->buf + newlen, strings, stringofs);
302 newlen += stringofs;
303
304 writeint(&gbs->exthdr[0x04], ehdrlen+stringofs-8, 4);
305 writeint(&gbs->exthdr[0x0c], gbs->filesize, 4);
306 gbs->crc = gbs_crc32(0, gbs->buf, gbs->filesize);
307 writeint(&gbs->exthdr[0x10], gbs->crc, 4);
308 hdrcrc = gbs_crc32(0, gbs->exthdr, ehdrlen+stringofs);
309 writeint(&gbs->exthdr[0x08], hdrcrc, 4);
310
311 } else {
312 if (gbs->version == 2) {
313 gbs->buf[3] = 1;
314 }
315 }
316 if (write(fd, gbs->buf, newlen) == newlen) {
317 int ret = 1;
318 close(fd);
319 if (rename(tmpname, name) == -1) {
320 fprintf(stderr, _("Could not rename %s to %s: %s\n"), tmpname, name, strerror(errno));
321 ret = 0;
322 }
323 return ret;
324 }
325 close(fd);
326
327 return 1;
328 }
329
gb_open(char * name)330 static regparm struct gbs *gb_open(char *name)
331 {
332 long fd, i, name_len;
333 struct stat st;
334 struct gbs *gbs = malloc(sizeof(struct gbs));
335 char *buf, *bootname;
336 uint8_t bootrom[256];
337 char *na_str = _("gb / not available");
338
339 memset(gbs, 0, sizeof(struct gbs));
340 gbs->silence_timeout = 2*60;
341 gbs->subsong_timeout = 2*60;
342 gbs->gap = 2;
343 gbs->fadeout = 3;
344 if ((fd = open(name, O_RDONLY)) == -1) {
345 fprintf(stderr, _("Could not open %s: %s\n"), name, strerror(errno));
346 gbs_free(gbs);
347 return NULL;
348 }
349 fstat(fd, &st);
350 gbs->buf = buf = malloc(st.st_size);
351 if (read(fd, buf, st.st_size) != st.st_size) {
352 fprintf(stderr, _("Could not read %s: %s\n"), name, strerror(errno));
353 gbs_free(gbs);
354 return NULL;
355 }
356 gbs->version = 0;
357 gbs->songs = 1;
358 gbs->defaultsong = 1;
359 gbs->defaultbank = 1;
360 gbs->load = 0;
361 gbs->init = 0x100;
362 gbs->play = gbs->init;
363 gbs->stack = 0xfffe;
364
365 /* For accuracy testing purposes, support boot rom. */
366 name_len = strlen(getenv("HOME")) + strlen(boot_rom_file) + 2;
367 bootname = malloc(name_len);
368 snprintf(bootname, name_len, "%s/%s", getenv("HOME"), boot_rom_file);
369 if ((fd = open(bootname, O_RDONLY)) != -1) {
370 if (read(fd, bootrom, sizeof(bootrom)) == sizeof(bootrom)) {
371 gbhw_enable_bootrom(bootrom);
372 gbs->init = 0;
373 }
374 }
375 free(bootname);
376
377 /* Test if this looks like a valid rom header title */
378 for (i=0x0134; i<0x0143; i++) {
379 if (!(isalnum(buf[i]) || isspace(buf[i])))
380 break;
381 }
382 if (buf[i] == 0) {
383 /* Title looks valid and is zero-terminated. */
384 gbs->title = &buf[0x0134];
385 } else {
386 gbs->title = na_str;
387 }
388 gbs->author = na_str;
389 gbs->copyright = na_str;
390 gbs->code = buf;
391 gbs->filesize = st.st_size;
392
393 gbs->subsong_info = malloc(gbs->songs * sizeof(struct gbs_subsong_info));
394 memset(gbs->subsong_info, 0, gbs->songs * sizeof(struct gbs_subsong_info));
395 gbs->codelen = st.st_size - 0x20;
396 gbs->crcnow = gbs_crc32(0, buf, gbs->filesize);
397 gbs->romsize = (gbs->codelen + 0x3fff) & ~0x3fff;
398
399 gbs->rom = calloc(1, gbs->romsize);
400 memcpy(gbs->rom, buf, gbs->codelen);
401
402 close(fd);
403
404 return gbs;
405 }
406
gbr_open(char * name)407 static regparm struct gbs *gbr_open(char *name)
408 {
409 long fd, i;
410 struct stat st;
411 struct gbs *gbs = malloc(sizeof(struct gbs));
412 char *buf;
413 char *na_str = _("gbr / not available");
414 uint16_t vsync_addr;
415 uint16_t timer_addr;
416
417 memset(gbs, 0, sizeof(struct gbs));
418 gbs->silence_timeout = 2;
419 gbs->subsong_timeout = 2*60;
420 gbs->gap = 2;
421 gbs->fadeout = 3;
422 if ((fd = open(name, O_RDONLY)) == -1) {
423 fprintf(stderr, _("Could not open %s: %s\n"), name, strerror(errno));
424 gbs_free(gbs);
425 return NULL;
426 }
427 fstat(fd, &st);
428 gbs->buf = buf = malloc(st.st_size);
429 if (read(fd, buf, st.st_size) != st.st_size) {
430 fprintf(stderr, _("Could not read %s: %s\n"), name, strerror(errno));
431 gbs_free(gbs);
432 return NULL;
433 }
434 if (strncmp(buf, GBR_MAGIC, 4) != 0) {
435 fprintf(stderr, _("Not a GBR-File: %s\n"), name);
436 gbs_free(gbs);
437 return NULL;
438 }
439 if (buf[0x05] != 0) {
440 fprintf(stderr, _("Unsupported default bank @0x0000: %d\n"), buf[0x05]);
441 gbs_free(gbs);
442 return NULL;
443 }
444 if (buf[0x07] < 1 || buf[0x07] > 3) {
445 fprintf(stderr, _("Unsupported timerflag value: %d\n"), buf[0x07]);
446 gbs_free(gbs);
447 return NULL;
448 }
449 gbs->version = 0;
450 gbs->songs = 255;
451 gbs->defaultsong = 1;
452 gbs->defaultbank = buf[0x06];
453 gbs->load = 0;
454 gbs->init = readint(&buf[0x08], 2);
455 vsync_addr = readint(&buf[0x0a], 2);
456 timer_addr = readint(&buf[0x0c], 2);
457
458 if (buf[0x07] == 1) {
459 gbs->play = vsync_addr;
460 } else {
461 gbs->play = timer_addr;
462 }
463 gbs->tma = buf[0x0e];
464 gbs->tac = buf[0x0f];
465 gbs->stack = 0xfffe;
466
467 /* Test if this looks like a valid rom header title */
468 for (i=0x0154; i<0x0163; i++) {
469 if (!(isalnum(buf[i]) || isspace(buf[i])))
470 break;
471 }
472 if (buf[i] == 0) {
473 /* Title looks valid and is zero-terminated. */
474 gbs->title = &buf[0x0154];
475 } else {
476 gbs->title = na_str;
477 }
478 gbs->author = na_str;
479 gbs->copyright = na_str;
480 gbs->code = &buf[0x20];
481 gbs->filesize = st.st_size;
482
483 gbs->subsong_info = malloc(gbs->songs * sizeof(struct gbs_subsong_info));
484 memset(gbs->subsong_info, 0, gbs->songs * sizeof(struct gbs_subsong_info));
485 gbs->codelen = st.st_size - 0x20;
486 gbs->crcnow = gbs_crc32(0, buf, gbs->filesize);
487 gbs->romsize = (gbs->codelen + 0x3fff) & ~0x3fff;
488
489 gbs->rom = calloc(1, gbs->romsize);
490 memcpy(gbs->rom, &buf[0x20], gbs->codelen);
491
492 gbs->rom[0x40] = 0xd9; /* reti */
493 gbs->rom[0x50] = 0xd9; /* reti */
494 if (buf[0x07] & 1) {
495 /* V-Blank */
496 gbs->rom[0x40] = 0xc3; /* jp imm16 */
497 gbs->rom[0x41] = vsync_addr & 0xff;
498 gbs->rom[0x42] = vsync_addr >> 8;
499 }
500 if (buf[0x07] & 2) {
501 /* Timer */
502 gbs->rom[0x50] = 0xc3; /* jp imm16 */
503 gbs->rom[0x51] = timer_addr & 0xff;
504 gbs->rom[0x52] = timer_addr >> 8;
505 }
506 close(fd);
507
508 return gbs;
509 }
510
gbs_open(char * name)511 regparm struct gbs *gbs_open(char *name)
512 {
513 long fd, i;
514 struct stat st;
515 struct gbs *gbs = malloc(sizeof(struct gbs));
516 char *buf;
517 char *buf2 = NULL;
518 long have_ehdr = 0;
519
520 memset(gbs, 0, sizeof(struct gbs));
521 gbs->silence_timeout = 2;
522 gbs->subsong_timeout = 2*60;
523 gbs->gap = 2;
524 gbs->fadeout = 3;
525 gbs->defaultbank = 1;
526 if ((fd = open(name, O_RDONLY)) == -1) {
527 fprintf(stderr, _("Could not open %s: %s\n"), name, strerror(errno));
528 gbs_free(gbs);
529 return NULL;
530 }
531 fstat(fd, &st);
532 gbs->buf = buf = malloc(st.st_size);
533 if (read(fd, buf, st.st_size) != st.st_size) {
534 fprintf(stderr, _("Could not read %s: %s\n"), name, strerror(errno));
535 gbs_free(gbs);
536 return NULL;
537 }
538 if (strncmp(buf, GBR_MAGIC, 4) == 0) {
539 gbs_free(gbs);
540 return gbr_open(name);
541 }
542 if (gbs_crc32(0, &buf[0x104], 48) == 0x46195417) {
543 gbs_free(gbs);
544 return gb_open(name);
545 }
546 if (strncmp(buf, GBS_MAGIC, 3) != 0) {
547 fprintf(stderr, _("Not a GBS-File: %s\n"), name);
548 gbs_free(gbs);
549 return NULL;
550 }
551 gbs->version = buf[0x03];
552 if (gbs->version != 1) {
553 fprintf(stderr, _("GBS Version %d unsupported.\n"), gbs->version);
554 gbs_free(gbs);
555 return NULL;
556 }
557
558 gbs->songs = buf[0x04];
559 if (gbs->songs < 1) {
560 fprintf(stderr, _("Number of subsongs = %d is unreasonable.\n"), gbs->songs);
561 gbs_free(gbs);
562 return NULL;
563 }
564
565 gbs->defaultsong = buf[0x05];
566 if (gbs->defaultsong < 1 || gbs->defaultsong > gbs->songs) {
567 fprintf(stderr, _("Default subsong %d is out of range [1..%d].\n"), gbs->defaultsong, gbs->songs);
568 gbs_free(gbs);
569 return NULL;
570 }
571
572 gbs->load = readint(&buf[0x06], 2);
573 gbs->init = readint(&buf[0x08], 2);
574 gbs->play = readint(&buf[0x0a], 2);
575 gbs->stack = readint(&buf[0x0c], 2);
576 gbs->tma = buf[0x0e];
577 gbs->tac = buf[0x0f];
578
579 memcpy(gbs->v1strings, &buf[0x10], 32);
580 memcpy(gbs->v1strings+33, &buf[0x30], 32);
581 memcpy(gbs->v1strings+66, &buf[0x50], 30);
582 gbs->title = gbs->v1strings;
583 gbs->author = gbs->v1strings+33;
584 gbs->copyright = gbs->v1strings+66;
585 gbs->code = &buf[0x70];
586 gbs->filesize = st.st_size;
587
588 gbs->subsong_info = malloc(gbs->songs * sizeof(struct gbs_subsong_info));
589 memset(gbs->subsong_info, 0, gbs->songs * sizeof(struct gbs_subsong_info));
590 gbs->codelen = (buf[0x6e] + (buf[0x6f] << 8)) << 4;
591 if ((0x70 + gbs->codelen) < (gbs->filesize - 8) &&
592 strncmp(&buf[0x70 + gbs->codelen], GBS_EXTHDR_MAGIC, 4) == 0) {
593 uint32_t crc, realcrc, ehdrlen;
594
595 gbs->exthdr = gbs->code + gbs->codelen;
596 ehdrlen = readint(&gbs->exthdr[0x04], 4) + 8;
597 crc = readint(&gbs->exthdr[0x08], 4);
598 writeint(&gbs->exthdr[0x08], 0, 4);
599
600 if ((realcrc=gbs_crc32(0, gbs->exthdr, ehdrlen)) == crc) {
601 have_ehdr = 1;
602 } else {
603 fprintf(stderr, _("Warning: Extended header found, but CRC does not match (0x%08x != 0x%08x).\n"),
604 crc, realcrc);
605 }
606 }
607 if (have_ehdr) {
608 buf2 = gbs->exthdr;
609 gbs->filesize = readint(&buf2[0x0c], 4);
610 gbs->crc = readint(&buf2[0x10], 4);
611 writeint(&buf2[0x10], 0, 4);
612 } else {
613 memcpy(gbs->v1strings+66, &buf[0x50], 32);
614 gbs->codelen = st.st_size - 0x70;
615 }
616 gbs->crcnow = gbs_crc32(0, buf, gbs->filesize);
617 if (have_ehdr) {
618 long gbs_titleex;
619 long gbs_authorex;
620 long gbs_copyex;
621 long entries;
622
623 gbs->version = 2;
624 entries = buf2[0x1c];
625 gbs->strings = gbs->exthdr + 32 + 8*entries;
626
627 gbs_titleex = readint(&buf2[0x14], 2);
628 gbs_authorex = readint(&buf2[0x16], 2);
629 gbs_copyex = readint(&buf2[0x18], 2);
630 if (gbs_titleex != 0xffff)
631 gbs->title = gbs->strings + gbs_titleex;
632 if (gbs_authorex != 0xffff)
633 gbs->author = gbs->strings + gbs_authorex;
634 if (gbs_copyex != 0xffff)
635 gbs->copyright = gbs->strings + gbs_copyex;
636
637 for (i=0; i<entries; i++) {
638 long ofs = readint(&buf2[32 + 8*i + 4], 2);
639 gbs->subsong_info[i].len = readint(&buf2[32 + 8*i], 4);
640 if (ofs == 0xffff)
641 gbs->subsong_info[i].title = NULL;
642 else gbs->subsong_info[i].title = gbs->strings + ofs;
643 }
644
645 if (gbs->crc != gbs->crcnow) {
646 fprintf(stderr, _("Warning: File CRC does not match (0x%08x != 0x%08x).\n"),
647 gbs->crc, gbs->crcnow);
648 }
649 }
650
651 gbs->romsize = (gbs->codelen + gbs->load + 0x3fff) & ~0x3fff;
652
653 gbs->rom = calloc(1, gbs->romsize);
654 memcpy(&gbs->rom[gbs->load], gbs->code, gbs->codelen);
655
656 for (i=0; i<8; i++) {
657 long addr = gbs->load + 8*i; /* jump address */
658 gbs->rom[8*i] = 0xc3; /* jp imm16 */
659 gbs->rom[8*i+1] = addr & 0xff;
660 gbs->rom[8*i+2] = addr >> 8;
661 }
662 if ((gbs->tac & 0x78) == 0x40) { /* ugetab int vector extension */
663 /* V-Blank */
664 gbs->rom[0x40] = 0xc3; /* jp imm16 */
665 gbs->rom[0x41] = (gbs->load + 0x40) & 0xff;
666 gbs->rom[0x42] = (gbs->load + 0x40) >> 8;
667 /* Timer */
668 gbs->rom[0x50] = 0xc3; /* jp imm16 */
669 gbs->rom[0x51] = (gbs->load + 0x48) & 0xff;
670 gbs->rom[0x52] = (gbs->load + 0x48) >> 8;
671 } else if (gbs->tac & 0x04) { /* timer enabled */
672 /* V-Blank */
673 gbs->rom[0x40] = 0xd9; /* reti */
674 /* Timer */
675 gbs->rom[0x50] = 0xc3; /* jp imm16 */
676 gbs->rom[0x51] = gbs->play & 0xff;
677 gbs->rom[0x52] = gbs->play >> 8;
678 } else {
679 /* V-Blank */
680 gbs->rom[0x40] = 0xc3; /* jp imm16 */
681 gbs->rom[0x41] = gbs->play & 0xff;
682 gbs->rom[0x42] = gbs->play >> 8;
683 /* Timer */
684 gbs->rom[0x50] = 0xd9; /* reti */
685 }
686 gbs->rom[0x48] = 0xd9; /* reti (LCD Stat) */
687 gbs->rom[0x58] = 0xd9; /* reti (Serial) */
688 gbs->rom[0x60] = 0xd9; /* reti (Joypad) */
689
690 close(fd);
691
692 return gbs;
693 }
694