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