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