void SyncOverwriteDialog::readTree(const QString &path, DirectoryEntry *directoryStructure, QTreeWidgetItem *subTree) { QDir overwrite(path); overwrite.setFilter(QDir::Dirs | QDir::Files | QDir::NoDotAndDotDot); QDirIterator dirIter(overwrite); while (dirIter.hasNext()) { dirIter.next(); QFileInfo fileInfo = dirIter.fileInfo(); QString file = fileInfo.fileName(); if (file == "meta.ini") { continue; } QTreeWidgetItem *newItem = new QTreeWidgetItem(subTree, QStringList(file)); if (fileInfo.isDir()) { DirectoryEntry *subDir = directoryStructure->findSubDirectory(ToWString(file)); if (subDir != nullptr) { readTree(fileInfo.absoluteFilePath(), subDir, newItem); } else { qCritical("no directory structure for %s?", file.toUtf8().constData()); delete newItem; newItem = nullptr; } } else { const FileEntry::Ptr entry = directoryStructure->findFile(ToWString(file)); QComboBox* combo = new QComboBox(ui->syncTree); combo->addItem(tr("<don't sync>"), -1); if (entry.get() != nullptr) { bool ignore; int origin = entry->getOrigin(ignore); addToComboBox(combo, ToQString(m_DirectoryStructure->getOriginByID(origin).getName()), origin); const std::vector<int> &alternatives = entry->getAlternatives(); for (std::vector<int>::const_iterator iter = alternatives.begin(); iter != alternatives.end(); ++iter) { addToComboBox(combo, ToQString(m_DirectoryStructure->getOriginByID(*iter).getName()), *iter); } combo->setCurrentIndex(combo->count() - 1); } else { combo->setCurrentIndex(0); } ui->syncTree->setItemWidget(newItem, 1, combo); } if (newItem != nullptr) { subTree->addChild(newItem); } } }
bool PluginList::saveLoadOrder(DirectoryEntry &directoryStructure) { if (m_GamePlugin->loadOrderMechanism() != IPluginGame::LoadOrderMechanism::FileTime) { // nothing to do return true; } qDebug("setting file times on esps"); for (ESPInfo &esp : m_ESPs) { std::wstring espName = ToWString(esp.m_Name); const FileEntry::Ptr fileEntry = directoryStructure.findFile(espName); if (fileEntry.get() != nullptr) { QString fileName; bool archive = false; int originid = fileEntry->getOrigin(archive); fileName = QString("%1\\%2").arg(QDir::toNativeSeparators(ToQString(directoryStructure.getOriginByID(originid).getPath()))).arg(esp.m_Name); HANDLE file = ::CreateFile(ToWString(fileName).c_str(), GENERIC_READ | GENERIC_WRITE, 0, nullptr, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, nullptr); if (file == INVALID_HANDLE_VALUE) { if (::GetLastError() == ERROR_SHARING_VIOLATION) { // file is locked, probably the game is running return false; } else { throw windows_error(QObject::tr("failed to access %1").arg(fileName).toUtf8().constData()); } } ULONGLONG temp = 0; temp = (145731ULL + esp.m_Priority) * 24 * 60 * 60 * 10000000ULL; FILETIME newWriteTime; newWriteTime.dwLowDateTime = (DWORD)(temp & 0xFFFFFFFF); newWriteTime.dwHighDateTime = (DWORD)(temp >> 32); esp.m_Time = newWriteTime; fileEntry->setFileTime(newWriteTime); if (!::SetFileTime(file, nullptr, nullptr, &newWriteTime)) { throw windows_error(QObject::tr("failed to set file time %1").arg(fileName).toUtf8().constData()); } CloseHandle(file); } }
void DirectoryRefresher::addModFilesToStructure(DirectoryEntry *directoryStructure, const QString &modName, int priority, const QString &directory, const QStringList &stealFiles) { std::wstring directoryW = ToWString(QDir::toNativeSeparators(directory)); if (stealFiles.length() > 0) { // instead of adding all the files of the target directory, we just change the root of the specified // files to this mod FilesOrigin &origin = directoryStructure->createOrigin(ToWString(modName), directoryW, priority); for (const QString &filename : stealFiles) { QFileInfo fileInfo(filename); FileEntry::Ptr file = directoryStructure->findFile(ToWString(fileInfo.fileName())); if (file.get() != nullptr) { if (file->getOrigin() == 0) { // replace data as the origin on this bsa file->removeOrigin(0); } origin.addFile(file->getIndex()); file->addOrigin(origin.getID(), file->getFileTime(), L""); } else { qWarning("%s not found", qPrintable(fileInfo.fileName())); } } } else { directoryStructure->addFromOrigin(ToWString(modName), directoryW, priority); } }
void PluginList::refresh(const QString &profileName , const DirectoryEntry &baseDirectory , const QString &pluginsFile , const QString &loadOrderFile , const QString &lockedOrderFile) { ChangeBracket<PluginList> layoutChange(this); m_ESPsByName.clear(); m_ESPsByPriority.clear(); m_ESPs.clear(); QStringList primaryPlugins = m_GamePlugin->primaryPlugins(); m_CurrentProfile = profileName; std::vector<FileEntry::Ptr> files = baseDirectory.getFiles(); for (auto iter = files.begin(); iter != files.end(); ++iter) { FileEntry::Ptr current = *iter; if (current.get() == nullptr) { continue; } QString filename = ToQString(current->getName()); QString extension = filename.right(3).toLower(); if ((extension == "esp") || (extension == "esm")) { bool forceEnabled = Settings::instance().forceEnableCoreFiles() && std::find(primaryPlugins.begin(), primaryPlugins.end(), filename.toLower()) != primaryPlugins.end(); bool archive = false; try { FilesOrigin &origin = baseDirectory.getOriginByID(current->getOrigin(archive)); QString iniPath = QFileInfo(filename).baseName() + ".ini"; bool hasIni = baseDirectory.findFile(ToWString(iniPath)).get() != nullptr; QString originName = ToQString(origin.getName()); unsigned int modIndex = ModInfo::getIndex(originName); if (modIndex != UINT_MAX) { ModInfo::Ptr modInfo = ModInfo::getByIndex(modIndex); originName = modInfo->name(); } m_ESPs.push_back(ESPInfo(filename, forceEnabled, originName, ToQString(current->getFullPath()), hasIni)); } catch (const std::exception &e) { reportError(tr("failed to update esp info for file %1 (source id: %2), error: %3").arg(filename).arg(current->getOrigin(archive)).arg(e.what())); } } } if (readLoadOrder(loadOrderFile)) { int maxPriority = 0; // assign known load orders for (std::vector<ESPInfo>::iterator espIter = m_ESPs.begin(); espIter != m_ESPs.end(); ++espIter) { std::map<QString, int>::const_iterator priorityIter = m_ESPLoadOrder.find(espIter->m_Name.toLower()); if (priorityIter != m_ESPLoadOrder.end()) { if (priorityIter->second > maxPriority) { maxPriority = priorityIter->second; } espIter->m_Priority = priorityIter->second; } else { espIter->m_Priority = -1; } } ++maxPriority; // assign maximum priorities for plugins with unknown priority for (std::vector<ESPInfo>::iterator espIter = m_ESPs.begin(); espIter != m_ESPs.end(); ++espIter) { if (espIter->m_Priority == -1) { espIter->m_Priority = maxPriority++; } } } else { // no load order stored, determine by date std::sort(m_ESPs.begin(), m_ESPs.end(), ByDate); for (size_t i = 0; i < m_ESPs.size(); ++i) { m_ESPs[i].m_Priority = i; } } std::sort(m_ESPs.begin(), m_ESPs.end(), ByPriority); // first, sort by priority // remove gaps from the priorities so we can use them as array indices without overflow for (int i = 0; i < static_cast<int>(m_ESPs.size()); ++i) { m_ESPs[i].m_Priority = i; } std::sort(m_ESPs.begin(), m_ESPs.end(), ByName); // sort by name so alphabetical sorting works updateIndices(); readEnabledFrom(pluginsFile); readLockedOrderFrom(lockedOrderFile); layoutChange.finish(); refreshLoadOrder(); emit dataChanged(this->index(0, 0), this->index(m_ESPs.size(), columnCount())); m_Refreshed(); }