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