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