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