1 #ifndef ROMALYZER_H
2 #define ROMALYZER_H
3 
4 #include <QtGui>
5 #include <QtXml>
6 #include <QThread>
7 #include <QMutex>
8 #include <QWaitCondition>
9 #include <QTime>
10 #include <QTimer>
11 #include <QMap>
12 #include <QHash>
13 #include <QPixmap>
14 #include <QStringList>
15 
16 #include "macros.h"
17 #include "checksumdbmgr.h"
18 #include "checksumscannerlog.h"
19 #include "collectionrebuilder.h"
20 #include "rompathcleaner.h"
21 #include "ui_romalyzer.h"
22 
23 #define QMC2_ROMALYZER_PAGE_REPORT		0
24 #define QMC2_ROMALYZER_PAGE_LOG			1
25 #define QMC2_ROMALYZER_PAGE_SETTINGS		2
26 #define QMC2_ROMALYZER_PAGE_CSF			3 // CSF: Check-sum finder
27 #if defined(QMC2_WIP_ENABLED) // FIXME: WIP
28 #define QMC2_ROMALYZER_PAGE_RPC			4 // RPC: ROM path cleaner
29 #define QMC2_ROMALYZER_PAGE_RCR			5 // RCR: ROM collection rebuilder
30 #else
31 #define QMC2_ROMALYZER_PAGE_RCR			4
32 #endif
33 
34 #define QMC2_ROMALYZER_COLUMN_SET		0
35 #define QMC2_ROMALYZER_COLUMN_MERGE		1
36 #define QMC2_ROMALYZER_COLUMN_TYPE		2
37 #define QMC2_ROMALYZER_COLUMN_EMUSTATUS		3
38 #define QMC2_ROMALYZER_COLUMN_FILESTATUS	4
39 #define QMC2_ROMALYZER_COLUMN_SIZE		5
40 #define QMC2_ROMALYZER_COLUMN_CRC		6
41 #define QMC2_ROMALYZER_COLUMN_SHA1		7
42 #define QMC2_ROMALYZER_COLUMN_MD5		8
43 
44 #define QMC2_ROMALYZER_CSF_COLUMN_ID		0
45 #define QMC2_ROMALYZER_CSF_COLUMN_FILENAME	1
46 #define QMC2_ROMALYZER_CSF_COLUMN_STATUS	2
47 #define QMC2_ROMALYZER_CSF_COLUMN_TYPE		3
48 #define QMC2_ROMALYZER_CSF_COLUMN_PATH		4
49 
50 #define QMC2_ROMALYZER_CSF_HASHTYPE_SHA1	0
51 #define QMC2_ROMALYZER_CSF_HASHTYPE_CRC		1
52 
53 #define QMC2_ROMALYZER_MERGE_STATUS_OK		0
54 #define QMC2_ROMALYZER_MERGE_STATUS_WARN	1
55 #define QMC2_ROMALYZER_MERGE_STATUS_CRIT	2
56 
57 #if defined(QMC2_LIBARCHIVE_ENABLED)
58 #define QMC2_ROMALYZER_RT_ZIP_BUILTIN		0
59 #define QMC2_ROMALYZER_RT_ZIP_LIBARCHIVE	1
60 #define QMC2_ROMALYZER_RT_FOLDERS		2
61 #else
62 #define QMC2_ROMALYZER_RT_ZIP_BUILTIN		0
63 #define QMC2_ROMALYZER_RT_FOLDERS		1
64 #endif
65 
66 #define QMC2_ROMALYZER_EMUSTATUS_GOOD		0x00000001
67 #define QMC2_ROMALYZER_EMUSTATUS_NODUMP		0x00000020
68 #define QMC2_ROMALYZER_EMUSTATUS_BADDUMP	0x00000400
69 #define QMC2_ROMALYZER_EMUSTATUS_UNKNOWN	0x00008000
70 
71 #define QMC2_ROMALYZER_PAUSE_TIMEOUT		250
72 #define QMC2_ROMALYZER_FLASH_TIME		100
73 #define QMC2_ROMALYZER_XMLPOSCACHE_SIZE		1000
74 #define QMC2_ROMALYZER_SEARCH_RESPONSE		5000
75 #define QMC2_ROMALYZER_EXPORT_RESPONSE		10
76 #define QMC2_ROMALYZER_CKSUM_SEARCH_RESPONSE	500
77 
78 #define QMC2_ROMALYZER_FILE_TOO_BIG		"QMC2_FILE_TOO_BIG"
79 #define QMC2_ROMALYZER_FILE_ERROR		"QMC2_FILE_ERROR"
80 #define QMC2_ROMALYZER_FILE_NOT_SUPPORTED	"QMC2_FILE_NOT_SUPPORTED"
81 #define QMC2_ROMALYZER_FILE_NOT_FOUND		"QMC2_FILE_NOT_FOUND"
82 #define QMC2_ROMALYZER_NO_DUMP			"QMC2_NO_DUMP"
83 
84 #define QMC2_ROMALYZER_ZIP_BUFFER_SIZE		QMC2_ZIP_BUFFER_SIZE
85 #define QMC2_ROMALYZER_FILE_BUFFER_SIZE		QMC2_FILE_BUFFER_SIZE
86 #define QMC2_ROMALYZER_PROGRESS_THRESHOLD	QMC2_ONE_MEGABYTE
87 
88 #define QMC2_CHD_HEADER_TAG_OFFSET		0
89 #define QMC2_CHD_HEADER_TAG_LENGTH		8
90 #define QMC2_CHD_HEADER_VERSION_OFFSET		12
91 #define QMC2_CHD_HEADER_FLAGS_OFFSET		16
92 #define QMC2_CHD_HEADER_FLAG_HASPARENT		0x00000001
93 #define QMC2_CHD_HEADER_FLAG_ALLOWSWRITES	0x00000002
94 #define QMC2_CHD_HEADER_COMPRESSION_OFFSET	20
95 #define QMC2_CHD_HEADER_COMPRESSION_NONE	0
96 #define QMC2_CHD_HEADER_COMPRESSION_ZLIB	1
97 #define QMC2_CHD_HEADER_COMPRESSION_ZLIB_PLUS	2
98 #define QMC2_CHD_HEADER_COMPRESSION_AV		3
99 
100 #define QMC2_CHD_HEADER_V3_TAG_OFFSET		QMC2_CHD_HEADER_TAG_OFFSET
101 #define QMC2_CHD_HEADER_V3_TAG_LENGTH		QMC2_CHD_HEADER_TAG_LENGTH
102 #define QMC2_CHD_HEADER_V3_VERSION_OFFSET	QMC2_CHD_HEADER_VERSION_OFFSET
103 #define QMC2_CHD_HEADER_V3_TOTALHUNKS_OFFSET	24
104 #define QMC2_CHD_HEADER_V3_LOGICALBYTES_OFFSET	28
105 #define QMC2_CHD_HEADER_V3_MD5_OFFSET		44
106 #define QMC2_CHD_HEADER_V3_MD5_LENGTH		16
107 #define QMC2_CHD_HEADER_V3_PARENTMD5_OFFSET	60
108 #define QMC2_CHD_HEADER_V3_PARENTMD5_LENGTH	16
109 #define QMC2_CHD_HEADER_V3_HUNKBYTES_OFFSET	76
110 #define QMC2_CHD_HEADER_V3_SHA1_OFFSET		80
111 #define QMC2_CHD_HEADER_V3_SHA1_LENGTH		20
112 #define QMC2_CHD_HEADER_V3_PARENTSHA1_OFFSET	100
113 #define QMC2_CHD_HEADER_V3_PARENTSHA1_LENGTH	20
114 #define QMC2_CHD_HEADER_V3_LENGTH		120
115 
116 #define QMC2_CHD_HEADER_V4_TAG_OFFSET		QMC2_CHD_HEADER_TAG_OFFSET
117 #define QMC2_CHD_HEADER_V4_TAG_LENGTH		QMC2_CHD_HEADER_TAG_LENGTH
118 #define QMC2_CHD_HEADER_V4_VERSION_OFFSET	QMC2_CHD_HEADER_VERSION_OFFSET
119 #define QMC2_CHD_HEADER_V4_TOTALHUNKS_OFFSET	24
120 #define QMC2_CHD_HEADER_V4_LOGICALBYTES_OFFSET	28
121 #define QMC2_CHD_HEADER_V4_HUNKBYTES_OFFSET	44
122 #define QMC2_CHD_HEADER_V4_SHA1_OFFSET		48
123 #define QMC2_CHD_HEADER_V4_SHA1_LENGTH		20
124 #define QMC2_CHD_HEADER_V4_PARENTSHA1_OFFSET	68
125 #define QMC2_CHD_HEADER_V4_PARENTSHA1_LENGTH	20
126 #define QMC2_CHD_HEADER_V4_RAWSHA1_OFFSET	88
127 #define QMC2_CHD_HEADER_V4_RAWSHA1_LENGTH	20
128 #define QMC2_CHD_HEADER_V4_LENGTH		108
129 
130 #define QMC2_CHD_HEADER_V5_TAG_OFFSET		QMC2_CHD_HEADER_TAG_OFFSET
131 #define QMC2_CHD_HEADER_V5_TAG_LENGTH		QMC2_CHD_HEADER_TAG_LENGTH
132 #define QMC2_CHD_HEADER_V5_VERSION_OFFSET	QMC2_CHD_HEADER_VERSION_OFFSET
133 #define QMC2_CHD_HEADER_V5_COMPRESSORS_OFFSET	16
134 #define QMC2_CHD_HEADER_V5_COMPRESSORS_COUNT	4	// 4 x UINT32
135 #define QMC2_CHD_HEADER_V5_LOGICALBYTES_OFFSET	32
136 #define QMC2_CHD_HEADER_V5_HUNKBYTES_OFFSET	56
137 #define QMC2_CHD_HEADER_V5_UNITBYTES_OFFSET	60
138 #define QMC2_CHD_HEADER_V5_RAWSHA1_OFFSET	64
139 #define QMC2_CHD_HEADER_V5_RAWSHA1_LENGTH	20
140 #define QMC2_CHD_HEADER_V5_SHA1_OFFSET		84
141 #define QMC2_CHD_HEADER_V5_SHA1_LENGTH		20
142 #define QMC2_CHD_HEADER_V5_PARENTSHA1_OFFSET	104
143 #define QMC2_CHD_HEADER_V5_PARENTSHA1_LENGTH	20
144 #define QMC2_CHD_HEADER_V5_LENGTH		124
145 
146 #define QMC2_CHD_CURRENT_VERSION		5
147 #define QMC2_CHD_CHECK_NULL_SHA1(ba)		((ba).startsWith(QByteArray("00000000000000000000")))
148 
149 #define crcToString(crc)			QString::number((crc), 16).rightJustified(8, '0')
150 
151 #define QMC2_CHECKSUM_SCANNER_FILE_UNKNOWN	-2
152 #define QMC2_CHECKSUM_SCANNER_FILE_NO_ACCESS	-1
153 #define QMC2_CHECKSUM_SCANNER_FILE_ZIP		0
154 #define QMC2_CHECKSUM_SCANNER_FILE_7Z		1
155 #define QMC2_CHECKSUM_SCANNER_FILE_CHD		2
156 #define QMC2_CHECKSUM_SCANNER_FILE_REGULAR	3
157 #define QMC2_CHECKSUM_SCANNER_FILE_ARCHIVE	4
158 
159 #define QMC2_CHECKSUM_SCANNER_MAX_QUEUED_MSGS	50
160 
161 #define QMC2_CHECKSUM_DB_QUERY_STATUS_UNKNOWN	-1
162 #define QMC2_CHECKSUM_DB_QUERY_STATUS_GOOD	0
163 #define QMC2_CHECKSUM_DB_QUERY_STATUS_BAD	1
164 
165 #define QMC2_ROMALYZER_REBUILD_ANIM_SPEED	500
166 
167 class CheckSumScannerThread : public QThread
168 {
169 	Q_OBJECT
170 
171 	public:
172 		bool exitThread;
173 		bool stopScan;
174 		bool isActive;
175 		bool isWaiting;
176 		bool isPaused;
177 		bool pauseRequested;
178 		bool scanIncrementally;
179 		bool deepScan;
180 		bool useHashCache;
181 #if defined(QMC2_LIBARCHIVE_ENABLED)
182 		bool useLibArchive;
183 #endif
184 		QMutex mutex;
185 		QMutex logSyncMutex;
186 		QWaitCondition waitCondition;
187 		QStringList scannedPaths;
188 		QTime scanTimer;
189 
190 		CheckSumScannerThread(CheckSumScannerLog *scannerLog, QString settingsKey, QObject *parent = 0);
191 		~CheckSumScannerThread();
192 
checkSumDb()193 		CheckSumDatabaseManager *checkSumDb() { return m_checkSumDb; }
pendingUpdates()194 		quint64 pendingUpdates() { return m_pendingUpdates; }
195 		QString status();
196 		void reopenCheckSumDb();
197 		int fileType(QString, bool &, bool &);
198 		void prepareIncrementalScan(QStringList *fileList);
199 		QString scanTime();
200 		void emitlog(QString);
201 		void flushMessageQueue();
202 
203 	public slots:
204 		void pause();
205 		void resume();
206 
207 	signals:
208 		void log(const QString &);
209 		void scanStarted();
210 		void scanFinished();
211 		void scanPaused();
212 		void scanResumed();
213 		void progressTextChanged(const QString &);
214 		void progressRangeChanged(int, int);
215 		void progressChanged(int);
216 
217 	protected:
218 		void run();
219 
220 	private:
221 		CheckSumDatabaseManager *m_checkSumDb;
222 		CheckSumScannerLog *m_scannerLog;
223 		quint64 m_pendingUpdates;
224 		bool m_preparingIncrementalScan;
225 		QString m_settingsKey;
226 		QHash<QString, bool> m_hashCache;
227 		QStringList m_queuedMessages;
228 		bool checkSumExists(QString sha1, QString crc, quint64 size = 0);
229 		void recursiveFileList(const QString &, QStringList *);
230 		bool scanZip(QString, QStringList *, QList<quint64> *, QStringList *, QStringList *);
231 		bool scanSevenZip(QString, QStringList *, QList<quint64> *, QStringList *, QStringList *);
232 #if defined(QMC2_LIBARCHIVE_ENABLED)
233 		bool scanArchive(QString, QStringList *, QList<quint64> *, QStringList *, QStringList *);
234 #endif
235 		bool scanChd(QString, quint64 *, QString *);
236 		bool scanRegularFile(QString, quint64 *, QString *, QString *);
237 };
238 
239 class ROMAlyzerXmlHandler : public QXmlDefaultHandler
240 {
241 	public:
242 		QString currentText;
243 		QTreeWidgetItem *parentItem;
244 		QTreeWidgetItem *childItem;
245 		QList<QTreeWidgetItem *> childItems;
246 		QStringList deviceReferences;
247 		QStringList optionalROMs;
248 		bool autoExpand;
249 		bool autoScroll;
250 		int emuStatus;
251 		int fileCounter;
252 		int romalyzerMode;
253 		QBrush redBrush;
254 		QBrush greenBrush;
255 		QBrush blueBrush;
256 		QBrush yellowBrush;
257 		QBrush brownBrush;
258 		QBrush greyBrush;
259 
260 		ROMAlyzerXmlHandler(QTreeWidgetItem *, bool expand = false, bool scroll = false, int mode = QMC2_ROMALYZER_MODE_SYSTEM);
261 
262 		bool startElement(const QString &, const QString &, const QString &, const QXmlAttributes &);
263 		bool endElement(const QString &, const QString &, const QString &);
264 		bool characters(const QString &);
265 };
266 
267 class ROMAlyzer : public QDialog, public Ui::ROMAlyzer
268 {
269 	Q_OBJECT
270 
271 	public:
272 		QTimer animTimer;
273 		QTimer checkSumDbStatusTimer;
274 		QTime miscTimer;
275 		int animSeq;
276 		QStringList romPaths;
277 		QStringList chdCompressionTypes;
278 		QMap<QString, QString> chdCompressionTypesV5;
279 		bool chdManagerRunning;
280 		bool chdManagerMD5Success;
281 		bool chdManagerSHA1Success;
282 		quint64 chdManagerCurrentHunk;
283 		quint64 chdManagerTotalHunks;
284 		QMenu *romFileContextMenu;
285 		QMenu *romSetContextMenu;
286 		QMenu *toolsMenu;
287 		QAction *actionRewriteSet;
288 		QAction *actionAnalyzeDeviceRefs;
289 		QAction *actionImportFromDataFile;
290 		QAction *actionExportToDataFile;
291 		QAction *actionCopyBadToClipboard;
292 		QString currentFilesSHA1Checksum;
293 		QString currentFilesCrcChecksum;
294 		quint64 currentFilesSize;
295 		QStringList wizardSelectedSets;
296 		QStringList analyzerBadSets;
297 		QMultiMap<QString, QStringList> setRewriterFileMap;
298 		QString setRewriterSetName;
299 		QTreeWidgetItem *setRewriterItem;
300 		int setRewriterSetCount;
301 		bool wizardSearch;
302 		bool quickSearch;
303 		qint64 lastRowCount;
304 
305 		ROMAlyzer(QWidget *, int romalyzerMode = QMC2_ROMALYZER_MODE_SYSTEM);
306 		~ROMAlyzer();
307 
saveState()308 		void saveState() { closeEvent(0); }
309 		bool readSevenZipFileData(QString, QString, QString, QByteArray *);
310 		bool readZipFileData(QString, QString, QString, QByteArray *);
311 		bool readFileData(QString, QString, QByteArray *);
312 		bool writeAllZipData(QString, QMap<QString, QByteArray> *, bool writeLog = false, QProgressBar *pBar = 0);
313 #if defined(QMC2_LIBARCHIVE_ENABLED)
314 		bool writeAllArchiveData(QString, QMap<QString, QByteArray> *, bool writeLog = false, QProgressBar *pBar = 0);
315 #endif
316 		bool writeAllFileData(QString, QMap<QString, QByteArray> *, bool writeLog = false, QProgressBar *pBar = 0);
317 		static QString humanReadable(quint64, int digits = 2);
318 		static QString &getXmlData(QString, bool includeDTD = false);
319 		static QString &getSoftwareXmlData(QString, QString, bool includeDTD = false);
320 		QString &getEffectiveFile(QTreeWidgetItem *, QString, QString, QString, QString, QString, QString, QString, QByteArray *, QString *, QString *, bool *, bool *, bool *, int, QString *, bool, bool *);
321 		bool createBackup(QString filePath);
checkSumDb()322 		CheckSumDatabaseManager *checkSumDb() { return m_checkSumDb; }
checkSumScannerLog()323 		CheckSumScannerLog *checkSumScannerLog() { return m_checkSumScannerLog; }
checkSumScannerThread()324 		CheckSumScannerThread *checkSumScannerThread() { return m_checkSumScannerThread; }
collectionRebuilder()325 		CollectionRebuilder *collectionRebuilder() { return m_collectionRebuilder; }
romPathCleaner()326 		RomPathCleaner *romPathCleaner() { return m_romPathCleaner; }
mode()327 		int mode() { return m_currentMode; }
328 		void setMode(int mode);
active()329 		bool active() { return m_active; }
setActive(bool active)330 		void setActive(bool active) { m_active = active; }
paused()331 		bool paused() { return m_paused; }
setPaused(bool paused)332 		void setPaused(bool paused) { m_paused = paused; }
settingsKey()333 		QString settingsKey() { return m_settingsKey; }
rebuilderActive()334 		bool rebuilderActive() { return collectionRebuilder() ? collectionRebuilder()->active() : false; }
335 
336 	public slots:
337 		// callback functions
338 		void on_pushButtonAnalyze_clicked();
339 		void on_pushButtonPause_clicked();
340 		void on_pushButtonClose_clicked();
341 		void on_pushButtonSearchForward_clicked();
342 		void on_pushButtonSearchBackward_clicked();
343 		void on_lineEditSoftwareLists_textChanged(QString);
344 		void on_lineEditSets_textChanged(QString);
345 		void on_treeWidgetChecksums_itemSelectionChanged();
346 		void on_spinBoxMaxLogSize_valueChanged(int);
347 		void on_toolButtonBrowseBackupFolder_clicked();
348 		void on_toolButtonBrowseCHDManagerExecutableFile_clicked();
349 		void on_toolButtonBrowseTemporaryWorkingDirectory_clicked();
350 		void on_toolButtonBrowseSetRewriterOutputPath_clicked();
351 		void on_toolButtonBrowseSetRewriterAdditionalRomPath_clicked();
352 		void on_toolButtonSaveLog_clicked();
353 		void on_checkBoxCalculateCRC_toggled(bool);
354 		void on_checkBoxCalculateMD5_toggled(bool);
355 		void on_checkBoxCalculateSHA1_toggled(bool);
356 		void on_comboBoxChecksumWizardHashType_currentIndexChanged(int);
357 		void on_lineEditChecksumWizardHash_textChanged(const QString &);
358 		void lineEditChecksumWizardHash_textChanged_delayed();
359 		void on_pushButtonChecksumWizardSearch_clicked();
360 		void on_treeWidgetChecksums_customContextMenuRequested(const QPoint &);
361 		void on_treeWidgetChecksumWizardSearchResult_itemSelectionChanged();
362 		void on_pushButtonChecksumWizardAnalyzeSelectedSets_clicked();
363 		void on_tabWidgetAnalysis_currentChanged(int);
364 		void on_toolButtonCheckSumDbAddPath_clicked();
365 		void on_toolButtonCheckSumDbRemovePath_clicked();
366 		void on_lineEditCheckSumDbDatabasePath_textChanged(const QString &);
367 		void on_toolButtonBrowseCheckSumDbDatabasePath_clicked();
368 		void on_toolButtonCheckSumDbViewLog_clicked();
369 		void on_pushButtonCheckSumDbScan_clicked();
370 		void on_pushButtonCheckSumDbPauseResumeScan_clicked();
371 		void on_listWidgetCheckSumDbScannedPaths_customContextMenuRequested(const QPoint &);
372 		void on_listWidgetCheckSumDbScannedPaths_itemSelectionChanged();
373 		void checkSumScannerLog_windowClosed();
374 		void checkSumScannerLog_windowOpened();
375 		void checkSumScannerThread_scanStarted();
376 		void checkSumScannerThread_scanFinished();
377 		void checkSumScannerThread_scanPaused();
378 		void checkSumScannerThread_scanResumed();
379 		void switchToCollectionRebuilder();
380 
381 		// miscellaneous slots
382 		void animationTimeout();
383 		void analyze();
384 		void selectItem(QString);
enableSearchEdit()385 		void enableSearchEdit() { lineEditSearchString->setEnabled(true); }
386 		void adjustIconSizes();
387 		void runChecksumWizard();
388 		void runSetRewriter();
389 		void copyToClipboard(bool onlyBadOrMissing = false);
390 		void copyBadToClipboard();
391 		void analyzeDeviceRefs();
392 		void importFromDataFile();
393 		void exportToDataFile();
394 		void updateCheckSumDbStatus();
395 		void log(const QString &);
396 		void indicateCheckSumDbQueryStatusGood();
397 		void indicateCheckSumDbQueryStatusBad();
398 		void indicateCheckSumDbQueryStatusUnknown();
399 		void softwareListLoadFinished(bool);
400 		void on_groupBoxSetRewriter_toggled(bool);
401 		void on_groupBoxCheckSumDatabase_toggled(bool);
402 
403 		// CHD manager process control
404 		void chdManagerStarted();
405 		void chdManagerFinished(int, QProcess::ExitStatus);
406 		void chdManagerReadyReadStandardOutput();
407 		void chdManagerReadyReadStandardError();
408 		void chdManagerError(QProcess::ProcessError);
409 
410 	protected:
411 		void closeEvent(QCloseEvent *);
412 		void showEvent(QShowEvent *);
413 		void hideEvent(QHideEvent *);
414 		void moveEvent(QMoveEvent *);
415 		void resizeEvent(QResizeEvent *);
416 
417 	private:
418 		CheckSumDatabaseManager *m_checkSumDb;
419 		CheckSumScannerLog *m_checkSumScannerLog;
420 		CheckSumScannerThread *m_checkSumScannerThread;
421 		QPixmap m_checkSumDbQueryStatusPixmap;
422 		QTimer m_checkSumTextChangedTimer;
423 		CollectionRebuilder *m_collectionRebuilder;
424 		RomPathCleaner *m_romPathCleaner;
425 		int m_currentMode;
426 		bool m_active;
427 		bool m_paused;
428 		QString m_settingsKey;
429 };
430 
431 #endif
432