6 // (C) 2010 Underground Software
8 // JLH = James L. Hammons <jlhamm@acm.org>
11 // --- ---------- -------------------------------------------------------------
12 // JLH 01/16/2010 Created this log ;-)
13 // JLH 02/28/2010 Added functions to look inside .ZIP files and handle contents
25 #include "universalhdr.h"
29 // Private function prototypes
31 static int gzfilelength(gzFile gd);
32 static bool CheckExtension(const char * filename, const char * ext);
33 //static int ParseFileType(uint8 header1, uint8 header2, uint32 size);
35 // Private variables/enums
39 // Generic ROM loading
41 uint32 JaguarLoadROM(uint8 * rom, char * path)
43 // We really should have some kind of sanity checking for the ROM size here to prevent
44 // a buffer overflow... !!! FIX !!!
45 #warning "!!! FIX !!! Should have sanity checking for ROM size to prevent buffer overflow!"
48 WriteLog("JaguarLoadROM: Attempting to load file '%s'...", path);
49 char * ext = strrchr(path, '.');
51 // No filename extension == YUO FAIL IT (it is loading the file).
52 // This is naive, but it works. But should probably come up with something a little
53 // more robust, to prevent problems with dopes trying to exploit this.
56 WriteLog("FAILED!\n");
60 WriteLog("Succeeded in finding extension (%s)!\n", ext);
61 WriteLog("VJ: Loading \"%s\"...", path);
63 if (strcasecmp(ext, ".zip") == 0)
65 // Handle ZIP file loading here...
66 WriteLog("(ZIPped)...");
68 uint8_t * buffer = NULL;
69 romSize = GetFileFromZIP(path, FT_SOFTWARE, buffer);
73 WriteLog("Failed!\n");
77 memcpy(rom, buffer, romSize);
82 // Handle gzipped files transparently [Adam Green]...
84 gzFile fp = gzopen(path, "rb");
88 WriteLog("Failed!\n");
92 romSize = gzfilelength(fp);
93 gzseek(fp, 0, SEEK_SET);
94 gzread(fp, rom, romSize);
98 WriteLog("OK (%i bytes)\n", romSize);
104 // Jaguar file loading
106 bool JaguarLoadFileOld(char * path)
108 jaguarROMSize = JaguarLoadROM(jaguarMainROM, path);
110 if (jaguarROMSize == 0)
112 // WriteLog("VJ: Could not load ROM from file \"%s\"...\nAborting!\n", newPath);
113 WriteLog("GUI: Could not load ROM from file \"%s\"...\nAborting load!\n", path);
114 #warning "!!! Need error dialog here !!!"
115 // Need to do something else here, like throw up an error dialog instead of aborting. !!! FIX !!!
118 return false; // This is a start...
121 jaguarMainROMCRC32 = crc32_calcCheckSum(jaguarMainROM, jaguarROMSize);
122 WriteLog("CRC: %08X\n", (unsigned int)jaguarMainROMCRC32);
125 jaguarRunAddress = 0x802000;
127 char * ext = strrchr(path, '.'); // Get the file's extension for non-cartridge checking
129 //NOTE: Should fix JaguarLoadROM() to replace .zip with what's *in* the zip (.abs, .j64, etc.)
130 #warning "!!! Need more robust file type checking in JaguarLoadROM() !!!"
131 if (strcasecmp(ext, ".rom") == 0)
133 // File extension ".ROM": Alpine image that loads/runs at $802000
134 WriteLog("GUI: Setting up homebrew (ROM)... Run address: 00802000, length: %08X\n", jaguarROMSize);
136 for(int i=jaguarROMSize-1; i>=0; i--)
137 jaguarMainROM[0x2000 + i] = jaguarMainROM[i];
139 memset(jaguarMainROM, 0xFF, 0x2000);
140 /* memcpy(jaguar_mainRam, jaguar_mainRom, jaguarRomSize);
141 memset(jaguar_mainRom, 0xFF, 0x600000);
142 memcpy(jaguar_mainRom + 0x2000, jaguar_mainRam, jaguarRomSize);
143 memset(jaguar_mainRam, 0x00, 0x400000);*/
146 Stubulator ROM vectors...
147 handler 001 at $00E00008
148 handler 002 at $00E008DE
149 handler 003 at $00E008E2
150 handler 004 at $00E008E6
151 handler 005 at $00E008EA
152 handler 006 at $00E008EE
153 handler 007 at $00E008F2
154 handler 008 at $00E0054A
155 handler 009 at $00E008FA
156 handler 010 at $00000000
157 handler 011 at $00000000
158 handler 012 at $00E008FE
159 handler 013 at $00E00902
160 handler 014 at $00E00906
161 handler 015 at $00E0090A
162 handler 016 at $00E0090E
163 handler 017 at $00E00912
164 handler 018 at $00E00916
165 handler 019 at $00E0091A
166 handler 020 at $00E0091E
167 handler 021 at $00E00922
168 handler 022 at $00E00926
169 handler 023 at $00E0092A
170 handler 024 at $00E0092E
171 handler 025 at $00E0107A
172 handler 026 at $00E0107A
173 handler 027 at $00E0107A
174 handler 028 at $00E008DA
175 handler 029 at $00E0107A
176 handler 030 at $00E0107A
177 handler 031 at $00E0107A
178 handler 032 at $00000000
180 Let's try setting up the illegal instruction vector for a stubulated jaguar...
182 /* SET32(jaguar_mainRam, 0x08, 0x00E008DE);
183 SET32(jaguar_mainRam, 0x0C, 0x00E008E2);
184 SET32(jaguar_mainRam, 0x10, 0x00E008E6); // <-- Should be here (it is)...
185 SET32(jaguar_mainRam, 0x14, 0x00E008EA);//*/
187 // Try setting the vector to say, $1000 and putting an instruction there that loops forever:
188 // This kludge works! Yeah!
189 SET32(jaguarMainRAM, 0x10, 0x00001000);
190 SET16(jaguarMainRAM, 0x1000, 0x60FE); // Here: bra Here
192 else if (strcasecmp(ext, ".abs") == 0)
194 // File extension ".ABS": Atari linker output file with header (w/o is useless to us here)
197 ABS Format sleuthing (LBUGDEMO.ABS):
199 000000 60 1B 00 00 05 0C 00 04 62 C0 00 00 04 28 00 00
200 000010 12 A6 00 00 00 00 00 80 20 00 FF FF 00 80 25 0C
203 DRI-format file detected...
204 Text segment size = 0x0000050c bytes
205 Data segment size = 0x000462c0 bytes
206 BSS Segment size = 0x00000428 bytes
207 Symbol Table size = 0x000012a6 bytes
208 Absolute Address for text segment = 0x00802000
209 Absolute Address for data segment = 0x0080250c
210 Absolute Address for BSS segment = 0x00004000
213 000000 01 50 00 03 00 00 00 00 00 03 83 10 00 00 05 3b
214 000010 00 1c 00 03 00 00 01 07 00 00 1d d0 00 03 64 98
215 000020 00 06 8b 80 00 80 20 00 00 80 20 00 00 80 3d d0
217 000030 2e 74 78 74 00 00 00 00 00 80 20 00 00 80 20 00 .txt (+36 bytes)
218 000040 00 00 1d d0 00 00 00 a8 00 00 00 00 00 00 00 00
219 000050 00 00 00 00 00 00 00 20
220 000058 2e 64 74 61 00 00 00 00 00 80 3d d0 00 80 3d d0 .dta (+36 bytes)
221 000068 00 03 64 98 00 00 1e 78 00 00 00 00 00 00 00 00
222 000078 00 00 00 00 00 00 00 40
223 000080 2e 62 73 73 00 00 00 00 00 00 50 00 00 00 50 00 .bss (+36 bytes)
224 000090 00 06 8b 80 00 03 83 10 00 00 00 00 00 00 00 00
225 0000a0 00 00 00 00 00 00 00 80
227 Header size is $A8 bytes...
229 BSD/COFF format file detected...
231 Symbol Table offset = 230160 ($00038310)
232 Symbol Table contains 1339 symbol entries ($0000053B)
233 The additional header size is 28 bytes ($001C)
234 Magic Number for RUN_HDR = 0x00000107
235 Text Segment Size = 7632 ($00001DD0)
236 Data Segment Size = 222360 ($00036498)
237 BSS Segment Size = 428928 ($00068B80)
238 Starting Address for executable = 0x00802000
239 Start of Text Segment = 0x00802000
240 Start of Data Segment = 0x00803dd0
242 if (jaguarMainROM[0] == 0x60 && jaguarMainROM[1] == 0x1B)
244 uint32 loadAddress = GET32(jaguarMainROM, 0x16), //runAddress = GET32(jaguar_mainRom, 0x2A),
245 codeSize = GET32(jaguarMainROM, 0x02) + GET32(jaguarMainROM, 0x06);
246 WriteLog("GUI: Setting up homebrew (ABS-1)... Run address: %08X, length: %08X\n", loadAddress, codeSize);
248 if (loadAddress < 0x800000)
249 memcpy(jaguarMainRAM + loadAddress, jaguarMainROM + 0x24, codeSize);
252 for(int i=codeSize-1; i>=0; i--)
253 jaguarMainROM[(loadAddress - 0x800000) + i] = jaguarMainROM[i + 0x24];
254 /* memcpy(jaguar_mainRam, jaguar_mainRom + 0x24, codeSize);
255 memset(jaguar_mainRom, 0xFF, 0x600000);
256 memcpy(jaguar_mainRom + (loadAddress - 0x800000), jaguar_mainRam, codeSize);
257 memset(jaguar_mainRam, 0x00, 0x400000);*/
260 jaguarRunAddress = loadAddress;
262 else if (jaguarMainROM[0] == 0x01 && jaguarMainROM[1] == 0x50)
264 uint32 loadAddress = GET32(jaguarMainROM, 0x28), runAddress = GET32(jaguarMainROM, 0x24),
265 codeSize = GET32(jaguarMainROM, 0x18) + GET32(jaguarMainROM, 0x1C);
266 WriteLog("GUI: Setting up homebrew (ABS-2)... Run address: %08X, length: %08X\n", runAddress, codeSize);
268 if (loadAddress < 0x800000)
269 memcpy(jaguarMainRAM + loadAddress, jaguarMainROM + 0xA8, codeSize);
272 for(int i=codeSize-1; i>=0; i--)
273 jaguarMainROM[(loadAddress - 0x800000) + i] = jaguarMainROM[i + 0xA8];
274 /* memcpy(jaguar_mainRam, jaguar_mainRom + 0xA8, codeSize);
275 memset(jaguar_mainRom, 0xFF, 0x600000);
276 memcpy(jaguar_mainRom + (loadAddress - 0x800000), jaguar_mainRam, codeSize);
277 memset(jaguar_mainRam, 0x00, 0x400000);*/
280 jaguarRunAddress = runAddress;
284 WriteLog("GUI: Couldn't find correct ABS/COF format: %02X %02X\n", jaguarMainROM[0], jaguarMainROM[1]);
288 else if (strcasecmp(ext, ".jag") == 0)
290 // File extension ".JAG": Atari server file with header
291 //NOTE: The bytes 'JAGR' should also be at position $1C...
292 // Also, there's *always* a $601A header at position $00...
293 if (jaguarMainROM[0] == 0x60 && jaguarMainROM[1] == 0x1A)
295 uint32 loadAddress = GET32(jaguarMainROM, 0x22), runAddress = GET32(jaguarMainROM, 0x2A);
296 //This is not always right! Especially when converted via bin2jag1!!!
297 //We should have access to the length of the furshlumiger file that was loaded anyway!
299 // uint32 progLength = GET32(jaguar_mainRom, 0x02);
302 // WriteLog("Jaguar: Setting up PD ROM... Run address: %08X, length: %08X\n", runAddress, progLength);
303 // memcpy(jaguar_mainRam + loadAddress, jaguar_mainRom + 0x2E, progLength);
304 WriteLog("GUI: Setting up homebrew (JAG)... Run address: %08X, length: %08X\n", runAddress, jaguarROMSize - 0x2E);
305 memcpy(jaguarMainRAM + loadAddress, jaguarMainROM + 0x2E, jaguarROMSize - 0x2E);
306 // SET32(jaguar_mainRam, 4, runAddress);
307 jaguarRunAddress = runAddress;
312 // .J64 (Jaguar cartridge ROM image) is implied by the FileList object...
318 // Jaguar file loading (second stab at it...)
319 // We do a more intelligent file analysis here instead of relying on (possible false)
320 // file extensions which people don't seem to give two shits about anyway. :-(
322 bool JaguarLoadFile(char * path)
324 // NOTE: We can further clean this up by fixing JaguarLoadROM to load to a buffer
325 // instead of assuming it goes into our ROM space.
326 jaguarROMSize = JaguarLoadROM(jaguarMainROM, path);
328 if (jaguarROMSize == 0)
330 // WriteLog("VJ: Could not load ROM from file \"%s\"...\nAborting!\n", newPath);
331 WriteLog("GUI: Could not load ROM from file \"%s\"...\nAborting load!\n", path);
332 #warning "!!! Need error dialog here !!!"
333 // Need to do something else here, like throw up an error dialog instead of aborting. !!! FIX !!!
334 return false; // This is a start...
337 jaguarMainROMCRC32 = crc32_calcCheckSum(jaguarMainROM, jaguarROMSize);
338 WriteLog("CRC: %08X\n", (unsigned int)jaguarMainROMCRC32);
339 // TODO: Check for EEPROM file in ZIP file. If there is no EEPROM in the user's EEPROM
340 // directory, copy the one from the ZIP file, if it exists.
342 jaguarRunAddress = 0x802000; // For non-BIOS runs, this is true
343 int fileType = ParseFileType(jaguarMainROM[0], jaguarMainROM[1], jaguarROMSize);
345 if (fileType == JST_ROM)
347 else if (fileType == JST_ALPINE)
349 // File extension ".ROM": Alpine image that loads/runs at $802000
350 WriteLog("GUI: Setting up Alpine ROM... Run address: 00802000, length: %08X\n", jaguarROMSize);
352 for(int i=jaguarROMSize-1; i>=0; i--)
353 jaguarMainROM[0x2000 + i] = jaguarMainROM[i];
355 memset(jaguarMainROM, 0xFF, 0x2000);
357 // Maybe instead of this, we could try requiring the STUBULATOR ROM? Just a thought...
358 // Try setting the vector to say, $1000 and putting an instruction there that loops forever:
359 // This kludge works! Yeah!
360 SET32(jaguarMainRAM, 0x10, 0x00001000);
361 SET16(jaguarMainRAM, 0x1000, 0x60FE); // Here: bra Here
364 else if (fileType == JST_ABS_TYPE1)
366 uint32 loadAddress = GET32(jaguarMainROM, 0x16),
367 codeSize = GET32(jaguarMainROM, 0x02) + GET32(jaguarMainROM, 0x06);
368 WriteLog("GUI: Setting up homebrew (ABS-1)... Run address: %08X, length: %08X\n", loadAddress, codeSize);
370 if (loadAddress < 0x800000)
371 memcpy(jaguarMainRAM + loadAddress, jaguarMainROM + 0x24, codeSize);
374 for(int i=codeSize-1; i>=0; i--)
375 jaguarMainROM[(loadAddress - 0x800000) + i] = jaguarMainROM[i + 0x24];
378 jaguarRunAddress = loadAddress;
381 else if (fileType == JST_ABS_TYPE2)
383 uint32 loadAddress = GET32(jaguarMainROM, 0x28), runAddress = GET32(jaguarMainROM, 0x24),
384 codeSize = GET32(jaguarMainROM, 0x18) + GET32(jaguarMainROM, 0x1C);
385 WriteLog("GUI: Setting up homebrew (ABS-2)... Run address: %08X, length: %08X\n", runAddress, codeSize);
387 if (loadAddress < 0x800000)
388 memcpy(jaguarMainRAM + loadAddress, jaguarMainROM + 0xA8, codeSize);
391 for(int i=codeSize-1; i>=0; i--)
392 jaguarMainROM[(loadAddress - 0x800000) + i] = jaguarMainROM[i + 0xA8];
395 jaguarRunAddress = runAddress;
398 else if (fileType == JST_JAGSERVER)
400 uint32 loadAddress = GET32(jaguarMainROM, 0x22), runAddress = GET32(jaguarMainROM, 0x2A);
401 WriteLog("GUI: Setting up homebrew (Jag Server)... Run address: %08X, length: %08X\n", runAddress, jaguarROMSize - 0x2E);
402 memcpy(jaguarMainRAM + loadAddress, jaguarMainROM + 0x2E, jaguarROMSize - 0x2E);
403 jaguarRunAddress = runAddress;
407 // We can assume we have JST_NONE at this point. :-P
408 // TODO: Add a dialog box that tells the user that they're trying to feed VJ a bogus file.
413 // Get the length of a (possibly) gzipped file
415 static int gzfilelength(gzFile gd)
417 int size = 0, length = 0;
418 unsigned char buffer[0x10000];
424 // Read in chunks until EOF
425 size = gzread(gd, buffer, 0x10000);
439 // Compare extension to passed in filename. If equal, return true; otherwise false.
441 static bool CheckExtension(const char * filename, const char * ext)
443 const char * filenameExt = strrchr(filename, '.'); // Get the file's extension (if any)
444 return (strcasecmp(filenameExt, ext) == 0 ? true : false);
448 // Get file from .ZIP
449 // Returns the size of the file inside the .ZIP file that we're looking at
450 // NOTE: If the thing we're looking for is found, it allocates it in the passed in buffer.
451 // Which means we have to deallocate it later.
453 uint32 GetFileFromZIP(const char * zipFile, FileType type, uint8 * &buffer)
455 // NOTE: We could easily check for this by discarding anything that's larger than the RAM/ROM
456 // size of the Jaguar console.
457 #warning "!!! FIX !!! Should have sanity checking for ROM size to prevent buffer overflow!"
458 const char ftStrings[5][32] = { "Software", "EEPROM", "Label", "Box Art", "Controller Overlay" };
459 ZIP * zip = openzip(0, 0, zipFile);
463 WriteLog("FILE: Could not open file '%s'!\n", zipFile);
470 // The order is here is important: If the file is found, we need to short-circuit the
471 // readzip() call because otherwise, 'ze' will be pointing to the wrong file!
472 while (!found && readzip(zip))
476 // Here we simply rely on the file extension to tell the truth, but we know
477 // that extensions lie like sons-a-bitches. So this is naive, we need to do
478 // something a little more robust to keep bad things from happening here.
479 #warning "!!! Checking for image by extension can be fooled !!!"
480 if ((type == FT_LABEL) && (CheckExtension(ze->name, ".png") || CheckExtension(ze->name, ".jpg") || CheckExtension(ze->name, ".gif")))
483 WriteLog("FILE: Found image file '%s'.\n", ze->name);
486 if ((type == FT_SOFTWARE) && (CheckExtension(ze->name, ".j64")
487 || CheckExtension(ze->name, ".rom") || CheckExtension(ze->name, ".abs")
488 || CheckExtension(ze->name, ".cof") || CheckExtension(ze->name, ".jag")))
491 WriteLog("FILE: Found software file '%s'.\n", ze->name);
494 if ((type == FT_EEPROM) && (CheckExtension(ze->name, ".eep") || CheckExtension(ze->name, ".eeprom")))
497 WriteLog("FILE: Found EEPROM file '%s'.\n", ze->name);
505 WriteLog("FILE: Uncompressing...");
506 // Insert file size sanity check here...
507 buffer = new uint8[ze->uncompressed_size];
509 if (readuncompresszip(zip, ze, (char *)buffer) == 0)
511 fileSize = ze->uncompressed_size;
512 WriteLog("success! (%u bytes)\n", fileSize);
518 WriteLog("FAILED!\n");
522 // Didn't find what we're looking for...
523 WriteLog("FILE: Failed to find file of type %s...\n", ftStrings[type]);
530 // Parse the file type based upon file size and/or headers.
532 uint32 ParseFileType(uint8 header1, uint8 header2, uint32 size)
534 // Check headers first...
537 if (header1 == 0x60 && header2 == 0x1B)
538 return JST_ABS_TYPE1;
541 if (header1 == 0x01 && header2 == 0x50)
542 return JST_ABS_TYPE2;
545 if (header1 == 0x60 && header2 == 0x1A)
546 return JST_JAGSERVER;
548 // And if that fails, try file sizes...
550 // If the file size is divisible by 1M, we probably have an regular ROM.
551 // We can also check our CRC32 against the internal ROM database to be sure.
552 if ((size % 1048576) == 0)
555 // If the file size + 8192 bytes is divisible by 1M, we probably have an
556 // Alpine format ROM.
557 if (((size + 8192) % 1048576) == 0)
565 // Check for universal header
567 bool HasUniversalHeader(uint8 * rom, uint32 romSize)
573 for(int i=0; i<8192; i++)
574 if (rom[i] != universalCartHeader[i])
584 Stubulator ROM vectors...
585 handler 001 at $00E00008
586 handler 002 at $00E008DE
587 handler 003 at $00E008E2
588 handler 004 at $00E008E6
589 handler 005 at $00E008EA
590 handler 006 at $00E008EE
591 handler 007 at $00E008F2
592 handler 008 at $00E0054A
593 handler 009 at $00E008FA
594 handler 010 at $00000000
595 handler 011 at $00000000
596 handler 012 at $00E008FE
597 handler 013 at $00E00902
598 handler 014 at $00E00906
599 handler 015 at $00E0090A
600 handler 016 at $00E0090E
601 handler 017 at $00E00912
602 handler 018 at $00E00916
603 handler 019 at $00E0091A
604 handler 020 at $00E0091E
605 handler 021 at $00E00922
606 handler 022 at $00E00926
607 handler 023 at $00E0092A
608 handler 024 at $00E0092E
609 handler 025 at $00E0107A
610 handler 026 at $00E0107A
611 handler 027 at $00E0107A
612 handler 028 at $00E008DA
613 handler 029 at $00E0107A
614 handler 030 at $00E0107A
615 handler 031 at $00E0107A
616 handler 032 at $00000000
618 Let's try setting up the illegal instruction vector for a stubulated jaguar...
620 SET32(jaguar_mainRam, 0x08, 0x00E008DE);
621 SET32(jaguar_mainRam, 0x0C, 0x00E008E2);
622 SET32(jaguar_mainRam, 0x10, 0x00E008E6); // <-- Should be here (it is)...
623 SET32(jaguar_mainRam, 0x14, 0x00E008EA);//*/
626 ABS Format sleuthing (LBUGDEMO.ABS):
628 000000 60 1B 00 00 05 0C 00 04 62 C0 00 00 04 28 00 00
629 000010 12 A6 00 00 00 00 00 80 20 00 FF FF 00 80 25 0C
632 DRI-format file detected...
633 Text segment size = 0x0000050c bytes
634 Data segment size = 0x000462c0 bytes
635 BSS Segment size = 0x00000428 bytes
636 Symbol Table size = 0x000012a6 bytes
637 Absolute Address for text segment = 0x00802000
638 Absolute Address for data segment = 0x0080250c
639 Absolute Address for BSS segment = 0x00004000
642 000000 01 50 00 03 00 00 00 00 00 03 83 10 00 00 05 3b
643 000010 00 1c 00 03 00 00 01 07 00 00 1d d0 00 03 64 98
644 000020 00 06 8b 80 00 80 20 00 00 80 20 00 00 80 3d d0
646 000030 2e 74 78 74 00 00 00 00 00 80 20 00 00 80 20 00 .txt (+36 bytes)
647 000040 00 00 1d d0 00 00 00 a8 00 00 00 00 00 00 00 00
648 000050 00 00 00 00 00 00 00 20
649 000058 2e 64 74 61 00 00 00 00 00 80 3d d0 00 80 3d d0 .dta (+36 bytes)
650 000068 00 03 64 98 00 00 1e 78 00 00 00 00 00 00 00 00
651 000078 00 00 00 00 00 00 00 40
652 000080 2e 62 73 73 00 00 00 00 00 00 50 00 00 00 50 00 .bss (+36 bytes)
653 000090 00 06 8b 80 00 03 83 10 00 00 00 00 00 00 00 00
654 0000a0 00 00 00 00 00 00 00 80
656 Header size is $A8 bytes...
658 BSD/COFF format file detected...
660 Symbol Table offset = 230160 ($00038310)
661 Symbol Table contains 1339 symbol entries ($0000053B)
662 The additional header size is 28 bytes ($001C)
663 Magic Number for RUN_HDR = 0x00000107
664 Text Segment Size = 7632 ($00001DD0)
665 Data Segment Size = 222360 ($00036498)
666 BSS Segment Size = 428928 ($00068B80)
667 Starting Address for executable = 0x00802000
668 Start of Text Segment = 0x00802000
669 Start of Data Segment = 0x00803dd0