Update file.cpp
[clinton/Virtual-Jaguar-Rx.git] / src / file.cpp
1 //
2 // FILE.CPP
3 //
4 // File support
5 // by James Hammons
6 // (C) 2010 Underground Software
7 //
8 // JLH = James Hammons <jlhamm@acm.org>
9 // JPM = Jean-Paul Mari <djipi.mari@gmail.com>
10 //
11 // Who When What
12 // --- ---------- ------------------------------------------------------------
13 // JLH 01/16/2010 Created this log ;-)
14 // JLH 02/28/2010 Added functions to look inside .ZIP files and handle
15 // contents
16 // JLH 06/01/2012 Added function to check ZIP file CRCs against file DB
17 // JPM 06/06/2016 Visual Studio support
18 // JPM 06/15/2016 ELF format support
19 // JPM 06/19/2016 Soft debugger support
20 // JPM 07/15/2016 DWARF format support
21 //
22
23 #include "file.h"
24 #if defined(_MSC_VER)
25 #include "_MSC_VER/config.h"
26 #endif // _MSC_VER
27 #include <stdarg.h>
28 #include <string.h>
29 #include "crc32.h"
30 #include "filedb.h"
31 #include "eeprom.h"
32 #include "jaguar.h"
33 #include "log.h"
34 #include "memory.h"
35 #include "universalhdr.h"
36 #include "unzip.h"
37 #include "zlib.h"
38 #include "libelf/libelf.h"
39 #include "libelf/gelf.h"
40 #include "libdwarf.h"
41 #include "debugger/ELFManager.h"
42 #include "debugger/DBGManager.h"
43
44
45 // Private function prototypes
46
47 static int gzfilelength(gzFile gd);
48 //#if defined(_MSC_VER) || defined(__MINGW64__)|| defined(__MINGW32__) || defined(__CYGWIN__)
49 static bool CheckExtension(const uint8_t *filename, const char *ext);
50 //#else
51 //static bool CheckExtension(const char * filename, const char * ext);
52 //#endif // _MSC_VER
53 //static int ParseFileType(uint8_t header1, uint8_t header2, uint32_t size);
54
55 // Private variables/enums
56
57
58 //
59 // Generic ROM loading
60 //
61 uint32_t JaguarLoadROM(uint8_t * &rom, char * path)
62 {
63 // We really should have some kind of sanity checking for the ROM size here to prevent
64 // a buffer overflow... !!! FIX !!!
65 #if defined(_MSC_VER)
66 #pragma message("Warning: !!! FIX !!! Should have sanity checking for ROM size to prevent buffer overflow!")
67 #else
68 #warning "!!! FIX !!! Should have sanity checking for ROM size to prevent buffer overflow!"
69 #endif // _MSC_VER
70 uint32_t romSize = 0;
71
72 WriteLog("FILE: JaguarLoadROM attempting to load file '%s'...", path);
73 char * ext = strrchr(path, '.');
74
75 // No filename extension == YUO FAIL IT (it is loading the file).
76 // This is naive, but it works. But should probably come up with something a little
77 // more robust, to prevent problems with dopes trying to exploit this.
78 if (ext == NULL)
79 {
80 WriteLog("FAILED!\n");
81 return 0;
82 }
83
84 WriteLog("\nFILE: Succeeded in finding extension (%s)!\n", ext);
85 WriteLog("FILE: Loading \"%s\"...", path);
86
87 if (strcasecmp(ext, ".zip") == 0)
88 {
89 // Handle ZIP file loading here...
90 WriteLog("(ZIPped)...");
91
92 // uint8_t * buffer = NULL;
93 // romSize = GetFileFromZIP(path, FT_SOFTWARE, buffer);
94 romSize = GetFileFromZIP(path, FT_SOFTWARE, rom);
95
96 if (romSize == 0)
97 {
98 WriteLog("Failed!\n");
99 return 0;
100 }
101
102 // memcpy(rom, buffer, romSize);
103 // delete[] buffer;
104 }
105 else
106 {
107 // Handle gzipped files transparently [Adam Green]...
108
109 gzFile fp = gzopen(path, "rb");
110
111 if (fp == NULL)
112 {
113 WriteLog("Failed!\n");
114 return 0;
115 }
116
117 romSize = gzfilelength(fp);
118 rom = new uint8_t[romSize];
119 gzseek(fp, 0, SEEK_SET);
120 gzread(fp, rom, romSize);
121 gzclose(fp);
122 }
123
124 WriteLog("OK (%i bytes)\n", romSize);
125
126 return romSize;
127 }
128
129
130 //
131 // Jaguar file loading
132 // We do a more intelligent file analysis here instead of relying on (possible
133 // false) file extensions which people don't seem to give two shits about
134 // anyway. :-(
135 //
136 bool JaguarLoadFile(char * path)
137 {
138 Elf *ElfMem;
139 GElf_Ehdr ElfEhdr, *PtrGElfEhdr;
140 Elf_Scn *PtrElfScn;
141 Elf_Data *PtrElfData;
142 GElf_Shdr GElfShdr, *PtrGElfShdr;
143 size_t NbrSect;
144 uint8_t *buffer = NULL;
145 char *NameSection;
146 size_t ElfSectionNameType;
147 int DBGType = DBG_NO_TYPE;
148 bool error;
149 int err;
150
151 jaguarROMSize = JaguarLoadROM(buffer, path);
152
153 if (jaguarROMSize == 0)
154 {
155 // It's up to the GUI to report errors, not us. :-)
156 WriteLog("FILE: Could not load ROM from file \"%s\"...\nAborting load!\n", path);
157 return false;
158 }
159
160 jaguarMainROMCRC32 = crc32_calcCheckSum(buffer, jaguarROMSize);
161 WriteLog("CRC: %08X\n", (unsigned int)jaguarMainROMCRC32);
162 // TODO: Check for EEPROM file in ZIP file. If there is no EEPROM in the user's EEPROM
163 // directory, copy the one from the ZIP file, if it exists.
164 EepromInit();
165 jaguarRunAddress = 0x802000; // For non-BIOS runs, this is true
166 int fileType = ParseFileType(buffer, jaguarROMSize);
167 jaguarCartInserted = false;
168 DBGManager_Reset();
169
170 if (fileType == JST_ROM)
171 {
172 jaguarCartInserted = true;
173 memcpy(jagMemSpace + 0x800000, buffer, jaguarROMSize);
174 // Checking something...
175 jaguarRunAddress = GET32(jagMemSpace, 0x800404);
176 WriteLog("FILE: Cartridge run address is reported as $%X...\n", jaguarRunAddress);
177 delete[] buffer;
178 return true;
179 }
180 else if (fileType == JST_ALPINE)
181 {
182 // File extension ".ROM": Alpine image that loads/runs at $802000
183 WriteLog("FILE: Setting up Alpine ROM... Run address: 00802000, length: %08X\n", jaguarROMSize);
184 memset(jagMemSpace + 0x800000, 0xFF, 0x2000);
185 memcpy(jagMemSpace + 0x802000, buffer, jaguarROMSize);
186 delete[] buffer;
187
188 // Maybe instead of this, we could try requiring the STUBULATOR ROM? Just a thought...
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
193 return true;
194 }
195 else if (fileType == JST_ELF32)
196 {
197 DBGType = DBG_ELF;
198
199 char *PtrELFExe = (char *)ELFManager_ExeCopy(buffer, jaguarROMSize);
200
201 if (PtrELFExe != NULL)
202 {
203 if ((elf_version(EV_CURRENT) != EV_NONE) && (ElfMem = ELFManager_MemOpen(PtrELFExe, jaguarROMSize)))
204 {
205 if (ELFManager_DwarfInit(ElfMem))
206 {
207 DBGType |= DBG_ELFDWARF;
208 }
209
210 if (!elf_getshdrnum(ElfMem, &NbrSect))
211 {
212 if (((PtrGElfEhdr = gelf_getehdr(ElfMem, &ElfEhdr)) != NULL) && ((PtrElfScn = elf_getscn(ElfMem, 0)) != NULL))
213 {
214 for (error = false; (PtrElfScn != NULL) && (error == false); PtrElfScn = elf_nextscn(ElfMem, PtrElfScn))
215 {
216 PtrElfData = NULL;
217
218 if ((PtrGElfShdr = gelf_getshdr(PtrElfScn, &GElfShdr)) == NULL)
219 {
220 error = true;
221 }
222 else
223 {
224 NameSection = elf_strptr(ElfMem, PtrGElfEhdr->e_shstrndx, (size_t)PtrGElfShdr->sh_name);
225 WriteLog("FILE: ELF Section name: %s\n", NameSection);
226
227 if ((ElfSectionNameType = ELFManager_GetSectionType(NameSection)) == ELF_NO_TYPE)
228 {
229 WriteLog("FILE: ELF Section not listed\n");
230 error = true;
231 }
232 else
233 {
234 switch (PtrGElfShdr->sh_type)
235 {
236 case SHT_NULL:
237 break;
238
239 case SHT_PROGBITS:
240 if ((PtrGElfShdr->sh_flags & (SHF_ALLOC | SHF_WRITE | SHF_EXECINSTR)))
241 {
242 if (PtrGElfShdr->sh_addr >= 0x800000)
243 {
244 memcpy(jagMemSpace + PtrGElfShdr->sh_addr, buffer + PtrGElfShdr->sh_offset, PtrGElfShdr->sh_size);
245 //error = false;
246 }
247 else
248 {
249 memcpy(jaguarMainRAM + PtrGElfShdr->sh_addr, buffer + PtrGElfShdr->sh_offset, PtrGElfShdr->sh_size);
250 }
251 }
252 else
253 {
254 switch (ElfSectionNameType)
255 {
256 case ELF_debug_aranges_TYPE:
257 case ELF_debug_info_TYPE:
258 case ELF_debug_abbrev_TYPE:
259 case ELF_debug_line_TYPE:
260 case ELF_debug_frame_TYPE:
261 case ELF_debug_ranges_TYPE:
262 case ELF_debug_str_TYPE:
263 case ELF_debug_loc_TYPE:
264 break;
265
266 case ELF_comment_TYPE:
267 break;
268
269 default:
270 error = true;
271 break;
272 }
273 }
274 break;
275
276 case SHT_NOBITS:
277 break;
278
279 case SHT_STRTAB:
280 case SHT_SYMTAB:
281 while ((error == false) && ((PtrElfData = elf_getdata(PtrElfScn, PtrElfData)) != NULL))
282 {
283 if (!ELFManager_AddTab(PtrElfData, ElfSectionNameType))
284 {
285 error = true;
286 }
287 }
288 break;
289
290 default:
291 error = true;
292 break;
293 }
294 }
295 }
296 }
297
298 jaguarRunAddress = (uint32_t)PtrGElfEhdr->e_entry;
299 WriteLog("FILE: Setting up ELF 32bits... Run address: %08X\n", jaguarRunAddress);
300 }
301 else
302 {
303 error = true;
304 }
305 }
306 else
307 {
308 error = true;
309 }
310 }
311 else
312 {
313 error = true;
314 WriteLog("FILE: libelf version is not recognized or libelf memory cannot be opened\n");
315 }
316 }
317 else
318 {
319 error = true;
320 WriteLog("FILE: ELFManager cannot allocate memory\n");
321 }
322
323 delete[] buffer;
324
325 if (error)
326 {
327 WriteLog("FILE: ELF parsing error\n");
328
329 if ((err = elf_errno()))
330 {
331 WriteLog("FILE: ELF error: %s\n", elf_errmsg(err));
332 }
333
334 return false;
335 }
336 else
337 {
338 DBGManager_SetType(DBGType);
339 return true;
340 }
341 }
342 else if (fileType == JST_ABS_TYPE1)
343 {
344 // For ABS type 1, run address == load address
345 uint32_t loadAddress = GET32(buffer, 0x16),
346 codeSize = GET32(buffer, 0x02) + GET32(buffer, 0x06);
347 WriteLog("FILE: Setting up homebrew (ABS-1)... Run address: %08X, length: %08X\n", loadAddress, codeSize);
348 memcpy(jagMemSpace + loadAddress, buffer + 0x24, codeSize);
349 delete[] buffer;
350 jaguarRunAddress = loadAddress;
351 return true;
352 }
353 else if (fileType == JST_ABS_TYPE2)
354 {
355 uint32_t loadAddress = GET32(buffer, 0x28), runAddress = GET32(buffer, 0x24),
356 codeSize = GET32(buffer, 0x18) + GET32(buffer, 0x1C);
357 WriteLog("FILE: Setting up homebrew (ABS-2)... Run address: %08X, length: %08X\n", runAddress, codeSize);
358 memcpy(jagMemSpace + loadAddress, buffer + 0xA8, codeSize);
359 delete[] buffer;
360 jaguarRunAddress = runAddress;
361 return true;
362 }
363 // NB: This is *wrong*
364 /*
365 Basically, if there is no "JAG" at position $1C, then the long there is the load/start
366 address in LITTLE ENDIAN.
367 If "JAG" is present, the the next character ("R" or "L") determines the size of the
368 JagServer command (2 bytes vs. 4). Following that are the commands themselves;
369 typically it will either be 2 (load) or 3 (load & run). Command headers go like so:
370 2:
371 Load address (long)
372 Length (long)
373 payload
374 3:
375 Load address (long)
376 Length (long)
377 Run address (long)
378 payload
379 5: (Reset)
380 [command only]
381 7: (Run at address)
382 Run address (long)
383 [no payload]
384 9: (Clear memory)
385 Start address (long)
386 End address (long)
387 [no payload]
388 10: (Poll for commands)
389 [command only]
390 12: (Load & run user program)
391 filname, terminated with NULL
392 [no payload]
393 $FFFF: (Halt)
394 [no payload]
395 */
396 else if (fileType == JST_JAGSERVER)
397 {
398 // This kind of shiaut should be in the detection code below...
399 // (and now it is! :-)
400 // if (buffer[0x1C] == 'J' && buffer[0x1D] == 'A' && buffer[0x1E] == 'G')
401 // {
402 // Still need to do some checking here for type 2 vs. type 3. This assumes 3
403 // Also, JAGR vs. JAGL (word command size vs. long command size)
404 uint32_t loadAddress = GET32(buffer, 0x22), runAddress = GET32(buffer, 0x2A);
405 WriteLog("FILE: Setting up homebrew (Jag Server)... Run address: $%X, length: $%X\n", runAddress, jaguarROMSize - 0x2E);
406 memcpy(jagMemSpace + loadAddress, buffer + 0x2E, jaguarROMSize - 0x2E);
407 delete[] buffer;
408 jaguarRunAddress = runAddress;
409
410 // Hmm. Is this kludge necessary?
411 SET32(jaguarMainRAM, 0x10, 0x00001000); // Set Exception #4 (Illegal Instruction)
412 SET16(jaguarMainRAM, 0x1000, 0x60FE); // Here: bra Here
413
414 return true;
415 // }
416 // else // Special WTFOMGBBQ type here...
417 // {
418 // uint32_t loadAddress = (buffer[0x1F] << 24) | (buffer[0x1E] << 16) | (buffer[0x1D] << 8) | buffer[0x1C];
419 // WriteLog("FILE: Setting up homebrew (GEMDOS WTFOMGBBQ type)... Run address: $%X, length: $%X\n", loadAddress, jaguarROMSize - 0x20);
420 // memcpy(jagMemSpace + loadAddress, buffer + 0x20, jaguarROMSize - 0x20);
421 // delete[] buffer;
422 // jaguarRunAddress = loadAddress;
423 // return true;
424 // }
425 }
426 else if (fileType == JST_WTFOMGBBQ)
427 {
428 uint32_t loadAddress = (buffer[0x1F] << 24) | (buffer[0x1E] << 16) | (buffer[0x1D] << 8) | buffer[0x1C];
429 WriteLog("FILE: Setting up homebrew (GEMDOS WTFOMGBBQ type)... Run address: $%X, length: $%X\n", loadAddress, jaguarROMSize - 0x20);
430 memcpy(jagMemSpace + loadAddress, buffer + 0x20, jaguarROMSize - 0x20);
431 delete[] buffer;
432 jaguarRunAddress = loadAddress;
433 return true;
434 }
435
436 // We can assume we have JST_NONE at this point. :-P
437 WriteLog("FILE: Failed to load headerless file.\n");
438 return false;
439 }
440
441
442 //
443 // "Debugger" file loading
444 // To keep the things separate between "Debugger" and "Alpine" loading until usage clarification has been done
445 //
446 bool DebuggerLoadFile(char * path)
447 {
448 return (AlpineLoadFile(path));
449 }
450
451
452 //
453 // "Alpine" file loading
454 // Since the developers were coming after us with torches and pitchforks, we
455 // decided to allow this kind of thing. ;-) But ONLY FOR THE DEVS, DAMMIT! >:-U
456 // O_O
457 //
458 bool AlpineLoadFile(char * path)
459 {
460 uint8_t * buffer = NULL;
461 jaguarROMSize = JaguarLoadROM(buffer, path);
462
463 if (jaguarROMSize == 0)
464 {
465 // It's up to the GUI to deal with failure, not us. ;-)
466 WriteLog("FILE: Could not load Alpine from file \"%s\"...\nAborting load!\n", path);
467 return false;
468 }
469
470 jaguarMainROMCRC32 = crc32_calcCheckSum(buffer, jaguarROMSize);
471 WriteLog("FILE: CRC is %08X\n", (unsigned int)jaguarMainROMCRC32);
472 EepromInit();
473
474 jaguarRunAddress = 0x802000;
475
476 WriteLog("FILE: Setting up Alpine ROM with non-standard length... Run address: 00802000, length: %08X\n", jaguarROMSize);
477
478 memset(jagMemSpace + 0x800000, 0xFF, 0x2000);
479 memcpy(jagMemSpace + 0x802000, buffer, jaguarROMSize);
480 delete[] buffer;
481
482 // Maybe instead of this, we could try requiring the STUBULATOR ROM? Just a thought...
483 // Try setting the vector to say, $1000 and putting an instruction there
484 // that loops forever:
485 // This kludge works! Yeah!
486 SET32(jaguarMainRAM, 0x10, 0x00001000); // Set Exception #4 (Illegal Instruction)
487 SET16(jaguarMainRAM, 0x1000, 0x60FE); // Here: bra Here
488
489 return true;
490 }
491
492
493 //
494 // Get the length of a (possibly) gzipped file
495 //
496 static int gzfilelength(gzFile gd)
497 {
498 int size = 0, length = 0;
499 unsigned char buffer[0x10000];
500
501 gzrewind(gd);
502
503 do
504 {
505 // Read in chunks until EOF
506 size = gzread(gd, buffer, 0x10000);
507
508 if (size <= 0)
509 break;
510
511 length += size;
512 }
513 while (!gzeof(gd));
514
515 gzrewind(gd);
516 return length;
517 }
518
519
520 //
521 // Compare extension to passed in filename. If equal, return true; otherwise false.
522 //
523 //#if defined(_MSC_VER) || defined(__MINGW64__)|| defined(__MINGW32__) || defined(__CYGWIN__)
524 static bool CheckExtension(const uint8_t *filename, const char *ext)
525 //#else
526 //static bool CheckExtension(const char * filename, const char * ext)
527 //#endif // _MSC_VER
528 {
529 // Sanity checking...
530 if ((filename == NULL) || (ext == NULL))
531 return false;
532
533 const char * filenameExt = strrchr((const char *)filename, '.'); // Get the file's extension (if any)
534
535 if (filenameExt == NULL)
536 return false;
537
538 return (strcasecmp(filenameExt, ext) == 0 ? true : false);
539 }
540
541
542 //
543 // Get file from .ZIP
544 // Returns the size of the file inside the .ZIP file that we're looking at
545 // NOTE: If the thing we're looking for is found, it allocates it in the passed in buffer.
546 // Which means we have to deallocate it later.
547 //
548 uint32_t GetFileFromZIP(const char * zipFile, FileType type, uint8_t * &buffer)
549 {
550 // NOTE: We could easily check for this by discarding anything that's larger than the RAM/ROM
551 // size of the Jaguar console.
552 #if defined(_MSC_VER)
553 #pragma message("Warning: !!! FIX !!! Should have sanity checking for ROM size to prevent buffer overflow!")
554 #else
555 #warning "!!! FIX !!! Should have sanity checking for ROM size to prevent buffer overflow!"
556 #endif // _MSC_VER
557 const char ftStrings[5][32] = { "Software", "EEPROM", "Label", "Box Art", "Controller Overlay" };
558 // ZIP * zip = openzip(0, 0, zipFile);
559 FILE * zip = fopen(zipFile, "rb");
560
561 if (zip == NULL)
562 {
563 WriteLog("FILE: Could not open file '%s'!\n", zipFile);
564 return 0;
565 }
566
567 // zipent * ze;
568 ZipFileEntry ze;
569 bool found = false;
570
571 // The order is here is important: If the file is found, we need to short-circuit the
572 // readzip() call because otherwise, 'ze' will be pointing to the wrong file!
573 // while (!found && readzip(zip))
574 while (!found && GetZIPHeader(zip, ze))
575 {
576 // ze = &zip->ent;
577
578 // Here we simply rely on the file extension to tell the truth, but we know
579 // that extensions lie like sons-a-bitches. So this is naive, we need to do
580 // something a little more robust to keep bad things from happening here.
581 #if defined(_MSC_VER)
582 #pragma message("Warning: !!! Checking for image by extension can be fooled !!!")
583 #else
584 #warning "!!! Checking for image by extension can be fooled !!!"
585 #endif // _MSC_VER
586 if ((type == FT_LABEL) && (CheckExtension(ze.filename, ".png") || CheckExtension(ze.filename, ".jpg") || CheckExtension(ze.filename, ".gif")))
587 {
588 found = true;
589 WriteLog("FILE: Found image file '%s'.\n", ze.filename);
590 }
591
592 if ((type == FT_SOFTWARE) && (CheckExtension(ze.filename, ".j64")
593 || CheckExtension(ze.filename, ".rom") || CheckExtension(ze.filename, ".abs")
594 || CheckExtension(ze.filename, ".cof") || CheckExtension(ze.filename, ".coff")
595 || CheckExtension(ze.filename, ".jag") || CheckExtension(ze.filename, ".elf")))
596 {
597 found = true;
598 WriteLog("FILE: Found software file '%s'.\n", ze.filename);
599 }
600
601 if ((type == FT_EEPROM) && (CheckExtension(ze.filename, ".eep") || CheckExtension(ze.filename, ".eeprom")))
602 {
603 found = true;
604 WriteLog("FILE: Found EEPROM file '%s'.\n", ze.filename);
605 }
606
607 if (!found)
608 fseek(zip, ze.compressedSize, SEEK_CUR);
609 }
610
611 uint32_t fileSize = 0;
612
613 if (found)
614 {
615 WriteLog("FILE: Uncompressing...");
616 // Insert file size sanity check here...
617 buffer = new uint8_t[ze.uncompressedSize];
618
619 // if (readuncompresszip(zip, ze.compressedSize, buffer) == 0)
620 // if (UncompressFileFromZIP(zip, ze.compressedSize, buffer) == 0)
621 if (UncompressFileFromZIP(zip, ze, buffer) == 0)
622 {
623 fileSize = ze.uncompressedSize;
624 WriteLog("success! (%u bytes)\n", fileSize);
625 }
626 else
627 {
628 delete[] buffer;
629 buffer = NULL;
630 WriteLog("FAILED!\n");
631 }
632 }
633 else
634 // Didn't find what we're looking for...
635 WriteLog("FILE: Failed to find file of type %s...\n", ftStrings[type]);
636
637 // closezip(zip);
638 fclose(zip);
639 return fileSize;
640 }
641
642
643 uint32_t GetFileDBIdentityFromZIP(const char * zipFile)
644 {
645 FILE * zip = fopen(zipFile, "rb");
646
647 if (zip == NULL)
648 {
649 WriteLog("FILE: Could not open file '%s'!\n", zipFile);
650 return 0;
651 }
652
653 ZipFileEntry ze;
654
655 // Loop through all files in the zip file under consideration
656 while (GetZIPHeader(zip, ze))
657 {
658 // & loop through all known CRC32s in our file DB to see if it's there!
659 uint32_t index = 0;
660
661 while (romList[index].crc32 != 0xFFFFFF)
662 {
663 if (romList[index].crc32 == ze.crc32)
664 {
665 fclose(zip);
666 return index;
667 }
668
669 index++;
670 }
671
672 // We didn't find it, so skip the compressed data...
673 fseek(zip, ze.compressedSize, SEEK_CUR);
674 }
675
676 fclose(zip);
677 return (uint32_t )-1;
678 }
679
680
681 bool FindFileInZIPWithCRC32(const char * zipFile, uint32_t crc)
682 {
683 FILE * zip = fopen(zipFile, "rb");
684
685 if (zip == NULL)
686 {
687 WriteLog("FILE: Could not open file '%s'!\n", zipFile);
688 return 0;
689 }
690
691 ZipFileEntry ze;
692
693 // Loop through all files in the zip file under consideration
694 while (GetZIPHeader(zip, ze))
695 {
696 if (ze.crc32 == crc)
697 {
698 fclose(zip);
699 return true;
700 }
701
702 fseek(zip, ze.compressedSize, SEEK_CUR);
703 }
704
705 fclose(zip);
706 return false;
707 }
708
709
710 //
711 // Parse the file type based upon file size and/or headers.
712 //
713 uint32_t ParseFileType(uint8_t * buffer, uint32_t size)
714 {
715 // Check headers first...
716
717 // ELF 32bits
718 if (buffer[EI_CLASS] == ELFCLASS32)
719 {
720 if (((BigToLittleEndian16(((Elf32_Ehdr *)buffer)->e_machine) & 0xFF) == EM_68K) && (BigToLittleEndian16(((Elf32_Ehdr *)buffer)->e_type) == ET_EXEC) && (buffer[0] == ELFMAG0) && (buffer[1] == ELFMAG1) && (buffer[2] == ELFMAG2) && (buffer[3] == ELFMAG3))
721 return JST_ELF32;
722 }
723
724 // ABS/COFF type 1
725 if (buffer[0] == 0x60 && buffer[1] == 0x1B)
726 return JST_ABS_TYPE1;
727
728 // ABS/COFF type 2
729 if (buffer[0] == 0x01 && buffer[1] == 0x50)
730 return JST_ABS_TYPE2;
731
732 // Jag Server & other old shite
733 if (buffer[0] == 0x60 && buffer[1] == 0x1A)
734 {
735 if (buffer[0x1C] == 'J' && buffer[0x1D] == 'A' && buffer[0x1E] == 'G')
736 return JST_JAGSERVER;
737 else
738 return JST_WTFOMGBBQ;
739 }
740
741 // And if that fails, try file sizes...
742
743 // If the file size is divisible by 1M, we probably have an regular ROM.
744 // We can also check our CRC32 against the internal ROM database to be sure.
745 // (We also check for the Memory Track cartridge size here as well...)
746 if ((size % 1048576) == 0 || size == 131072)
747 return JST_ROM;
748
749 // If the file size + 8192 bytes is divisible by 1M, we probably have an
750 // Alpine format ROM.
751 if (((size + 8192) % 1048576) == 0)
752 return JST_ALPINE;
753
754 // Headerless crap
755 return JST_NONE;
756 }
757
758 //
759 // Check for universal header
760 //
761 bool HasUniversalHeader(uint8_t * rom, uint32_t romSize)
762 {
763 // Sanity check
764 if (romSize < 8192)
765 return false;
766
767 for(int i=0; i<8192; i++)
768 if (rom[i] != universalCartHeader[i])
769 return false;
770
771 return true;
772 }
773
774 #if 0
775 // Misc. doco
776
777 /*
778 Stubulator ROM vectors...
779 handler 001 at $00E00008
780 handler 002 at $00E008DE
781 handler 003 at $00E008E2
782 handler 004 at $00E008E6
783 handler 005 at $00E008EA
784 handler 006 at $00E008EE
785 handler 007 at $00E008F2
786 handler 008 at $00E0054A
787 handler 009 at $00E008FA
788 handler 010 at $00000000
789 handler 011 at $00000000
790 handler 012 at $00E008FE
791 handler 013 at $00E00902
792 handler 014 at $00E00906
793 handler 015 at $00E0090A
794 handler 016 at $00E0090E
795 handler 017 at $00E00912
796 handler 018 at $00E00916
797 handler 019 at $00E0091A
798 handler 020 at $00E0091E
799 handler 021 at $00E00922
800 handler 022 at $00E00926
801 handler 023 at $00E0092A
802 handler 024 at $00E0092E
803 handler 025 at $00E0107A
804 handler 026 at $00E0107A
805 handler 027 at $00E0107A
806 handler 028 at $00E008DA
807 handler 029 at $00E0107A
808 handler 030 at $00E0107A
809 handler 031 at $00E0107A
810 handler 032 at $00000000
811
812 Let's try setting up the illegal instruction vector for a stubulated jaguar...
813
814 SET32(jaguar_mainRam, 0x08, 0x00E008DE);
815 SET32(jaguar_mainRam, 0x0C, 0x00E008E2);
816 SET32(jaguar_mainRam, 0x10, 0x00E008E6); // <-- Should be here (it is)...
817 SET32(jaguar_mainRam, 0x14, 0x00E008EA);//*/
818
819 /*
820 ABS Format sleuthing (LBUGDEMO.ABS):
821
822 000000 60 1B 00 00 05 0C 00 04 62 C0 00 00 04 28 00 00
823 000010 12 A6 00 00 00 00 00 80 20 00 FF FF 00 80 25 0C
824 000020 00 00 40 00
825
826 DRI-format file detected...
827 Text segment size = 0x0000050c bytes
828 Data segment size = 0x000462c0 bytes
829 BSS Segment size = 0x00000428 bytes
830 Symbol Table size = 0x000012a6 bytes
831 Absolute Address for text segment = 0x00802000
832 Absolute Address for data segment = 0x0080250c
833 Absolute Address for BSS segment = 0x00004000
834
835 (CRZDEMO.ABS):
836 000000 01 50 00 03 00 00 00 00 00 03 83 10 00 00 05 3b
837 000010 00 1c 00 03 00 00 01 07 00 00 1d d0 00 03 64 98
838 000020 00 06 8b 80 00 80 20 00 00 80 20 00 00 80 3d d0
839
840 000030 2e 74 78 74 00 00 00 00 00 80 20 00 00 80 20 00 .txt (+36 bytes)
841 000040 00 00 1d d0 00 00 00 a8 00 00 00 00 00 00 00 00
842 000050 00 00 00 00 00 00 00 20
843 000058 2e 64 74 61 00 00 00 00 00 80 3d d0 00 80 3d d0 .dta (+36 bytes)
844 000068 00 03 64 98 00 00 1e 78 00 00 00 00 00 00 00 00
845 000078 00 00 00 00 00 00 00 40
846 000080 2e 62 73 73 00 00 00 00 00 00 50 00 00 00 50 00 .bss (+36 bytes)
847 000090 00 06 8b 80 00 03 83 10 00 00 00 00 00 00 00 00
848 0000a0 00 00 00 00 00 00 00 80
849
850 Header size is $A8 bytes...
851
852 BSD/COFF format file detected...
853 3 sections specified
854 Symbol Table offset = 230160 ($00038310)
855 Symbol Table contains 1339 symbol entries ($0000053B)
856 The additional header size is 28 bytes ($001C)
857 Magic Number for RUN_HDR = 0x00000107
858 Text Segment Size = 7632 ($00001DD0)
859 Data Segment Size = 222360 ($00036498)
860 BSS Segment Size = 428928 ($00068B80)
861 Starting Address for executable = 0x00802000
862 Start of Text Segment = 0x00802000
863 Start of Data Segment = 0x00803dd0
864 */
865 #endif