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