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