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