/*
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 "profile.h"
#include "report.h"
#include "gameinfo.h"
#include "windows_error.h"
#include "dummybsa.h"
#include "modinfo.h"
#include "safewritefile.h"
#include
#include
#include
#include
#include
#include
#include
#include
#define WIN32_LEAN_AND_MEAN
#include
#include
#include
using namespace MOBase;
using namespace MOShared;
Profile::Profile()
: m_SaveTimer(NULL)
{
initTimer();
}
void Profile::touchFile(QString fileName)
{
QFile modList(m_Directory.filePath(fileName));
if (!modList.open(QIODevice::ReadWrite)) {
throw std::runtime_error(QObject::tr("failed to create %1").arg(m_Directory.filePath(fileName)).toUtf8().constData());
}
}
Profile::Profile(const QString &name, bool useDefaultSettings)
: m_SaveTimer(NULL)
{
initTimer();
QString profilesDir = QDir::fromNativeSeparators(ToQString(GameInfo::instance().getProfilesDir()));
QDir profileBase(profilesDir);
QString fixedName = name;
if (!fixDirectoryName(fixedName)) {
throw MyException(tr("invalid profile name %1").arg(name));
}
if (!profileBase.exists() || !profileBase.mkdir(fixedName)) {
throw MyException(tr("failed to create %1").arg(fixedName).toUtf8().constData());
}
QString fullPath = profilesDir + "/" + fixedName;
m_Directory = QDir(fullPath);
try {
// create files. Needs to happen after m_Directory was set!
touchFile("modlist.txt");
touchFile("archives.txt");
GameInfo::instance().createProfile(ToWString(fullPath), useDefaultSettings);
} catch (...) {
// clean up in case of an error
shellDelete(QStringList(profileBase.absoluteFilePath(fixedName)));
throw;
}
refreshModStatus();
}
Profile::Profile(const QDir& directory)
: m_Directory(directory), m_SaveTimer(NULL)
{
initTimer();
if (!QFile::exists(m_Directory.filePath("modlist.txt"))) {
qWarning("missing modlist.txt in %s", qPrintable(directory.path()));
}
GameInfo::instance().repairProfile(ToWString(m_Directory.absolutePath()));
if (!QFile::exists(getIniFileName())) {
reportError(QObject::tr("\"%1\" is missing or inaccessible").arg(getIniFileName()));
}
refreshModStatus();
}
Profile::Profile(const Profile& reference)
: m_Directory(reference.m_Directory), m_SaveTimer(NULL)
{
initTimer();
refreshModStatus();
}
Profile::~Profile()
{
writeModlistNow();
}
void Profile::initTimer()
{
m_SaveTimer = new QTimer(this);
m_SaveTimer->setSingleShot(true);
connect(m_SaveTimer, SIGNAL(timeout()), this, SLOT(writeModlistNow()));
}
bool Profile::exists() const
{
return m_Directory.exists();
}
void Profile::writeModlist() const
{
if (!m_SaveTimer->isActive()) {
m_SaveTimer->start(2000);
}
}
void Profile::cancelWriteModlist() const
{
m_SaveTimer->stop();
}
void Profile::writeModlistNow(bool onlyOnTimer) const
{
if (onlyOnTimer && !m_SaveTimer->isActive()) return;
m_SaveTimer->stop();
if (!m_Directory.exists()) return;
try {
QString fileName = getModlistFileName();
SafeWriteFile file(fileName);
file->write(QString("# This file was automatically generated by Mod Organizer.\r\n").toUtf8());
if (m_ModStatus.empty()) {
return;
}
for (int i = m_ModStatus.size() - 1; i >= 0; --i) {
// the priority order was inverted on load so it has to be inverted again
unsigned int index = m_ModIndexByPriority[i];
if (index != UINT_MAX) {
ModInfo::Ptr modInfo = ModInfo::getByIndex(index);
std::vector flags = modInfo->getFlags();
if ((modInfo->getFixedPriority() == INT_MIN)) {
if (std::find(flags.begin(), flags.end(), ModInfo::FLAG_FOREIGN) != flags.end()) {
file->write("*");
} else if (m_ModStatus[index].m_Enabled) {
file->write("+");
} else {
file->write("-");
}
file->write(modInfo->name().toUtf8());
file->write("\r\n");
}
}
}
if (file.commitIfDifferent(m_LastModlistHash)) {
qDebug("%s saved", QDir::toNativeSeparators(fileName).toUtf8().constData());
}
} catch (const std::exception &e) {
reportError(tr("failed to write mod list: %1").arg(e.what()));
return;
}
}
void Profile::createTweakedIniFile()
{
QString tweakedIni = m_Directory.absoluteFilePath("initweaks.ini");
if (QFile::exists(tweakedIni) && !shellDeleteQuiet(tweakedIni)) {
reportError(tr("failed to update tweaked ini file, wrong settings may be used: %1").arg(windowsErrorString(::GetLastError())));
return;
}
for (unsigned int i = 0; i < m_ModStatus.size(); ++i) {
unsigned int idx = modIndexByPriority(i);
if ((idx != UINT_MAX) && m_ModStatus[idx].m_Enabled) {
ModInfo::Ptr modInfo = ModInfo::getByIndex(idx);
mergeTweaks(modInfo, tweakedIni);
}
}
mergeTweak(getProfileTweaks(), tweakedIni);
bool error = false;
if (!::WritePrivateProfileStringW(L"Archive", L"bInvalidateOlderFiles", L"1", ToWString(tweakedIni).c_str())) {
error = true;
}
if (localSavesEnabled()) {
if (!::WritePrivateProfileStringW(L"General", L"bUseMyGamesDirectory", L"0", ToWString(tweakedIni).c_str())) {
error = true;
}
if (!::WritePrivateProfileStringW(L"General", L"SLocalSavePath",
AppConfig::localSavePlaceholder(),
ToWString(tweakedIni).c_str())) {
error = true;
}
}
if (error) {
reportError(tr("failed to create tweaked ini: %1").arg(getCurrentErrorStringA().c_str()));
}
qDebug("%s saved", qPrintable(QDir::toNativeSeparators(tweakedIni)));
}
void Profile::refreshModStatus()
{
QFile file(getModlistFileName());
if (!file.open(QIODevice::ReadOnly)) {
throw MyException(tr("\"%1\" is missing or inaccessible").arg(getModlistFileName()));
}
bool modStatusModified = false;
m_ModStatus.clear();
m_ModStatus.resize(ModInfo::getNumMods());
std::set namesRead;
// load mods from file and update enabled state and priority for them
int index = 0;
while (!file.atEnd()) {
QByteArray line = file.readLine().trimmed();
bool enabled = true;
QString modName;
if (line.length() == 0) {
// empty line
continue;
} else if (line.at(0) == '#') {
// comment line
continue;
} else if (line.at(0) == '-') {
enabled = false;
modName = QString::fromUtf8(line.mid(1).trimmed().constData());
} else if ((line.at(0) == '+')
|| (line.at(0) == '*')) {
modName = QString::fromUtf8(line.mid(1).trimmed().constData());
} else {
modName = QString::fromUtf8(line.trimmed().constData());
}
if (modName.size() > 0) {
QString lookupName = modName + (line.at(0) == '*' ? ModInfoForeign::INT_IDENTIFIER : "");
if (namesRead.find(lookupName) != namesRead.end()) {
continue;
} else {
namesRead.insert(lookupName);
}
unsigned int modIndex = ModInfo::getIndex(lookupName);
if (modIndex != UINT_MAX) {
ModInfo::Ptr info = ModInfo::getByIndex(modIndex);
if ((modIndex < m_ModStatus.size())
&& (info->getFixedPriority() == INT_MIN)) {
m_ModStatus[modIndex].m_Enabled = enabled || info->alwaysEnabled();
if (m_ModStatus[modIndex].m_Priority == -1) {
if (static_cast(index) >= m_ModStatus.size()) {
throw MyException(tr("invalid index %1").arg(index));
}
m_ModStatus[modIndex].m_Priority = index++;
}
} else {
qDebug("mod \"%s\" (profile \"%s\") not found",
modName.toUtf8().constData(), m_Directory.path().toUtf8().constData());
// need to rewrite the modlist to fix this
modStatusModified = true;
}
}
} else {
// line was empty after trimming
}
}
int numKnownMods = index;
int topInsert = 0;
// invert priority order to match that of the pluginlist. Also
// give priorities to mods not referenced in the profile
for (size_t i = 0; i < m_ModStatus.size(); ++i) {
ModInfo::Ptr modInfo = ModInfo::getByIndex(i);
if (modInfo->getFixedPriority() == INT_MAX) {
continue;
}
if (m_ModStatus[i].m_Priority != -1) {
m_ModStatus[i].m_Priority = numKnownMods - m_ModStatus[i].m_Priority - 1;
} else {
if (static_cast(index) >= m_ModStatus.size()) {
throw MyException(tr("invalid index %1").arg(index));
}
if (modInfo->hasFlag(ModInfo::FLAG_FOREIGN)) {
m_ModStatus[i].m_Priority = --topInsert;
} else {
m_ModStatus[i].m_Priority = index++;
}
// also, mark the mod-list as changed
modStatusModified = true;
}
}
// to support insertion of new mods at the top we may now have mods with negative priority. shift them all up
// to align priority with 0
if (topInsert < 0) {
int offset = topInsert * -1;
for (size_t i = 0; i < m_ModStatus.size(); ++i) {
ModInfo::Ptr modInfo = ModInfo::getByIndex(i);
if (modInfo->getFixedPriority() == INT_MAX) {
continue;
}
m_ModStatus[i].m_Priority += offset;
}
}
file.close();
updateIndices();
if (modStatusModified) {
writeModlist();
}
}
void Profile::dumpModStatus() const
{
for (unsigned int i = 0; i < m_ModStatus.size(); ++i) {
ModInfo::Ptr info = ModInfo::getByIndex(i);
qWarning("%d: %s - %d (%s)", i, info->name().toUtf8().constData(), m_ModStatus[i].m_Priority,
m_ModStatus[i].m_Enabled ? "enabled" : "disabled");
}
}
void Profile::updateIndices()
{
m_NumRegularMods = 0;
m_ModIndexByPriority.clear();
m_ModIndexByPriority.resize(m_ModStatus.size(), UINT_MAX);
for (unsigned int i = 0; i < m_ModStatus.size(); ++i) {
int priority = m_ModStatus[i].m_Priority;
if (priority < 0) {
// don't assign this to mapping at all, it's probably the overwrite mod
continue;
} else if (priority >= static_cast(m_ModIndexByPriority.size())) {
qCritical("invalid priority %d for mod", priority);
continue;
} else {
++m_NumRegularMods;
m_ModIndexByPriority.at(priority) = i;
}
}
}
std::vector > Profile::getActiveMods()
{
std::vector > result;
for (std::vector::const_iterator iter = m_ModIndexByPriority.begin();
iter != m_ModIndexByPriority.end(); ++iter) {
if ((*iter != UINT_MAX) && m_ModStatus[*iter].m_Enabled) {
ModInfo::Ptr modInfo = ModInfo::getByIndex(*iter);
result.push_back(std::make_tuple(modInfo->internalName(), modInfo->absolutePath(), m_ModStatus[*iter].m_Priority));
}
}
unsigned int overwriteIndex = ModInfo::findMod([](ModInfo::Ptr mod) -> bool {
std::vector flags = mod->getFlags();
return std::find(flags.begin(), flags.end(), ModInfo::FLAG_OVERWRITE) != flags.end(); });
if (overwriteIndex != UINT_MAX) {
ModInfo::Ptr overwriteInfo = ModInfo::getByIndex(overwriteIndex);
result.push_back(std::make_tuple(overwriteInfo->name(), overwriteInfo->absolutePath(), UINT_MAX));
} else {
reportError(tr("Overwrite directory couldn't be parsed"));
}
return result;
}
unsigned int Profile::modIndexByPriority(unsigned int priority) const
{
if (priority >= m_ModStatus.size()) {
throw MyException(tr("invalid priority %1").arg(priority));
}
return m_ModIndexByPriority[priority];
}
void Profile::setModEnabled(unsigned int index, bool enabled)
{
if (index >= m_ModStatus.size()) {
throw MyException(tr("invalid index %1").arg(index));
}
ModInfo::Ptr modInfo = ModInfo::getByIndex(index);
// we could quit in the following case, this shouldn't be a change anyway,
// but at least this allows the situation to be fixed in case of an error
if (modInfo->alwaysEnabled()) {
enabled = true;
}
if (enabled != m_ModStatus[index].m_Enabled) {
m_ModStatus[index].m_Enabled = enabled;
emit modStatusChanged(index);
}
}
bool Profile::modEnabled(unsigned int index) const
{
if (index >= m_ModStatus.size()) {
throw MyException(tr("invalid index %1").arg(index));
}
return m_ModStatus[index].m_Enabled;
}
int Profile::getModPriority(unsigned int index) const
{
if (index >= m_ModStatus.size()) {
throw MyException(tr("invalid index %1").arg(index));
}
return m_ModStatus[index].m_Priority;
}
void Profile::setModPriority(unsigned int index, int &newPriority)
{
if (m_ModStatus.at(index).m_Overwrite) {
// can't change priority of the overwrite
return;
}
int newPriorityTemp = (std::max)(0, (std::min)(m_ModStatus.size() - 1, newPriority));
// don't try to place below overwrite
while ((m_ModIndexByPriority.at(newPriorityTemp) >= m_ModStatus.size()) ||
m_ModStatus.at(m_ModIndexByPriority.at(newPriorityTemp)).m_Overwrite) {
--newPriorityTemp;
}
int oldPriority = m_ModStatus.at(index).m_Priority;
if (newPriorityTemp > oldPriority) {
// priority is higher than the old, so the gap we left is in lower priorities
for (int i = oldPriority + 1; i <= newPriorityTemp; ++i) {
--m_ModStatus.at(m_ModIndexByPriority.at(i)).m_Priority;
}
} else {
for (int i = newPriorityTemp; i < oldPriority; ++i) {
++m_ModStatus.at(m_ModIndexByPriority.at(i)).m_Priority;
}
++newPriority;
}
m_ModStatus.at(index).m_Priority = newPriorityTemp;
updateIndices();
writeModlist();
}
Profile Profile::createFrom(const QString &name, const Profile &reference)
{
QString profileDirectory = QDir::fromNativeSeparators(ToQString(GameInfo::instance().getProfilesDir())).append("/").append(name);
reference.copyFilesTo(profileDirectory);
return Profile(QDir(profileDirectory));
}
Profile *Profile::createPtrFrom(const QString &name, const Profile &reference)
{
QString profileDirectory = QDir::fromNativeSeparators(ToQString(GameInfo::instance().getProfilesDir())).append("/").append(name);
reference.copyFilesTo(profileDirectory);
return new Profile(QDir(profileDirectory));
}
void Profile::copyFilesTo(QString &target) const
{
copyDir(m_Directory.absolutePath(), target, false);
}
std::vector Profile::splitDZString(const wchar_t *buffer) const
{
std::vector result;
const wchar_t *pos = buffer;
size_t length = wcslen(pos);
while (length != 0U) {
result.push_back(pos);
pos += length + 1;
length = wcslen(pos);
}
return result;
}
void Profile::mergeTweak(const QString &tweakName, const QString &tweakedIni) const
{
static const int bufferSize = 32768;
std::wstring tweakNameW = ToWString(tweakName);
std::wstring tweakedIniW = ToWString(tweakedIni);
QScopedArrayPointer buffer(new wchar_t[bufferSize]);
// retrieve a list of sections
DWORD size = ::GetPrivateProfileSectionNamesW(
buffer.data(), bufferSize, tweakNameW.c_str());
if (size == bufferSize - 2) {
// unfortunately there is no good way to find the required size
// of the buffer
throw MyException(QString("Buffer too small. Please report this as a bug. "
"For now you might want to split up %1").arg(tweakName));
}
std::vector sections = splitDZString(buffer.data());
// now iterate over all sections and retrieve a list of keys in each
for (std::vector::iterator iter = sections.begin();
iter != sections.end(); ++iter) {
// retrieve the names of all keys
size = ::GetPrivateProfileStringW(iter->c_str(), NULL, NULL, buffer.data(),
bufferSize, tweakNameW.c_str());
if (size == bufferSize - 2) {
throw MyException(QString("Buffer too small. Please report this as a bug. "
"For now you might want to split up %1").arg(tweakName));
}
std::vector keys = splitDZString(buffer.data());
for (std::vector::iterator keyIter = keys.begin();
keyIter != keys.end(); ++keyIter) {
//TODO this treats everything as strings but how could I differentiate the type?
::GetPrivateProfileStringW(iter->c_str(), keyIter->c_str(),
NULL, buffer.data(), bufferSize, ToWString(tweakName).c_str());
::WritePrivateProfileStringW(iter->c_str(), keyIter->c_str(),
buffer.data(), tweakedIniW.c_str());
}
}
}
void Profile::mergeTweaks(ModInfo::Ptr modInfo, const QString &tweakedIni) const
{
std::vector iniTweaks = modInfo->getIniTweaks();
for (std::vector::iterator iter = iniTweaks.begin();
iter != iniTweaks.end(); ++iter) {
mergeTweak(*iter, tweakedIni);
}
}
bool Profile::invalidationActive(bool *supported) const
{
if (GameInfo::instance().requiresBSAInvalidation()) {
if (supported != NULL) {
*supported = true;
}
wchar_t buffer[1024];
std::wstring iniFileName = ToWString(QDir::toNativeSeparators(getIniFileName()));
// epic ms fail: GetPrivateProfileString uses errno (for whatever reason) to signal a fail since the return value
// has a different meaning (number of bytes copied). HOWEVER, it will not set errno to 0 if NO error occured
errno = 0;
if (::GetPrivateProfileStringW(L"Archive", GameInfo::instance().archiveListKey().c_str(),
L"", buffer, 1024, iniFileName.c_str()) == 0) {
if (errno != 0x02) {
if (supported != NULL) {
*supported = false;
}
return false;
} else {
QString errorMessage = tr("failed to parse ini file (%1)").arg(ToQString(iniFileName));
throw windows_error(errorMessage.toUtf8().constData());
}
}
QStringList archives = ToQString(buffer).split(',');
for (int i = 0; i < archives.count(); ++i) {
QString bsaName = archives.at(i).trimmed();
if (GameInfo::instance().isInvalidationBSA(ToWString(bsaName))) {
return true;
}
}
} else {
*supported = false;
}
return false;
}
void Profile::deactivateInvalidation() const
{
if (GameInfo::instance().requiresBSAInvalidation()) {
wchar_t buffer[1024];
std::wstring iniFileName = ToWString(QDir::toNativeSeparators(getIniFileName()));
errno = 0;
if (::GetPrivateProfileStringW(L"Archive", GameInfo::instance().archiveListKey().c_str(),
L"", buffer, 1024, iniFileName.c_str()) == 0) {
if (errno == 0x02) {
QString errorMessage = tr("failed to parse ini file (%1): %2").arg(QDir::toNativeSeparators(getIniFileName())).arg(::GetLastError());
throw windows_error(errorMessage.toUtf8().constData());
} else {
return;
}
}
QStringList archives = ToQString(buffer).split(", ");
for (int i = 0; i < archives.count();) {
QString bsaName = archives.at(i).trimmed();
if (GameInfo::instance().isInvalidationBSA(ToWString(bsaName))) {
archives.removeAt(i);
} else {
++i;
}
}
// just to be safe...
::SetFileAttributesW(iniFileName.c_str(), FILE_ATTRIBUTE_NORMAL);
if (!::WritePrivateProfileStringW(L"Archive", GameInfo::instance().archiveListKey().c_str(),
ToWString(archives.join(", ").toUtf8()).c_str(), iniFileName.c_str()) ||
!::WritePrivateProfileStringW(L"Archive", L"bInvalidateOlderFiles", L"0", iniFileName.c_str()) ||
!::WritePrivateProfileStringW(L"Archive", L"SInvalidationFile", L"ArchiveInvalidation.txt", iniFileName.c_str())) {
QString errorMessage = tr("failed to modify \"%1\"").arg(ToQString(iniFileName));
throw windows_error(errorMessage.toUtf8().constData());
}
}
}
void Profile::activateInvalidation(const QString& dataDirectory) const
{
if (GameInfo::instance().requiresBSAInvalidation()) {
wchar_t buffer[1024];
std::wstring iniFileName = ToWString(QDir::toNativeSeparators(getIniFileName()));
errno = 0;
if (::GetPrivateProfileStringW(L"Archive", GameInfo::instance().archiveListKey().c_str(),
L"", buffer, 1024, iniFileName.c_str()) == 0) {
if (errno == 0x02) {
throw windows_error("failed to parse ini file");
} else {
// ignore. shouldn't have gotten here anyway
return;
}
}
QStringList archives = ToQString(buffer).split(", ");
QString invalidationBSA = ToQString(GameInfo::instance().getInvalidationBSA());
if (!archives.contains(invalidationBSA)) {
archives.insert(0, invalidationBSA);
}
// just to be safe...
::SetFileAttributesW(iniFileName.c_str(), FILE_ATTRIBUTE_NORMAL);
if (!::WritePrivateProfileStringW(L"Archive", GameInfo::instance().archiveListKey().c_str(),
ToWString(archives.join(", ").toUtf8()).c_str(), iniFileName.c_str()) ||
!::WritePrivateProfileStringW(L"Archive", L"bInvalidateOlderFiles", L"1", iniFileName.c_str()) ||
!::WritePrivateProfileStringW(L"Archive", L"SInvalidationFile", L"", iniFileName.c_str())) {
QString errorMessage = tr("failed to modify \"%1\"").arg(ToQString(iniFileName));
throw windows_error(errorMessage.toUtf8().constData());
}
QString bsaFile = dataDirectory + "/" + invalidationBSA;
if (!QFile::exists(bsaFile)) {
DummyBSA bsa;
bsa.write(bsaFile);
}
}
}
bool Profile::localSavesEnabled() const
{
return m_Directory.exists("saves");
}
bool Profile::enableLocalSaves(bool enable)
{
if (enable) {
if (m_Directory.exists("_saves")) {
m_Directory.rename("_saves", "saves");
} else {
m_Directory.mkdir("saves");
}
} else {
QMessageBox::StandardButton res = QMessageBox::question(QApplication::activeModalWidget(), tr("Delete savegames?"),
tr("Do you want to delete local savegames? (If you select \"No\", the save games "
"will show up again if you re-enable local savegames)"),
QMessageBox::Yes | QMessageBox::No | QMessageBox::Cancel,
QMessageBox::Cancel);
if (res == QMessageBox::Yes) {
shellDelete(QStringList(m_Directory.absoluteFilePath("saves")));
} else if (res == QMessageBox::No) {
m_Directory.rename("saves", "_saves");
} else {
return false;
}
}
// default: assume success
return true;
}
QString Profile::getModlistFileName() const
{
return QDir::cleanPath(m_Directory.absoluteFilePath("modlist.txt"));
}
QString Profile::getPluginsFileName() const
{
return QDir::cleanPath(m_Directory.absoluteFilePath("plugins.txt"));
}
QString Profile::getLoadOrderFileName() const
{
return QDir::cleanPath(m_Directory.absoluteFilePath("loadorder.txt"));
}
QString Profile::getLockedOrderFileName() const
{
return QDir::cleanPath(m_Directory.absoluteFilePath("lockedorder.txt"));
}
QString Profile::getArchivesFileName() const
{
return QDir::cleanPath(m_Directory.absoluteFilePath("archives.txt"));
}
QString Profile::getDeleterFileName() const
{
return QDir::cleanPath(m_Directory.absoluteFilePath("hide_plugins.txt"));
}
QString Profile::getIniFileName() const
{
std::wstring primaryIniFile = *(GameInfo::instance().getIniFileNames().begin());
return m_Directory.absoluteFilePath(ToQString(primaryIniFile));
}
QString Profile::getProfileTweaks() const
{
return QDir::cleanPath(m_Directory.absoluteFilePath(ToQString(AppConfig::profileTweakIni())));
}
QString Profile::getPath() const
{
return QDir::cleanPath(m_Directory.absolutePath());
}
void Profile::rename(const QString &newName)
{
QDir profileDir(QDir::fromNativeSeparators(ToQString(GameInfo::instance().getProfilesDir())));
profileDir.rename(getName(), newName);
m_Directory = profileDir.absoluteFilePath(newName);
}