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
28 // Private function prototypes
30 static int gzfilelength(gzFile gd);
31 static bool CheckExtension(const char * filename, const char * ext);
32 static int ParseFileType(uint8 header1, uint8 header2, uint32 size);
34 // Private variables/enums
36 // JST = Jaguar Software Type
37 enum { JST_NONE = 0, JST_ROM, JST_ALPINE, JST_ABS_TYPE1, JST_ABS_TYPE2, JST_JAGSERVER };
38 //static int softwareType = JST_NONE;
41 // Generic ROM loading
43 uint32 JaguarLoadROM(uint8 * rom, char * path)
45 // We really should have some kind of sanity checking for the ROM size here to prevent
46 // a buffer overflow... !!! FIX !!!
47 #warning "!!! FIX !!! Should have sanity checking for ROM size to prevent buffer overflow!"
50 WriteLog("JaguarLoadROM: Attempting to load file '%s'...", path);
51 char * ext = strrchr(path, '.');
53 // No filename extension == YUO FAIL IT (it is loading the file).
54 // This is naive, but it works. But should probably come up with something a little
55 // more robust, to prevent problems with dopes trying to exploit this.
58 WriteLog("FAILED!\n");
62 WriteLog("Succeeded in finding extension (%s)!\n", ext);
63 WriteLog("VJ: Loading \"%s\"...", path);
65 if (strcasecmp(ext, ".zip") == 0)
67 // Handle ZIP file loading here...
68 WriteLog("(ZIPped)...");
70 uint8_t * buffer = NULL;
71 romSize = GetFileFromZIP(path, FT_SOFTWARE, buffer);
75 WriteLog("Failed!\n");
79 memcpy(rom, buffer, romSize);
84 // Handle gzipped files transparently [Adam Green]...
86 gzFile fp = gzopen(path, "rb");
90 WriteLog("Failed!\n");
94 romSize = gzfilelength(fp);
95 gzseek(fp, 0, SEEK_SET);
96 gzread(fp, rom, romSize);
100 WriteLog("OK (%i bytes)\n", romSize);
106 // Jaguar file loading
108 bool JaguarLoadFileOld(char * path)
110 jaguarROMSize = JaguarLoadROM(jaguarMainROM, path);
112 if (jaguarROMSize == 0)
114 // WriteLog("VJ: Could not load ROM from file \"%s\"...\nAborting!\n", newPath);
115 WriteLog("GUI: Could not load ROM from file \"%s\"...\nAborting load!\n", path);
116 #warning "!!! Need error dialog here !!!"
117 // Need to do something else here, like throw up an error dialog instead of aborting. !!! FIX !!!
120 return false; // This is a start...
123 jaguarMainROMCRC32 = crc32_calcCheckSum(jaguarMainROM, jaguarROMSize);
124 WriteLog("CRC: %08X\n", (unsigned int)jaguarMainROMCRC32);
127 jaguarRunAddress = 0x802000;
129 char * ext = strrchr(path, '.'); // Get the file's extension for non-cartridge checking
131 //NOTE: Should fix JaguarLoadROM() to replace .zip with what's *in* the zip (.abs, .j64, etc.)
132 #warning "!!! Need more robust file type checking in JaguarLoadROM() !!!"
133 if (strcasecmp(ext, ".rom") == 0)
135 // File extension ".ROM": Alpine image that loads/runs at $802000
136 WriteLog("GUI: Setting up homebrew (ROM)... Run address: 00802000, length: %08X\n", jaguarROMSize);
138 for(int i=jaguarROMSize-1; i>=0; i--)
139 jaguarMainROM[0x2000 + i] = jaguarMainROM[i];
141 memset(jaguarMainROM, 0xFF, 0x2000);
142 /* memcpy(jaguar_mainRam, jaguar_mainRom, jaguarRomSize);
143 memset(jaguar_mainRom, 0xFF, 0x600000);
144 memcpy(jaguar_mainRom + 0x2000, jaguar_mainRam, jaguarRomSize);
145 memset(jaguar_mainRam, 0x00, 0x400000);*/
148 Stubulator ROM vectors...
149 handler 001 at $00E00008
150 handler 002 at $00E008DE
151 handler 003 at $00E008E2
152 handler 004 at $00E008E6
153 handler 005 at $00E008EA
154 handler 006 at $00E008EE
155 handler 007 at $00E008F2
156 handler 008 at $00E0054A
157 handler 009 at $00E008FA
158 handler 010 at $00000000
159 handler 011 at $00000000
160 handler 012 at $00E008FE
161 handler 013 at $00E00902
162 handler 014 at $00E00906
163 handler 015 at $00E0090A
164 handler 016 at $00E0090E
165 handler 017 at $00E00912
166 handler 018 at $00E00916
167 handler 019 at $00E0091A
168 handler 020 at $00E0091E
169 handler 021 at $00E00922
170 handler 022 at $00E00926
171 handler 023 at $00E0092A
172 handler 024 at $00E0092E
173 handler 025 at $00E0107A
174 handler 026 at $00E0107A
175 handler 027 at $00E0107A
176 handler 028 at $00E008DA
177 handler 029 at $00E0107A
178 handler 030 at $00E0107A
179 handler 031 at $00E0107A
180 handler 032 at $00000000
182 Let's try setting up the illegal instruction vector for a stubulated jaguar...
184 /* SET32(jaguar_mainRam, 0x08, 0x00E008DE);
185 SET32(jaguar_mainRam, 0x0C, 0x00E008E2);
186 SET32(jaguar_mainRam, 0x10, 0x00E008E6); // <-- Should be here (it is)...
187 SET32(jaguar_mainRam, 0x14, 0x00E008EA);//*/
189 // Try setting the vector to say, $1000 and putting an instruction there that loops forever:
190 // This kludge works! Yeah!
191 SET32(jaguarMainRAM, 0x10, 0x00001000);
192 SET16(jaguarMainRAM, 0x1000, 0x60FE); // Here: bra Here
194 else if (strcasecmp(ext, ".abs") == 0)
196 // File extension ".ABS": Atari linker output file with header (w/o is useless to us here)
199 ABS Format sleuthing (LBUGDEMO.ABS):
201 000000 60 1B 00 00 05 0C 00 04 62 C0 00 00 04 28 00 00
202 000010 12 A6 00 00 00 00 00 80 20 00 FF FF 00 80 25 0C
205 DRI-format file detected...
206 Text segment size = 0x0000050c bytes
207 Data segment size = 0x000462c0 bytes
208 BSS Segment size = 0x00000428 bytes
209 Symbol Table size = 0x000012a6 bytes
210 Absolute Address for text segment = 0x00802000
211 Absolute Address for data segment = 0x0080250c
212 Absolute Address for BSS segment = 0x00004000
215 000000 01 50 00 03 00 00 00 00 00 03 83 10 00 00 05 3b
216 000010 00 1c 00 03 00 00 01 07 00 00 1d d0 00 03 64 98
217 000020 00 06 8b 80 00 80 20 00 00 80 20 00 00 80 3d d0
219 000030 2e 74 78 74 00 00 00 00 00 80 20 00 00 80 20 00 .txt (+36 bytes)
220 000040 00 00 1d d0 00 00 00 a8 00 00 00 00 00 00 00 00
221 000050 00 00 00 00 00 00 00 20
222 000058 2e 64 74 61 00 00 00 00 00 80 3d d0 00 80 3d d0 .dta (+36 bytes)
223 000068 00 03 64 98 00 00 1e 78 00 00 00 00 00 00 00 00
224 000078 00 00 00 00 00 00 00 40
225 000080 2e 62 73 73 00 00 00 00 00 00 50 00 00 00 50 00 .bss (+36 bytes)
226 000090 00 06 8b 80 00 03 83 10 00 00 00 00 00 00 00 00
227 0000a0 00 00 00 00 00 00 00 80
229 Header size is $A8 bytes...
231 BSD/COFF format file detected...
233 Symbol Table offset = 230160 ($00038310)
234 Symbol Table contains 1339 symbol entries ($0000053B)
235 The additional header size is 28 bytes ($001C)
236 Magic Number for RUN_HDR = 0x00000107
237 Text Segment Size = 7632 ($00001DD0)
238 Data Segment Size = 222360 ($00036498)
239 BSS Segment Size = 428928 ($00068B80)
240 Starting Address for executable = 0x00802000
241 Start of Text Segment = 0x00802000
242 Start of Data Segment = 0x00803dd0
244 if (jaguarMainROM[0] == 0x60 && jaguarMainROM[1] == 0x1B)
246 uint32 loadAddress = GET32(jaguarMainROM, 0x16), //runAddress = GET32(jaguar_mainRom, 0x2A),
247 codeSize = GET32(jaguarMainROM, 0x02) + GET32(jaguarMainROM, 0x06);
248 WriteLog("GUI: Setting up homebrew (ABS-1)... Run address: %08X, length: %08X\n", loadAddress, codeSize);
250 if (loadAddress < 0x800000)
251 memcpy(jaguarMainRAM + loadAddress, jaguarMainROM + 0x24, codeSize);
254 for(int i=codeSize-1; i>=0; i--)
255 jaguarMainROM[(loadAddress - 0x800000) + i] = jaguarMainROM[i + 0x24];
256 /* memcpy(jaguar_mainRam, jaguar_mainRom + 0x24, codeSize);
257 memset(jaguar_mainRom, 0xFF, 0x600000);
258 memcpy(jaguar_mainRom + (loadAddress - 0x800000), jaguar_mainRam, codeSize);
259 memset(jaguar_mainRam, 0x00, 0x400000);*/
262 jaguarRunAddress = loadAddress;
264 else if (jaguarMainROM[0] == 0x01 && jaguarMainROM[1] == 0x50)
266 uint32 loadAddress = GET32(jaguarMainROM, 0x28), runAddress = GET32(jaguarMainROM, 0x24),
267 codeSize = GET32(jaguarMainROM, 0x18) + GET32(jaguarMainROM, 0x1C);
268 WriteLog("GUI: Setting up homebrew (ABS-2)... Run address: %08X, length: %08X\n", runAddress, codeSize);
270 if (loadAddress < 0x800000)
271 memcpy(jaguarMainRAM + loadAddress, jaguarMainROM + 0xA8, codeSize);
274 for(int i=codeSize-1; i>=0; i--)
275 jaguarMainROM[(loadAddress - 0x800000) + i] = jaguarMainROM[i + 0xA8];
276 /* memcpy(jaguar_mainRam, jaguar_mainRom + 0xA8, codeSize);
277 memset(jaguar_mainRom, 0xFF, 0x600000);
278 memcpy(jaguar_mainRom + (loadAddress - 0x800000), jaguar_mainRam, codeSize);
279 memset(jaguar_mainRam, 0x00, 0x400000);*/
282 jaguarRunAddress = runAddress;
286 WriteLog("GUI: Couldn't find correct ABS/COF format: %02X %02X\n", jaguarMainROM[0], jaguarMainROM[1]);
290 else if (strcasecmp(ext, ".jag") == 0)
292 // File extension ".JAG": Atari server file with header
293 //NOTE: The bytes 'JAGR' should also be at position $1C...
294 // Also, there's *always* a $601A header at position $00...
295 if (jaguarMainROM[0] == 0x60 && jaguarMainROM[1] == 0x1A)
297 uint32 loadAddress = GET32(jaguarMainROM, 0x22), runAddress = GET32(jaguarMainROM, 0x2A);
298 //This is not always right! Especially when converted via bin2jag1!!!
299 //We should have access to the length of the furshlumiger file that was loaded anyway!
301 // uint32 progLength = GET32(jaguar_mainRom, 0x02);
304 // WriteLog("Jaguar: Setting up PD ROM... Run address: %08X, length: %08X\n", runAddress, progLength);
305 // memcpy(jaguar_mainRam + loadAddress, jaguar_mainRom + 0x2E, progLength);
306 WriteLog("GUI: Setting up homebrew (JAG)... Run address: %08X, length: %08X\n", runAddress, jaguarROMSize - 0x2E);
307 memcpy(jaguarMainRAM + loadAddress, jaguarMainROM + 0x2E, jaguarROMSize - 0x2E);
308 // SET32(jaguar_mainRam, 4, runAddress);
309 jaguarRunAddress = runAddress;
314 // .J64 (Jaguar cartridge ROM image) is implied by the FileList object...
320 // Jaguar file loading (second stab at it...)
321 // We do a more intelligent file analysis here instead of relying on (possible false)
322 // file extensions which people don't seem to give two shits about anyway. :-(
324 bool JaguarLoadFile(char * path)
326 // NOTE: We can further clean this up by fixing JaguarLoadROM to load to a buffer
327 // instead of assuming it goes into our ROM space.
328 jaguarROMSize = JaguarLoadROM(jaguarMainROM, path);
330 if (jaguarROMSize == 0)
332 // WriteLog("VJ: Could not load ROM from file \"%s\"...\nAborting!\n", newPath);
333 WriteLog("GUI: Could not load ROM from file \"%s\"...\nAborting load!\n", path);
334 #warning "!!! Need error dialog here !!!"
335 // Need to do something else here, like throw up an error dialog instead of aborting. !!! FIX !!!
336 return false; // This is a start...
339 jaguarMainROMCRC32 = crc32_calcCheckSum(jaguarMainROM, jaguarROMSize);
340 WriteLog("CRC: %08X\n", (unsigned int)jaguarMainROMCRC32);
341 // TODO: Check for EEPROM file in ZIP file. If there is no EEPROM in the user's EEPROM
342 // directory, copy the one from the ZIP file, if it exists.
344 jaguarRunAddress = 0x802000; // For non-BIOS runs, this is true
345 int fileType = ParseFileType(jaguarMainROM[0], jaguarMainROM[1], jaguarROMSize);
347 if (fileType == JST_ROM)
349 else if (fileType == JST_ALPINE)
351 // File extension ".ROM": Alpine image that loads/runs at $802000
352 WriteLog("GUI: Setting up Alpine ROM... Run address: 00802000, length: %08X\n", jaguarROMSize);
354 for(int i=jaguarROMSize-1; i>=0; i--)
355 jaguarMainROM[0x2000 + i] = jaguarMainROM[i];
357 memset(jaguarMainROM, 0xFF, 0x2000);
359 // Maybe instead of this, we could try requiring the STUBULATOR ROM? Just a thought...
360 // Try setting the vector to say, $1000 and putting an instruction there that loops forever:
361 // This kludge works! Yeah!
362 SET32(jaguarMainRAM, 0x10, 0x00001000);
363 SET16(jaguarMainRAM, 0x1000, 0x60FE); // Here: bra Here
366 else if (fileType == JST_ABS_TYPE1)
368 uint32 loadAddress = GET32(jaguarMainROM, 0x16),
369 codeSize = GET32(jaguarMainROM, 0x02) + GET32(jaguarMainROM, 0x06);
370 WriteLog("GUI: Setting up homebrew (ABS-1)... Run address: %08X, length: %08X\n", loadAddress, codeSize);
372 if (loadAddress < 0x800000)
373 memcpy(jaguarMainRAM + loadAddress, jaguarMainROM + 0x24, codeSize);
376 for(int i=codeSize-1; i>=0; i--)
377 jaguarMainROM[(loadAddress - 0x800000) + i] = jaguarMainROM[i + 0x24];
380 jaguarRunAddress = loadAddress;
383 else if (fileType == JST_ABS_TYPE2)
385 uint32 loadAddress = GET32(jaguarMainROM, 0x28), runAddress = GET32(jaguarMainROM, 0x24),
386 codeSize = GET32(jaguarMainROM, 0x18) + GET32(jaguarMainROM, 0x1C);
387 WriteLog("GUI: Setting up homebrew (ABS-2)... Run address: %08X, length: %08X\n", runAddress, codeSize);
389 if (loadAddress < 0x800000)
390 memcpy(jaguarMainRAM + loadAddress, jaguarMainROM + 0xA8, codeSize);
393 for(int i=codeSize-1; i>=0; i--)
394 jaguarMainROM[(loadAddress - 0x800000) + i] = jaguarMainROM[i + 0xA8];
397 jaguarRunAddress = runAddress;
400 else if (fileType == JST_JAGSERVER)
402 uint32 loadAddress = GET32(jaguarMainROM, 0x22), runAddress = GET32(jaguarMainROM, 0x2A);
403 WriteLog("GUI: Setting up homebrew (Jag Server)... Run address: %08X, length: %08X\n", runAddress, jaguarROMSize - 0x2E);
404 memcpy(jaguarMainRAM + loadAddress, jaguarMainROM + 0x2E, jaguarROMSize - 0x2E);
405 jaguarRunAddress = runAddress;
409 // We can assume we have JST_NONE at this point. :-P
410 // TODO: Add a dialog box that tells the user that they're trying to feed VJ a bogus file.
415 // Get the length of a (possibly) gzipped file
417 static int gzfilelength(gzFile gd)
419 int size = 0, length = 0;
420 unsigned char buffer[0x10000];
426 // Read in chunks until EOF
427 size = gzread(gd, buffer, 0x10000);
441 // Compare extension to passed in filename. If equal, return true; otherwise false.
443 static bool CheckExtension(const char * filename, const char * ext)
445 const char * filenameExt = strrchr(filename, '.'); // Get the file's extension (if any)
446 return (strcasecmp(filenameExt, ext) == 0 ? true : false);
450 // Get file from .ZIP
451 // Returns the size of the file inside the .ZIP file that we're looking at
452 // NOTE: If the thing we're looking for is found, it allocates it in the passed in buffer.
453 // Which means we have to deallocate it later.
455 uint32 GetFileFromZIP(const char * zipFile, FileType type, uint8 * &buffer)
457 // NOTE: We could easily check for this by discarding anything that's larger than the RAM/ROM
458 // size of the Jaguar console.
459 #warning "!!! FIX !!! Should have sanity checking for ROM size to prevent buffer overflow!"
460 const char ftStrings[5][32] = { "Software", "EEPROM", "Label", "Box Art", "Controller Overlay" };
461 ZIP * zip = openzip(0, 0, zipFile);
465 WriteLog("FILE: Could not open file '%s'!\n", zipFile);
472 // The order is here is important: If the file is found, we need to short-circuit the
473 // readzip() call because otherwise, 'ze' will be pointing to the wrong file!
474 while (!found && readzip(zip))
478 // Here we simply rely on the file extension to tell the truth, but we know
479 // that extensions lie like sons-a-bitches. So this is naive, we need to do
480 // something a little more robust to keep bad things from happening here.
481 #warning "!!! Checking for image by extension can be fooled !!!"
482 if ((type == FT_LABEL) && (CheckExtension(ze->name, ".png") || CheckExtension(ze->name, ".jpg") || CheckExtension(ze->name, ".gif")))
485 WriteLog("FILE: Found image file '%s'.\n", ze->name);
488 if ((type == FT_SOFTWARE) && (CheckExtension(ze->name, ".j64") || CheckExtension(ze->name, ".rom") || CheckExtension(ze->name, ".abs") || CheckExtension(ze->name, ".cof")))
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 static int ParseFileType(uint8 header1, uint8 header2, uint32 size)
534 // If the file size is divisible by 1M, we probably have an regular ROM.
535 // We can also check our CRC32 against the internal ROM database to be sure.
536 if ((size % 1048576) == 0)
539 // If the file size + 8192 bytes is divisible by 1M, we probably have an
540 // Alpine format ROM.
541 if (((size + 8192) % 1048576) == 0)
544 // So much for low hanging fruit. Now try some other types.
547 if (header1 == 0x60 && header2 == 0x1B)
548 return JST_ABS_TYPE1;
551 if (header1 == 0x01 && header2 == 0x50)
552 return JST_ABS_TYPE2;
555 if (header1 == 0x60 && header2 == 0x1A)
556 return JST_JAGSERVER;
566 Stubulator ROM vectors...
567 handler 001 at $00E00008
568 handler 002 at $00E008DE
569 handler 003 at $00E008E2
570 handler 004 at $00E008E6
571 handler 005 at $00E008EA
572 handler 006 at $00E008EE
573 handler 007 at $00E008F2
574 handler 008 at $00E0054A
575 handler 009 at $00E008FA
576 handler 010 at $00000000
577 handler 011 at $00000000
578 handler 012 at $00E008FE
579 handler 013 at $00E00902
580 handler 014 at $00E00906
581 handler 015 at $00E0090A
582 handler 016 at $00E0090E
583 handler 017 at $00E00912
584 handler 018 at $00E00916
585 handler 019 at $00E0091A
586 handler 020 at $00E0091E
587 handler 021 at $00E00922
588 handler 022 at $00E00926
589 handler 023 at $00E0092A
590 handler 024 at $00E0092E
591 handler 025 at $00E0107A
592 handler 026 at $00E0107A
593 handler 027 at $00E0107A
594 handler 028 at $00E008DA
595 handler 029 at $00E0107A
596 handler 030 at $00E0107A
597 handler 031 at $00E0107A
598 handler 032 at $00000000
600 Let's try setting up the illegal instruction vector for a stubulated jaguar...
602 SET32(jaguar_mainRam, 0x08, 0x00E008DE);
603 SET32(jaguar_mainRam, 0x0C, 0x00E008E2);
604 SET32(jaguar_mainRam, 0x10, 0x00E008E6); // <-- Should be here (it is)...
605 SET32(jaguar_mainRam, 0x14, 0x00E008EA);//*/
608 ABS Format sleuthing (LBUGDEMO.ABS):
610 000000 60 1B 00 00 05 0C 00 04 62 C0 00 00 04 28 00 00
611 000010 12 A6 00 00 00 00 00 80 20 00 FF FF 00 80 25 0C
614 DRI-format file detected...
615 Text segment size = 0x0000050c bytes
616 Data segment size = 0x000462c0 bytes
617 BSS Segment size = 0x00000428 bytes
618 Symbol Table size = 0x000012a6 bytes
619 Absolute Address for text segment = 0x00802000
620 Absolute Address for data segment = 0x0080250c
621 Absolute Address for BSS segment = 0x00004000
624 000000 01 50 00 03 00 00 00 00 00 03 83 10 00 00 05 3b
625 000010 00 1c 00 03 00 00 01 07 00 00 1d d0 00 03 64 98
626 000020 00 06 8b 80 00 80 20 00 00 80 20 00 00 80 3d d0
628 000030 2e 74 78 74 00 00 00 00 00 80 20 00 00 80 20 00 .txt (+36 bytes)
629 000040 00 00 1d d0 00 00 00 a8 00 00 00 00 00 00 00 00
630 000050 00 00 00 00 00 00 00 20
631 000058 2e 64 74 61 00 00 00 00 00 80 3d d0 00 80 3d d0 .dta (+36 bytes)
632 000068 00 03 64 98 00 00 1e 78 00 00 00 00 00 00 00 00
633 000078 00 00 00 00 00 00 00 40
634 000080 2e 62 73 73 00 00 00 00 00 00 50 00 00 00 50 00 .bss (+36 bytes)
635 000090 00 06 8b 80 00 03 83 10 00 00 00 00 00 00 00 00
636 0000a0 00 00 00 00 00 00 00 80
638 Header size is $A8 bytes...
640 BSD/COFF format file detected...
642 Symbol Table offset = 230160 ($00038310)
643 Symbol Table contains 1339 symbol entries ($0000053B)
644 The additional header size is 28 bytes ($001C)
645 Magic Number for RUN_HDR = 0x00000107
646 Text Segment Size = 7632 ($00001DD0)
647 Data Segment Size = 222360 ($00036498)
648 BSS Segment Size = 428928 ($00068B80)
649 Starting Address for executable = 0x00802000
650 Start of Text Segment = 0x00802000
651 Start of Data Segment = 0x00803dd0