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