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);
70 romSize = GetFileFromZIP(path, FT_SOFTWARE, rom);
74 WriteLog("Failed!\n");
78 // memcpy(rom, buffer, romSize);
83 // Handle gzipped files transparently [Adam Green]...
85 gzFile fp = gzopen(path, "rb");
89 WriteLog("Failed!\n");
93 romSize = gzfilelength(fp);
94 rom = new uint8[romSize];
95 gzseek(fp, 0, SEEK_SET);
96 gzread(fp, rom, romSize);
100 WriteLog("OK (%i bytes)\n", romSize);
106 // Jaguar file loading
107 // We do a more intelligent file analysis here instead of relying on (possible false)
108 // file extensions which people don't seem to give two shits about anyway. :-(
110 bool JaguarLoadFile(char * path)
112 uint8 * buffer = NULL;
113 jaguarROMSize = JaguarLoadROM(buffer, path);
115 if (jaguarROMSize == 0)
117 // It's up to the GUI to report errors, not us. :-)
118 WriteLog("FILE: Could not load ROM from file \"%s\"...\nAborting load!\n", path);
122 jaguarMainROMCRC32 = crc32_calcCheckSum(buffer, jaguarROMSize);
123 WriteLog("CRC: %08X\n", (unsigned int)jaguarMainROMCRC32);
124 // TODO: Check for EEPROM file in ZIP file. If there is no EEPROM in the user's EEPROM
125 // directory, copy the one from the ZIP file, if it exists.
127 jaguarRunAddress = 0x802000; // For non-BIOS runs, this is true
128 int fileType = ParseFileType(buffer[0], buffer[1], jaguarROMSize);
129 jaguarCartInserted = false;
131 if (fileType == JST_ROM)
133 jaguarCartInserted = true;
134 memcpy(jagMemSpace + 0x800000, buffer, jaguarROMSize);
138 else if (fileType == JST_ALPINE)
140 // File extension ".ROM": Alpine image that loads/runs at $802000
141 WriteLog("FILE: Setting up Alpine ROM... Run address: 00802000, length: %08X\n", jaguarROMSize);
142 memset(jagMemSpace + 0x800000, 0xFF, 0x2000);
143 memcpy(jagMemSpace + 0x802000, buffer, jaguarROMSize);
146 // Maybe instead of this, we could try requiring the STUBULATOR ROM? Just a thought...
147 // Try setting the vector to say, $1000 and putting an instruction there that loops forever:
148 // This kludge works! Yeah!
149 SET32(jaguarMainRAM, 0x10, 0x00001000);
150 SET16(jaguarMainRAM, 0x1000, 0x60FE); // Here: bra Here
153 else if (fileType == JST_ABS_TYPE1)
155 // For ABS type 1, run address == load address
156 uint32 loadAddress = GET32(buffer, 0x16),
157 codeSize = GET32(buffer, 0x02) + GET32(buffer, 0x06);
158 WriteLog("FILE: Setting up homebrew (ABS-1)... Run address: %08X, length: %08X\n", loadAddress, codeSize);
159 memcpy(jagMemSpace + loadAddress, buffer + 0x24, codeSize);
161 jaguarRunAddress = loadAddress;
164 else if (fileType == JST_ABS_TYPE2)
166 uint32 loadAddress = GET32(buffer, 0x28), runAddress = GET32(buffer, 0x24),
167 codeSize = GET32(buffer, 0x18) + GET32(buffer, 0x1C);
168 WriteLog("FILE: Setting up homebrew (ABS-2)... Run address: %08X, length: %08X\n", runAddress, codeSize);
169 memcpy(jagMemSpace + loadAddress, buffer + 0xA8, codeSize);
171 jaguarRunAddress = runAddress;
174 else if (fileType == JST_JAGSERVER)
176 uint32 loadAddress = GET32(buffer, 0x22), runAddress = GET32(buffer, 0x2A);
177 WriteLog("FILE: Setting up homebrew (Jag Server)... Run address: %08X, length: %08X\n", runAddress, jaguarROMSize - 0x2E);
178 memcpy(jagMemSpace + loadAddress, buffer + 0x2E, jaguarROMSize - 0x2E);
180 jaguarRunAddress = runAddress;
184 // We can assume we have JST_NONE at this point. :-P
189 // "Alpine" file loading
190 // Since the developers were coming after us with torches and pitchforks, we decided to
191 // allow this kind of thing. ;-) But ONLY FOR THE DEVS, DAMMIT! >:-U O_O
193 bool AlpineLoadFile(char * path)
195 uint8 * buffer = NULL;
196 jaguarROMSize = JaguarLoadROM(buffer, path);
198 if (jaguarROMSize == 0)
200 // It's up to the GUI to deal with failure, not us. ;-)
201 WriteLog("FILE: Could not load Alpine from file \"%s\"...\nAborting load!\n", path);
205 jaguarMainROMCRC32 = crc32_calcCheckSum(buffer, jaguarROMSize);
206 WriteLog("CRC: %08X\n", (unsigned int)jaguarMainROMCRC32);
209 jaguarRunAddress = 0x802000;
211 WriteLog("FILE: Setting up Alpine ROM with non-standard length... Run address: 00802000, length: %08X\n", jaguarROMSize);
213 memset(jagMemSpace + 0x800000, 0xFF, 0x2000);
214 memcpy(jagMemSpace + 0x802000, buffer, jaguarROMSize);
217 // Maybe instead of this, we could try requiring the STUBULATOR ROM? Just a thought...
218 // Try setting the vector to say, $1000 and putting an instruction there that loops forever:
219 // This kludge works! Yeah!
220 SET32(jaguarMainRAM, 0x10, 0x00001000);
221 SET16(jaguarMainRAM, 0x1000, 0x60FE); // Here: bra Here
227 // Get the length of a (possibly) gzipped file
229 static int gzfilelength(gzFile gd)
231 int size = 0, length = 0;
232 unsigned char buffer[0x10000];
238 // Read in chunks until EOF
239 size = gzread(gd, buffer, 0x10000);
253 // Compare extension to passed in filename. If equal, return true; otherwise false.
255 static bool CheckExtension(const char * filename, const char * ext)
257 // Sanity checking...
258 if ((filename == NULL) || (ext == NULL))
261 const char * filenameExt = strrchr(filename, '.'); // Get the file's extension (if any)
263 if (filenameExt == NULL)
266 return (strcasecmp(filenameExt, ext) == 0 ? true : false);
270 // Get file from .ZIP
271 // Returns the size of the file inside the .ZIP file that we're looking at
272 // NOTE: If the thing we're looking for is found, it allocates it in the passed in buffer.
273 // Which means we have to deallocate it later.
275 uint32 GetFileFromZIP(const char * zipFile, FileType type, uint8 * &buffer)
277 // NOTE: We could easily check for this by discarding anything that's larger than the RAM/ROM
278 // size of the Jaguar console.
279 #warning "!!! FIX !!! Should have sanity checking for ROM size to prevent buffer overflow!"
280 const char ftStrings[5][32] = { "Software", "EEPROM", "Label", "Box Art", "Controller Overlay" };
281 ZIP * zip = openzip(0, 0, zipFile);
285 WriteLog("FILE: Could not open file '%s'!\n", zipFile);
292 // The order is here is important: If the file is found, we need to short-circuit the
293 // readzip() call because otherwise, 'ze' will be pointing to the wrong file!
294 while (!found && readzip(zip))
298 // Here we simply rely on the file extension to tell the truth, but we know
299 // that extensions lie like sons-a-bitches. So this is naive, we need to do
300 // something a little more robust to keep bad things from happening here.
301 #warning "!!! Checking for image by extension can be fooled !!!"
302 if ((type == FT_LABEL) && (CheckExtension(ze->name, ".png") || CheckExtension(ze->name, ".jpg") || CheckExtension(ze->name, ".gif")))
305 WriteLog("FILE: Found image file '%s'.\n", ze->name);
308 if ((type == FT_SOFTWARE) && (CheckExtension(ze->name, ".j64")
309 || CheckExtension(ze->name, ".rom") || CheckExtension(ze->name, ".abs")
310 || CheckExtension(ze->name, ".cof") || CheckExtension(ze->name, ".jag")))
313 WriteLog("FILE: Found software file '%s'.\n", ze->name);
316 if ((type == FT_EEPROM) && (CheckExtension(ze->name, ".eep") || CheckExtension(ze->name, ".eeprom")))
319 WriteLog("FILE: Found EEPROM file '%s'.\n", ze->name);
327 WriteLog("FILE: Uncompressing...");
328 // Insert file size sanity check here...
329 buffer = new uint8[ze->uncompressed_size];
331 if (readuncompresszip(zip, ze, (char *)buffer) == 0)
333 fileSize = ze->uncompressed_size;
334 WriteLog("success! (%u bytes)\n", fileSize);
340 WriteLog("FAILED!\n");
344 // Didn't find what we're looking for...
345 WriteLog("FILE: Failed to find file of type %s...\n", ftStrings[type]);
352 // Parse the file type based upon file size and/or headers.
354 uint32 ParseFileType(uint8 header1, uint8 header2, uint32 size)
356 // Check headers first...
359 if (header1 == 0x60 && header2 == 0x1B)
360 return JST_ABS_TYPE1;
363 if (header1 == 0x01 && header2 == 0x50)
364 return JST_ABS_TYPE2;
367 if (header1 == 0x60 && header2 == 0x1A)
368 return JST_JAGSERVER;
370 // And if that fails, try file sizes...
372 // If the file size is divisible by 1M, we probably have an regular ROM.
373 // We can also check our CRC32 against the internal ROM database to be sure.
374 // (We also check for the Memory Track cartridge size here as well...)
375 if ((size % 1048576) == 0 || size == 131072)
378 // If the file size + 8192 bytes is divisible by 1M, we probably have an
379 // Alpine format ROM.
380 if (((size + 8192) % 1048576) == 0)
388 // Check for universal header
390 bool HasUniversalHeader(uint8 * rom, uint32 romSize)
396 for(int i=0; i<8192; i++)
397 if (rom[i] != universalCartHeader[i])
407 Stubulator ROM vectors...
408 handler 001 at $00E00008
409 handler 002 at $00E008DE
410 handler 003 at $00E008E2
411 handler 004 at $00E008E6
412 handler 005 at $00E008EA
413 handler 006 at $00E008EE
414 handler 007 at $00E008F2
415 handler 008 at $00E0054A
416 handler 009 at $00E008FA
417 handler 010 at $00000000
418 handler 011 at $00000000
419 handler 012 at $00E008FE
420 handler 013 at $00E00902
421 handler 014 at $00E00906
422 handler 015 at $00E0090A
423 handler 016 at $00E0090E
424 handler 017 at $00E00912
425 handler 018 at $00E00916
426 handler 019 at $00E0091A
427 handler 020 at $00E0091E
428 handler 021 at $00E00922
429 handler 022 at $00E00926
430 handler 023 at $00E0092A
431 handler 024 at $00E0092E
432 handler 025 at $00E0107A
433 handler 026 at $00E0107A
434 handler 027 at $00E0107A
435 handler 028 at $00E008DA
436 handler 029 at $00E0107A
437 handler 030 at $00E0107A
438 handler 031 at $00E0107A
439 handler 032 at $00000000
441 Let's try setting up the illegal instruction vector for a stubulated jaguar...
443 SET32(jaguar_mainRam, 0x08, 0x00E008DE);
444 SET32(jaguar_mainRam, 0x0C, 0x00E008E2);
445 SET32(jaguar_mainRam, 0x10, 0x00E008E6); // <-- Should be here (it is)...
446 SET32(jaguar_mainRam, 0x14, 0x00E008EA);//*/
449 ABS Format sleuthing (LBUGDEMO.ABS):
451 000000 60 1B 00 00 05 0C 00 04 62 C0 00 00 04 28 00 00
452 000010 12 A6 00 00 00 00 00 80 20 00 FF FF 00 80 25 0C
455 DRI-format file detected...
456 Text segment size = 0x0000050c bytes
457 Data segment size = 0x000462c0 bytes
458 BSS Segment size = 0x00000428 bytes
459 Symbol Table size = 0x000012a6 bytes
460 Absolute Address for text segment = 0x00802000
461 Absolute Address for data segment = 0x0080250c
462 Absolute Address for BSS segment = 0x00004000
465 000000 01 50 00 03 00 00 00 00 00 03 83 10 00 00 05 3b
466 000010 00 1c 00 03 00 00 01 07 00 00 1d d0 00 03 64 98
467 000020 00 06 8b 80 00 80 20 00 00 80 20 00 00 80 3d d0
469 000030 2e 74 78 74 00 00 00 00 00 80 20 00 00 80 20 00 .txt (+36 bytes)
470 000040 00 00 1d d0 00 00 00 a8 00 00 00 00 00 00 00 00
471 000050 00 00 00 00 00 00 00 20
472 000058 2e 64 74 61 00 00 00 00 00 80 3d d0 00 80 3d d0 .dta (+36 bytes)
473 000068 00 03 64 98 00 00 1e 78 00 00 00 00 00 00 00 00
474 000078 00 00 00 00 00 00 00 40
475 000080 2e 62 73 73 00 00 00 00 00 00 50 00 00 00 50 00 .bss (+36 bytes)
476 000090 00 06 8b 80 00 03 83 10 00 00 00 00 00 00 00 00
477 0000a0 00 00 00 00 00 00 00 80
479 Header size is $A8 bytes...
481 BSD/COFF format file detected...
483 Symbol Table offset = 230160 ($00038310)
484 Symbol Table contains 1339 symbol entries ($0000053B)
485 The additional header size is 28 bytes ($001C)
486 Magic Number for RUN_HDR = 0x00000107
487 Text Segment Size = 7632 ($00001DD0)
488 Data Segment Size = 222360 ($00036498)
489 BSS Segment Size = 428928 ($00068B80)
490 Starting Address for executable = 0x00802000
491 Start of Text Segment = 0x00802000
492 Start of Data Segment = 0x00803dd0