]> Shamusworld >> Repos - virtualjaguar/blob - src/gui/filethread.cpp
Various UI enhancements, like keyboard list searching in cart dialog.
[virtualjaguar] / src / gui / filethread.cpp
1 //
2 // filethread.cpp - File discovery thread
3 //
4 // by James L. Hammons
5 // (C) 2010 Underground Software
6 //
7 // JLH = James L. Hammons <jlhamm@acm.org>
8 //
9 // Who  When        What
10 // ---  ----------  -------------------------------------------------------------
11 // JLH  01/28/2010  Created this file
12 // JLH  02/16/2010  Moved RomIdentifier stuff to its own file
13 // JLH  03/02/2010  Added .ZIP file fishing
14 // JLH  06/28/2011  Cleanup in the file parsing/fishing code, to make it easier
15 //                  to follow the flow of the logic
16 //
17
18 #include "filethread.h"
19
20 #include <QtGui>
21 #include "crc32.h"
22 #include "file.h"
23 #include "filedb.h"
24 #include "memory.h"
25 #include "settings.h"
26
27 #define VERBOSE_LOGGING
28
29 FileThread::FileThread(QObject * parent/*= 0*/): QThread(parent), abort(false)
30 {
31 }
32
33 FileThread::~FileThread()
34 {
35         mutex.lock();
36         abort = true;
37         condition.wakeOne();
38         mutex.unlock();
39
40         wait();
41 }
42
43 void FileThread::Go(bool allowUnknown/*= false*/)
44 {
45         allowUnknownSoftware = allowUnknown;
46         QMutexLocker locker(&mutex);
47         start();
48 }
49
50 /*
51 Our strategy here is like so:
52 Look at the files in the directory pointed to by ROMPath.
53 For each file in the directory, take the CRC32 of it and compare it to the CRC
54 in the romList[]. If there's a match, put it in a list and note it's index value
55 in romList for future reference.
56
57 When constructing the list, use the index to pull up an image of the cart and
58 put that in the list. User picks from a graphical image of the cart.
59
60 Ideally, the label will go into the archive along with the ROM image, but that's
61 for the future...
62 Maybe box art, screenshots will go as well...
63 The future is NOW! :-)
64 */
65
66 //
67 // Here's the thread's actual execution path...
68 //
69 void FileThread::run(void)
70 {
71         QDir romDir(vjs.ROMPath);
72         QFileInfoList list = romDir.entryInfoList();
73
74         for(int i=0; i<list.size(); i++)
75         {
76                 if (abort)
77 #ifdef VERBOSE_LOGGING
78 {
79 printf("FileThread: Aborting!!!\n");
80 #endif
81                         return;
82 #ifdef VERBOSE_LOGGING
83 }
84 #endif
85
86                 HandleFile(list.at(i));
87         }
88 }
89
90 //
91 // This handles file identification and ZIP extraction.
92 //
93 void FileThread::HandleFile(QFileInfo fileInfo)
94 {
95         bool haveZIPFile = (fileInfo.suffix().compare("zip", Qt::CaseInsensitive) == 0
96                 ? true : false);
97         uint32_t fileSize = 0;
98         uint8 * buffer = NULL;
99
100         if (haveZIPFile)
101         {
102                 // ZIP files are special: They contain more than just the software now... ;-)
103                 // So now we fish around inside them to pull out the stuff we want.
104                 // Probably also need more stringent error checking as well... :-O
105                 fileSize = GetFileFromZIP(fileInfo.filePath().toAscii(), FT_SOFTWARE, buffer);
106
107                 if (fileSize == 0)
108                         return;
109         }
110         else
111         {
112                 QFile file(fileInfo.filePath());
113
114                 if (!file.open(QIODevice::ReadOnly))
115                         return;
116
117                 fileSize = fileInfo.size();
118
119                 if (fileSize == 0)
120                         return;
121
122                 buffer = new uint8[fileSize];
123                 file.read((char *)buffer, fileSize);
124                 file.close();
125         }
126
127         // Try to divine the file type by size & header
128         int fileType = ParseFileType(buffer[0], buffer[1], fileSize);
129
130         // Check for Alpine ROM w/Universal Header
131         bool foundUniversalHeader = HasUniversalHeader(buffer, fileSize);
132         uint32 crc;
133
134 //printf("FileThread: About to calc checksum on file with size %u... (buffer=%08X)\n", size, buffer);
135         if (foundUniversalHeader)
136                 crc = crc32_calcCheckSum(buffer + 8192, fileSize - 8192);
137         else
138                 crc = crc32_calcCheckSum(buffer, fileSize);
139
140         uint32 index = FindCRCIndexInFileList(crc);
141
142         if ((index != 0xFFFFFFFF) && (romList[index].flags & FF_BIOS))
143                 HandleBIOSFile(buffer, crc);
144
145         delete[] buffer;
146
147         // Here we filter out files *not* in the DB (if configured that way) and
148         // BIOS files.
149         if (index == 0xFFFFFFFF)
150         {
151                 // If we allow unknown software, we pass the (-1) index on, otherwise...
152                 if (!allowUnknownSoftware)
153                         return;                                                         // CRC wasn't found, so bail...
154         }
155         else if (romList[index].flags & FF_BIOS)
156                 return;
157
158 //Here's a little problem. When we create the image here and pass it off to FilePicker,
159 //we can clobber this image before we have a chance to copy it out in the FilePicker function
160 //because we can be back here before FilePicker can respond.
161 // So now we create the image on the heap, problem solved. :-)
162         QImage * img = NULL;
163
164         // See if we can fish out a label. :-)
165         if (haveZIPFile)
166         {
167                 uint32 size = GetFileFromZIP(fileInfo.filePath().toAscii(), FT_LABEL, buffer);
168 //printf("FT: Label size = %u bytes.\n", size);
169
170                 if (size > 0)
171                 {
172                         QImage label;
173                         bool successful = label.loadFromData(buffer, size);
174                         img = new QImage;
175                         *img = label.scaled(365, 168, Qt::IgnoreAspectRatio, Qt::SmoothTransformation);
176 //printf("FT: Label %s: %ux%u.\n", (successful ? "succeeded" : "did not succeed"), img->width(), img->height());
177                         delete[] buffer;
178                 }
179 //printf("FileThread: Attempted to load image. Size: %u x %u...\n", img.width(), img.height());
180         }
181
182 //      emit FoundAFile2(index, fileInfo.canonicalFilePath(), img, fileSize);
183         emit FoundAFile3(index, fileInfo.canonicalFilePath(), img, fileSize, foundUniversalHeader, fileType, crc);
184 }
185
186 //
187 // Handle checking/copying BIOS files into Jaguar core memory
188 //
189 void FileThread::HandleBIOSFile(uint8 * buffer, uint32 crc)
190 {
191 /*
192         { 0x55A0669C, "[BIOS] Atari Jaguar Developer CD (World)", FF_BIOS },
193         { 0x687068D5, "[BIOS] Atari Jaguar CD (World)", FF_BIOS },
194         { 0x8D15DBC6, "[BIOS] Atari Jaguar Stubulator '94 (World)", FF_BIOS },
195         { 0xE60277BB, "[BIOS] Atari Jaguar Stubulator '93 (World)", FF_BIOS },
196         { 0xFB731AAA, "[BIOS] Atari Jaguar (World)", FF_BIOS },
197
198 uint8 jaguarBootROM[0x040000];                                  // 68K CPU BIOS ROM--uses only half of this!
199 uint8 jaguarCDBootROM[0x040000];                                // 68K CPU CD BIOS ROM (256K)
200 uint8 jaguarDevBootROM1[0x040000];                              // 68K CPU Stubulator 1 ROM--uses only half of this!
201 uint8 jaguarDevBootROM2[0x040000];                              // 68K CPU Stubulator 2 ROM--uses only half of this!
202 uint8 jaguarDevCDBootROM[0x040000];                             // 68K CPU Dev CD BIOS ROM (256K)
203
204 enum { BIOS_NORMAL=0x01, BIOS_CD=0x02, BIOS_STUB1=0x04, BIOS_STUB2=0x08, BIOS_DEV_CD=0x10 };
205 extern int biosAvailable;
206 */
207         if (crc == 0xFB731AAA && !(biosAvailable & BIOS_NORMAL))
208         {
209                 memcpy(jaguarBootROM, buffer, 0x20000);
210                 biosAvailable |= BIOS_NORMAL;
211         }
212         else if (crc == 0x687068D5 && !(biosAvailable & BIOS_CD))
213         {
214                 memcpy(jaguarCDBootROM, buffer, 0x40000);
215                 biosAvailable |= BIOS_CD;
216         }
217         else if (crc == 0x8D15DBC6 && !(biosAvailable & BIOS_STUB1))
218         {
219                 memcpy(jaguarDevBootROM1, buffer, 0x20000);
220                 biosAvailable |= BIOS_STUB1;
221         }
222         else if (crc == 0xE60277BB && !(biosAvailable & BIOS_STUB2))
223         {
224                 memcpy(jaguarDevBootROM2, buffer, 0x20000);
225                 biosAvailable |= BIOS_STUB2;
226         }
227         else if (crc == 0x55A0669C && !(biosAvailable & BIOS_DEV_CD))
228         {
229                 memcpy(jaguarDevCDBootROM, buffer, 0x40000);
230                 biosAvailable |= BIOS_DEV_CD;
231         }
232 }
233
234 //
235 // Find a CRC in the ROM list (simple brute force algorithm).
236 // If it's there, return the index, otherwise return $FFFFFFFF
237 //
238 uint32 FileThread::FindCRCIndexInFileList(uint32 crc)
239 {
240         // Instead of a simple brute-force search, we should probably do a binary
241         // partition search instead, since the CRCs are sorted numerically.
242 #warning "!!! Should do binary partition search here !!!"
243         for(int i=0; romList[i].crc32!=0xFFFFFFFF; i++)
244         {
245                 if (romList[i].crc32 == crc)
246                         return i;
247         }
248
249         return 0xFFFFFFFF;
250 }