| 1 | // |
| 2 | // filethread.cpp - File discovery thread |
| 3 | // |
| 4 | // by James Hammons |
| 5 | // (C) 2010 Underground Software |
| 6 | // |
| 7 | // JLH = James Hammons <jlhamm@acm.org> |
| 8 | // JPM = Jean-Paul Mari <djipi.mari@gmail.com> |
| 9 | // |
| 10 | // Who When What |
| 11 | // --- ---------- ------------------------------------------------------------- |
| 12 | // JLH 01/28/2010 Created this file |
| 13 | // JLH 02/16/2010 Moved RomIdentifier stuff to its own file |
| 14 | // JLH 03/02/2010 Added .ZIP file fishing |
| 15 | // JLH 06/28/2011 Cleanup in the file parsing/fishing code, to make it easier |
| 16 | // to follow the flow of the logic |
| 17 | // |
| 18 | // JPM 06/06/2016 Visual Studio support |
| 19 | |
| 20 | #include "filethread.h" |
| 21 | |
| 22 | #include "crc32.h" |
| 23 | #include "file.h" |
| 24 | #include "filedb.h" |
| 25 | //#include "memory.h" |
| 26 | #include "settings.h" |
| 27 | |
| 28 | #define VERBOSE_LOGGING |
| 29 | |
| 30 | FileThread::FileThread(QObject * parent/*= 0*/): QThread(parent), abort(false) |
| 31 | { |
| 32 | } |
| 33 | |
| 34 | FileThread::~FileThread() |
| 35 | { |
| 36 | mutex.lock(); |
| 37 | abort = true; |
| 38 | condition.wakeOne(); |
| 39 | mutex.unlock(); |
| 40 | |
| 41 | wait(); |
| 42 | } |
| 43 | |
| 44 | void FileThread::Go(bool allowUnknown/*= false*/) |
| 45 | { |
| 46 | allowUnknownSoftware = allowUnknown; |
| 47 | QMutexLocker locker(&mutex); |
| 48 | start(); |
| 49 | } |
| 50 | |
| 51 | /* |
| 52 | Our strategy here is like so: |
| 53 | Look at the files in the directory pointed to by ROMPath. |
| 54 | For each file in the directory, take the CRC32 of it and compare it to the CRC |
| 55 | in the romList[]. If there's a match, put it in a list and note it's index value |
| 56 | in romList for future reference. |
| 57 | |
| 58 | When constructing the list, use the index to pull up an image of the cart and |
| 59 | put that in the list. User picks from a graphical image of the cart. |
| 60 | |
| 61 | Ideally, the label will go into the archive along with the ROM image, but that's |
| 62 | for the future... |
| 63 | Maybe box art, screenshots will go as well... |
| 64 | The future is NOW! :-) |
| 65 | */ |
| 66 | |
| 67 | // |
| 68 | // Here's the thread's actual execution path... |
| 69 | // |
| 70 | void FileThread::run(void) |
| 71 | { |
| 72 | QDir romDir(vjs.ROMPath); |
| 73 | QFileInfoList list = romDir.entryInfoList(); |
| 74 | |
| 75 | for(int i=0; i<list.size(); i++) |
| 76 | { |
| 77 | if (abort) |
| 78 | #ifdef VERBOSE_LOGGING |
| 79 | { |
| 80 | printf("FileThread: Aborting!!!\n"); |
| 81 | #endif |
| 82 | return; |
| 83 | #ifdef VERBOSE_LOGGING |
| 84 | } |
| 85 | #endif |
| 86 | |
| 87 | HandleFile(list.at(i)); |
| 88 | } |
| 89 | } |
| 90 | |
| 91 | // |
| 92 | // This handles file identification and ZIP extraction. |
| 93 | // |
| 94 | void FileThread::HandleFile(QFileInfo fileInfo) |
| 95 | { |
| 96 | // Really, need to come up with some kind of cacheing scheme here, so we don't |
| 97 | // fish through these files every time we run VJ :-P |
| 98 | #ifdef _MSC_VER |
| 99 | #pragma message("Warning: !!! Need to come up with some kind of cacheing scheme here !!!") |
| 100 | #else |
| 101 | #warning "!!! Need to come up with some kind of cacheing scheme here !!!" |
| 102 | #endif // _MSC_VER |
| 103 | bool haveZIPFile = (fileInfo.suffix().compare("zip", Qt::CaseInsensitive) == 0 |
| 104 | ? true : false); |
| 105 | uint32_t fileSize = 0; |
| 106 | uint8_t * buffer = NULL; |
| 107 | |
| 108 | if (haveZIPFile) |
| 109 | { |
| 110 | // ZIP files are special: They contain more than just the software now... ;-) |
| 111 | // So now we fish around inside them to pull out the stuff we want. |
| 112 | // Probably also need more stringent error checking as well... :-O |
| 113 | fileSize = GetFileFromZIP(fileInfo.filePath().toUtf8(), FT_SOFTWARE, buffer); |
| 114 | |
| 115 | if (fileSize == 0) |
| 116 | return; |
| 117 | } |
| 118 | else |
| 119 | { |
| 120 | QFile file(fileInfo.filePath()); |
| 121 | |
| 122 | if (!file.open(QIODevice::ReadOnly)) |
| 123 | return; |
| 124 | |
| 125 | fileSize = fileInfo.size(); |
| 126 | |
| 127 | if (fileSize == 0) |
| 128 | return; |
| 129 | |
| 130 | buffer = new uint8_t[fileSize]; |
| 131 | file.read((char *)buffer, fileSize); |
| 132 | file.close(); |
| 133 | } |
| 134 | |
| 135 | // Try to divine the file type by size & header |
| 136 | int fileType = ParseFileType(buffer, fileSize); |
| 137 | |
| 138 | // Check for Alpine ROM w/Universal Header |
| 139 | bool foundUniversalHeader = HasUniversalHeader(buffer, fileSize); |
| 140 | uint32_t crc; |
| 141 | |
| 142 | //printf("FileThread: About to calc checksum on file with size %u... (buffer=%08X)\n", size, buffer); |
| 143 | if (foundUniversalHeader) |
| 144 | crc = crc32_calcCheckSum(buffer + 8192, fileSize - 8192); |
| 145 | else |
| 146 | crc = crc32_calcCheckSum(buffer, fileSize); |
| 147 | |
| 148 | uint32_t index = FindCRCIndexInFileList(crc); |
| 149 | delete[] buffer; |
| 150 | |
| 151 | // Here we filter out files that are *not* in the DB and of unknown type, |
| 152 | // and BIOS files. If desired, this can be overriden with a config option. |
| 153 | if ((index == 0xFFFFFFFF) && (fileType == JST_NONE)) |
| 154 | { |
| 155 | // If we allow unknown software, we pass the (-1) index on, otherwise... |
| 156 | if (!allowUnknownSoftware) |
| 157 | return; // CRC wasn't found, so bail... |
| 158 | } |
| 159 | else if ((index != 0xFFFFFFFF) && romList[index].flags & FF_BIOS) |
| 160 | return; |
| 161 | |
| 162 | //Here's a little problem. When we create the image here and pass it off to FilePicker, |
| 163 | //we can clobber this image before we have a chance to copy it out in the FilePicker function |
| 164 | //because we can be back here before FilePicker can respond. |
| 165 | // So now we create the image on the heap, problem solved. :-) |
| 166 | QImage * img = NULL; |
| 167 | |
| 168 | // See if we can fish out a label. :-) |
| 169 | if (haveZIPFile) |
| 170 | { |
| 171 | uint32_t size = GetFileFromZIP(fileInfo.filePath().toUtf8(), FT_LABEL, buffer); |
| 172 | //printf("FT: Label size = %u bytes.\n", size); |
| 173 | |
| 174 | if (size > 0) |
| 175 | { |
| 176 | QImage label; |
| 177 | bool successful = label.loadFromData(buffer, size); |
| 178 | img = new QImage; |
| 179 | *img = label.scaled(365, 168, Qt::IgnoreAspectRatio, Qt::SmoothTransformation); |
| 180 | //printf("FT: Label %s: %ux%u.\n", (successful ? "succeeded" : "did not succeed"), img->width(), img->height()); |
| 181 | delete[] buffer; |
| 182 | } |
| 183 | //printf("FileThread: Attempted to load image. Size: %u x %u...\n", img.width(), img.height()); |
| 184 | } |
| 185 | |
| 186 | // emit FoundAFile2(index, fileInfo.canonicalFilePath(), img, fileSize); |
| 187 | emit FoundAFile3(index, fileInfo.canonicalFilePath(), img, fileSize, foundUniversalHeader, fileType, crc); |
| 188 | } |
| 189 | |
| 190 | // |
| 191 | // Find a CRC in the ROM list (simple brute force algorithm). |
| 192 | // If it's there, return the index, otherwise return $FFFFFFFF |
| 193 | // |
| 194 | uint32_t FileThread::FindCRCIndexInFileList(uint32_t crc) |
| 195 | { |
| 196 | // Instead of a simple brute-force search, we should probably do a binary |
| 197 | // partition search instead, since the CRCs are sorted numerically. |
| 198 | #ifdef _MSC_VER |
| 199 | #pragma message("Warning: !!! Should do binary partition search here !!!") |
| 200 | #else |
| 201 | #warning "!!! Should do binary partition search here !!!" |
| 202 | #endif // _MSC_VER |
| 203 | for(int i=0; romList[i].crc32!=0xFFFFFFFF; i++) |
| 204 | { |
| 205 | if (romList[i].crc32 == crc) |
| 206 | return i; |
| 207 | } |
| 208 | |
| 209 | return 0xFFFFFFFF; |
| 210 | } |