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