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