static __forceinline void doValidateHash(HANDLE &fileHandle, const int &fileDescriptor, const QByteArray &expectedHash, const QString &filePath) { QFile checkFile; //Now re-open the file for reading if(g_useFileDescrForQFile) { checkFile.open(fileDescriptor, QIODevice::ReadOnly); } else { checkFile.setFileName(filePath); for(int i = 0; i < 64; i++) { if(checkFile.open(QIODevice::ReadOnly)) break; if(!i) qWarning("Failed to re-open file on first attemp, retrying..."); Sleep(100); } } //Opened successfully if((!checkFile.isOpen()) || checkFile.peek(1).isEmpty()) { QFile::remove(filePath); MUTILS_THROW_FMT("File '%s' could not be read!", MUTILS_UTF8(QFileInfo(filePath).fileName())); } //Verify file contents const QByteArray hash = FileHash::computeHash(checkFile); checkFile.close(); //Compare hashes if(hash.isNull() || _stricmp(hash.constData(), expectedHash.constData())) { qWarning("\nFile checksum error:\n A = %s\n B = %s\n", expectedHash.constData(), hash.constData()); CLOSE_HANDLE(fileHandle); QFile::remove(filePath); MUTILS_THROW_FMT("File '%s' is corruputed, take care!", MUTILS_UTF8(QFileInfo(filePath).fileName())); } }
int CueSheetModel::parseCueFile(QFile &cueFile, const QDir &baseDir, QCoreApplication *application, const QTextCodec *codec) { cueFile.reset(); qDebug("\n[Cue Sheet Import]"); bool bForceLatin1 = false; //Reject very large files, as parsing might take until forever if(cueFile.size() >= 10485760i64) { qWarning("File is very big. Probably not a Cue Sheet. Rejecting..."); return 2; } //Test selected Codepage for decoding errors qDebug("Character encoding is: %s.", codec->name().constData()); const QString replacementSymbol = QString(QChar(QChar::ReplacementCharacter)); QByteArray testData = cueFile.peek(1048576); if((!testData.isEmpty()) && codec->toUnicode(testData.constData(), testData.size()).contains(replacementSymbol)) { qWarning("Decoding error using selected codepage (%s). Enforcing Latin-1.", codec->name().constData()); bForceLatin1 = true; } testData.clear(); //Init text stream QTextStream cueStream(&cueFile); cueStream.setAutoDetectUnicode(false); cueStream.setCodec(bForceLatin1 ? "latin1" : codec->name()); cueStream.seek(0i64); //Create regular expressions QRegExp rxFile("^FILE\\s+(\"[^\"]+\"|\\S+)\\s+(\\w+)$", Qt::CaseInsensitive); QRegExp rxTrack("^TRACK\\s+(\\d+)\\s(\\w+)$", Qt::CaseInsensitive); QRegExp rxIndex("^INDEX\\s+(\\d+)\\s+([0-9:]+)$", Qt::CaseInsensitive); QRegExp rxTitle("^TITLE\\s+(\"[^\"]+\"|\\S+)$", Qt::CaseInsensitive); QRegExp rxPerformer("^PERFORMER\\s+(\"[^\"]+\"|\\S+)$", Qt::CaseInsensitive); QRegExp rxGenre("^REM\\s+GENRE\\s+(\"[^\"]+\"|\\S+)$", Qt::CaseInsensitive); QRegExp rxYear("^REM\\s+DATE\\s+(\\d+)$", Qt::CaseInsensitive); bool bPreamble = true; bool bUnsupportedTrack = false; CueSheetFile *currentFile = NULL; CueSheetTrack *currentTrack = NULL; m_albumTitle.clear(); m_albumPerformer.clear(); m_albumGenre.clear(); m_albumYear = 0; //Loop over the Cue Sheet until all lines were processed for(int lines = 0; lines < INT_MAX; lines++) { if(application) { application->processEvents(); if(lines < 128) Sleep(10); } if(cueStream.atEnd()) { qDebug("End of Cue Sheet file."); break; } QString line = cueStream.readLine().trimmed(); /* --- FILE --- */ if(rxFile.indexIn(line) >= 0) { qDebug("%03d File: <%s> <%s>", lines, rxFile.cap(1).toUtf8().constData(), rxFile.cap(2).toUtf8().constData()); if(currentFile) { if(currentTrack) { if(currentTrack->isValid()) { currentFile->addTrack(currentTrack); currentTrack = NULL; } else { LAMEXP_DELETE(currentTrack); } } if(currentFile->isValid()) { m_files.append(currentFile); currentFile = NULL; } else { LAMEXP_DELETE(currentFile); } } else { LAMEXP_DELETE(currentTrack); } if(!rxFile.cap(2).compare("WAVE", Qt::CaseInsensitive) || !rxFile.cap(2).compare("MP3", Qt::CaseInsensitive) || !rxFile.cap(2).compare("AIFF", Qt::CaseInsensitive)) { currentFile = new CueSheetFile(baseDir.absoluteFilePath(UNQUOTE(rxFile.cap(1)))); qDebug("%03d File path: <%s>", lines, currentFile->fileName().toUtf8().constData()); } else { bUnsupportedTrack = true; qWarning("%03d Skipping unsupported file of type '%s'.", lines, rxFile.cap(2).toUtf8().constData()); currentFile = NULL; } bPreamble = false; currentTrack = NULL; continue; } /* --- TRACK --- */ if(rxTrack.indexIn(line) >= 0) { if(currentFile) { qDebug("%03d Track: <%s> <%s>", lines, rxTrack.cap(1).toUtf8().constData(), rxTrack.cap(2).toUtf8().constData()); if(currentTrack) { if(currentTrack->isValid()) { currentFile->addTrack(currentTrack); currentTrack = NULL; } else { LAMEXP_DELETE(currentTrack); } } if(!rxTrack.cap(2).compare("AUDIO", Qt::CaseInsensitive)) { currentTrack = new CueSheetTrack(currentFile, rxTrack.cap(1).toInt()); } else { bUnsupportedTrack = true; qWarning("%03d Skipping unsupported track of type '%s'.", lines, rxTrack.cap(2).toUtf8().constData()); currentTrack = NULL; } } else { LAMEXP_DELETE(currentTrack); } bPreamble = false; continue; } /* --- INDEX --- */ if(rxIndex.indexIn(line) >= 0) { if(currentFile && currentTrack) { qDebug("%03d Index: <%s> <%s>", lines, rxIndex.cap(1).toUtf8().constData(), rxIndex.cap(2).toUtf8().constData()); if(rxIndex.cap(1).toInt() == 1) { currentTrack->setStartIndex(parseTimeIndex(rxIndex.cap(2))); } } continue; } /* --- TITLE --- */ if(rxTitle.indexIn(line) >= 0) { if(bPreamble) { m_albumTitle = UNQUOTE(rxTitle.cap(1)).simplified(); } else if(currentFile && currentTrack) { qDebug("%03d Title: <%s>", lines, rxTitle.cap(1).toUtf8().constData()); currentTrack->setTitle(UNQUOTE(rxTitle.cap(1)).simplified()); } continue; } /* --- PERFORMER --- */ if(rxPerformer.indexIn(line) >= 0) { if(bPreamble) { m_albumPerformer = UNQUOTE(rxPerformer.cap(1)).simplified(); } else if(currentFile && currentTrack) { qDebug("%03d Title: <%s>", lines, rxPerformer.cap(1).toUtf8().constData()); currentTrack->setPerformer(UNQUOTE(rxPerformer.cap(1)).simplified()); } continue; } /* --- GENRE --- */ if(rxGenre.indexIn(line) >= 0) { if(bPreamble) { QString temp = UNQUOTE(rxGenre.cap(1)).simplified(); for(int i = 0; g_lamexp_generes[i]; i++) { if(temp.compare(g_lamexp_generes[i], Qt::CaseInsensitive) == 0) { m_albumGenre = QString(g_lamexp_generes[i]); break; } } } else if(currentFile && currentTrack) { qDebug("%03d Genre: <%s>", lines, rxGenre.cap(1).toUtf8().constData()); QString temp = UNQUOTE(rxGenre.cap(1).simplified()); for(int i = 0; g_lamexp_generes[i]; i++) { if(temp.compare(g_lamexp_generes[i], Qt::CaseInsensitive) == 0) { currentTrack->setGenre(QString(g_lamexp_generes[i])); break; } } } continue; } /* --- YEAR --- */ if(rxYear.indexIn(line) >= 0) { if(bPreamble) { bool ok = false; unsigned int temp = rxYear.cap(1).toUInt(&ok); if(ok) m_albumYear = temp; } else if(currentFile && currentTrack) { qDebug("%03d Year: <%s>", lines, rxPerformer.cap(1).toUtf8().constData()); bool ok = false; unsigned int temp = rxYear.cap(1).toUInt(&ok); if(ok) currentTrack->setYear(temp); } continue; } } //Append the very last track/file that is still pending if(currentFile) { if(currentTrack) { if(currentTrack->isValid()) { currentFile->addTrack(currentTrack); currentTrack = NULL; } else { LAMEXP_DELETE(currentTrack); } } if(currentFile->isValid()) { m_files.append(currentFile); currentFile = NULL; } else { LAMEXP_DELETE(currentFile); } } //Finally calculate duration of each track int nFiles = m_files.count(); for(int i = 0; i < nFiles; i++) { if(application) { application->processEvents(); Sleep(10); } CueSheetFile *currentFile = m_files.at(i); int nTracks = currentFile->trackCount(); if(nTracks > 1) { for(int j = 1; j < nTracks; j++) { CueSheetTrack *currentTrack = currentFile->track(j); CueSheetTrack *previousTrack = currentFile->track(j-1); double duration = currentTrack->startIndex() - previousTrack->startIndex(); previousTrack->setDuration(qMax(0.0, duration)); } } } //Sanity check of track numbers if(nFiles > 0) { bool hasTracks = false; int previousTrackNo = -1; bool trackNo[100]; for(int i = 0; i < 100; i++) { trackNo[i] = false; } for(int i = 0; i < nFiles; i++) { if(application) { application->processEvents(); Sleep(10); } CueSheetFile *currentFile = m_files.at(i); int nTracks = currentFile->trackCount(); if(nTracks > 1) { for(int j = 0; j < nTracks; j++) { int currentTrackNo = currentFile->track(j)->trackNo(); if(currentTrackNo > 99) { qWarning("Track #%02d is invalid (maximum is 99), Cue Sheet is inconsistent!", currentTrackNo); return ErrorInconsistent; } if(currentTrackNo <= previousTrackNo) { qWarning("Non-increasing track numbers (%02d -> %02d), Cue Sheet is inconsistent!", previousTrackNo, currentTrackNo); return ErrorInconsistent; } if(trackNo[currentTrackNo]) { qWarning("Track #%02d exists multiple times, Cue Sheet is inconsistent!", currentTrackNo); return ErrorInconsistent; } trackNo[currentTrackNo] = true; previousTrackNo = currentTrackNo; hasTracks = true; } } } if(!hasTracks) { qWarning("Could not find at least one valid track in the Cue Sheet!"); return ErrorInconsistent; } return ErrorSuccess; } else { qWarning("Could not find at least one valid input file in the Cue Sheet!"); return bUnsupportedTrack ? ErrorUnsupported : ErrorBadFile; } }