/*
Copyright (C) 2012 Sebastian Herbord. All rights reserved.
This file is part of Mod Organizer.
Mod Organizer is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.
Mod Organizer is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with Mod Organizer. If not, see .
*/
#include "directoryrefresher.h"
#include "iplugingame.h"
#include "modinfo.h"
#include "report.h"
#include "utility.h"
#include
#include
#include
using namespace MOBase;
using namespace MOShared;
DirectoryRefresher::DirectoryRefresher() : m_DirectoryStructure(nullptr) {}
DirectoryRefresher::~DirectoryRefresher() { delete m_DirectoryStructure; }
DirectoryEntry* DirectoryRefresher::getDirectoryStructure() {
QMutexLocker locker(&m_RefreshLock);
DirectoryEntry* result = m_DirectoryStructure;
m_DirectoryStructure = nullptr;
return result;
}
void DirectoryRefresher::setMods(const std::vector>& mods,
const std::set& managedArchives) {
QMutexLocker locker(&m_RefreshLock);
m_Mods.clear();
for (auto mod = mods.begin(); mod != mods.end(); ++mod) {
QString name = std::get<0>(*mod);
ModInfo::Ptr info = ModInfo::getByIndex(ModInfo::getIndex(name));
m_Mods.push_back(EntryInfo(name, std::get<1>(*mod), info->stealFiles(), info->archives(), std::get<2>(*mod)));
}
m_EnabledArchives = managedArchives;
}
void DirectoryRefresher::cleanStructure(DirectoryEntry* structure) {
static const wchar_t* files[] = {L"meta.ini", L"readme.txt"};
for (int i = 0; i < sizeof(files) / sizeof(wchar_t*); ++i) {
structure->removeFile(files[i]);
}
static const wchar_t* dirs[] = {L"fomod"};
for (int i = 0; i < sizeof(dirs) / sizeof(wchar_t*); ++i) {
structure->removeDir(std::wstring(dirs[i]));
}
}
void DirectoryRefresher::addModBSAToStructure(DirectoryEntry* directoryStructure, const QString& modName, int priority,
const QString& directory, const QStringList& archives) {
std::wstring directoryW = ToWString(QDir::toNativeSeparators(directory));
for (const QString& archive : archives) {
QFileInfo fileInfo(archive);
if (m_EnabledArchives.find(fileInfo.fileName()) != m_EnabledArchives.end()) {
try {
// directoryStructure->addFromBSA(ToWString(modName), directoryW,
// ToWString(QDir::toNativeSeparators(fileInfo.absoluteFilePath())),
// priority);
} catch (const std::exception& e) {
throw MyException(tr("failed to parse bsa %1: %2").arg(archive, e.what()));
}
}
}
}
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 DirectoryRefresher::addModToStructure(DirectoryEntry* directoryStructure, const QString& modName, int priority,
const QString& directory, const QStringList& stealFiles,
const QStringList& archives) {
addModFilesToStructure(directoryStructure, modName, priority, directory, stealFiles);
addModBSAToStructure(directoryStructure, modName, priority, directory, archives);
}
void DirectoryRefresher::refresh() {
QMutexLocker locker(&m_RefreshLock);
delete m_DirectoryStructure;
m_DirectoryStructure = new DirectoryEntry(L"data", nullptr, 0);
IPluginGame* game = qApp->property("managed_game").value();
std::wstring dataDirectory = QDir::toNativeSeparators(game->dataDirectory().absolutePath()).toStdWString();
m_DirectoryStructure->addFromOrigin(L"data", dataDirectory, 0);
// TODO what was the point of having the priority in this tuple? the list is already sorted by priority
auto iter = m_Mods.begin();
// TODO i is the priority here, where higher = more important. the input vector is also sorted by priority but
// inverted!
for (int i = 1; iter != m_Mods.end(); ++iter, ++i) {
try {
addModToStructure(m_DirectoryStructure, iter->modName, i, iter->absolutePath, iter->stealFiles,
iter->archives);
} catch (const std::exception& e) {
emit error(tr("failed to read mod (%1): %2").arg(iter->modName, e.what()));
}
emit progress((i * 100) / static_cast(m_Mods.size()) + 1);
}
emit progress(100);
cleanStructure(m_DirectoryStructure);
emit refreshed();
}