Added ELF section types check and new error messages
[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
60b5816d
JPM
20//\r
21\r
22#include "file.h"\r
23#if defined(_MSC_VER)\r
24#include "_MSC_VER/config.h"\r
25#endif // _MSC_VER\r
26#include <stdarg.h>\r
27#include <string.h>\r
28#include "crc32.h"\r
29#include "filedb.h"\r
30#include "eeprom.h"\r
31#include "jaguar.h"\r
32#include "log.h"\r
33#include "memory.h"\r
34#include "universalhdr.h"\r
35#include "unzip.h"\r
36#include "zlib.h"\r
37#include "libelf/libelf.h"\r
38#include "libelf/gelf.h"\r
39#include "libdwarf.h"\r
60b5816d
JPM
40#include "debugger/ELFManager.h"\r
41#include "debugger/DBGManager.h"\r
d56efc77 42#include "settings.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
d56efc77 225 WriteLog("FILE: ELF Section %s found\n", NameSection);\r
60b5816d 226\r
d56efc77 227 if (((ElfSectionNameType = ELFManager_GetSectionType(NameSection)) == ELF_NO_TYPE) && vjs.ELFSectionsCheck)\r
60b5816d 228 {\r
d56efc77 229 WriteLog("FILE: ELF Section %s not recognized\n", NameSection);\r
60b5816d
JPM
230 error = true;\r
231 }\r
232 else\r
233 {\r
234 switch (PtrGElfShdr->sh_type)\r
235 {\r
7575ebee 236 case SHT_NULL:\r
60b5816d
JPM
237 break;\r
238\r
7575ebee 239 case SHT_PROGBITS:\r
60b5816d
JPM
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
7575ebee
JPM
256 case ELF_debug_TYPE:\r
257 case ELF_debug_abbrev_TYPE:\r
60b5816d 258 case ELF_debug_aranges_TYPE:\r
7575ebee 259 case ELF_debug_frame_TYPE:\r
60b5816d 260 case ELF_debug_info_TYPE:\r
60b5816d 261 case ELF_debug_line_TYPE:\r
7575ebee
JPM
262 case ELF_debug_loc_TYPE:\r
263 case ELF_debug_macinfo_TYPE:\r
264 case ELF_debug_pubnames_TYPE:\r
265 case ELF_debug_pubtypes_TYPE:\r
60b5816d
JPM
266 case ELF_debug_ranges_TYPE:\r
267 case ELF_debug_str_TYPE:\r
7575ebee 268 case ELF_debug_types_TYPE: \r
60b5816d
JPM
269 break;\r
270\r
88b2434e
JPM
271 case ELF_heap_TYPE:\r
272 break;\r
273\r
60b5816d
JPM
274 case ELF_comment_TYPE:\r
275 break;\r
276\r
277 default:\r
7575ebee 278 WriteLog("FILE: ELF section %s is not recognized\n", NameSection);\r
60b5816d
JPM
279 error = true;\r
280 break;\r
281 }\r
282 }\r
283 break;\r
284\r
7575ebee 285 case SHT_NOBITS:\r
60b5816d
JPM
286 break;\r
287\r
7575ebee
JPM
288 case SHT_STRTAB:\r
289 case SHT_SYMTAB:\r
60b5816d
JPM
290 while ((error == false) && ((PtrElfData = elf_getdata(PtrElfScn, PtrElfData)) != NULL))\r
291 {\r
292 if (!ELFManager_AddTab(PtrElfData, ElfSectionNameType))\r
293 {\r
7575ebee 294 WriteLog("FILE: ELF tab cannot be allocated\n");\r
60b5816d
JPM
295 error = true;\r
296 }\r
297 }\r
298 break;\r
299\r
300 default:\r
7575ebee 301 WriteLog("FILE: ELF SHT type %i not recognized\n", PtrGElfShdr->sh_type);\r
60b5816d
JPM
302 error = true;\r
303 break;\r
304 }\r
305 }\r
306 }\r
307 }\r
308\r
7575ebee 309 // Set the executable address\r
60b5816d
JPM
310 jaguarRunAddress = (uint32_t)PtrGElfEhdr->e_entry;\r
311 WriteLog("FILE: Setting up ELF 32bits... Run address: %08X\n", jaguarRunAddress);\r
312 }\r
313 else\r
314 {\r
315 error = true;\r
316 }\r
317 }\r
318 else\r
319 {\r
7575ebee 320 WriteLog("FILE: Cannot get the number of the ELF sections\n");\r
60b5816d
JPM
321 error = true;\r
322 }\r
323 }\r
324 else\r
325 {\r
326 error = true;\r
327 WriteLog("FILE: libelf version is not recognized or libelf memory cannot be opened\n");\r
328 }\r
329 }\r
330 else\r
331 {\r
332 error = true;\r
333 WriteLog("FILE: ELFManager cannot allocate memory\n");\r
334 }\r
335\r
336 delete[] buffer;\r
337\r
338 if (error)\r
339 {\r
340 WriteLog("FILE: ELF parsing error\n");\r
341\r
342 if ((err = elf_errno()))\r
343 {\r
344 WriteLog("FILE: ELF error: %s\n", elf_errmsg(err));\r
345 }\r
346\r
347 return false;\r
348 }\r
349 else\r
350 {\r
351 DBGManager_SetType(DBGType);\r
352 return true;\r
353 }\r
354 }\r
355 else if (fileType == JST_ABS_TYPE1)\r
356 {\r
357 // For ABS type 1, run address == load address\r
358 uint32_t loadAddress = GET32(buffer, 0x16),\r
359 codeSize = GET32(buffer, 0x02) + GET32(buffer, 0x06);\r
360 WriteLog("FILE: Setting up homebrew (ABS-1)... Run address: %08X, length: %08X\n", loadAddress, codeSize);\r
361 memcpy(jagMemSpace + loadAddress, buffer + 0x24, codeSize);\r
362 delete[] buffer;\r
363 jaguarRunAddress = loadAddress;\r
364 return true;\r
365 }\r
366 else if (fileType == JST_ABS_TYPE2)\r
367 {\r
368 uint32_t loadAddress = GET32(buffer, 0x28), runAddress = GET32(buffer, 0x24),\r
369 codeSize = GET32(buffer, 0x18) + GET32(buffer, 0x1C);\r
370 WriteLog("FILE: Setting up homebrew (ABS-2)... Run address: %08X, length: %08X\n", runAddress, codeSize);\r
371 memcpy(jagMemSpace + loadAddress, buffer + 0xA8, codeSize);\r
372 delete[] buffer;\r
373 jaguarRunAddress = runAddress;\r
374 return true;\r
375 }\r
376 // NB: This is *wrong*\r
377 /*\r
378 Basically, if there is no "JAG" at position $1C, then the long there is the load/start\r
379 address in LITTLE ENDIAN.\r
380 If "JAG" is present, the the next character ("R" or "L") determines the size of the\r
381 JagServer command (2 bytes vs. 4). Following that are the commands themselves;\r
382 typically it will either be 2 (load) or 3 (load & run). Command headers go like so:\r
383 2:\r
384 Load address (long)\r
385 Length (long)\r
386 payload\r
387 3:\r
388 Load address (long)\r
389 Length (long)\r
390 Run address (long)\r
391 payload\r
392 5: (Reset)\r
393 [command only]\r
394 7: (Run at address)\r
395 Run address (long)\r
396 [no payload]\r
397 9: (Clear memory)\r
398 Start address (long)\r
399 End address (long)\r
400 [no payload]\r
401 10: (Poll for commands)\r
402 [command only]\r
403 12: (Load & run user program)\r
404 filname, terminated with NULL\r
405 [no payload]\r
406 $FFFF: (Halt)\r
407 [no payload]\r
408 */\r
409 else if (fileType == JST_JAGSERVER)\r
410 {\r
411 // This kind of shiaut should be in the detection code below...\r
412 // (and now it is! :-)\r
413// if (buffer[0x1C] == 'J' && buffer[0x1D] == 'A' && buffer[0x1E] == 'G')\r
414// {\r
415 // Still need to do some checking here for type 2 vs. type 3. This assumes 3\r
416 // Also, JAGR vs. JAGL (word command size vs. long command size)\r
417 uint32_t loadAddress = GET32(buffer, 0x22), runAddress = GET32(buffer, 0x2A);\r
418 WriteLog("FILE: Setting up homebrew (Jag Server)... Run address: $%X, length: $%X\n", runAddress, jaguarROMSize - 0x2E);\r
419 memcpy(jagMemSpace + loadAddress, buffer + 0x2E, jaguarROMSize - 0x2E);\r
420 delete[] buffer;\r
421 jaguarRunAddress = runAddress;\r
422\r
423// Hmm. Is this kludge necessary?\r
424SET32(jaguarMainRAM, 0x10, 0x00001000); // Set Exception #4 (Illegal Instruction)\r
425SET16(jaguarMainRAM, 0x1000, 0x60FE); // Here: bra Here\r
426\r
427 return true;\r
428// }\r
429// else // Special WTFOMGBBQ type here...\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 else if (fileType == JST_WTFOMGBBQ)\r
440 {\r
441 uint32_t loadAddress = (buffer[0x1F] << 24) | (buffer[0x1E] << 16) | (buffer[0x1D] << 8) | buffer[0x1C];\r
442 WriteLog("FILE: Setting up homebrew (GEMDOS WTFOMGBBQ type)... Run address: $%X, length: $%X\n", loadAddress, jaguarROMSize - 0x20);\r
443 memcpy(jagMemSpace + loadAddress, buffer + 0x20, jaguarROMSize - 0x20);\r
444 delete[] buffer;\r
445 jaguarRunAddress = loadAddress;\r
446 return true;\r
447 }\r
448\r
449 // We can assume we have JST_NONE at this point. :-P\r
450 WriteLog("FILE: Failed to load headerless file.\n");\r
451 return false;\r
452}\r
453\r
454\r
455//\r
456// "Debugger" file loading\r
457// To keep the things separate between "Debugger" and "Alpine" loading until usage clarification has been done\r
458//\r
459bool DebuggerLoadFile(char * path)\r
460{\r
461 return (AlpineLoadFile(path));\r
462}\r
463\r
464\r
465//\r
466// "Alpine" file loading\r
467// Since the developers were coming after us with torches and pitchforks, we\r
468// decided to allow this kind of thing. ;-) But ONLY FOR THE DEVS, DAMMIT! >:-U\r
469// O_O\r
470//\r
471bool AlpineLoadFile(char * path)\r
472{\r
473 uint8_t * buffer = NULL;\r
474 jaguarROMSize = JaguarLoadROM(buffer, path);\r
475\r
476 if (jaguarROMSize == 0)\r
477 {\r
478 // It's up to the GUI to deal with failure, not us. ;-)\r
479 WriteLog("FILE: Could not load Alpine from file \"%s\"...\nAborting load!\n", path);\r
480 return false;\r
481 }\r
482\r
483 jaguarMainROMCRC32 = crc32_calcCheckSum(buffer, jaguarROMSize);\r
484 WriteLog("FILE: CRC is %08X\n", (unsigned int)jaguarMainROMCRC32);\r
485 EepromInit();\r
486\r
487 jaguarRunAddress = 0x802000;\r
488\r
489 WriteLog("FILE: Setting up Alpine ROM with non-standard length... Run address: 00802000, length: %08X\n", jaguarROMSize);\r
490\r
491 memset(jagMemSpace + 0x800000, 0xFF, 0x2000);\r
492 memcpy(jagMemSpace + 0x802000, buffer, jaguarROMSize);\r
493 delete[] buffer;\r
494\r
495// Maybe instead of this, we could try requiring the STUBULATOR ROM? Just a thought...\r
496 // Try setting the vector to say, $1000 and putting an instruction there\r
497 // that loops forever:\r
498 // This kludge works! Yeah!\r
499 SET32(jaguarMainRAM, 0x10, 0x00001000); // Set Exception #4 (Illegal Instruction)\r
500 SET16(jaguarMainRAM, 0x1000, 0x60FE); // Here: bra Here\r
501\r
502 return true;\r
503}\r
504\r
505\r
506//\r
507// Get the length of a (possibly) gzipped file\r
508//\r
509static int gzfilelength(gzFile gd)\r
510{\r
511 int size = 0, length = 0;\r
512 unsigned char buffer[0x10000];\r
513\r
514 gzrewind(gd);\r
515\r
516 do\r
517 {\r
518 // Read in chunks until EOF\r
519 size = gzread(gd, buffer, 0x10000);\r
520\r
521 if (size <= 0)\r
522 break;\r
523\r
524 length += size;\r
525 }\r
526 while (!gzeof(gd));\r
527\r
528 gzrewind(gd);\r
529 return length;\r
530}\r
531\r
532\r
533//\r
534// Compare extension to passed in filename. If equal, return true; otherwise false.\r
535//\r
536//#if defined(_MSC_VER) || defined(__MINGW64__)|| defined(__MINGW32__) || defined(__CYGWIN__)\r
537static bool CheckExtension(const uint8_t *filename, const char *ext)\r
538//#else\r
539//static bool CheckExtension(const char * filename, const char * ext)\r
540//#endif // _MSC_VER\r
541{\r
542 // Sanity checking...\r
543 if ((filename == NULL) || (ext == NULL))\r
544 return false;\r
545\r
546 const char * filenameExt = strrchr((const char *)filename, '.'); // Get the file's extension (if any)\r
547\r
548 if (filenameExt == NULL)\r
549 return false;\r
550\r
551 return (strcasecmp(filenameExt, ext) == 0 ? true : false);\r
552}\r
553\r
554\r
555//\r
556// Get file from .ZIP\r
557// Returns the size of the file inside the .ZIP file that we're looking at\r
558// NOTE: If the thing we're looking for is found, it allocates it in the passed in buffer.\r
559// Which means we have to deallocate it later.\r
560//\r
561uint32_t GetFileFromZIP(const char * zipFile, FileType type, uint8_t * &buffer)\r
562{\r
563// NOTE: We could easily check for this by discarding anything that's larger than the RAM/ROM\r
564// size of the Jaguar console.\r
565#if defined(_MSC_VER)\r
566#pragma message("Warning: !!! FIX !!! Should have sanity checking for ROM size to prevent buffer overflow!")\r
567#else\r
568#warning "!!! FIX !!! Should have sanity checking for ROM size to prevent buffer overflow!"\r
569#endif // _MSC_VER\r
570 const char ftStrings[5][32] = { "Software", "EEPROM", "Label", "Box Art", "Controller Overlay" };\r
571// ZIP * zip = openzip(0, 0, zipFile);\r
572 FILE * zip = fopen(zipFile, "rb");\r
573\r
574 if (zip == NULL)\r
575 {\r
576 WriteLog("FILE: Could not open file '%s'!\n", zipFile);\r
577 return 0;\r
578 }\r
579\r
580// zipent * ze;\r
581 ZipFileEntry ze;\r
582 bool found = false;\r
583\r
584 // The order is here is important: If the file is found, we need to short-circuit the\r
585 // readzip() call because otherwise, 'ze' will be pointing to the wrong file!\r
586// while (!found && readzip(zip))\r
587 while (!found && GetZIPHeader(zip, ze))\r
588 {\r
589// ze = &zip->ent;\r
590\r
591 // Here we simply rely on the file extension to tell the truth, but we know\r
592 // that extensions lie like sons-a-bitches. So this is naive, we need to do\r
593 // something a little more robust to keep bad things from happening here.\r
594#if defined(_MSC_VER)\r
595#pragma message("Warning: !!! Checking for image by extension can be fooled !!!")\r
596#else\r
597#warning "!!! Checking for image by extension can be fooled !!!"\r
598#endif // _MSC_VER\r
599 if ((type == FT_LABEL) && (CheckExtension(ze.filename, ".png") || CheckExtension(ze.filename, ".jpg") || CheckExtension(ze.filename, ".gif")))\r
600 {\r
601 found = true;\r
602 WriteLog("FILE: Found image file '%s'.\n", ze.filename);\r
603 }\r
604\r
605 if ((type == FT_SOFTWARE) && (CheckExtension(ze.filename, ".j64")\r
606 || CheckExtension(ze.filename, ".rom") || CheckExtension(ze.filename, ".abs")\r
607 || CheckExtension(ze.filename, ".cof") || CheckExtension(ze.filename, ".coff")\r
608 || CheckExtension(ze.filename, ".jag") || CheckExtension(ze.filename, ".elf")))\r
609 {\r
610 found = true;\r
611 WriteLog("FILE: Found software file '%s'.\n", ze.filename);\r
612 }\r
613\r
614 if ((type == FT_EEPROM) && (CheckExtension(ze.filename, ".eep") || CheckExtension(ze.filename, ".eeprom")))\r
615 {\r
616 found = true;\r
617 WriteLog("FILE: Found EEPROM file '%s'.\n", ze.filename);\r
618 }\r
619\r
620 if (!found)\r
621 fseek(zip, ze.compressedSize, SEEK_CUR);\r
622 }\r
623\r
624 uint32_t fileSize = 0;\r
625\r
626 if (found)\r
627 {\r
628 WriteLog("FILE: Uncompressing...");\r
629// Insert file size sanity check here...\r
630 buffer = new uint8_t[ze.uncompressedSize];\r
631\r
632// if (readuncompresszip(zip, ze.compressedSize, buffer) == 0)\r
633// if (UncompressFileFromZIP(zip, ze.compressedSize, buffer) == 0)\r
634 if (UncompressFileFromZIP(zip, ze, buffer) == 0)\r
635 {\r
636 fileSize = ze.uncompressedSize;\r
637 WriteLog("success! (%u bytes)\n", fileSize);\r
638 }\r
639 else\r
640 {\r
641 delete[] buffer;\r
642 buffer = NULL;\r
643 WriteLog("FAILED!\n");\r
644 }\r
645 }\r
646 else\r
647 // Didn't find what we're looking for...\r
648 WriteLog("FILE: Failed to find file of type %s...\n", ftStrings[type]);\r
649\r
650// closezip(zip);\r
651 fclose(zip);\r
652 return fileSize;\r
653}\r
654\r
655\r
656uint32_t GetFileDBIdentityFromZIP(const char * zipFile)\r
657{\r
658 FILE * zip = fopen(zipFile, "rb");\r
659\r
660 if (zip == NULL)\r
661 {\r
662 WriteLog("FILE: Could not open file '%s'!\n", zipFile);\r
663 return 0;\r
664 }\r
665\r
666 ZipFileEntry ze;\r
667\r
668 // Loop through all files in the zip file under consideration\r
669 while (GetZIPHeader(zip, ze))\r
670 {\r
671 // & loop through all known CRC32s in our file DB to see if it's there!\r
672 uint32_t index = 0;\r
673\r
674 while (romList[index].crc32 != 0xFFFFFF)\r
675 {\r
676 if (romList[index].crc32 == ze.crc32)\r
677 {\r
678 fclose(zip);\r
679 return index;\r
680 }\r
681\r
682 index++;\r
683 }\r
684\r
685 // We didn't find it, so skip the compressed data...\r
686 fseek(zip, ze.compressedSize, SEEK_CUR);\r
687 }\r
688\r
689 fclose(zip);\r
690 return (uint32_t )-1;\r
691}\r
692\r
693\r
694bool FindFileInZIPWithCRC32(const char * zipFile, uint32_t crc)\r
695{\r
696 FILE * zip = fopen(zipFile, "rb");\r
697\r
698 if (zip == NULL)\r
699 {\r
700 WriteLog("FILE: Could not open file '%s'!\n", zipFile);\r
701 return 0;\r
702 }\r
703\r
704 ZipFileEntry ze;\r
705\r
706 // Loop through all files in the zip file under consideration\r
707 while (GetZIPHeader(zip, ze))\r
708 {\r
709 if (ze.crc32 == crc)\r
710 {\r
711 fclose(zip);\r
712 return true;\r
713 }\r
714\r
715 fseek(zip, ze.compressedSize, SEEK_CUR);\r
716 }\r
717\r
718 fclose(zip);\r
719 return false;\r
720}\r
721\r
722\r
723//\r
724// Parse the file type based upon file size and/or headers.\r
725//\r
726uint32_t ParseFileType(uint8_t * buffer, uint32_t size)\r
727{\r
728 // Check headers first...\r
729\r
730 // ELF 32bits\r
731 if (buffer[EI_CLASS] == ELFCLASS32)\r
732 {\r
733 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
734 return JST_ELF32;\r
735 }\r
736\r
737 // ABS/COFF type 1\r
738 if (buffer[0] == 0x60 && buffer[1] == 0x1B)\r
739 return JST_ABS_TYPE1;\r
740\r
741 // ABS/COFF type 2\r
742 if (buffer[0] == 0x01 && buffer[1] == 0x50)\r
743 return JST_ABS_TYPE2;\r
744\r
745 // Jag Server & other old shite\r
746 if (buffer[0] == 0x60 && buffer[1] == 0x1A)\r
747 {\r
748 if (buffer[0x1C] == 'J' && buffer[0x1D] == 'A' && buffer[0x1E] == 'G')\r
749 return JST_JAGSERVER;\r
750 else\r
751 return JST_WTFOMGBBQ;\r
752 }\r
753\r
754 // And if that fails, try file sizes...\r
755\r
756 // If the file size is divisible by 1M, we probably have an regular ROM.\r
757 // We can also check our CRC32 against the internal ROM database to be sure.\r
758 // (We also check for the Memory Track cartridge size here as well...)\r
759 if ((size % 1048576) == 0 || size == 131072)\r
760 return JST_ROM;\r
761\r
762 // If the file size + 8192 bytes is divisible by 1M, we probably have an\r
763 // Alpine format ROM.\r
764 if (((size + 8192) % 1048576) == 0)\r
765 return JST_ALPINE;\r
766\r
767 // Headerless crap\r
768 return JST_NONE;\r
769}\r
770\r
771//\r
772// Check for universal header\r
773//\r
774bool HasUniversalHeader(uint8_t * rom, uint32_t romSize)\r
775{\r
776 // Sanity check\r
777 if (romSize < 8192)\r
778 return false;\r
779\r
780 for(int i=0; i<8192; i++)\r
781 if (rom[i] != universalCartHeader[i])\r
782 return false;\r
783\r
784 return true;\r
785}\r
786\r
787#if 0\r
788// Misc. doco\r
789\r
790/*\r
791Stubulator ROM vectors...\r
792handler 001 at $00E00008\r
793handler 002 at $00E008DE\r
794handler 003 at $00E008E2\r
795handler 004 at $00E008E6\r
796handler 005 at $00E008EA\r
797handler 006 at $00E008EE\r
798handler 007 at $00E008F2\r
799handler 008 at $00E0054A\r
800handler 009 at $00E008FA\r
801handler 010 at $00000000\r
802handler 011 at $00000000\r
803handler 012 at $00E008FE\r
804handler 013 at $00E00902\r
805handler 014 at $00E00906\r
806handler 015 at $00E0090A\r
807handler 016 at $00E0090E\r
808handler 017 at $00E00912\r
809handler 018 at $00E00916\r
810handler 019 at $00E0091A\r
811handler 020 at $00E0091E\r
812handler 021 at $00E00922\r
813handler 022 at $00E00926\r
814handler 023 at $00E0092A\r
815handler 024 at $00E0092E\r
816handler 025 at $00E0107A\r
817handler 026 at $00E0107A\r
818handler 027 at $00E0107A\r
819handler 028 at $00E008DA\r
820handler 029 at $00E0107A\r
821handler 030 at $00E0107A\r
822handler 031 at $00E0107A\r
823handler 032 at $00000000\r
824\r
825Let's try setting up the illegal instruction vector for a stubulated jaguar...\r
826\r
827 SET32(jaguar_mainRam, 0x08, 0x00E008DE);\r
828 SET32(jaguar_mainRam, 0x0C, 0x00E008E2);\r
829 SET32(jaguar_mainRam, 0x10, 0x00E008E6); // <-- Should be here (it is)...\r
830 SET32(jaguar_mainRam, 0x14, 0x00E008EA);//*/\r
831\r
832/*\r
833ABS Format sleuthing (LBUGDEMO.ABS):\r
834\r
835000000 60 1B 00 00 05 0C 00 04 62 C0 00 00 04 28 00 00\r
836000010 12 A6 00 00 00 00 00 80 20 00 FF FF 00 80 25 0C\r
837000020 00 00 40 00\r
838\r
839DRI-format file detected...\r
840Text segment size = 0x0000050c bytes\r
841Data segment size = 0x000462c0 bytes\r
842BSS Segment size = 0x00000428 bytes\r
843Symbol Table size = 0x000012a6 bytes\r
844Absolute Address for text segment = 0x00802000\r
845Absolute Address for data segment = 0x0080250c\r
846Absolute Address for BSS segment = 0x00004000\r
847\r
848(CRZDEMO.ABS):\r
849000000 01 50 00 03 00 00 00 00 00 03 83 10 00 00 05 3b\r
850000010 00 1c 00 03 00 00 01 07 00 00 1d d0 00 03 64 98\r
851000020 00 06 8b 80 00 80 20 00 00 80 20 00 00 80 3d d0\r
852\r
853000030 2e 74 78 74 00 00 00 00 00 80 20 00 00 80 20 00 .txt (+36 bytes)\r
854000040 00 00 1d d0 00 00 00 a8 00 00 00 00 00 00 00 00\r
855000050 00 00 00 00 00 00 00 20\r
856000058 2e 64 74 61 00 00 00 00 00 80 3d d0 00 80 3d d0 .dta (+36 bytes)\r
857000068 00 03 64 98 00 00 1e 78 00 00 00 00 00 00 00 00\r
858000078 00 00 00 00 00 00 00 40\r
859000080 2e 62 73 73 00 00 00 00 00 00 50 00 00 00 50 00 .bss (+36 bytes)\r
860000090 00 06 8b 80 00 03 83 10 00 00 00 00 00 00 00 00\r
8610000a0 00 00 00 00 00 00 00 80\r
862\r
863Header size is $A8 bytes...\r
864\r
865BSD/COFF format file detected...\r
8663 sections specified\r
867Symbol Table offset = 230160 ($00038310)\r
868Symbol Table contains 1339 symbol entries ($0000053B)\r
869The additional header size is 28 bytes ($001C)\r
870Magic Number for RUN_HDR = 0x00000107\r
871Text Segment Size = 7632 ($00001DD0)\r
872Data Segment Size = 222360 ($00036498)\r
873BSS Segment Size = 428928 ($00068B80)\r
874Starting Address for executable = 0x00802000\r
875Start of Text Segment = 0x00802000\r
876Start of Data Segment = 0x00803dd0\r
877*/\r
878#endif\r