2 // filepicker.cpp - A ROM chooser
5 // (C) 2010 Underground Software
7 // JLH = James L. Hammons <jlhamm@acm.org>
10 // --- ---------- -------------------------------------------------------------
11 // JLH 01/22/2010 Created this file
12 // JLH 02/06/2010 Modified to use Qt model/view framework
13 // JLH 03/08/2010 Added large cart view and info text
16 #include "filepicker.h"
20 #include "filelistmodel.h"
21 #include "filethread.h"
22 #include "imagedelegate.h"
23 //#include "settings.h"
27 Our strategy here is like so:
28 Look at the files in the directory pointed to by ROMPath.
29 For each file in the directory, take the CRC32 of it and compare it to the CRC
30 in the romList[]. If there's a match, put it in a list and note it's index value
31 in romList for future reference.
33 When constructing the list, use the index to pull up an image of the cart and
34 put that in the list. User picks from a graphical image of the cart.
36 Ideally, the label will go into the archive along with the ROM image, but that's
38 Maybe box art, screenshots will go as well...
40 I'm thinking compatibility info should be displayed as well... Just to stop the
41 inevitable stupid questions from people too lazy to do basic research for themselves.
46 - Should keep a QImage of the blank cart with blank label
47 - Should keep a QImage of the blank cart? (For overpainting the ROMs label)
48 - Should we have a special Alpine image? Floppy image (for COF/ABS)?
50 - Need some way of keeping track of cart size and compatibility info
51 [compat info needs to be BAD DUMP or % of what works]
52 - Need to properly scale the thumbnails images in the list
55 //could use Window as well...
56 //FilePickerWindow::FilePickerWindow(QWidget * parent/*= 0*/): QWidget(parent, Qt::Dialog)
57 FilePickerWindow::FilePickerWindow(QWidget * parent/*= 0*/): QWidget(parent, Qt::Window),
60 setWindowTitle(tr("Insert Cartridge..."));
62 //is there any reason why this must be cast as a QAbstractListModel? No
63 //Also, need to think about data structure for the model...
64 model = new FileListModel;
65 fileList = new QListView;
66 fileList->setModel(model);
67 // fileList->setItemDelegate(new ImageDelegate(this));
68 fileList->setItemDelegate(new ImageDelegate());
71 // fileList->setVerticalScrollBarPolicy(Qt::ScrollBarAlwaysOn);
72 // fileList->setSizePolicy(QSizePolicy::Fixed, QSizePolicy::Expanding);
73 //small problem with this is that it doesn't take scrollbar into account...
74 QSize sbSize = fileList->verticalScrollBar()->minimumSizeHint();
75 printf("VSB minimumSizeHint: %u, %u\n", sbSize.width(), sbSize.height());
76 QSize sbSize2 = fileList->verticalScrollBar()->sizeHint();
77 printf("VSB sizeHint: %u, %u\n", sbSize2.width(), sbSize2.height());
78 QRect sbRect = fileList->verticalScrollBar()->normalGeometry();
79 printf("VSB normalGeometry: %u, %u\n", sbRect.width(), sbRect.height());
80 QSize sbSize3 = fileList->verticalScrollBar()->size();
81 printf("VSB size: %u, %u\n", sbSize3.width(), sbSize3.height());
82 // int sbWidth = fileList->verticalScrollBar()->width();
83 int sbWidth = fileList->verticalScrollBar()->size().width();
84 fileList->setFixedWidth((488/4) + 4 + sbWidth);//ick
86 // This sets it to the "too large size" as the minimum!
87 QScrollBar * vsb = new QScrollBar(Qt::Vertical, this);
88 int sbWidth = vsb->size().width();
89 printf("VSB size width: %u\n", sbWidth);
90 int sbWidth2 = vsb->sizeHint().width();
91 printf("VSB sizeHint width: %u\n", sbWidth2);
94 // fileList->setSizePolicy(QSizePolicy::Minimum, QSizePolicy::Preferred);
95 // fileList->verticalScrollBar()->setSizePolicy(QSizePolicy::Minimum, QSizePolicy::Preferred);
96 fileList->setFixedWidth((488/4) + 4 + sbWidth);//ick
99 // QVBoxLayout * layout = new QVBoxLayout;
100 QHBoxLayout * layout = new QHBoxLayout;
102 layout->addWidget(fileList);
104 // Weird note: This layout has to be added *before* putting anything into it...
105 QVBoxLayout * vLayout = new QVBoxLayout;
106 layout->addLayout(vLayout);
108 cartImage = new QLabel;
109 QImage cartImg(":/res/cart-blank.png");
110 QPainter painter(&cartImg);
111 painter.drawPixmap(23, 87, QPixmap(":/res/label-blank.png"));
113 cartImage->setPixmap(QPixmap::fromImage(cartImg));
114 cartImage->setMargin(4);
115 vLayout->addWidget(cartImage);
117 title = new QLabel(QString(tr("<h2>...</h2>")));
119 title->setAlignment(Qt::AlignCenter);
120 vLayout->addWidget(title);
123 QHBoxLayout * dataLayout = new QHBoxLayout;
124 vLayout->addLayout(dataLayout);
126 QLabel * labels = new QLabel(QString(tr(
129 "<b>Compatibility: </b><br>"
132 labels->setAlignment(Qt::AlignRight);
133 labels->setSizePolicy(QSizePolicy::Minimum, QSizePolicy::Preferred);
134 dataLayout->addWidget(labels);
135 data = new QLabel(QString(tr(
141 data->setSizePolicy(QSizePolicy::Expanding, QSizePolicy::Preferred);
142 dataLayout->addWidget(data);
144 //#warning "!!! Icon size for pushbutton is tiny !!!"
145 insertCart = new QPushButton(this);
146 insertCart->setIconSize(QSize(40, 40));
147 insertCart->setIcon(QIcon(":/res/insert.png"));
148 insertCart->setDefault(true); // We want this button to be the default
149 insertCart->setSizePolicy(QSizePolicy::Minimum, QSizePolicy::Preferred);
150 dataLayout->addWidget(insertCart);
152 QLabel * text2 = new QLabel(QString(tr(
154 "<tr><td align='right'><b>Type: </b></td><td>4MB Cartridge</td></tr>"
155 "<tr><td align='right'><b>CRC32: </b></td><td>FEDCBA98</td></tr>"
156 "<tr><td align='right'><b>Compatibility: </b></td><td>DOES NOT WORK</td></tr>"
157 "<tr><td align='right'><b>Notes: </b></td><td>Universal Header detected; Requires DSP</td></tr>"
160 vLayout->addWidget(text2);
163 fileThread = new FileThread(this);
164 // connect(fileThread, SIGNAL(FoundAFile(unsigned long)), this, SLOT(AddFileToList(unsigned long)));
165 // connect(fileThread, SIGNAL(FoundAFile2(unsigned long, QString, QImage *, unsigned long)), this, SLOT(AddFileToList2(unsigned long, QString, QImage *, unsigned long)));
166 connect(fileThread, SIGNAL(FoundAFile3(unsigned long, QString, QImage *,
167 unsigned long, bool, unsigned long, unsigned long)), this,
168 SLOT(AddFileToList3(unsigned long, QString, QImage *, unsigned long,
169 bool, unsigned long, unsigned long)));
171 // Let's defer this to the main window, so we can have some control over when this is done.
174 New sizes: 373x172 (label), 420x340 (cart)
177 // QItemSelectionModel * ism = fileList->selectionModel();
178 // connect(ism, SIGNAL(currentChanged(const QModelIndex &, const QModelIndex &)), this, SLOT(UpdateSelection(const QModelIndex &, const QModelIndex &)));
179 connect(fileList->selectionModel(), SIGNAL(currentChanged(const QModelIndex &, const QModelIndex &)), this, SLOT(UpdateSelection(const QModelIndex &, const QModelIndex &)));
181 connect(insertCart, SIGNAL(clicked()), this, SLOT(LoadButtonPressed()));
184 QString FilePickerWindow::GetSelectedPrettyName(void)
186 return prettyFilename;
189 void FilePickerWindow::ScanSoftwareFolder(bool allow/*= false*/)
191 // "allow" is whether or not to allow scanning for unknown software.
193 fileThread->Go(allow);
197 // This slot gets called by the FileThread's run() function when it finds a
198 // match in the filesystem to a ROM on our CRC list.
200 void FilePickerWindow::AddFileToList(unsigned long index)
202 printf("FilePickerWindow: Found match [%s]...\n", romList[index].name);
203 // NOTE: The model *ignores* what you send it, so this is crap. !!! FIX !!! [DONE, somewhat]
204 // model->AddData(QIcon(":/res/generic.png"));
205 // model->AddData(index);
208 void FilePickerWindow::AddFileToList2(unsigned long index, QString str, QImage * img, unsigned long size)
210 if (index != 0xFFFFFFFF)
211 printf("FilePickerWindow(2): Found match [%s]...\n", romList[index].name);
215 model->AddData(index, str, *img, size);
216 //It would be better to pass the pointer into the model though...
220 model->AddData(index, str, QImage(), size);
223 void FilePickerWindow::AddFileToList3(unsigned long index, QString str, QImage * img, unsigned long size, bool haveUniversalHeader, unsigned long fileType, unsigned long crc)
225 if (index != 0xFFFFFFFF)
226 printf("FilePickerWindow(3): Found match [%s]...\n", romList[index].name);
230 model->AddData(index, str, *img, size, haveUniversalHeader, fileType, crc);
231 //It would be better to pass the pointer into the model though...
235 model->AddData(index, str, QImage(), size, haveUniversalHeader, fileType, crc);
238 void FilePickerWindow::LoadButtonPressed(void)
240 // TODO: Get the text of the current selection, call the MainWin slot for loading
241 emit(RequestLoad(currentFile));
246 // This slot gets called when the QListView gets clicked on. Updates
247 // the cart graphic and accompanying text.
249 void FilePickerWindow::UpdateSelection(const QModelIndex & current, const QModelIndex &/*previous*/)
252 QString s = current.model()->data(current, Qt::EditRole).toString();
253 unsigned long i = current.model()->data(current, Qt::DisplayRole).toUInt();
254 QImage label = current.model()->data(current, Qt::DecorationRole).value<QImage>();
255 // printf("FPW: %s\n", s.toAscii().data());
256 unsigned long fileSize = current.model()->data(current, Qt::WhatsThisRole).toUInt();
258 // QString s = current.model()->data(current, FLM_FILENAME).toString();
259 currentFile = current.model()->data(current, FLM_FILENAME).toString();
260 unsigned long i = current.model()->data(current, FLM_INDEX).toUInt();
261 QImage label = current.model()->data(current, FLM_LABEL).value<QImage>();
262 unsigned long fileSize = current.model()->data(current, FLM_FILESIZE).toUInt();
263 bool haveUniversalHeader = current.model()->data(current, FLM_UNIVERSALHDR).toBool();
264 unsigned long fileType = current.model()->data(current, FLM_FILETYPE).toUInt();
265 uint32 crc = (uint32)current.model()->data(current, FLM_CRC).toUInt();
266 // printf("FPW: %s\n", s.toAscii().data());
267 bool haveUnknown = (i == 0xFFFFFFFF ? true : false);
270 // Disallow loading completely unknown files, but allow all others.
271 insertCart->setEnabled(haveUnknown && (fileType == JST_NONE) ? false : true);
275 //373x172 is label size...
280 QImage cartImg(":/res/cart-blank.png");
281 QPainter painter(&cartImg);
282 painter.drawPixmap(23, 87, QPixmap(":/res/label-blank.png"));
284 cartSmall = cartImg.scaled(488/4, 395/4, Qt::IgnoreAspectRatio, Qt::SmoothTransformation);
286 QImage cart(":/res/cart-blank.png");
287 QPainter painter(&cart);
288 //Though this should probably be done when this is loaded, instead of every time here...
289 //QImage scaledImg = label.scaled(373, 172, Qt::IgnoreAspectRatio, Qt::SmoothTransformation);
290 //painter.drawPixmap(23, 87, QPixmap::fromImage(scaledImg));
291 // Now, looks like it is...
292 // painter.drawPixmap(23, 87, QPixmap::fromImage(label));
293 painter.drawPixmap(27, 89, QPixmap::fromImage(label));
294 // painter.drawPixmap(23, 87, 373, 172, QPixmap::fromImage(label));
296 // Well, heck. This should be done to the label *before* we get here.
297 painter.drawPixmap(27, 89, QPixmap::fromImage(QImage(":/res/upper-left.png")));
298 painter.drawPixmap(27+355, 89, QPixmap::fromImage(QImage(":/res/upper-right.png")));
301 cartImage->setPixmap(QPixmap::fromImage(cart));
305 // We should try to be intelligent with our updates here, and only redraw when
306 // we're going from a selection with a label to a selection without. Now, we
307 // redraw regardless.
310 // We now have to sources of data for the passed in files:
312 // - The file type detection
313 // This means we have to be mindful of what's passed back by that stuff.
314 // We can assume that if it wasn't found in the DB, then the fileType
316 // The DB takes precedence over the fileType.
317 if ((!haveUnknown && (romList[i].flags & FF_ROM))
318 || (haveUnknown && (fileType == JST_ROM)))
320 cart = QImage(":/res/cart-blank.png");
321 QPainter painter(&cart);
322 painter.drawPixmap(27, 89, QPixmap::fromImage(QImage(":/res/label-blank.png")));
325 else if ((!haveUnknown && (romList[i].flags & FF_ALPINE))
326 || (haveUnknown && (fileType == JST_ALPINE)))
328 cart = QImage(":/res/alpine-file.png");
330 else if (haveUnknown && (fileType == JST_ABS_TYPE1 || fileType == JST_ABS_TYPE2 || fileType == JST_JAGSERVER))
332 cart = QImage(":/res/homebrew-file.png");
335 cart = QImage(":/res/unknown-file.png");
337 cartImage->setPixmap(QPixmap::fromImage(cart));
344 prettyFilename = romList[i].name;
347 int lastSlashPos = currentFile.lastIndexOf('/');
348 prettyFilename = "\"" + currentFile.mid(lastSlashPos + 1) + "\"";
351 title->setText(QString("<h2>%1</h2>").arg(prettyFilename));
353 //Kludge for now, we'll have to fix this later...
354 // So let's fix it now!
355 QString fileTypeString, crcString, notes, compatibility;
360 if (romList[i].flags & FF_ROM)
361 fileTypeString = QString(tr("%1MB Cartridge")).arg(fileSize / 1048576);
362 else if (romList[i].flags & FF_ALPINE)
363 fileTypeString = QString(tr("%1MB Alpine ROM")).arg(fileSize / 1048576);
365 fileTypeString = QString(tr("*** UNKNOWN *** (%1 bytes)")).arg(fileSize);
368 if ((!haveUnknown && (romList[i].flags & FF_ROM)) || (haveUnknown && (fileType == JST_ROM)))
369 fileTypeString = QString(tr("%1MB Cartridge")).arg(fileSize / 1048576);
370 else if ((!haveUnknown && (romList[i].flags & FF_ALPINE)) || (haveUnknown && (fileType == JST_ALPINE)))
371 fileTypeString = QString(tr("%1MB Alpine ROM")).arg(fileSize / 1048576);
372 else if (haveUnknown && (fileType == JST_ABS_TYPE1 || fileType == JST_ABS_TYPE2))
373 fileTypeString = QString(tr("ABS/COF Executable (%1 bytes)")).arg(fileSize);
374 else if (haveUnknown && (fileType == JST_JAGSERVER))
375 fileTypeString = QString(tr("Jaguar Server Executable (%1 bytes)")).arg(fileSize);
377 fileTypeString = QString(tr("*** UNKNOWN *** (%1 bytes)")).arg(fileSize);
380 // crcString = QString("%1").arg(romList[i].crc32, 8, 16, QChar('0')).toUpper();
381 crcString = QString("%1").arg(crc, 8, 16, QChar('0')).toUpper();
383 if (!haveUnknown && (romList[i].flags & FF_NON_WORKING))
384 compatibility = "DOES NOT WORK";
386 compatibility = "Unknown";
388 // This is going to need some formatting love before long...
389 if (!haveUnknown && (romList[i].flags & FF_BAD_DUMP))
390 notes = "<b>BAD DUMP</b>";
392 if (haveUniversalHeader)
393 notes += " Universal Header detected";
395 if (!haveUnknown && (romList[i].flags & FF_REQ_DSP))
396 notes += " Requires DSP";
398 if (!haveUnknown && (romList[i].flags & FF_VERIFIED))
399 notes += " <i>(Verified)</i>";
401 data->setText(QString("%1<br>%2<br>%3<br>%4")
402 .arg(fileTypeString).arg(crcString).arg(compatibility).arg(notes));
406 Super Duper Awesome Guy (World)
410 Compatibility: DOES NOT WORK
411 Notes: Universal Header detected; Requires DSP
414 Stupid Homebrew Game That Sux
416 Type: ABS/COF Executable (43853 bytes)
418 Compatibility: Unknown
419 Notes: $4000 Load, $4000 Run
422 Action Hopscotch Plus (Prototype)
426 Compatibility: 80% (or ****)
427 Notes: EEPROM available