]> Shamusworld >> Repos - virtualjaguar/blob - src/file.cpp
Added logfile logging, ZIP file fishing
[virtualjaguar] / src / file.cpp
1 //
2 // FILE.CPP
3 //
4 // File support
5 // by James L. Hammons
6 // (C) 2010 Underground Software
7 //
8 // JLH = James L. Hammons <jlhamm@acm.org>
9 //
10 // Who  When        What
11 // ---  ----------  -------------------------------------------------------------
12 // JLH  01/16/2010  Created this log ;-)
13 // JLH  02/28/2010  Added functions to look inside .ZIP files and handle contents
14 //
15
16 #include "file.h"
17
18 #include <stdarg.h>
19 #include <string.h>
20 #include "crc32.h"
21 #include "eeprom.h"
22 #include "jaguar.h"
23 #include "log.h"
24 #include "memory.h"
25 #include "unzip.h"
26 #include "zlib.h"
27
28 // Private function prototypes
29
30 static int gzfilelength(gzFile gd);
31 static bool CheckExtension(const char * filename, const char * ext);
32
33 //
34 // Generic ROM loading
35 //
36 uint32 JaguarLoadROM(uint8 * rom, char * path)
37 {
38 // We really should have some kind of sanity checking for the ROM size here to prevent
39 // a buffer overflow... !!! FIX !!!
40 #warning "!!! FIX !!! Should have sanity checking for ROM size to prevent buffer overflow!"
41         uint32 romSize = 0;
42
43 WriteLog("JaguarLoadROM: Attempting to load file '%s'...", path);
44         char * ext = strrchr(path, '.');
45 if (ext == NULL)
46         WriteLog("FAILED!\n");
47 else
48         WriteLog("Succeeded in finding extension (%s)!\n", ext);
49
50         if (ext != NULL)
51         {
52                 WriteLog("VJ: Loading \"%s\"...", path);
53
54                 if (strcasecmp(ext, ".zip") == 0)
55                 {
56                         // Handle ZIP file loading here...
57                         WriteLog("(ZIPped)...");
58
59                         if (load_zipped_file(0, 0, path, NULL, &rom, &romSize) == -1)
60                         {
61                                 WriteLog("Failed!\n");
62                                 return 0;
63                         }
64                 }
65                 else
66                 {
67 /*                      FILE * fp = fopen(path, "rb");
68
69                         if (fp == NULL)
70                         {
71                                 WriteLog("Failed!\n");
72                                 return 0;
73                         }
74
75                         fseek(fp, 0, SEEK_END);
76                         romSize = ftell(fp);
77                         fseek(fp, 0, SEEK_SET);
78                         fread(rom, 1, romSize, fp);
79                         fclose(fp);*/
80
81                         // Handle gzipped files transparently [Adam Green]...
82
83                         gzFile fp = gzopen(path, "rb");
84
85                         if (fp == NULL)
86                         {
87                                 WriteLog("Failed!\n");
88                                 return 0;
89                         }
90
91                         romSize = gzfilelength(fp);
92                         gzseek(fp, 0, SEEK_SET);
93                         gzread(fp, rom, romSize);
94                         gzclose(fp);
95                 }
96
97                 WriteLog("OK (%i bytes)\n", romSize);
98         }
99
100         return romSize;
101 }
102
103 //
104 // Jaguar file loading
105 //
106 bool JaguarLoadFile(char * path)
107 {
108 //      jaguarRomSize = JaguarLoadROM(mem, path);
109         jaguarROMSize = JaguarLoadROM(jaguarMainROM, path);
110
111 /*//This is not *nix friendly for some reason...
112 //              if (!UserSelectFile(path, newPath))
113         if (!UserSelectFile((strlen(path) == 0 ? (char *)"." : path), newPath))
114         {
115                 WriteLog("VJ: Could not find valid ROM in directory \"%s\"...\nAborting!\n", path);
116                 log_done();
117                 exit(0);
118         }*/
119
120         if (jaguarROMSize == 0)
121         {
122 //                      WriteLog("VJ: Could not load ROM from file \"%s\"...\nAborting!\n", newPath);
123                 WriteLog("GUI: Could not load ROM from file \"%s\"...\nAborting load!\n", path);
124 // Need to do something else here, like throw up an error dialog instead of aborting. !!! FIX !!!
125 //              log_done();
126 //              exit(0);
127                 return false;                                                           // This is a start...
128         }
129
130         jaguarMainROMCRC32 = crc32_calcCheckSum(jaguarMainROM, jaguarROMSize);
131         WriteLog("CRC: %08X\n", (unsigned int)jaguarMainROMCRC32);
132         EepromInit();
133
134         jaguarRunAddress = 0x802000;
135
136         char * ext = strrchr(path, '.');                                // Get the file's extension for non-cartridge checking
137
138 //NOTE: Should fix JaguarLoadROM() to replace .zip with what's *in* the zip (.abs, .j64, etc.)
139         if (strcasecmp(ext, ".rom") == 0)
140         {
141                 // File extension ".ROM": Alpine image that loads/runs at $802000
142                 WriteLog("GUI: Setting up homebrew (ROM)... Run address: 00802000, length: %08X\n", jaguarROMSize);
143
144                 for(int i=jaguarROMSize-1; i>=0; i--)
145                         jaguarMainROM[0x2000 + i] = jaguarMainROM[i];
146
147                 memset(jaguarMainROM, 0xFF, 0x2000);
148 /*              memcpy(jaguar_mainRam, jaguar_mainRom, jaguarRomSize);
149                 memset(jaguar_mainRom, 0xFF, 0x600000);
150                 memcpy(jaguar_mainRom + 0x2000, jaguar_mainRam, jaguarRomSize);
151                 memset(jaguar_mainRam, 0x00, 0x400000);*/
152
153 /*
154 Stubulator ROM vectors...
155 handler 001 at $00E00008
156 handler 002 at $00E008DE
157 handler 003 at $00E008E2
158 handler 004 at $00E008E6
159 handler 005 at $00E008EA
160 handler 006 at $00E008EE
161 handler 007 at $00E008F2
162 handler 008 at $00E0054A
163 handler 009 at $00E008FA
164 handler 010 at $00000000
165 handler 011 at $00000000
166 handler 012 at $00E008FE
167 handler 013 at $00E00902
168 handler 014 at $00E00906
169 handler 015 at $00E0090A
170 handler 016 at $00E0090E
171 handler 017 at $00E00912
172 handler 018 at $00E00916
173 handler 019 at $00E0091A
174 handler 020 at $00E0091E
175 handler 021 at $00E00922
176 handler 022 at $00E00926
177 handler 023 at $00E0092A
178 handler 024 at $00E0092E
179 handler 025 at $00E0107A
180 handler 026 at $00E0107A
181 handler 027 at $00E0107A
182 handler 028 at $00E008DA
183 handler 029 at $00E0107A
184 handler 030 at $00E0107A
185 handler 031 at $00E0107A
186 handler 032 at $00000000
187
188 Let's try setting up the illegal instruction vector for a stubulated jaguar...
189 */
190 /*              SET32(jaguar_mainRam, 0x08, 0x00E008DE);
191                 SET32(jaguar_mainRam, 0x0C, 0x00E008E2);
192                 SET32(jaguar_mainRam, 0x10, 0x00E008E6);        // <-- Should be here (it is)...
193                 SET32(jaguar_mainRam, 0x14, 0x00E008EA);//*/
194
195                 // Try setting the vector to say, $1000 and putting an instruction there that loops forever:
196                 // This kludge works! Yeah!
197                 SET32(jaguarMainRAM, 0x10, 0x00001000);
198                 SET16(jaguarMainRAM, 0x1000, 0x60FE);           // Here: bra Here
199         }
200         else if (strcasecmp(ext, ".abs") == 0)
201         {
202                 // File extension ".ABS": Atari linker output file with header (w/o is useless to us here)
203
204 /*
205 ABS Format sleuthing (LBUGDEMO.ABS):
206
207 000000  60 1B 00 00 05 0C 00 04 62 C0 00 00 04 28 00 00
208 000010  12 A6 00 00 00 00 00 80 20 00 FF FF 00 80 25 0C
209 000020  00 00 40 00
210
211 DRI-format file detected...
212 Text segment size = 0x0000050c bytes
213 Data segment size = 0x000462c0 bytes
214 BSS Segment size = 0x00000428 bytes
215 Symbol Table size = 0x000012a6 bytes
216 Absolute Address for text segment = 0x00802000
217 Absolute Address for data segment = 0x0080250c
218 Absolute Address for BSS segment = 0x00004000
219
220 (CRZDEMO.ABS):
221 000000  01 50 00 03 00 00 00 00 00 03 83 10 00 00 05 3b
222 000010  00 1c 00 03 00 00 01 07 00 00 1d d0 00 03 64 98
223 000020  00 06 8b 80 00 80 20 00 00 80 20 00 00 80 3d d0
224
225 000030  2e 74 78 74 00 00 00 00 00 80 20 00 00 80 20 00 .txt (+36 bytes)
226 000040  00 00 1d d0 00 00 00 a8 00 00 00 00 00 00 00 00
227 000050  00 00 00 00 00 00 00 20
228 000058  2e 64 74 61 00 00 00 00 00 80 3d d0 00 80 3d d0 .dta (+36 bytes)
229 000068  00 03 64 98 00 00 1e 78 00 00 00 00 00 00 00 00
230 000078  00 00 00 00 00 00 00 40
231 000080  2e 62 73 73 00 00 00 00 00 00 50 00 00 00 50 00 .bss (+36 bytes)
232 000090  00 06 8b 80 00 03 83 10 00 00 00 00 00 00 00 00
233 0000a0  00 00 00 00 00 00 00 80
234
235 Header size is $A8 bytes...
236
237 BSD/COFF format file detected...
238 3 sections specified
239 Symbol Table offset = 230160                            ($00038310)
240 Symbol Table contains 1339 symbol entries       ($0000053B)
241 The additional header size is 28 bytes          ($001C)
242 Magic Number for RUN_HDR = 0x00000107
243 Text Segment Size = 7632                                        ($00001DD0)
244 Data Segment Size = 222360                                      ($00036498)
245 BSS Segment Size = 428928                                       ($00068B80)
246 Starting Address for executable = 0x00802000
247 Start of Text Segment = 0x00802000
248 Start of Data Segment = 0x00803dd0
249 */
250                 if (jaguarMainROM[0] == 0x60 && jaguarMainROM[1] == 0x1B)
251                 {
252                         uint32 loadAddress = GET32(jaguarMainROM, 0x16), //runAddress = GET32(jaguar_mainRom, 0x2A),
253                                 codeSize = GET32(jaguarMainROM, 0x02) + GET32(jaguarMainROM, 0x06);
254                         WriteLog("GUI: Setting up homebrew (ABS-1)... Run address: %08X, length: %08X\n", loadAddress, codeSize);
255
256                         if (loadAddress < 0x800000)
257                                 memcpy(jaguarMainRAM + loadAddress, jaguarMainROM + 0x24, codeSize);
258                         else
259                         {
260                                 for(int i=codeSize-1; i>=0; i--)
261                                         jaguarMainROM[(loadAddress - 0x800000) + i] = jaguarMainROM[i + 0x24];
262 /*                              memcpy(jaguar_mainRam, jaguar_mainRom + 0x24, codeSize);
263                                 memset(jaguar_mainRom, 0xFF, 0x600000);
264                                 memcpy(jaguar_mainRom + (loadAddress - 0x800000), jaguar_mainRam, codeSize);
265                                 memset(jaguar_mainRam, 0x00, 0x400000);*/
266                         }
267
268                         jaguarRunAddress = loadAddress;
269                 }
270                 else if (jaguarMainROM[0] == 0x01 && jaguarMainROM[1] == 0x50)
271                 {
272                         uint32 loadAddress = GET32(jaguarMainROM, 0x28), runAddress = GET32(jaguarMainROM, 0x24),
273                                 codeSize = GET32(jaguarMainROM, 0x18) + GET32(jaguarMainROM, 0x1C);
274                         WriteLog("GUI: Setting up homebrew (ABS-2)... Run address: %08X, length: %08X\n", runAddress, codeSize);
275
276                         if (loadAddress < 0x800000)
277                                 memcpy(jaguarMainRAM + loadAddress, jaguarMainROM + 0xA8, codeSize);
278                         else
279                         {
280                                 for(int i=codeSize-1; i>=0; i--)
281                                         jaguarMainROM[(loadAddress - 0x800000) + i] = jaguarMainROM[i + 0xA8];
282 /*                              memcpy(jaguar_mainRam, jaguar_mainRom + 0xA8, codeSize);
283                                 memset(jaguar_mainRom, 0xFF, 0x600000);
284                                 memcpy(jaguar_mainRom + (loadAddress - 0x800000), jaguar_mainRam, codeSize);
285                                 memset(jaguar_mainRam, 0x00, 0x400000);*/
286                         }
287
288                         jaguarRunAddress = runAddress;
289                 }
290                 else
291                 {
292                         WriteLog("GUI: Couldn't find correct ABS/COF format: %02X %02X\n", jaguarMainROM[0], jaguarMainROM[1]);
293                         return false;
294                 }
295         }
296         else if (strcasecmp(ext, ".jag") == 0)
297         {
298                 // File extension ".JAG": Atari server file with header
299 //NOTE: The bytes 'JAGR' should also be at position $1C...
300 //      Also, there's *always* a $601A header at position $00...
301                 if (jaguarMainROM[0] == 0x60 && jaguarMainROM[1] == 0x1A)
302                 {
303                         uint32 loadAddress = GET32(jaguarMainROM, 0x22), runAddress = GET32(jaguarMainROM, 0x2A);
304 //This is not always right! Especially when converted via bin2jag1!!!
305 //We should have access to the length of the furshlumiger file that was loaded anyway!
306 //Now, we do! ;-)
307 //                      uint32 progLength = GET32(jaguar_mainRom, 0x02);
308 //jaguarRomSize
309 //jaguarRunAddress
310 //                      WriteLog("Jaguar: Setting up PD ROM... Run address: %08X, length: %08X\n", runAddress, progLength);
311 //                      memcpy(jaguar_mainRam + loadAddress, jaguar_mainRom + 0x2E, progLength);
312                         WriteLog("GUI: Setting up homebrew (JAG)... Run address: %08X, length: %08X\n", runAddress, jaguarROMSize - 0x2E);
313                         memcpy(jaguarMainRAM + loadAddress, jaguarMainROM + 0x2E, jaguarROMSize - 0x2E);
314 //              SET32(jaguar_mainRam, 4, runAddress);
315                         jaguarRunAddress = runAddress;
316                 }
317                 else
318                         return false;
319         }
320         // .J64 (Jaguar cartridge ROM image) is implied by the FileList object...
321
322         return true;
323 }
324
325 //
326 // Get the length of a (possibly) gzipped file
327 //
328 static int gzfilelength(gzFile gd)
329 {
330    int size = 0, length = 0;
331    unsigned char buffer[0x10000];
332
333    gzrewind(gd);
334
335    do
336    {
337       // Read in chunks until EOF
338       size = gzread(gd, buffer, 0x10000);
339
340       if (size <= 0)
341         break;
342
343       length += size;
344    }
345    while (!gzeof(gd));
346
347    gzrewind(gd);
348    return length;
349 }
350
351 //
352 // Compare extension to passed in filename. If equal, return true; otherwise false.
353 //
354 static bool CheckExtension(const char * filename, const char * ext)
355 {
356         const char * filenameExt = strrchr(filename, '.');      // Get the file's extension (if any)
357         return (strcasecmp(filenameExt, ext) == 0 ? true : false);
358 }
359
360 //
361 // Get file from .ZIP
362 // Returns the size of the file inside the .ZIP file that we're looking at
363 //
364 uint32 GetFileFromZIP(const char * zipFile, FileType type, uint8 * &buffer)
365 {
366 #warning "!!! FIX !!! Should have sanity checking for ROM size to prevent buffer overflow!"
367         const char ftStrings[5][32] = { "Software", "EEPROM", "Label", "Box Art", "Controller Overlay" };
368         ZIP * zip = openzip(0, 0, zipFile);
369
370         if (zip == NULL)
371         {
372                 WriteLog("FILE: Could not open file '%s'!\n", zipFile);
373                 return 0;
374         }
375
376         while (readzip(zip))
377         {
378                 zipent * ze = &zip->ent;
379                 bool found = false;
380
381                 if ((type == FT_LABEL) && (CheckExtension(ze->name, ".png") || CheckExtension(ze->name, ".jpg") || CheckExtension(ze->name, ".gif")))
382                 {
383                         found = true;
384                         WriteLog("FILE: Found image file '%s'.\n", ze->name);
385                 }
386
387                 if ((type == FT_SOFTWARE) && (CheckExtension(ze->name, ".j64") || CheckExtension(ze->name, ".rom") || CheckExtension(ze->name, ".abs") || CheckExtension(ze->name, ".cof")))
388                 {
389                         found = true;
390                         WriteLog("FILE: Found software file '%s'.\n", ze->name);
391                 }
392
393                 if ((type == FT_EEPROM) && (CheckExtension(ze->name, ".eep") || CheckExtension(ze->name, ".eeprom")))
394                 {
395                         found = true;
396                         WriteLog("FILE: Found EEPROM file '%s'.\n", ze->name);
397                 }
398
399                 if (found)
400                 {
401                         WriteLog("FILE: Uncompressing...");
402                         buffer = new uint8[ze->uncompressed_size];
403
404                         if (readuncompresszip(zip, ze, (char *)buffer) == 0)
405                         {
406                                 WriteLog("success! (%u bytes)\n", ze->uncompressed_size);
407                                 return ze->uncompressed_size;
408                         }
409                         else
410                         {
411                                 WriteLog("FAILED!\n");
412                                 delete[] buffer;
413                                 return 0;
414                         }
415                 }
416         }
417
418         closezip(zip);
419
420         WriteLog("FILE: Failed to find file of type %s...\n", ftStrings[type]);
421         // Didn't find what we're looking for...
422         return 0;
423 }
424
425 //ParseFileType, etc.
426