2b63d400d9b7407b2da6ef955a5a2c46baf861e8
[clinton/Virtual-Jaguar-Rx.git] / src / file.cpp
1 <<<<<<< HEAD
2 //
3 // FILE.CPP
4 //
5 // File support
6 // by James Hammons
7 // (C) 2010 Underground Software
8 //
9 // JLH = James Hammons <jlhamm@acm.org>
10 // JPM = Jean-Paul Mari <djipi.mari@gmail.com>
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
16 // contents
17 // JLH 06/01/2012 Added function to check ZIP file CRCs against file DB
18 // JPM 06/06/2016 Visual Studio support
19 // JPM 06/15/2016 ELF format support
20 // JPM 06/19/2016 Soft debugger support
21 // JPM 07/15/2016 DWARF format support
22 //
23
24 #include "file.h"
25 #if defined(_MSC_VER)
26 #include "_MSC_VER/config.h"
27 #endif // _MSC_VER
28 #include <stdarg.h>
29 #include <string.h>
30 #include "crc32.h"
31 #include "filedb.h"
32 #include "eeprom.h"
33 #include "jaguar.h"
34 #include "log.h"
35 #include "memory.h"
36 #include "universalhdr.h"
37 #include "unzip.h"
38 #include "zlib.h"
39 #include "libelf/libelf.h"
40 #include "libelf/gelf.h"
41 #include "libdwarf.h"
42 #include "debugger/ELFManager.h"
43 #include "debugger/DBGManager.h"
44
45
46 // Private function prototypes
47
48 static int gzfilelength(gzFile gd);
49 //#if defined(_MSC_VER) || defined(__MINGW64__)|| defined(__MINGW32__) || defined(__CYGWIN__)
50 static bool CheckExtension(const uint8_t *filename, const char *ext);
51 //#else
52 //static bool CheckExtension(const char * filename, const char * ext);
53 //#endif // _MSC_VER
54 //static int ParseFileType(uint8_t header1, uint8_t header2, uint32_t size);
55
56 // Private variables/enums
57
58
59 //
60 // Generic ROM loading
61 //
62 uint32_t JaguarLoadROM(uint8_t * &rom, char * path)
63 {
64 // We really should have some kind of sanity checking for the ROM size here to prevent
65 // a buffer overflow... !!! FIX !!!
66 #if defined(_MSC_VER)
67 #pragma message("Warning: !!! FIX !!! Should have sanity checking for ROM size to prevent buffer overflow!")
68 #else
69 #warning "!!! FIX !!! Should have sanity checking for ROM size to prevent buffer overflow!"
70 #endif // _MSC_VER
71 uint32_t romSize = 0;
72
73 WriteLog("FILE: JaguarLoadROM attempting to load file '%s'...", path);
74 char * ext = strrchr(path, '.');
75
76 // No filename extension == YUO FAIL IT (it is loading the file).
77 // This is naive, but it works. But should probably come up with something a little
78 // more robust, to prevent problems with dopes trying to exploit this.
79 if (ext == NULL)
80 {
81 WriteLog("FAILED!\n");
82 return 0;
83 }
84
85 WriteLog("\nFILE: Succeeded in finding extension (%s)!\n", ext);
86 WriteLog("FILE: Loading \"%s\"...", path);
87
88 if (strcasecmp(ext, ".zip") == 0)
89 {
90 // Handle ZIP file loading here...
91 WriteLog("(ZIPped)...");
92
93 // uint8_t * buffer = NULL;
94 // romSize = GetFileFromZIP(path, FT_SOFTWARE, buffer);
95 romSize = GetFileFromZIP(path, FT_SOFTWARE, rom);
96
97 if (romSize == 0)
98 {
99 WriteLog("Failed!\n");
100 return 0;
101 }
102
103 // memcpy(rom, buffer, romSize);
104 // delete[] buffer;
105 }
106 else
107 {
108 // Handle gzipped files transparently [Adam Green]...
109
110 gzFile fp = gzopen(path, "rb");
111
112 if (fp == NULL)
113 {
114 WriteLog("Failed!\n");
115 return 0;
116 }
117
118 romSize = gzfilelength(fp);
119 rom = new uint8_t[romSize];
120 gzseek(fp, 0, SEEK_SET);
121 gzread(fp, rom, romSize);
122 gzclose(fp);
123 }
124
125 WriteLog("OK (%i bytes)\n", romSize);
126
127 return romSize;
128 }
129
130
131 //
132 // Jaguar file loading
133 // We do a more intelligent file analysis here instead of relying on (possible
134 // false) file extensions which people don't seem to give two shits about
135 // anyway. :-(
136 //
137 bool JaguarLoadFile(char * path)
138 {
139 Elf *ElfMem;
140 GElf_Ehdr ElfEhdr, *PtrGElfEhdr;
141 Elf_Scn *PtrElfScn;
142 Elf_Data *PtrElfData;
143 GElf_Shdr GElfShdr, *PtrGElfShdr;
144 size_t NbrSect;
145 uint8_t *buffer = NULL;
146 char *NameSection;
147 size_t ElfSectionNameType;
148 int DBGType = DBG_NO_TYPE;
149 bool error;
150 int err;
151
152 jaguarROMSize = JaguarLoadROM(buffer, path);
153
154 if (jaguarROMSize == 0)
155 {
156 // It's up to the GUI to report errors, not us. :-)
157 WriteLog("FILE: Could not load ROM from file \"%s\"...\nAborting load!\n", path);
158 return false;
159 }
160
161 jaguarMainROMCRC32 = crc32_calcCheckSum(buffer, jaguarROMSize);
162 WriteLog("CRC: %08X\n", (unsigned int)jaguarMainROMCRC32);
163 // TODO: Check for EEPROM file in ZIP file. If there is no EEPROM in the user's EEPROM
164 // directory, copy the one from the ZIP file, if it exists.
165 EepromInit();
166 jaguarRunAddress = 0x802000; // For non-BIOS runs, this is true
167 int fileType = ParseFileType(buffer, jaguarROMSize);
168 jaguarCartInserted = false;
169 DBGManager_Reset();
170
171 if (fileType == JST_ROM)
172 {
173 jaguarCartInserted = true;
174 memcpy(jagMemSpace + 0x800000, buffer, jaguarROMSize);
175 // Checking something...
176 jaguarRunAddress = GET32(jagMemSpace, 0x800404);
177 WriteLog("FILE: Cartridge run address is reported as $%X...\n", jaguarRunAddress);
178 delete[] buffer;
179 return true;
180 }
181 else if (fileType == JST_ALPINE)
182 {
183 // File extension ".ROM": Alpine image that loads/runs at $802000
184 WriteLog("FILE: Setting up Alpine ROM... Run address: 00802000, length: %08X\n", jaguarROMSize);
185 memset(jagMemSpace + 0x800000, 0xFF, 0x2000);
186 memcpy(jagMemSpace + 0x802000, buffer, jaguarROMSize);
187 delete[] buffer;
188
189 // Maybe instead of this, we could try requiring the STUBULATOR ROM? Just a thought...
190 // Try setting the vector to say, $1000 and putting an instruction there that loops forever:
191 // This kludge works! Yeah!
192 SET32(jaguarMainRAM, 0x10, 0x00001000);
193 SET16(jaguarMainRAM, 0x1000, 0x60FE); // Here: bra Here
194 return true;
195 }
196 else if (fileType == JST_ELF32)
197 {
198 DBGType = DBG_ELF;
199
200 char *PtrELFExe = (char *)ELFManager_ExeCopy(buffer, jaguarROMSize);
201
202 if (PtrELFExe != NULL)
203 {
204 if ((elf_version(EV_CURRENT) != EV_NONE) && (ElfMem = ELFManager_MemOpen(PtrELFExe, jaguarROMSize)))
205 {
206 if (ELFManager_DwarfInit(ElfMem))
207 {
208 DBGType |= DBG_ELFDWARF;
209 }
210
211 if (!elf_getshdrnum(ElfMem, &NbrSect))
212 {
213 if (((PtrGElfEhdr = gelf_getehdr(ElfMem, &ElfEhdr)) != NULL) && ((PtrElfScn = elf_getscn(ElfMem, 0)) != NULL))
214 {
215 for (error = false; (PtrElfScn != NULL) && (error == false); PtrElfScn = elf_nextscn(ElfMem, PtrElfScn))
216 {
217 PtrElfData = NULL;
218
219 if ((PtrGElfShdr = gelf_getshdr(PtrElfScn, &GElfShdr)) == NULL)
220 {
221 error = true;
222 }
223 else
224 {
225 NameSection = elf_strptr(ElfMem, PtrGElfEhdr->e_shstrndx, (size_t)PtrGElfShdr->sh_name);
226 WriteLog("FILE: ELF Section name: %s\n", NameSection);
227
228 if ((ElfSectionNameType = ELFManager_GetSectionType(NameSection)) == ELF_NO_TYPE)
229 {
230 WriteLog("FILE: ELF Section not listed\n");
231 error = true;
232 }
233 else
234 {
235 switch (PtrGElfShdr->sh_type)
236 {
237 case SHT_NULL:
238 break;
239
240 case SHT_PROGBITS:
241 if ((PtrGElfShdr->sh_flags & (SHF_ALLOC | SHF_WRITE | SHF_EXECINSTR)))
242 {
243 if (PtrGElfShdr->sh_addr >= 0x800000)
244 {
245 memcpy(jagMemSpace + PtrGElfShdr->sh_addr, buffer + PtrGElfShdr->sh_offset, PtrGElfShdr->sh_size);
246 //error = false;
247 }
248 else
249 {
250 memcpy(jaguarMainRAM + PtrGElfShdr->sh_addr, buffer + PtrGElfShdr->sh_offset, PtrGElfShdr->sh_size);
251 }
252 }
253 else
254 {
255 switch (ElfSectionNameType)
256 {
257 case ELF_debug_aranges_TYPE:
258 case ELF_debug_info_TYPE:
259 case ELF_debug_abbrev_TYPE:
260 case ELF_debug_line_TYPE:
261 case ELF_debug_frame_TYPE:
262 case ELF_debug_ranges_TYPE:
263 case ELF_debug_str_TYPE:
264 case ELF_debug_loc_TYPE:
265 break;
266
267 case ELF_comment_TYPE:
268 break;
269
270 default:
271 error = true;
272 break;
273 }
274 }
275 break;
276
277 case SHT_NOBITS:
278 break;
279
280 case SHT_STRTAB:
281 case SHT_SYMTAB:
282 while ((error == false) && ((PtrElfData = elf_getdata(PtrElfScn, PtrElfData)) != NULL))
283 {
284 if (!ELFManager_AddTab(PtrElfData, ElfSectionNameType))
285 {
286 error = true;
287 }
288 }
289 break;
290
291 default:
292 error = true;
293 break;
294 }
295 }
296 }
297 }
298
299 jaguarRunAddress = (uint32_t)PtrGElfEhdr->e_entry;
300 WriteLog("FILE: Setting up ELF 32bits... Run address: %08X\n", jaguarRunAddress);
301 }
302 else
303 {
304 error = true;
305 }
306 }
307 else
308 {
309 error = true;
310 }
311 }
312 else
313 {
314 error = true;
315 WriteLog("FILE: libelf version is not recognized or libelf memory cannot be opened\n");
316 }
317 }
318 else
319 {
320 error = true;
321 WriteLog("FILE: ELFManager cannot allocate memory\n");
322 }
323
324 delete[] buffer;
325
326 if (error)
327 {
328 WriteLog("FILE: ELF parsing error\n");
329
330 if ((err = elf_errno()))
331 {
332 WriteLog("FILE: ELF error: %s\n", elf_errmsg(err));
333 }
334
335 return false;
336 }
337 else
338 {
339 DBGManager_SetType(DBGType);
340 return true;
341 }
342 }
343 else if (fileType == JST_ABS_TYPE1)
344 {
345 // For ABS type 1, run address == load address
346 uint32_t loadAddress = GET32(buffer, 0x16),
347 codeSize = GET32(buffer, 0x02) + GET32(buffer, 0x06);
348 WriteLog("FILE: Setting up homebrew (ABS-1)... Run address: %08X, length: %08X\n", loadAddress, codeSize);
349 memcpy(jagMemSpace + loadAddress, buffer + 0x24, codeSize);
350 delete[] buffer;
351 jaguarRunAddress = loadAddress;
352 return true;
353 }
354 else if (fileType == JST_ABS_TYPE2)
355 {
356 uint32_t loadAddress = GET32(buffer, 0x28), runAddress = GET32(buffer, 0x24),
357 codeSize = GET32(buffer, 0x18) + GET32(buffer, 0x1C);
358 WriteLog("FILE: Setting up homebrew (ABS-2)... Run address: %08X, length: %08X\n", runAddress, codeSize);
359 memcpy(jagMemSpace + loadAddress, buffer + 0xA8, codeSize);
360 delete[] buffer;
361 jaguarRunAddress = runAddress;
362 return true;
363 }
364 // NB: This is *wrong*
365 /*
366 Basically, if there is no "JAG" at position $1C, then the long there is the load/start
367 address in LITTLE ENDIAN.
368 If "JAG" is present, the the next character ("R" or "L") determines the size of the
369 JagServer command (2 bytes vs. 4). Following that are the commands themselves;
370 typically it will either be 2 (load) or 3 (load & run). Command headers go like so:
371 2:
372 Load address (long)
373 Length (long)
374 payload
375 3:
376 Load address (long)
377 Length (long)
378 Run address (long)
379 payload
380 5: (Reset)
381 [command only]
382 7: (Run at address)
383 Run address (long)
384 [no payload]
385 9: (Clear memory)
386 Start address (long)
387 End address (long)
388 [no payload]
389 10: (Poll for commands)
390 [command only]
391 12: (Load & run user program)
392 filname, terminated with NULL
393 [no payload]
394 $FFFF: (Halt)
395 [no payload]
396 */
397 else if (fileType == JST_JAGSERVER)
398 {
399 // This kind of shiaut should be in the detection code below...
400 // (and now it is! :-)
401 // if (buffer[0x1C] == 'J' && buffer[0x1D] == 'A' && buffer[0x1E] == 'G')
402 // {
403 // Still need to do some checking here for type 2 vs. type 3. This assumes 3
404 // Also, JAGR vs. JAGL (word command size vs. long command size)
405 uint32_t loadAddress = GET32(buffer, 0x22), runAddress = GET32(buffer, 0x2A);
406 WriteLog("FILE: Setting up homebrew (Jag Server)... Run address: $%X, length: $%X\n", runAddress, jaguarROMSize - 0x2E);
407 memcpy(jagMemSpace + loadAddress, buffer + 0x2E, jaguarROMSize - 0x2E);
408 delete[] buffer;
409 jaguarRunAddress = runAddress;
410
411 // Hmm. Is this kludge necessary?
412 SET32(jaguarMainRAM, 0x10, 0x00001000); // Set Exception #4 (Illegal Instruction)
413 SET16(jaguarMainRAM, 0x1000, 0x60FE); // Here: bra Here
414
415 return true;
416 // }
417 // else // Special WTFOMGBBQ type here...
418 // {
419 // uint32_t loadAddress = (buffer[0x1F] << 24) | (buffer[0x1E] << 16) | (buffer[0x1D] << 8) | buffer[0x1C];
420 // WriteLog("FILE: Setting up homebrew (GEMDOS WTFOMGBBQ type)... Run address: $%X, length: $%X\n", loadAddress, jaguarROMSize - 0x20);
421 // memcpy(jagMemSpace + loadAddress, buffer + 0x20, jaguarROMSize - 0x20);
422 // delete[] buffer;
423 // jaguarRunAddress = loadAddress;
424 // return true;
425 // }
426 }
427 else if (fileType == JST_WTFOMGBBQ)
428 {
429 uint32_t loadAddress = (buffer[0x1F] << 24) | (buffer[0x1E] << 16) | (buffer[0x1D] << 8) | buffer[0x1C];
430 WriteLog("FILE: Setting up homebrew (GEMDOS WTFOMGBBQ type)... Run address: $%X, length: $%X\n", loadAddress, jaguarROMSize - 0x20);
431 memcpy(jagMemSpace + loadAddress, buffer + 0x20, jaguarROMSize - 0x20);
432 delete[] buffer;
433 jaguarRunAddress = loadAddress;
434 return true;
435 }
436
437 // We can assume we have JST_NONE at this point. :-P
438 WriteLog("FILE: Failed to load headerless file.\n");
439 return false;
440 }
441
442
443 //
444 // "Debugger" file loading
445 // To keep the things separate between "Debugger" and "Alpine" loading until usage clarification has been done
446 //
447 bool DebuggerLoadFile(char * path)
448 {
449 return (AlpineLoadFile(path));
450 }
451
452
453 //
454 // "Alpine" file loading
455 // Since the developers were coming after us with torches and pitchforks, we
456 // decided to allow this kind of thing. ;-) But ONLY FOR THE DEVS, DAMMIT! >:-U
457 // O_O
458 //
459 bool AlpineLoadFile(char * path)
460 {
461 uint8_t * buffer = NULL;
462 jaguarROMSize = JaguarLoadROM(buffer, path);
463
464 if (jaguarROMSize == 0)
465 {
466 // It's up to the GUI to deal with failure, not us. ;-)
467 WriteLog("FILE: Could not load Alpine from file \"%s\"...\nAborting load!\n", path);
468 return false;
469 }
470
471 jaguarMainROMCRC32 = crc32_calcCheckSum(buffer, jaguarROMSize);
472 WriteLog("FILE: CRC is %08X\n", (unsigned int)jaguarMainROMCRC32);
473 EepromInit();
474
475 jaguarRunAddress = 0x802000;
476
477 WriteLog("FILE: Setting up Alpine ROM with non-standard length... Run address: 00802000, length: %08X\n", jaguarROMSize);
478
479 memset(jagMemSpace + 0x800000, 0xFF, 0x2000);
480 memcpy(jagMemSpace + 0x802000, buffer, jaguarROMSize);
481 delete[] buffer;
482
483 // Maybe instead of this, we could try requiring the STUBULATOR ROM? Just a thought...
484 // Try setting the vector to say, $1000 and putting an instruction there
485 // that loops forever:
486 // This kludge works! Yeah!
487 SET32(jaguarMainRAM, 0x10, 0x00001000); // Set Exception #4 (Illegal Instruction)
488 SET16(jaguarMainRAM, 0x1000, 0x60FE); // Here: bra Here
489
490 return true;
491 }
492
493
494 //
495 // Get the length of a (possibly) gzipped file
496 //
497 static int gzfilelength(gzFile gd)
498 {
499 int size = 0, length = 0;
500 unsigned char buffer[0x10000];
501
502 gzrewind(gd);
503
504 do
505 {
506 // Read in chunks until EOF
507 size = gzread(gd, buffer, 0x10000);
508
509 if (size <= 0)
510 break;
511
512 length += size;
513 }
514 while (!gzeof(gd));
515
516 gzrewind(gd);
517 return length;
518 }
519
520
521 //
522 // Compare extension to passed in filename. If equal, return true; otherwise false.
523 //
524 //#if defined(_MSC_VER) || defined(__MINGW64__)|| defined(__MINGW32__) || defined(__CYGWIN__)
525 static bool CheckExtension(const uint8_t *filename, const char *ext)
526 //#else
527 //static bool CheckExtension(const char * filename, const char * ext)
528 //#endif // _MSC_VER
529 {
530 // Sanity checking...
531 if ((filename == NULL) || (ext == NULL))
532 return false;
533
534 const char * filenameExt = strrchr((const char *)filename, '.'); // Get the file's extension (if any)
535
536 if (filenameExt == NULL)
537 return false;
538
539 return (strcasecmp(filenameExt, ext) == 0 ? true : false);
540 }
541
542
543 //
544 // Get file from .ZIP
545 // Returns the size of the file inside the .ZIP file that we're looking at
546 // NOTE: If the thing we're looking for is found, it allocates it in the passed in buffer.
547 // Which means we have to deallocate it later.
548 //
549 uint32_t GetFileFromZIP(const char * zipFile, FileType type, uint8_t * &buffer)
550 {
551 // NOTE: We could easily check for this by discarding anything that's larger than the RAM/ROM
552 // size of the Jaguar console.
553 #if defined(_MSC_VER)
554 #pragma message("Warning: !!! FIX !!! Should have sanity checking for ROM size to prevent buffer overflow!")
555 #else
556 #warning "!!! FIX !!! Should have sanity checking for ROM size to prevent buffer overflow!"
557 #endif // _MSC_VER
558 const char ftStrings[5][32] = { "Software", "EEPROM", "Label", "Box Art", "Controller Overlay" };
559 // ZIP * zip = openzip(0, 0, zipFile);
560 FILE * zip = fopen(zipFile, "rb");
561
562 if (zip == NULL)
563 {
564 WriteLog("FILE: Could not open file '%s'!\n", zipFile);
565 return 0;
566 }
567
568 // zipent * ze;
569 ZipFileEntry ze;
570 bool found = false;
571
572 // The order is here is important: If the file is found, we need to short-circuit the
573 // readzip() call because otherwise, 'ze' will be pointing to the wrong file!
574 // while (!found && readzip(zip))
575 while (!found && GetZIPHeader(zip, ze))
576 {
577 // ze = &zip->ent;
578
579 // Here we simply rely on the file extension to tell the truth, but we know
580 // that extensions lie like sons-a-bitches. So this is naive, we need to do
581 // something a little more robust to keep bad things from happening here.
582 #if defined(_MSC_VER)
583 #pragma message("Warning: !!! Checking for image by extension can be fooled !!!")
584 #else
585 #warning "!!! Checking for image by extension can be fooled !!!"
586 #endif // _MSC_VER
587 if ((type == FT_LABEL) && (CheckExtension(ze.filename, ".png") || CheckExtension(ze.filename, ".jpg") || CheckExtension(ze.filename, ".gif")))
588 {
589 found = true;
590 WriteLog("FILE: Found image file '%s'.\n", ze.filename);
591 }
592
593 if ((type == FT_SOFTWARE) && (CheckExtension(ze.filename, ".j64")
594 || CheckExtension(ze.filename, ".rom") || CheckExtension(ze.filename, ".abs")
595 || CheckExtension(ze.filename, ".cof") || CheckExtension(ze.filename, ".coff")
596 || CheckExtension(ze.filename, ".jag") || CheckExtension(ze.filename, ".elf")))
597 {
598 found = true;
599 WriteLog("FILE: Found software file '%s'.\n", ze.filename);
600 }
601
602 if ((type == FT_EEPROM) && (CheckExtension(ze.filename, ".eep") || CheckExtension(ze.filename, ".eeprom")))
603 {
604 found = true;
605 WriteLog("FILE: Found EEPROM file '%s'.\n", ze.filename);
606 }
607
608 if (!found)
609 fseek(zip, ze.compressedSize, SEEK_CUR);
610 }
611
612 uint32_t fileSize = 0;
613
614 if (found)
615 {
616 WriteLog("FILE: Uncompressing...");
617 // Insert file size sanity check here...
618 buffer = new uint8_t[ze.uncompressedSize];
619
620 // if (readuncompresszip(zip, ze.compressedSize, buffer) == 0)
621 // if (UncompressFileFromZIP(zip, ze.compressedSize, buffer) == 0)
622 if (UncompressFileFromZIP(zip, ze, buffer) == 0)
623 {
624 fileSize = ze.uncompressedSize;
625 WriteLog("success! (%u bytes)\n", fileSize);
626 }
627 else
628 {
629 delete[] buffer;
630 buffer = NULL;
631 WriteLog("FAILED!\n");
632 }
633 }
634 else
635 // Didn't find what we're looking for...
636 WriteLog("FILE: Failed to find file of type %s...\n", ftStrings[type]);
637
638 // closezip(zip);
639 fclose(zip);
640 return fileSize;
641 }
642
643
644 uint32_t GetFileDBIdentityFromZIP(const char * zipFile)
645 {
646 FILE * zip = fopen(zipFile, "rb");
647
648 if (zip == NULL)
649 {
650 WriteLog("FILE: Could not open file '%s'!\n", zipFile);
651 return 0;
652 }
653
654 ZipFileEntry ze;
655
656 // Loop through all files in the zip file under consideration
657 while (GetZIPHeader(zip, ze))
658 {
659 // & loop through all known CRC32s in our file DB to see if it's there!
660 uint32_t index = 0;
661
662 while (romList[index].crc32 != 0xFFFFFF)
663 {
664 if (romList[index].crc32 == ze.crc32)
665 {
666 fclose(zip);
667 return index;
668 }
669
670 index++;
671 }
672
673 // We didn't find it, so skip the compressed data...
674 fseek(zip, ze.compressedSize, SEEK_CUR);
675 }
676
677 fclose(zip);
678 return (uint32_t )-1;
679 }
680
681
682 bool FindFileInZIPWithCRC32(const char * zipFile, uint32_t crc)
683 {
684 FILE * zip = fopen(zipFile, "rb");
685
686 if (zip == NULL)
687 {
688 WriteLog("FILE: Could not open file '%s'!\n", zipFile);
689 return 0;
690 }
691
692 ZipFileEntry ze;
693
694 // Loop through all files in the zip file under consideration
695 while (GetZIPHeader(zip, ze))
696 {
697 if (ze.crc32 == crc)
698 {
699 fclose(zip);
700 return true;
701 }
702
703 fseek(zip, ze.compressedSize, SEEK_CUR);
704 }
705
706 fclose(zip);
707 return false;
708 }
709
710
711 //
712 // Parse the file type based upon file size and/or headers.
713 //
714 uint32_t ParseFileType(uint8_t * buffer, uint32_t size)
715 {
716 // Check headers first...
717
718 // ELF 32bits
719 if (buffer[EI_CLASS] == ELFCLASS32)
720 {
721 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))
722 return JST_ELF32;
723 }
724
725 // ABS/COFF type 1
726 if (buffer[0] == 0x60 && buffer[1] == 0x1B)
727 return JST_ABS_TYPE1;
728
729 // ABS/COFF type 2
730 if (buffer[0] == 0x01 && buffer[1] == 0x50)
731 return JST_ABS_TYPE2;
732
733 // Jag Server & other old shite
734 if (buffer[0] == 0x60 && buffer[1] == 0x1A)
735 {
736 if (buffer[0x1C] == 'J' && buffer[0x1D] == 'A' && buffer[0x1E] == 'G')
737 return JST_JAGSERVER;
738 else
739 return JST_WTFOMGBBQ;
740 }
741
742 // And if that fails, try file sizes...
743
744 // If the file size is divisible by 1M, we probably have an regular ROM.
745 // We can also check our CRC32 against the internal ROM database to be sure.
746 // (We also check for the Memory Track cartridge size here as well...)
747 if ((size % 1048576) == 0 || size == 131072)
748 return JST_ROM;
749
750 // If the file size + 8192 bytes is divisible by 1M, we probably have an
751 // Alpine format ROM.
752 if (((size + 8192) % 1048576) == 0)
753 return JST_ALPINE;
754
755 // Headerless crap
756 return JST_NONE;
757 }
758
759 //
760 // Check for universal header
761 //
762 bool HasUniversalHeader(uint8_t * rom, uint32_t romSize)
763 {
764 // Sanity check
765 if (romSize < 8192)
766 return false;
767
768 for(int i=0; i<8192; i++)
769 if (rom[i] != universalCartHeader[i])
770 return false;
771
772 return true;
773 }
774
775 #if 0
776 // Misc. doco
777
778 /*
779 Stubulator ROM vectors...
780 handler 001 at $00E00008
781 handler 002 at $00E008DE
782 handler 003 at $00E008E2
783 handler 004 at $00E008E6
784 handler 005 at $00E008EA
785 handler 006 at $00E008EE
786 handler 007 at $00E008F2
787 handler 008 at $00E0054A
788 handler 009 at $00E008FA
789 handler 010 at $00000000
790 handler 011 at $00000000
791 handler 012 at $00E008FE
792 handler 013 at $00E00902
793 handler 014 at $00E00906
794 handler 015 at $00E0090A
795 handler 016 at $00E0090E
796 handler 017 at $00E00912
797 handler 018 at $00E00916
798 handler 019 at $00E0091A
799 handler 020 at $00E0091E
800 handler 021 at $00E00922
801 handler 022 at $00E00926
802 handler 023 at $00E0092A
803 handler 024 at $00E0092E
804 handler 025 at $00E0107A
805 handler 026 at $00E0107A
806 handler 027 at $00E0107A
807 handler 028 at $00E008DA
808 handler 029 at $00E0107A
809 handler 030 at $00E0107A
810 handler 031 at $00E0107A
811 handler 032 at $00000000
812
813 Let's try setting up the illegal instruction vector for a stubulated jaguar...
814
815 SET32(jaguar_mainRam, 0x08, 0x00E008DE);
816 SET32(jaguar_mainRam, 0x0C, 0x00E008E2);
817 SET32(jaguar_mainRam, 0x10, 0x00E008E6); // <-- Should be here (it is)...
818 SET32(jaguar_mainRam, 0x14, 0x00E008EA);//*/
819
820 /*
821 ABS Format sleuthing (LBUGDEMO.ABS):
822
823 000000 60 1B 00 00 05 0C 00 04 62 C0 00 00 04 28 00 00
824 000010 12 A6 00 00 00 00 00 80 20 00 FF FF 00 80 25 0C
825 000020 00 00 40 00
826
827 DRI-format file detected...
828 Text segment size = 0x0000050c bytes
829 Data segment size = 0x000462c0 bytes
830 BSS Segment size = 0x00000428 bytes
831 Symbol Table size = 0x000012a6 bytes
832 Absolute Address for text segment = 0x00802000
833 Absolute Address for data segment = 0x0080250c
834 Absolute Address for BSS segment = 0x00004000
835
836 (CRZDEMO.ABS):
837 000000 01 50 00 03 00 00 00 00 00 03 83 10 00 00 05 3b
838 000010 00 1c 00 03 00 00 01 07 00 00 1d d0 00 03 64 98
839 000020 00 06 8b 80 00 80 20 00 00 80 20 00 00 80 3d d0
840
841 000030 2e 74 78 74 00 00 00 00 00 80 20 00 00 80 20 00 .txt (+36 bytes)
842 000040 00 00 1d d0 00 00 00 a8 00 00 00 00 00 00 00 00
843 000050 00 00 00 00 00 00 00 20
844 000058 2e 64 74 61 00 00 00 00 00 80 3d d0 00 80 3d d0 .dta (+36 bytes)
845 000068 00 03 64 98 00 00 1e 78 00 00 00 00 00 00 00 00
846 000078 00 00 00 00 00 00 00 40
847 000080 2e 62 73 73 00 00 00 00 00 00 50 00 00 00 50 00 .bss (+36 bytes)
848 000090 00 06 8b 80 00 03 83 10 00 00 00 00 00 00 00 00
849 0000a0 00 00 00 00 00 00 00 80
850
851 Header size is $A8 bytes...
852
853 BSD/COFF format file detected...
854 3 sections specified
855 Symbol Table offset = 230160 ($00038310)
856 Symbol Table contains 1339 symbol entries ($0000053B)
857 The additional header size is 28 bytes ($001C)
858 Magic Number for RUN_HDR = 0x00000107
859 Text Segment Size = 7632 ($00001DD0)
860 Data Segment Size = 222360 ($00036498)
861 BSS Segment Size = 428928 ($00068B80)
862 Starting Address for executable = 0x00802000
863 Start of Text Segment = 0x00802000
864 Start of Data Segment = 0x00803dd0
865 */
866 #endif
867 =======
868 //
869 // FILE.CPP
870 //
871 // File support
872 // by James Hammons
873 // (C) 2010 Underground Software
874 //
875 // JLH = James Hammons <jlhamm@acm.org>
876 // JPM = Jean-Paul Mari <djipi.mari@gmail.com>
877 //
878 // Who When What
879 // --- ---------- ------------------------------------------------------------
880 // JLH 01/16/2010 Created this log ;-)
881 // JLH 02/28/2010 Added functions to look inside .ZIP files and handle
882 // contents
883 // JLH 06/01/2012 Added function to check ZIP file CRCs against file DB
884 // JPM 06/06/2016 Visual Studio support
885 // JPM 06/15/2016 ELF format support
886 // JPM 06/19/2016 Soft debugger support
887 // JPM 07/15/2016 DWARF format support
888 //
889
890 #include "file.h"
891 #if defined(_MSC_VER)
892 #include "_MSC_VER/config.h"
893 #endif // _MSC_VER
894 #include <stdarg.h>
895 #include <string.h>
896 #include "crc32.h"
897 #include "filedb.h"
898 #include "eeprom.h"
899 #include "jaguar.h"
900 #include "log.h"
901 #include "memory.h"
902 #include "universalhdr.h"
903 #include "unzip.h"
904 #include "zlib.h"
905 #include "libelf/libelf.h"
906 #include "libelf/gelf.h"
907 #include "libdwarf.h"
908 #include "debugger/ELFManager.h"
909 #include "debugger/DBGManager.h"
910
911
912 // Private function prototypes
913
914 static int gzfilelength(gzFile gd);
915 #if defined(_MSC_VER) || defined(__MINGW64__)|| defined(__MINGW32__) || defined(__CYGWIN__)
916 static bool CheckExtension(const uint8_t * filename, const char * ext);
917 #else
918 static bool CheckExtension(const char * filename, const char * ext);
919 #endif // _MSC_VER
920 //static int ParseFileType(uint8_t header1, uint8_t header2, uint32_t size);
921
922 // Private variables/enums
923
924
925 //
926 // Generic ROM loading
927 //
928 uint32_t JaguarLoadROM(uint8_t * &rom, char * path)
929 {
930 // We really should have some kind of sanity checking for the ROM size here to prevent
931 // a buffer overflow... !!! FIX !!!
932 #if defined(_MSC_VER)
933 #pragma message("Warning: !!! FIX !!! Should have sanity checking for ROM size to prevent buffer overflow!")
934 #else
935 #warning "!!! FIX !!! Should have sanity checking for ROM size to prevent buffer overflow!"
936 #endif // _MSC_VER
937 uint32_t romSize = 0;
938
939 WriteLog("FILE: JaguarLoadROM attempting to load file '%s'...", path);
940 char * ext = strrchr(path, '.');
941
942 // No filename extension == YUO FAIL IT (it is loading the file).
943 // This is naive, but it works. But should probably come up with something a little
944 // more robust, to prevent problems with dopes trying to exploit this.
945 if (ext == NULL)
946 {
947 WriteLog("FAILED!\n");
948 return 0;
949 }
950
951 WriteLog("\nFILE: Succeeded in finding extension (%s)!\n", ext);
952 WriteLog("FILE: Loading \"%s\"...", path);
953
954 if (strcasecmp(ext, ".zip") == 0)
955 {
956 // Handle ZIP file loading here...
957 WriteLog("(ZIPped)...");
958
959 // uint8_t * buffer = NULL;
960 // romSize = GetFileFromZIP(path, FT_SOFTWARE, buffer);
961 romSize = GetFileFromZIP(path, FT_SOFTWARE, rom);
962
963 if (romSize == 0)
964 {
965 WriteLog("Failed!\n");
966 return 0;
967 }
968
969 // memcpy(rom, buffer, romSize);
970 // delete[] buffer;
971 }
972 else
973 {
974 // Handle gzipped files transparently [Adam Green]...
975
976 gzFile fp = gzopen(path, "rb");
977
978 if (fp == NULL)
979 {
980 WriteLog("Failed!\n");
981 return 0;
982 }
983
984 romSize = gzfilelength(fp);
985 rom = new uint8_t[romSize];
986 gzseek(fp, 0, SEEK_SET);
987 gzread(fp, rom, romSize);
988 gzclose(fp);
989 }
990
991 WriteLog("OK (%i bytes)\n", romSize);
992
993 return romSize;
994 }
995
996
997 //
998 // Jaguar file loading
999 // We do a more intelligent file analysis here instead of relying on (possible
1000 // false) file extensions which people don't seem to give two shits about
1001 // anyway. :-(
1002 //
1003 bool JaguarLoadFile(char * path)
1004 {
1005 Elf *ElfMem;
1006 GElf_Ehdr ElfEhdr, *PtrGElfEhdr;
1007 Elf_Scn *PtrElfScn;
1008 Elf_Data *PtrElfData;
1009 GElf_Shdr GElfShdr, *PtrGElfShdr;
1010 size_t NbrSect;
1011 uint8_t *buffer = NULL;
1012 char *NameSection;
1013 size_t ElfSectionNameType;
1014 int DBGType = DBG_NO_TYPE;
1015 bool error;
1016 int err;
1017
1018 jaguarROMSize = JaguarLoadROM(buffer, path);
1019
1020 if (jaguarROMSize == 0)
1021 {
1022 // It's up to the GUI to report errors, not us. :-)
1023 WriteLog("FILE: Could not load ROM from file \"%s\"...\nAborting load!\n", path);
1024 return false;
1025 }
1026
1027 jaguarMainROMCRC32 = crc32_calcCheckSum(buffer, jaguarROMSize);
1028 WriteLog("CRC: %08X\n", (unsigned int)jaguarMainROMCRC32);
1029 // TODO: Check for EEPROM file in ZIP file. If there is no EEPROM in the user's EEPROM
1030 // directory, copy the one from the ZIP file, if it exists.
1031 EepromInit();
1032 jaguarRunAddress = 0x802000; // For non-BIOS runs, this is true
1033 int fileType = ParseFileType(buffer, jaguarROMSize);
1034 jaguarCartInserted = false;
1035 DBGManager_Reset();
1036
1037 if (fileType == JST_ROM)
1038 {
1039 jaguarCartInserted = true;
1040 memcpy(jagMemSpace + 0x800000, buffer, jaguarROMSize);
1041 // Checking something...
1042 jaguarRunAddress = GET32(jagMemSpace, 0x800404);
1043 WriteLog("FILE: Cartridge run address is reported as $%X...\n", jaguarRunAddress);
1044 delete[] buffer;
1045 return true;
1046 }
1047 else if (fileType == JST_ALPINE)
1048 {
1049 // File extension ".ROM": Alpine image that loads/runs at $802000
1050 WriteLog("FILE: Setting up Alpine ROM... Run address: 00802000, length: %08X\n", jaguarROMSize);
1051 memset(jagMemSpace + 0x800000, 0xFF, 0x2000);
1052 memcpy(jagMemSpace + 0x802000, buffer, jaguarROMSize);
1053 delete[] buffer;
1054
1055 // Maybe instead of this, we could try requiring the STUBULATOR ROM? Just a thought...
1056 // Try setting the vector to say, $1000 and putting an instruction there that loops forever:
1057 // This kludge works! Yeah!
1058 SET32(jaguarMainRAM, 0x10, 0x00001000);
1059 SET16(jaguarMainRAM, 0x1000, 0x60FE); // Here: bra Here
1060 return true;
1061 }
1062 else if (fileType == JST_ELF32)
1063 {
1064 DBGType = DBG_ELF;
1065
1066 char *PtrELFExe = (char *)ELFManager_ExeCopy(buffer, jaguarROMSize);
1067
1068 if (PtrELFExe != NULL)
1069 {
1070 if ((elf_version(EV_CURRENT) != EV_NONE) && (ElfMem = ELFManager_MemOpen(PtrELFExe, jaguarROMSize)))
1071 {
1072 if (ELFManager_DwarfInit(ElfMem))
1073 {
1074 DBGType |= DBG_ELFDWARF;
1075 }
1076
1077 if (!elf_getshdrnum(ElfMem, &NbrSect))
1078 {
1079 if (((PtrGElfEhdr = gelf_getehdr(ElfMem, &ElfEhdr)) != NULL) && ((PtrElfScn = elf_getscn(ElfMem, 0)) != NULL))
1080 {
1081 for (error = false; (PtrElfScn != NULL) && (error == false); PtrElfScn = elf_nextscn(ElfMem, PtrElfScn))
1082 {
1083 PtrElfData = NULL;
1084
1085 if ((PtrGElfShdr = gelf_getshdr(PtrElfScn, &GElfShdr)) == NULL)
1086 {
1087 error = true;
1088 }
1089 else
1090 {
1091 NameSection = elf_strptr(ElfMem, PtrGElfEhdr->e_shstrndx, (size_t)PtrGElfShdr->sh_name);
1092 WriteLog("FILE: ELF Section name: %s\n", NameSection);
1093
1094 if ((ElfSectionNameType = ELFManager_GetSectionType(NameSection)) == ELF_NO_TYPE)
1095 {
1096 WriteLog("FILE: ELF Section not listed\n");
1097 error = true;
1098 }
1099 else
1100 {
1101 switch (PtrGElfShdr->sh_type)
1102 {
1103 case SHT_NULL:
1104 break;
1105
1106 case SHT_PROGBITS:
1107 if ((PtrGElfShdr->sh_flags & (SHF_ALLOC | SHF_WRITE | SHF_EXECINSTR)))
1108 {
1109 if (PtrGElfShdr->sh_addr >= 0x800000)
1110 {
1111 memcpy(jagMemSpace + PtrGElfShdr->sh_addr, buffer + PtrGElfShdr->sh_offset, PtrGElfShdr->sh_size);
1112 //error = false;
1113 }
1114 else
1115 {
1116 memcpy(jaguarMainRAM + PtrGElfShdr->sh_addr, buffer + PtrGElfShdr->sh_offset, PtrGElfShdr->sh_size);
1117 }
1118 }
1119 else
1120 {
1121 switch (ElfSectionNameType)
1122 {
1123 case ELF_debug_aranges_TYPE:
1124 case ELF_debug_info_TYPE:
1125 case ELF_debug_abbrev_TYPE:
1126 case ELF_debug_line_TYPE:
1127 case ELF_debug_frame_TYPE:
1128 case ELF_debug_ranges_TYPE:
1129 case ELF_debug_str_TYPE:
1130 case ELF_debug_loc_TYPE:
1131 break;
1132
1133 case ELF_comment_TYPE:
1134 break;
1135
1136 default:
1137 error = true;
1138 break;
1139 }
1140 }
1141 break;
1142
1143 case SHT_NOBITS:
1144 break;
1145
1146 case SHT_STRTAB:
1147 case SHT_SYMTAB:
1148 while ((error == false) && ((PtrElfData = elf_getdata(PtrElfScn, PtrElfData)) != NULL))
1149 {
1150 if (!ELFManager_AddTab(PtrElfData, ElfSectionNameType))
1151 {
1152 error = true;
1153 }
1154 }
1155 break;
1156
1157 default:
1158 error = true;
1159 break;
1160 }
1161 }
1162 }
1163 }
1164
1165 jaguarRunAddress = (uint32_t)PtrGElfEhdr->e_entry;
1166 WriteLog("FILE: Setting up ELF 32bits... Run address: %08X\n", jaguarRunAddress);
1167 }
1168 else
1169 {
1170 error = true;
1171 }
1172 }
1173 else
1174 {
1175 error = true;
1176 }
1177 }
1178 else
1179 {
1180 error = true;
1181 WriteLog("FILE: libelf version is not recognized or libelf memory cannot be opened\n");
1182 }
1183 }
1184 else
1185 {
1186 error = true;
1187 WriteLog("FILE: ELFManager cannot allocate memory\n");
1188 }
1189
1190 delete[] buffer;
1191
1192 if (error)
1193 {
1194 WriteLog("FILE: ELF parsing error\n");
1195
1196 if (err = elf_errno())
1197 {
1198 WriteLog("FILE: ELF error: %s\n", elf_errmsg(err));
1199 }
1200
1201 return false;
1202 }
1203 else
1204 {
1205 DBGManager_SetType(DBGType);
1206 return true;
1207 }
1208 }
1209 else if (fileType == JST_ABS_TYPE1)
1210 {
1211 // For ABS type 1, run address == load address
1212 uint32_t loadAddress = GET32(buffer, 0x16),
1213 codeSize = GET32(buffer, 0x02) + GET32(buffer, 0x06);
1214 WriteLog("FILE: Setting up homebrew (ABS-1)... Run address: %08X, length: %08X\n", loadAddress, codeSize);
1215 memcpy(jagMemSpace + loadAddress, buffer + 0x24, codeSize);
1216 delete[] buffer;
1217 jaguarRunAddress = loadAddress;
1218 return true;
1219 }
1220 else if (fileType == JST_ABS_TYPE2)
1221 {
1222 uint32_t loadAddress = GET32(buffer, 0x28), runAddress = GET32(buffer, 0x24),
1223 codeSize = GET32(buffer, 0x18) + GET32(buffer, 0x1C);
1224 WriteLog("FILE: Setting up homebrew (ABS-2)... Run address: %08X, length: %08X\n", runAddress, codeSize);
1225 memcpy(jagMemSpace + loadAddress, buffer + 0xA8, codeSize);
1226 delete[] buffer;
1227 jaguarRunAddress = runAddress;
1228 return true;
1229 }
1230 // NB: This is *wrong*
1231 /*
1232 Basically, if there is no "JAG" at position $1C, then the long there is the load/start
1233 address in LITTLE ENDIAN.
1234 If "JAG" is present, the the next character ("R" or "L") determines the size of the
1235 JagServer command (2 bytes vs. 4). Following that are the commands themselves;
1236 typically it will either be 2 (load) or 3 (load & run). Command headers go like so:
1237 2:
1238 Load address (long)
1239 Length (long)
1240 payload
1241 3:
1242 Load address (long)
1243 Length (long)
1244 Run address (long)
1245 payload
1246 5: (Reset)
1247 [command only]
1248 7: (Run at address)
1249 Run address (long)
1250 [no payload]
1251 9: (Clear memory)
1252 Start address (long)
1253 End address (long)
1254 [no payload]
1255 10: (Poll for commands)
1256 [command only]
1257 12: (Load & run user program)
1258 filname, terminated with NULL
1259 [no payload]
1260 $FFFF: (Halt)
1261 [no payload]
1262 */
1263 else if (fileType == JST_JAGSERVER)
1264 {
1265 // This kind of shiaut should be in the detection code below...
1266 // (and now it is! :-)
1267 // if (buffer[0x1C] == 'J' && buffer[0x1D] == 'A' && buffer[0x1E] == 'G')
1268 // {
1269 // Still need to do some checking here for type 2 vs. type 3. This assumes 3
1270 // Also, JAGR vs. JAGL (word command size vs. long command size)
1271 uint32_t loadAddress = GET32(buffer, 0x22), runAddress = GET32(buffer, 0x2A);
1272 WriteLog("FILE: Setting up homebrew (Jag Server)... Run address: $%X, length: $%X\n", runAddress, jaguarROMSize - 0x2E);
1273 memcpy(jagMemSpace + loadAddress, buffer + 0x2E, jaguarROMSize - 0x2E);
1274 delete[] buffer;
1275 jaguarRunAddress = runAddress;
1276
1277 // Hmm. Is this kludge necessary?
1278 SET32(jaguarMainRAM, 0x10, 0x00001000); // Set Exception #4 (Illegal Instruction)
1279 SET16(jaguarMainRAM, 0x1000, 0x60FE); // Here: bra Here
1280
1281 return true;
1282 // }
1283 // else // Special WTFOMGBBQ type here...
1284 // {
1285 // uint32_t loadAddress = (buffer[0x1F] << 24) | (buffer[0x1E] << 16) | (buffer[0x1D] << 8) | buffer[0x1C];
1286 // WriteLog("FILE: Setting up homebrew (GEMDOS WTFOMGBBQ type)... Run address: $%X, length: $%X\n", loadAddress, jaguarROMSize - 0x20);
1287 // memcpy(jagMemSpace + loadAddress, buffer + 0x20, jaguarROMSize - 0x20);
1288 // delete[] buffer;
1289 // jaguarRunAddress = loadAddress;
1290 // return true;
1291 // }
1292 }
1293 else if (fileType == JST_WTFOMGBBQ)
1294 {
1295 uint32_t loadAddress = (buffer[0x1F] << 24) | (buffer[0x1E] << 16) | (buffer[0x1D] << 8) | buffer[0x1C];
1296 WriteLog("FILE: Setting up homebrew (GEMDOS WTFOMGBBQ type)... Run address: $%X, length: $%X\n", loadAddress, jaguarROMSize - 0x20);
1297 memcpy(jagMemSpace + loadAddress, buffer + 0x20, jaguarROMSize - 0x20);
1298 delete[] buffer;
1299 jaguarRunAddress = loadAddress;
1300 return true;
1301 }
1302
1303 // We can assume we have JST_NONE at this point. :-P
1304 WriteLog("FILE: Failed to load headerless file.\n");
1305 return false;
1306 }
1307
1308
1309 //
1310 // "Debugger" file loading
1311 // To keep the things separate between "Debugger" and "Alpine" loading until usage clarification has been done
1312 //
1313 bool DebuggerLoadFile(char * path)
1314 {
1315 return (AlpineLoadFile(path));
1316 }
1317
1318
1319 //
1320 // "Alpine" file loading
1321 // Since the developers were coming after us with torches and pitchforks, we
1322 // decided to allow this kind of thing. ;-) But ONLY FOR THE DEVS, DAMMIT! >:-U
1323 // O_O
1324 //
1325 bool AlpineLoadFile(char * path)
1326 {
1327 uint8_t * buffer = NULL;
1328 jaguarROMSize = JaguarLoadROM(buffer, path);
1329
1330 if (jaguarROMSize == 0)
1331 {
1332 // It's up to the GUI to deal with failure, not us. ;-)
1333 WriteLog("FILE: Could not load Alpine from file \"%s\"...\nAborting load!\n", path);
1334 return false;
1335 }
1336
1337 jaguarMainROMCRC32 = crc32_calcCheckSum(buffer, jaguarROMSize);
1338 WriteLog("FILE: CRC is %08X\n", (unsigned int)jaguarMainROMCRC32);
1339 EepromInit();
1340
1341 jaguarRunAddress = 0x802000;
1342
1343 WriteLog("FILE: Setting up Alpine ROM with non-standard length... Run address: 00802000, length: %08X\n", jaguarROMSize);
1344
1345 memset(jagMemSpace + 0x800000, 0xFF, 0x2000);
1346 memcpy(jagMemSpace + 0x802000, buffer, jaguarROMSize);
1347 delete[] buffer;
1348
1349 // Maybe instead of this, we could try requiring the STUBULATOR ROM? Just a thought...
1350 // Try setting the vector to say, $1000 and putting an instruction there
1351 // that loops forever:
1352 // This kludge works! Yeah!
1353 SET32(jaguarMainRAM, 0x10, 0x00001000); // Set Exception #4 (Illegal Instruction)
1354 SET16(jaguarMainRAM, 0x1000, 0x60FE); // Here: bra Here
1355
1356 return true;
1357 }
1358
1359
1360 //
1361 // Get the length of a (possibly) gzipped file
1362 //
1363 static int gzfilelength(gzFile gd)
1364 {
1365 int size = 0, length = 0;
1366 unsigned char buffer[0x10000];
1367
1368 gzrewind(gd);
1369
1370 do
1371 {
1372 // Read in chunks until EOF
1373 size = gzread(gd, buffer, 0x10000);
1374
1375 if (size <= 0)
1376 break;
1377
1378 length += size;
1379 }
1380 while (!gzeof(gd));
1381
1382 gzrewind(gd);
1383 return length;
1384 }
1385
1386
1387 //
1388 // Compare extension to passed in filename. If equal, return true; otherwise false.
1389 //
1390 #if defined(_MSC_VER) || defined(__MINGW64__)|| defined(__MINGW32__) || defined(__CYGWIN__)
1391 static bool CheckExtension(const uint8_t * filename, const char * ext)
1392 #else
1393 static bool CheckExtension(const char * filename, const char * ext)
1394 #endif // _MSC_VER
1395 {
1396 // Sanity checking...
1397 if ((filename == NULL) || (ext == NULL))
1398 return false;
1399
1400 const char * filenameExt = strrchr((const char *)filename, '.'); // Get the file's extension (if any)
1401
1402 if (filenameExt == NULL)
1403 return false;
1404
1405 return (strcasecmp(filenameExt, ext) == 0 ? true : false);
1406 }
1407
1408
1409 //
1410 // Get file from .ZIP
1411 // Returns the size of the file inside the .ZIP file that we're looking at
1412 // NOTE: If the thing we're looking for is found, it allocates it in the passed in buffer.
1413 // Which means we have to deallocate it later.
1414 //
1415 uint32_t GetFileFromZIP(const char * zipFile, FileType type, uint8_t * &buffer)
1416 {
1417 // NOTE: We could easily check for this by discarding anything that's larger than the RAM/ROM
1418 // size of the Jaguar console.
1419 #if defined(_MSC_VER)
1420 #pragma message("Warning: !!! FIX !!! Should have sanity checking for ROM size to prevent buffer overflow!")
1421 #else
1422 #warning "!!! FIX !!! Should have sanity checking for ROM size to prevent buffer overflow!"
1423 #endif // _MSC_VER
1424 const char ftStrings[5][32] = { "Software", "EEPROM", "Label", "Box Art", "Controller Overlay" };
1425 // ZIP * zip = openzip(0, 0, zipFile);
1426 FILE * zip = fopen(zipFile, "rb");
1427
1428 if (zip == NULL)
1429 {
1430 WriteLog("FILE: Could not open file '%s'!\n", zipFile);
1431 return 0;
1432 }
1433
1434 // zipent * ze;
1435 ZipFileEntry ze;
1436 bool found = false;
1437
1438 // The order is here is important: If the file is found, we need to short-circuit the
1439 // readzip() call because otherwise, 'ze' will be pointing to the wrong file!
1440 // while (!found && readzip(zip))
1441 while (!found && GetZIPHeader(zip, ze))
1442 {
1443 // ze = &zip->ent;
1444
1445 // Here we simply rely on the file extension to tell the truth, but we know
1446 // that extensions lie like sons-a-bitches. So this is naive, we need to do
1447 // something a little more robust to keep bad things from happening here.
1448 #if defined(_MSC_VER)
1449 #pragma message("Warning: !!! Checking for image by extension can be fooled !!!")
1450 #else
1451 #warning "!!! Checking for image by extension can be fooled !!!"
1452 #endif // _MSC_VER
1453 if ((type == FT_LABEL) && (CheckExtension(ze.filename, ".png") || CheckExtension(ze.filename, ".jpg") || CheckExtension(ze.filename, ".gif")))
1454 {
1455 found = true;
1456 WriteLog("FILE: Found image file '%s'.\n", ze.filename);
1457 }
1458
1459 if ((type == FT_SOFTWARE) && (CheckExtension(ze.filename, ".j64")
1460 || CheckExtension(ze.filename, ".rom") || CheckExtension(ze.filename, ".abs")
1461 || CheckExtension(ze.filename, ".cof") || CheckExtension(ze.filename, ".coff")
1462 || CheckExtension(ze.filename, ".jag") || CheckExtension(ze.filename, ".elf")))
1463 {
1464 found = true;
1465 WriteLog("FILE: Found software file '%s'.\n", ze.filename);
1466 }
1467
1468 if ((type == FT_EEPROM) && (CheckExtension(ze.filename, ".eep") || CheckExtension(ze.filename, ".eeprom")))
1469 {
1470 found = true;
1471 WriteLog("FILE: Found EEPROM file '%s'.\n", ze.filename);
1472 }
1473
1474 if (!found)
1475 fseek(zip, ze.compressedSize, SEEK_CUR);
1476 }
1477
1478 uint32_t fileSize = 0;
1479
1480 if (found)
1481 {
1482 WriteLog("FILE: Uncompressing...");
1483 // Insert file size sanity check here...
1484 buffer = new uint8_t[ze.uncompressedSize];
1485
1486 // if (readuncompresszip(zip, ze.compressedSize, buffer) == 0)
1487 // if (UncompressFileFromZIP(zip, ze.compressedSize, buffer) == 0)
1488 if (UncompressFileFromZIP(zip, ze, buffer) == 0)
1489 {
1490 fileSize = ze.uncompressedSize;
1491 WriteLog("success! (%u bytes)\n", fileSize);
1492 }
1493 else
1494 {
1495 delete[] buffer;
1496 buffer = NULL;
1497 WriteLog("FAILED!\n");
1498 }
1499 }
1500 else
1501 // Didn't find what we're looking for...
1502 WriteLog("FILE: Failed to find file of type %s...\n", ftStrings[type]);
1503
1504 // closezip(zip);
1505 fclose(zip);
1506 return fileSize;
1507 }
1508
1509
1510 uint32_t GetFileDBIdentityFromZIP(const char * zipFile)
1511 {
1512 FILE * zip = fopen(zipFile, "rb");
1513
1514 if (zip == NULL)
1515 {
1516 WriteLog("FILE: Could not open file '%s'!\n", zipFile);
1517 return 0;
1518 }
1519
1520 ZipFileEntry ze;
1521
1522 // Loop through all files in the zip file under consideration
1523 while (GetZIPHeader(zip, ze))
1524 {
1525 // & loop through all known CRC32s in our file DB to see if it's there!
1526 uint32_t index = 0;
1527
1528 while (romList[index].crc32 != 0xFFFFFF)
1529 {
1530 if (romList[index].crc32 == ze.crc32)
1531 {
1532 fclose(zip);
1533 return index;
1534 }
1535
1536 index++;
1537 }
1538
1539 // We didn't find it, so skip the compressed data...
1540 fseek(zip, ze.compressedSize, SEEK_CUR);
1541 }
1542
1543 fclose(zip);
1544 return -1;
1545 }
1546
1547
1548 bool FindFileInZIPWithCRC32(const char * zipFile, uint32_t crc)
1549 {
1550 FILE * zip = fopen(zipFile, "rb");
1551
1552 if (zip == NULL)
1553 {
1554 WriteLog("FILE: Could not open file '%s'!\n", zipFile);
1555 return 0;
1556 }
1557
1558 ZipFileEntry ze;
1559
1560 // Loop through all files in the zip file under consideration
1561 while (GetZIPHeader(zip, ze))
1562 {
1563 if (ze.crc32 == crc)
1564 {
1565 fclose(zip);
1566 return true;
1567 }
1568
1569 fseek(zip, ze.compressedSize, SEEK_CUR);
1570 }
1571
1572 fclose(zip);
1573 return false;
1574 }
1575
1576
1577 //
1578 // Parse the file type based upon file size and/or headers.
1579 //
1580 uint32_t ParseFileType(uint8_t * buffer, uint32_t size)
1581 {
1582 // Check headers first...
1583
1584 // ELF 32bits
1585 if (buffer[EI_CLASS] == ELFCLASS32)
1586 {
1587 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))
1588 return JST_ELF32;
1589 }
1590
1591 // ABS/COFF type 1
1592 if (buffer[0] == 0x60 && buffer[1] == 0x1B)
1593 return JST_ABS_TYPE1;
1594
1595 // ABS/COFF type 2
1596 if (buffer[0] == 0x01 && buffer[1] == 0x50)
1597 return JST_ABS_TYPE2;
1598
1599 // Jag Server & other old shite
1600 if (buffer[0] == 0x60 && buffer[1] == 0x1A)
1601 {
1602 if (buffer[0x1C] == 'J' && buffer[0x1D] == 'A' && buffer[0x1E] == 'G')
1603 return JST_JAGSERVER;
1604 else
1605 return JST_WTFOMGBBQ;
1606 }
1607
1608 // And if that fails, try file sizes...
1609
1610 // If the file size is divisible by 1M, we probably have an regular ROM.
1611 // We can also check our CRC32 against the internal ROM database to be sure.
1612 // (We also check for the Memory Track cartridge size here as well...)
1613 if ((size % 1048576) == 0 || size == 131072)
1614 return JST_ROM;
1615
1616 // If the file size + 8192 bytes is divisible by 1M, we probably have an
1617 // Alpine format ROM.
1618 if (((size + 8192) % 1048576) == 0)
1619 return JST_ALPINE;
1620
1621 // Headerless crap
1622 return JST_NONE;
1623 }
1624
1625 //
1626 // Check for universal header
1627 //
1628 bool HasUniversalHeader(uint8_t * rom, uint32_t romSize)
1629 {
1630 // Sanity check
1631 if (romSize < 8192)
1632 return false;
1633
1634 for(int i=0; i<8192; i++)
1635 if (rom[i] != universalCartHeader[i])
1636 return false;
1637
1638 return true;
1639 }
1640
1641 #if 0
1642 // Misc. doco
1643
1644 /*
1645 Stubulator ROM vectors...
1646 handler 001 at $00E00008
1647 handler 002 at $00E008DE
1648 handler 003 at $00E008E2
1649 handler 004 at $00E008E6
1650 handler 005 at $00E008EA
1651 handler 006 at $00E008EE
1652 handler 007 at $00E008F2
1653 handler 008 at $00E0054A
1654 handler 009 at $00E008FA
1655 handler 010 at $00000000
1656 handler 011 at $00000000
1657 handler 012 at $00E008FE
1658 handler 013 at $00E00902
1659 handler 014 at $00E00906
1660 handler 015 at $00E0090A
1661 handler 016 at $00E0090E
1662 handler 017 at $00E00912
1663 handler 018 at $00E00916
1664 handler 019 at $00E0091A
1665 handler 020 at $00E0091E
1666 handler 021 at $00E00922
1667 handler 022 at $00E00926
1668 handler 023 at $00E0092A
1669 handler 024 at $00E0092E
1670 handler 025 at $00E0107A
1671 handler 026 at $00E0107A
1672 handler 027 at $00E0107A
1673 handler 028 at $00E008DA
1674 handler 029 at $00E0107A
1675 handler 030 at $00E0107A
1676 handler 031 at $00E0107A
1677 handler 032 at $00000000
1678
1679 Let's try setting up the illegal instruction vector for a stubulated jaguar...
1680
1681 SET32(jaguar_mainRam, 0x08, 0x00E008DE);
1682 SET32(jaguar_mainRam, 0x0C, 0x00E008E2);
1683 SET32(jaguar_mainRam, 0x10, 0x00E008E6); // <-- Should be here (it is)...
1684 SET32(jaguar_mainRam, 0x14, 0x00E008EA);//*/
1685
1686 /*
1687 ABS Format sleuthing (LBUGDEMO.ABS):
1688
1689 000000 60 1B 00 00 05 0C 00 04 62 C0 00 00 04 28 00 00
1690 000010 12 A6 00 00 00 00 00 80 20 00 FF FF 00 80 25 0C
1691 000020 00 00 40 00
1692
1693 DRI-format file detected...
1694 Text segment size = 0x0000050c bytes
1695 Data segment size = 0x000462c0 bytes
1696 BSS Segment size = 0x00000428 bytes
1697 Symbol Table size = 0x000012a6 bytes
1698 Absolute Address for text segment = 0x00802000
1699 Absolute Address for data segment = 0x0080250c
1700 Absolute Address for BSS segment = 0x00004000
1701
1702 (CRZDEMO.ABS):
1703 000000 01 50 00 03 00 00 00 00 00 03 83 10 00 00 05 3b
1704 000010 00 1c 00 03 00 00 01 07 00 00 1d d0 00 03 64 98
1705 000020 00 06 8b 80 00 80 20 00 00 80 20 00 00 80 3d d0
1706
1707 000030 2e 74 78 74 00 00 00 00 00 80 20 00 00 80 20 00 .txt (+36 bytes)
1708 000040 00 00 1d d0 00 00 00 a8 00 00 00 00 00 00 00 00
1709 000050 00 00 00 00 00 00 00 20
1710 000058 2e 64 74 61 00 00 00 00 00 80 3d d0 00 80 3d d0 .dta (+36 bytes)
1711 000068 00 03 64 98 00 00 1e 78 00 00 00 00 00 00 00 00
1712 000078 00 00 00 00 00 00 00 40
1713 000080 2e 62 73 73 00 00 00 00 00 00 50 00 00 00 50 00 .bss (+36 bytes)
1714 000090 00 06 8b 80 00 03 83 10 00 00 00 00 00 00 00 00
1715 0000a0 00 00 00 00 00 00 00 80
1716
1717 Header size is $A8 bytes...
1718
1719 BSD/COFF format file detected...
1720 3 sections specified
1721 Symbol Table offset = 230160 ($00038310)
1722 Symbol Table contains 1339 symbol entries ($0000053B)
1723 The additional header size is 28 bytes ($001C)
1724 Magic Number for RUN_HDR = 0x00000107
1725 Text Segment Size = 7632 ($00001DD0)
1726 Data Segment Size = 222360 ($00036498)
1727 BSS Segment Size = 428928 ($00068B80)
1728 Starting Address for executable = 0x00802000
1729 Start of Text Segment = 0x00802000
1730 Start of Data Segment = 0x00803dd0
1731 */
1732 #endif
1733 >>>>>>> parent of dfd8b1d... Delete file.cpp