summaryrefslogtreecommitdiff
path: root/src/shared
diff options
context:
space:
mode:
authorTannin <Tannin@Serenity>2013-02-03 12:49:25 +0100
committerTannin <Tannin@Serenity>2013-02-03 12:49:25 +0100
commit981f8b3acf7e76f27c02f4ced80d55b424cc7497 (patch)
tree0f504d99d1e5fe197258febff3c147d0725abda4 /src/shared
initial commit to mercurial repository.
Corresponds to MO version 0.12.6
Diffstat (limited to 'src/shared')
-rw-r--r--src/shared/appconfig.cpp30
-rw-r--r--src/shared/appconfig.h36
-rw-r--r--src/shared/appconfig.inc12
-rw-r--r--src/shared/directoryentry.cpp799
-rw-r--r--src/shared/directoryentry.h313
-rw-r--r--src/shared/error_report.cpp99
-rw-r--r--src/shared/error_report.h47
-rw-r--r--src/shared/fallout3info.cpp244
-rw-r--r--src/shared/fallout3info.h94
-rw-r--r--src/shared/falloutnvinfo.cpp246
-rw-r--r--src/shared/falloutnvinfo.h95
-rw-r--r--src/shared/gameinfo.cpp204
-rw-r--r--src/shared/gameinfo.h187
-rw-r--r--src/shared/inject.cpp142
-rw-r--r--src/shared/inject.h27
-rw-r--r--src/shared/oblivioninfo.cpp252
-rw-r--r--src/shared/oblivioninfo.h92
-rw-r--r--src/shared/shared.pro56
-rw-r--r--src/shared/skyriminfo.cpp278
-rw-r--r--src/shared/skyriminfo.h100
-rw-r--r--src/shared/util.cpp119
-rw-r--r--src/shared/util.h46
-rw-r--r--src/shared/windows_error.cpp45
-rw-r--r--src/shared/windows_error.h40
24 files changed, 3603 insertions, 0 deletions
diff --git a/src/shared/appconfig.cpp b/src/shared/appconfig.cpp
new file mode 100644
index 00000000..0d933d0a
--- /dev/null
+++ b/src/shared/appconfig.cpp
@@ -0,0 +1,30 @@
+/*
+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 <http://www.gnu.org/licenses/>.
+*/
+
+#include "appconfig.h"
+
+namespace AppConfig {
+
+#define PARWSTRING wstring
+#define APPPARAM(partype, parid, value) partype parid ## () { return value; }
+#include "appconfig.inc"
+#undef PARWSTRING
+#undef APPPARAM
+
+}
diff --git a/src/shared/appconfig.h b/src/shared/appconfig.h
new file mode 100644
index 00000000..b10ecc3c
--- /dev/null
+++ b/src/shared/appconfig.h
@@ -0,0 +1,36 @@
+/*
+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 <http://www.gnu.org/licenses/>.
+*/
+
+#ifndef APPCONFIG_H
+#define APPCONFIG_H
+
+#include <string>
+
+namespace AppConfig {
+
+
+#define PARWSTRING wstring
+#define APPPARAM(partype, parid, value) partype parid ## ();
+#include "appconfig.inc"
+#undef PARWSTRING
+#undef APPPARAM
+
+}
+
+#endif // APPCONFIG_H
diff --git a/src/shared/appconfig.inc b/src/shared/appconfig.inc
new file mode 100644
index 00000000..7710925a
--- /dev/null
+++ b/src/shared/appconfig.inc
@@ -0,0 +1,12 @@
+APPPARAM(std::wstring, translationPrefix, L"organizer")
+APPPARAM(std::wstring, pluginPath, L"plugins")
+APPPARAM(std::wstring, stylesheetsPath, L"stylesheets")
+APPPARAM(std::wstring, logFile, L"ModOrganizer.log")
+APPPARAM(std::wstring, proxyDLLTarget, L"steam_api.dll")
+APPPARAM(std::wstring, proxyDLLOrig, L"steam_api_orig.dll") // needs to be identical to the value used in proxydll-project
+APPPARAM(std::wstring, proxyDLLSource, L"proxy.dll")
+APPPARAM(std::wstring, hookDLLName, L"hook.dll") // needs to be identical to the value used in proxydll-project
+APPPARAM(const wchar_t*, localSavePlaceholder, L"__MOProfileSave__\\")
+
+APPPARAM(std::wstring, firstStepsTutorial, L"tutorial_firststeps_main.js")
+APPPARAM(int, numLogFiles, 5) // number of log files to keep
diff --git a/src/shared/directoryentry.cpp b/src/shared/directoryentry.cpp
new file mode 100644
index 00000000..748ead99
--- /dev/null
+++ b/src/shared/directoryentry.cpp
@@ -0,0 +1,799 @@
+/*
+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 <http://www.gnu.org/licenses/>.
+*/
+
+#include "directoryentry.h"
+#define WIN32_LEAN_AND_MEAN
+#include <Windows.h>
+#include <sstream>
+#include <algorithm>
+#include <bsatk.h>
+#include "error_report.h"
+#include "util.h"
+#include "windows_error.h"
+#include <boost/bind.hpp>
+
+
+
+class OriginConnection {
+
+public:
+
+ typedef int Index;
+ static const int INVALID_INDEX = INT_MIN;
+
+public:
+
+ OriginConnection()
+ : m_NextID(0)
+ {}
+
+ FilesOrigin& createOrigin(const std::wstring &originName, const std::wstring &directory, int priority,
+ boost::shared_ptr<FileRegister> fileRegister, boost::shared_ptr<OriginConnection> originConnection) {
+ int newID = createID();
+ m_Origins[newID] = FilesOrigin(newID, originName, directory, priority, fileRegister, originConnection);
+ m_OriginsNameMap[originName] = newID;
+ m_OriginsPriorityMap[priority] = newID;
+ return m_Origins[newID];
+ }
+
+ bool exists(const std::wstring &name) {
+ std::map<std::wstring, int>::iterator iter = m_OriginsNameMap.find(name);
+ return iter != m_OriginsNameMap.end();
+ }
+
+ FilesOrigin &getByID(Index ID) {
+ return m_Origins[ID];
+ }
+
+ FilesOrigin &getByName(const std::wstring &name) {
+ std::map<std::wstring, int>::iterator iter = m_OriginsNameMap.find(name);
+ if (iter != m_OriginsNameMap.end()) {
+ return m_Origins[iter->second];
+ } else {
+ std::ostringstream stream;
+ stream << "invalid origin name: " << ToString(name, false);
+ throw std::runtime_error(stream.str());
+ }
+ }
+
+ void changePriorityLookup(int oldPriority, int newPriority)
+ {
+ auto iter = m_OriginsPriorityMap.find(oldPriority);
+ if (iter != m_OriginsPriorityMap.end()) {
+ Index idx = iter->second;
+ m_OriginsPriorityMap.erase(iter);
+ m_OriginsPriorityMap[newPriority] = idx;
+ }
+ }
+
+ void changeNameLookup(const std::wstring &oldName, const std::wstring &newName)
+ {
+ auto iter = m_OriginsNameMap.find(oldName);
+ if (iter != m_OriginsNameMap.end()) {
+ Index idx = iter->second;
+ m_OriginsNameMap.erase(iter);
+ m_OriginsNameMap[newName] = idx;
+ } else {
+ log("failed to change name lookup from %ls to %ls", oldName.c_str(), newName.c_str());
+ }
+ }
+
+private:
+
+ Index createID() {
+ return m_NextID++;
+ }
+
+private:
+
+ Index m_NextID;
+
+ std::map<Index, FilesOrigin> m_Origins;
+ std::map<std::wstring, Index> m_OriginsNameMap;
+ std::map<int, Index> m_OriginsPriorityMap;
+
+};
+
+
+//
+// FilesOrigin
+//
+
+
+void FilesOrigin::enable(bool enabled)
+{
+ if (!enabled) {
+ std::set<FileEntry::Index> copy = m_Files;
+ for (auto iter = copy.begin(); iter != copy.end(); ++iter) {
+ m_FileRegister->removeOrigin(*iter, m_ID);
+ }
+ m_Files.clear();
+ }
+ m_Disabled = !enabled;
+}
+
+
+void FilesOrigin::removeFile(FileEntry::Index index)
+{
+ auto iter = m_Files.find(index);
+ if (iter != m_Files.end()) {
+ m_Files.erase(iter);
+ }
+}
+
+
+
+std::wstring tail(const std::wstring &source, const size_t count)
+{
+ if (count >= source.length()) {
+ return source;
+ }
+
+ return source.substr(source.length() - count);
+}
+
+
+void FilesOrigin::setPriority(int priority)
+{
+ m_OriginConnection->changePriorityLookup(m_Priority, priority);
+
+ m_Priority = priority;
+}
+
+
+void FilesOrigin::setName(const std::wstring &name)
+{
+ m_OriginConnection->changeNameLookup(m_Name, name);
+ // change path too
+ if (tail(m_Path, m_Name.length()) == m_Name) {
+ m_Path = m_Path.substr(0, m_Path.length() - m_Name.length()).append(name);
+ }
+ m_Name = name;
+}
+
+std::vector<FileEntry*> FilesOrigin::getFiles() const
+{
+ std::vector<FileEntry*> result;
+
+ for (auto iter = m_Files.begin(); iter != m_Files.end(); ++iter) {
+ result.push_back(m_FileRegister->getFile(*iter));
+ }
+
+ return result;
+}
+
+
+
+//
+// FileEntry
+//
+
+void FileEntry::addOrigin(int origin, FILETIME fileTime, const std::wstring &archive)
+{
+ if (m_Origin == -1) {
+ m_Origin = origin;
+ m_FileTime = fileTime;
+ m_Archive = archive;
+// } else if (FilesOrigin::getByID(origin).getPriority() > FilesOrigin::getByID(m_Origin).getPriority()) {
+ } else if (m_Parent->getOriginByID(origin).getPriority() > m_Parent->getOriginByID(m_Origin).getPriority()) {
+ if (std::find(m_Alternatives.begin(), m_Alternatives.end(), m_Origin) == m_Alternatives.end()) {
+ m_Alternatives.push_back(m_Origin);
+ }
+ m_Origin = origin;
+ m_FileTime = fileTime;
+ m_Archive = archive;
+ } else {
+ bool found = false;
+ if (m_Origin == origin) {
+ // already an origin
+ return;
+ }
+ for (std::vector<int>::iterator iter = m_Alternatives.begin(); iter != m_Alternatives.end(); ++iter) {
+ if (*iter == origin) {
+ // already an origin
+ return;
+ }
+ if (m_Parent->getOriginByID(*iter).getPriority() < m_Parent->getOriginByID(origin).getPriority()) {
+ m_Alternatives.insert(iter, origin);
+ found = true;
+ break;
+ }
+ }
+ if (!found) {
+ m_Alternatives.push_back(origin);
+ }
+ }
+}
+
+bool FileEntry::removeOrigin(int origin)
+{
+ if (m_Origin == origin) {
+ if (!m_Alternatives.empty()) {
+ // find alternative with the highest priority
+ std::vector<int>::iterator currentIter = m_Alternatives.begin();
+ for (std::vector<int>::iterator iter = m_Alternatives.begin(); iter != m_Alternatives.end(); ++iter) {
+ if ((m_Parent->getOriginByID(*iter).getPriority() > m_Parent->getOriginByID(*currentIter).getPriority()) &&
+ (*iter != origin)) {
+ currentIter = iter;
+ }
+ }
+ int currentID = *currentIter;
+ m_Alternatives.erase(currentIter);
+
+ m_Origin = currentID;
+
+ // now we need to update the file time...
+ std::wstring filePath = getFullPath();
+ HANDLE file = ::CreateFile(filePath.c_str(), GENERIC_READ | GENERIC_WRITE,
+ 0, NULL, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, NULL);
+ if (!::GetFileTime(file, NULL, NULL, &m_FileTime)) {
+ // maybe this file is in a bsa, but there is no easy way to find out which. User should refresh
+ // the view to find out
+ m_Archive = L"bsa?";
+ } else {
+ m_Archive = L"";
+ }
+
+ ::CloseHandle(file);
+
+ } else {
+ m_Origin = -1;
+ return true;
+ }
+ } else {
+ std::vector<int>::iterator newEnd = std::remove(m_Alternatives.begin(), m_Alternatives.end(), origin);
+ m_Alternatives.erase(newEnd, m_Alternatives.end());
+ }
+ return false;
+}
+
+
+// sorted by priority descending
+static bool ByOriginPriority(DirectoryEntry *entry, int LHS, int RHS)
+{
+ return entry->getOriginByID(LHS).getPriority() < entry->getOriginByID(RHS).getPriority();
+}
+
+
+FileEntry::FileEntry(Index index, const std::wstring &name, DirectoryEntry *parent)
+ : m_Index(index), m_Name(name), m_Parent(parent), m_Origin(-1), m_Archive(L"")
+{
+}
+
+
+void FileEntry::sortOrigins()
+{
+ m_Alternatives.push_back(m_Origin);
+ std::sort(m_Alternatives.begin(), m_Alternatives.end(), boost::bind(ByOriginPriority, m_Parent, _1, _2));
+ m_Origin = m_Alternatives[m_Alternatives.size() - 1];
+ m_Alternatives.pop_back();
+}
+
+
+bool FileEntry::recurseParents(std::wstring &path, const DirectoryEntry *parent) const
+{
+ if (parent == NULL) {
+ return false;
+ } else {
+ // don't append the topmost parent because it is the virtual data-root
+ if (recurseParents(path, parent->getParent())) {
+ path.append(L"\\").append(parent->getName());
+ }
+ return true;
+ }
+}
+
+std::wstring FileEntry::getFullPath() const
+{
+ std::wstring result;
+ bool ignore = false;
+ result = m_Parent->getOriginByID(getOrigin(ignore)).getPath(); //base directory for origin
+ recurseParents(result, m_Parent); // all intermediate directories
+ result.append(L"\\").append(m_Name); // the actual filename
+ return result;
+}
+
+std::wstring FileEntry::getRelativePath() const
+{
+ std::wstring result;
+ recurseParents(result, m_Parent); // all intermediate directories
+ result.append(L"\\").append(m_Name); // the actual filename
+ return result;
+}
+
+
+
+
+
+//
+// DirectoryEntry
+//
+DirectoryEntry::DirectoryEntry(const std::wstring &name, DirectoryEntry *parent, int originID)
+ : m_OriginConnection(new OriginConnection),
+ m_Name(name), m_Parent(parent), m_Populated(false), m_Origin(originID)
+{
+ m_FileRegister.reset(new FileRegister(m_OriginConnection));
+}
+
+DirectoryEntry::DirectoryEntry(const std::wstring &name, DirectoryEntry *parent, int originID,
+ boost::shared_ptr<FileRegister> fileRegister, boost::shared_ptr<OriginConnection> originConnection)
+ : m_FileRegister(fileRegister), m_OriginConnection(originConnection),
+ m_Name(name), m_Parent(parent), m_Populated(false), m_Origin(originID)
+{}
+
+
+DirectoryEntry::~DirectoryEntry()
+{
+ clear();
+}
+
+
+const std::wstring &DirectoryEntry::getName() const
+{
+ return m_Name;
+}
+
+
+void DirectoryEntry::clear()
+{
+ m_Files.clear();
+ for (std::vector<DirectoryEntry*>::iterator iter = m_SubDirectories.begin(); iter != m_SubDirectories.end(); ++iter) {
+ delete *iter;
+ }
+ m_SubDirectories.clear();
+}
+
+
+FilesOrigin &DirectoryEntry::createOrigin(const std::wstring &originName, const std::wstring &directory, int priority)
+{
+ if (m_OriginConnection->exists(originName)) {
+ FilesOrigin &origin = m_OriginConnection->getByName(originName);
+ origin.enable(true);
+ return origin;
+ } else {
+ return m_OriginConnection->createOrigin(originName, directory, priority, m_FileRegister, m_OriginConnection);
+ }
+}
+
+
+void DirectoryEntry::addFromOrigin(const std::wstring &originName, const std::wstring &directory, int priority)
+{
+ FilesOrigin &origin = createOrigin(originName, directory, priority);
+ wchar_t *buffer = new wchar_t[MAXPATH_UNICODE + 1];
+ memset(buffer, L'\0', MAXPATH_UNICODE + 1);
+ try {
+ int offset = _snwprintf(buffer, MAXPATH_UNICODE, L"%ls", directory.c_str());
+ addFiles(origin, buffer, offset);
+ } catch (...) {
+ delete [] buffer;
+ buffer = NULL;
+ }
+ delete [] buffer;
+ m_Populated = true;
+}
+
+
+void DirectoryEntry::addFromBSA(const std::wstring &originName, std::wstring &directory, const std::wstring &fileName, int priority)
+{
+ FilesOrigin &origin = createOrigin(originName, directory, priority);
+
+ WIN32_FILE_ATTRIBUTE_DATA fileData;
+ if (::GetFileAttributesExW(fileName.c_str(), GetFileExInfoStandard, &fileData) == 0) {
+ throw windows_error("failed to determine file time");
+ }
+
+ BSA::Archive archive;
+ BSA::EErrorCode res = archive.read(ToString(fileName, false).c_str());
+ if ((res != BSA::ERROR_NONE) && (res != BSA::ERROR_INVALIDHASHES)) {
+ std::ostringstream stream;
+ stream << "invalid bsa file: " << ToString(fileName, false) << " errorcode " << res << " - " << ::GetLastError();
+ throw std::runtime_error(stream.str());
+ }
+ size_t namePos = fileName.find_last_of(L"\\/");
+ if (namePos == std::wstring::npos) {
+ namePos = 0;
+ } else {
+ ++namePos;
+ }
+
+ addFiles(origin, archive.getRoot(), fileData.ftLastWriteTime, fileName.substr(namePos));
+ m_Populated = true;
+}
+
+
+void DirectoryEntry::addFiles(FilesOrigin &origin, wchar_t *buffer, int bufferOffset)
+{
+ WIN32_FIND_DATAW findData;
+
+ _snwprintf(buffer + bufferOffset, MAXPATH_UNICODE - bufferOffset, L"\\*");
+ HANDLE searchHandle = ::FindFirstFileExW(buffer, FindExInfoStandard, &findData, FindExSearchNameMatch, NULL, FIND_FIRST_EX_CASE_SENSITIVE);
+ if (searchHandle != INVALID_HANDLE_VALUE) {
+ BOOL result = true;
+ while (result) {
+ if (findData.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY) {
+ if ((wcscmp(findData.cFileName, L".") != 0) &&
+ (wcscmp(findData.cFileName, L"..") != 0)) {
+ int offset = _snwprintf(buffer + bufferOffset, MAXPATH_UNICODE, L"\\%ls", findData.cFileName);
+ // recurse into subdirectories
+ getSubDirectory(findData.cFileName, true, origin.getID())->addFiles(origin, buffer, bufferOffset + offset);
+ }
+ } else {
+ insert(findData.cFileName, origin, findData.ftLastWriteTime, L"");
+ }
+ result = ::FindNextFileW(searchHandle, &findData);
+ }
+ }
+ ::FindClose(searchHandle);
+}
+
+
+void DirectoryEntry::addFiles(FilesOrigin &origin, BSA::Folder::Ptr archiveFolder, FILETIME &fileTime, const std::wstring &archiveName)
+{
+ // add files
+ for (unsigned int fileIdx = 0; fileIdx < archiveFolder->getNumFiles(); ++fileIdx) {
+ BSA::File::Ptr file = archiveFolder->getFile(fileIdx);
+ insert(ToWString(file->getName(), true), origin, fileTime, archiveName);
+ }
+
+ // recurse into subdirectories
+ for (unsigned int folderIdx = 0; folderIdx < archiveFolder->getNumSubFolders(); ++folderIdx) {
+ BSA::Folder::Ptr folder = archiveFolder->getSubFolder(folderIdx);
+ DirectoryEntry *folderEntry = getSubDirectoryRecursive(ToWString(folder->getName(), true), true, origin.getID());
+
+ folderEntry->addFiles(origin, folder, fileTime, archiveName);
+ }
+}
+
+
+void DirectoryEntry::removeFile(const std::wstring &filePath, int *origin)
+{
+ size_t pos = filePath.find_first_of(L"\\/");
+ if (pos == std::string::npos) {
+ this->remove(filePath, origin);
+ } else {
+ std::wstring dirName = filePath.substr(0, pos);
+ std::wstring rest = filePath.substr(pos + 1);
+ DirectoryEntry *entry = getSubDirectoryRecursive(dirName, false);
+ if (entry != NULL) {
+ entry->removeFile(rest, origin);
+ }
+ }
+}
+
+void DirectoryEntry::removeDirRecursive()
+{
+ while (!m_Files.empty()) {
+ m_FileRegister->removeFile(m_Files.begin()->second);
+ }
+
+ for (auto iter = m_SubDirectories.begin(); iter != m_SubDirectories.end(); ++iter) {
+ (*iter)->removeDirRecursive();
+ }
+ m_SubDirectories.clear();
+}
+
+void DirectoryEntry::removeDir(const std::wstring &path)
+{
+ size_t pos = path.find_first_of(L"\\/");
+ if (pos == std::string::npos) {
+ for (auto iter = m_SubDirectories.begin(); iter != m_SubDirectories.end(); ++iter) {
+ if (_wcsicmp((*iter)->getName().c_str(), path.c_str()) == 0) {
+ (*iter)->removeDirRecursive();
+ m_SubDirectories.erase(iter);
+ break;
+ }
+ }
+ } else {
+ std::wstring dirName = path.substr(0, pos);
+ std::wstring rest = path.substr(pos + 1);
+ DirectoryEntry *entry = getSubDirectoryRecursive(dirName, false);
+ if (entry != NULL) {
+ entry->removeDir(rest);
+ }
+ }
+}
+
+
+void DirectoryEntry::insertFile(const std::wstring &filePath, FilesOrigin &origin, FILETIME fileTime)
+{
+ size_t pos = filePath.find_first_of(L"\\/");
+ if (pos == std::string::npos) {
+ this->insert(filePath, origin, fileTime, L"");
+ } else {
+ std::wstring dirName = filePath.substr(0, pos);
+ std::wstring rest = filePath.substr(pos + 1);
+ getSubDirectoryRecursive(dirName, true, origin.getID())->insertFile(rest, origin, fileTime);
+ }
+}
+
+
+void DirectoryEntry::removeFile(FileEntry::Index index)
+{
+ if (m_Files.size() != 0) {
+ auto iter = std::find_if(m_Files.begin(), m_Files.end(),
+ [&index](const std::pair<std::wstring, FileEntry::Index> &iter) -> bool {
+ return iter.second == index; } );
+ if (iter != m_Files.end()) {
+ m_Files.erase(iter);
+ } else {
+ log("file \"%ls\" not in directory \"%ls\"",
+ m_FileRegister->getFile(index)->getName().c_str(),
+ this->getName().c_str());
+ }
+ } else {
+ log("file \"%ls\" not in directory \"%ls\", directory empty",
+ m_FileRegister->getFile(index)->getName().c_str(),
+ this->getName().c_str());
+ }
+}
+
+
+int DirectoryEntry::anyOrigin() const
+{
+ bool ignore;
+ for (auto iter = m_Files.begin(); iter != m_Files.end(); ++iter) {
+ FileEntry *entry = m_FileRegister->getFile(iter->second);
+ if (!entry->isFromArchive()) {
+ return entry->getOrigin(ignore);
+ }
+ }
+
+ // if we got here, no file directly within this directory is a valid indicator for a mod, thus
+ // we continue looking in subdirectories
+ for (std::vector<DirectoryEntry*>::const_iterator iter = m_SubDirectories.begin(); iter != m_SubDirectories.end(); ++iter) {
+ int res = (*iter)->anyOrigin();
+ if (res != -1){
+ return res;
+ }
+ }
+ return m_Origin;
+}
+
+
+bool DirectoryEntry::originExists(const std::wstring &name) const
+{
+ return m_OriginConnection->exists(name);
+}
+
+
+FilesOrigin &DirectoryEntry::getOriginByID(int ID) const
+{
+ return m_OriginConnection->getByID(ID);
+}
+
+
+FilesOrigin &DirectoryEntry::getOriginByName(const std::wstring &name) const
+{
+ return m_OriginConnection->getByName(name);
+}
+
+
+int DirectoryEntry::getOrigin(const std::wstring &path, bool &archive)
+{
+ const DirectoryEntry *directory = NULL;
+ const FileEntry *file = searchFile(path, &directory);
+ if (file != NULL) {
+ return file->getOrigin(archive);
+ } else {
+ if (directory != NULL) {
+ return directory->anyOrigin();
+ } else {
+ return -1;
+ }
+ }
+}
+
+std::vector<FileEntry*> DirectoryEntry::getFiles() const
+{
+ std::vector<FileEntry*> result;
+ for (auto iter = m_Files.begin(); iter != m_Files.end(); ++iter) {
+ result.push_back(m_FileRegister->getFile(iter->second));
+ }
+ return result;
+}
+
+
+const FileEntry *DirectoryEntry::searchFile(const std::wstring &path, const DirectoryEntry **directory) const
+{
+ if (directory != NULL) {
+ *directory = NULL;
+ }
+
+ if ((path.length() == 0) ||
+ (path == L"*")) {
+ // no file name -> the path ended on a (back-)slash
+ *directory = this;
+
+ return NULL;
+ }
+
+ size_t len = path.find_first_of(L"\\/");
+
+ if (len == std::string::npos) {
+ // no more path components
+ auto iter = m_Files.find(path);
+ if (iter != m_Files.end()) {
+ return m_FileRegister->getFile(iter->second);
+ } else if (directory != NULL) {
+ DirectoryEntry *temp = findSubDirectory(path);
+ if (temp != NULL) {
+ *directory = temp;
+ }
+ }
+ } else {
+ // file is in in a subdirectory, recurse into the matching subdirectory
+ std::wstring pathComponent = path.substr(0, len);
+ DirectoryEntry *temp = findSubDirectory(pathComponent);
+ if (temp != NULL) {
+ return temp->searchFile(path.substr(len + 1), directory);
+ }
+ }
+ return NULL;
+}
+
+/*
+void DirectoryEntry::sortOrigins()
+{
+ for (std::set<FileEntry>::iterator iter = m_Files.begin(); iter != m_Files.end(); ++iter) {
+ const_cast<FileEntry&>(*iter).sortOrigins();
+ }
+ for (std::vector<DirectoryEntry*>::iterator iter = m_SubDirectories.begin(); iter != m_SubDirectories.end(); ++iter) {
+ (*iter)->sortOrigins();
+ }
+}
+*/
+
+DirectoryEntry *DirectoryEntry::findSubDirectory(const std::wstring &name) const
+{
+ for (std::vector<DirectoryEntry*>::const_iterator iter = m_SubDirectories.begin(); iter != m_SubDirectories.end(); ++iter) {
+ if (_wcsicmp((*iter)->getName().c_str(), name.c_str()) == 0) {
+ return *iter;
+ }
+ }
+ return NULL;
+}
+
+
+
+const FileEntry *DirectoryEntry::findFile(const std::wstring &name)
+{
+ auto iter = m_Files.find(name);
+ if (iter != m_Files.end()) {
+ return m_FileRegister->getFile(iter->second);
+ } else {
+ return NULL;
+ }
+}
+
+DirectoryEntry *DirectoryEntry::getSubDirectory(const std::wstring &name, bool create, int originID)
+{
+ for (std::vector<DirectoryEntry*>::iterator iter = m_SubDirectories.begin(); iter != m_SubDirectories.end(); ++iter) {
+ if (_wcsicmp((*iter)->getName().c_str(), name.c_str()) == 0) {
+ return *iter;
+ }
+ }
+ if (create) {
+ std::vector<DirectoryEntry*>::iterator iter = m_SubDirectories.insert(m_SubDirectories.end(),
+ new DirectoryEntry(name, this, originID, m_FileRegister, m_OriginConnection));
+ return *iter;
+ } else {
+ return NULL;
+ }
+}
+
+
+DirectoryEntry *DirectoryEntry::getSubDirectoryRecursive(const std::wstring &path, bool create, int originID)
+{
+ if (path.length() == 0) {
+ // path ended with a backslash?
+ return this;
+ }
+
+ size_t pos = path.find_first_of(L"\\/");
+ if (pos == std::wstring::npos) {
+ return getSubDirectory(path, create);
+ } else {
+ DirectoryEntry *nextChild = getSubDirectory(path.substr(0, pos), create, originID);
+ if (nextChild == NULL) {
+ return NULL;
+ } else {
+ return nextChild->getSubDirectoryRecursive(path.substr(pos + 1), create, originID);
+ }
+ }
+}
+
+
+
+FileRegister::FileRegister(boost::shared_ptr<OriginConnection> originConnection)
+ : m_OriginConnection(originConnection)
+{
+}
+
+FileEntry::Index FileRegister::generateIndex()
+{
+ static FileEntry::Index sIndex = 0;
+ return sIndex++;
+}
+
+bool FileRegister::indexValid(FileEntry::Index index) const
+{
+ return m_Files.find(index) != m_Files.end();
+}
+
+FileEntry &FileRegister::createFile(const std::wstring &name, DirectoryEntry *parent)
+{
+ FileEntry::Index index = generateIndex();
+ m_Files[index] = FileEntry(index, name, parent);
+ return m_Files[index];
+}
+
+
+FileEntry *FileRegister::getFile(FileEntry::Index index)
+{
+ auto iter = m_Files.find(index);
+ if (iter != m_Files.end()) {
+ return &iter->second;
+ }
+ return NULL;
+}
+
+
+void FileRegister::unregisterFile(FileEntry &file)
+{
+ bool ignore;
+ // unregister from origin
+ int originID = file.getOrigin(ignore);
+ m_OriginConnection->getByID(originID).removeFile(file.getIndex());
+ const std::vector<int> &alternatives = file.getAlternatives();
+ for (auto iter = alternatives.begin(); iter != alternatives.end(); ++iter) {
+ m_OriginConnection->getByID(*iter).removeFile(file.getIndex());
+ }
+
+ // unregister from directory
+ if (file.getParent() != NULL) {
+ file.getParent()->removeFile(file.getIndex());
+ }
+}
+
+
+void FileRegister::removeFile(FileEntry::Index index)
+{
+ auto iter = m_Files.find(index);
+ if (iter != m_Files.end()) {
+ unregisterFile(iter->second);
+ m_Files.erase(index);
+ }
+}
+
+void FileRegister::removeOrigin(FileEntry::Index index, int originID)
+{
+ auto iter = m_Files.find(index);
+ if (iter != m_Files.end()) {
+ if (iter->second.removeOrigin(originID)) {
+ unregisterFile(iter->second);
+ }
+ }
+}
+
+void FileRegister::sortOrigins()
+{
+ for (auto iter = m_Files.begin(); iter != m_Files.end(); ++iter) {
+ iter->second.sortOrigins();
+ }
+}
diff --git a/src/shared/directoryentry.h b/src/shared/directoryentry.h
new file mode 100644
index 00000000..72e96357
--- /dev/null
+++ b/src/shared/directoryentry.h
@@ -0,0 +1,313 @@
+/*
+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 <http://www.gnu.org/licenses/>.
+*/
+
+#ifndef DIRECTORYENTRY_H
+#define DIRECTORYENTRY_H
+
+#include <string>
+#include <set>
+#include <vector>
+#include <map>
+#include <cassert>
+#define WIN32_MEAN_AND_LEAN
+#include <Windows.h>
+#include <bsatk.h>
+#include <boost/shared_ptr.hpp>
+#include "util.h"
+
+
+class DirectoryEntry;
+class OriginConnection;
+class FileRegister;
+
+
+class FileEntry {
+
+public:
+
+ typedef unsigned int Index;
+
+public:
+
+ FileEntry() : m_Index(UINT_MAX), m_Name(), m_Parent(NULL) {}
+
+ FileEntry(Index index, const std::wstring &name, DirectoryEntry *parent);
+
+ Index getIndex() const { return m_Index; }
+
+ void addOrigin(int origin, FILETIME fileTime, const std::wstring &archive);
+ // remove the specified origin from the list of origins that contain this file. if no origin is left,
+ // the file is effectively deleted and true is returned. otherwise, false is returned
+ bool removeOrigin(int origin);
+ void sortOrigins();
+
+ // gets the list of alternative origins (origins with lower priority than the primary one).
+ // if sortOrigins has been called, it is sorted by priority (ascending)
+ const std::vector<int> &getAlternatives() const { return m_Alternatives; }
+
+ const std::wstring &getName() const { return m_Name; }
+ int getOrigin(bool &archive) const { archive = (m_Archive.length() != 0); return m_Origin; }
+ const std::wstring &getArchive() const { return m_Archive; }
+ bool isFromArchive() const { return m_Archive.length() != 0; }
+ std::wstring getFullPath() const;
+ std::wstring getRelativePath() const;
+ DirectoryEntry *getParent() { return m_Parent; }
+
+ void setFileTime(FILETIME fileTime) const { m_FileTime = fileTime; }
+ FILETIME getFileTime() const { return m_FileTime; }
+
+private:
+
+ bool recurseParents(std::wstring &path, const DirectoryEntry *parent) const;
+
+ void determineTime();
+
+private:
+
+ Index m_Index;
+ std::wstring m_Name;
+ int m_Origin;
+ std::wstring m_Archive;
+ std::vector<int> m_Alternatives;
+ DirectoryEntry *m_Parent;
+ mutable FILETIME m_FileTime;
+
+ friend bool operator<(const FileEntry &lhs, const FileEntry &rhs) {
+ return _wcsicmp(lhs.m_Name.c_str(), rhs.m_Name.c_str()) < 0;
+ }
+ friend bool operator==(const FileEntry &lhs, const FileEntry &rhs) {
+ return _wcsicmp(lhs.m_Name.c_str(), rhs.m_Name.c_str()) == 0;
+ }
+};
+
+
+// represents a mod or the data directory, providing files to the tree
+class FilesOrigin {
+ friend class OriginConnection;
+public:
+
+ FilesOrigin()
+ : m_ID(0), m_Disabled(false), m_Name(), m_Path(), m_Priority(0) { }
+
+ // sets priority for this origin, but it will overwrite the exisiting mapping for this priority,
+ // the previous origin will no longer be referenced
+ void setPriority(int priority);
+
+ int getPriority() const { return m_Priority; }
+
+ void setName(const std::wstring &name);
+ const std::wstring &getName() const { return m_Name; }
+
+ int getID() const { return m_ID; }
+ const std::wstring &getPath() const { return m_Path; }
+
+ std::vector<FileEntry*> getFiles() const;
+
+ void enable(bool enabled);
+ bool isDisabled() const { return m_Disabled; }
+
+ void addFile(FileEntry::Index index) { m_Files.insert(index); }
+ void removeFile(FileEntry::Index index);
+
+private:
+
+ FilesOrigin(int ID, const std::wstring &name, const std::wstring &path, int priority,
+ boost::shared_ptr<FileRegister> fileRegister, boost::shared_ptr<OriginConnection> originConnection)
+ : m_ID(ID), m_Disabled(false), m_Name(name), m_Path(path), m_Priority(priority),
+ m_FileRegister(fileRegister), m_OriginConnection(originConnection) {}
+
+private:
+
+ int m_ID;
+
+ bool m_Disabled;
+
+ std::set<FileEntry::Index> m_Files;
+ std::wstring m_Name;
+ std::wstring m_Path;
+ int m_Priority;
+ boost::shared_ptr<FileRegister> m_FileRegister;
+ boost::shared_ptr<OriginConnection> m_OriginConnection;
+
+};
+
+
+class FileRegister
+{
+
+public:
+
+ FileRegister(boost::shared_ptr<OriginConnection> originConnection);
+
+ bool indexValid(FileEntry::Index index) const;
+
+ FileEntry &createFile(const std::wstring &name, DirectoryEntry *parent);
+ FileEntry *getFile(FileEntry::Index index);
+
+ void removeFile(FileEntry::Index index);
+ void removeOrigin(FileEntry::Index index, int originID);
+
+ void sortOrigins();
+
+private:
+
+ FileEntry::Index generateIndex();
+
+ void unregisterFile(FileEntry &file);
+
+private:
+
+ std::map<FileEntry::Index, FileEntry> m_Files;
+
+ boost::shared_ptr<OriginConnection> m_OriginConnection;
+
+};
+
+
+class DirectoryEntry
+{
+public:
+
+ DirectoryEntry(const std::wstring &name, DirectoryEntry *parent, int originID);
+
+ DirectoryEntry(const std::wstring &name, DirectoryEntry *parent, int originID,
+ boost::shared_ptr<FileRegister> fileRegister, boost::shared_ptr<OriginConnection> originConnection);
+
+ ~DirectoryEntry();
+
+ void clear();
+ bool isPopulated() const { return m_Populated; }
+
+ boost::shared_ptr<FileRegister> getRegister() { return m_FileRegister; }
+ const DirectoryEntry *getParent() const { return m_Parent; }
+
+ // add files to this directory (and subdirectories) from the specified origin. That origin may exist or not
+ void addFromOrigin(const std::wstring &originName, const std::wstring &directory, int priority);
+ void addFromBSA(const std::wstring &originName, std::wstring &directory, const std::wstring &fileName, int priority);
+
+ const std::wstring &getName() const;
+
+ boost::shared_ptr<FileRegister> getFileRegister() { return m_FileRegister; }
+
+ bool originExists(const std::wstring &name) const;
+ FilesOrigin &getOriginByID(int ID) const;
+ FilesOrigin &getOriginByName(const std::wstring &name) const;
+
+ int getOrigin(const std::wstring &path, bool &archive);
+
+ std::vector<FileEntry*> getFiles() const;
+
+ void getSubDirectories(std::vector<DirectoryEntry*>::const_iterator &begin, std::vector<DirectoryEntry*>::const_iterator &end) const {
+ begin = m_SubDirectories.begin(); end = m_SubDirectories.end();
+ }
+
+ DirectoryEntry *findSubDirectory(const std::wstring &name) const;
+
+ /** retrieve a file in this directory by name.
+ * @param name name of the file
+ * @return fileentry object for the file or NULL if no file matches
+ */
+ const FileEntry *findFile(const std::wstring &name);
+
+ /** search through this directory and all subdirectories for a file by the specified name.
+ if directory is not NULL, the referenced variable will be set to true if the path refers to a directory.
+ the returned pointer is NULL in that case */
+ const FileEntry *searchFile(const std::wstring &path, const DirectoryEntry **directory) const;
+
+ void insertFile(const std::wstring &filePath, FilesOrigin &origin, FILETIME fileTime);
+
+ void removeFile(FileEntry::Index index);
+
+ // remove the specified file from the tree. This can be a path leading to a file in a subdirectory
+ void removeFile(const std::wstring &filePath, int *origin = NULL);
+
+ /**
+ * @brief remove the specified directory
+ * @param path directory to remove
+ */
+ void removeDir(const std::wstring &path);
+
+ void remove(const std::wstring &fileName, int *origin) {
+ auto iter = m_Files.find(fileName);
+ if (iter != m_Files.end()) {
+ if (origin != NULL) {
+ FileEntry *entry = m_FileRegister->getFile(iter->second);
+ if (entry != NULL) {
+ bool ignore;
+ *origin = entry->getOrigin(ignore);
+ }
+ }
+ m_FileRegister->removeFile(iter->second);
+ }
+ }
+
+private:
+
+ void insert(const std::wstring &fileName, FilesOrigin &origin, FILETIME fileTime, const std::wstring &archive) {
+ auto iter = m_Files.find(fileName);
+ FileEntry *file = NULL;
+ if (iter != m_Files.end()) {
+ file = m_FileRegister->getFile(iter->second);
+ } else {
+ file = &m_FileRegister->createFile(fileName, this);
+ m_Files[fileName] = file->getIndex();
+ }
+ file->addOrigin(origin.getID(), fileTime, archive);
+ origin.addFile(file->getIndex());
+ }
+
+ FilesOrigin &createOrigin(const std::wstring &originName, const std::wstring &directory, int priority);
+ void addFiles(FilesOrigin &origin, wchar_t *buffer, int bufferOffset);
+ void addFiles(FilesOrigin &origin, BSA::Folder::Ptr archiveFolder, FILETIME &fileTime, const std::wstring &archiveName);
+
+ DirectoryEntry *getSubDirectory(const std::wstring &name, bool create, int originID = -1);
+
+ DirectoryEntry *getSubDirectoryRecursive(const std::wstring &path, bool create, int originID = -1);
+
+ int anyOrigin() const;
+
+ void removeDirRecursive();
+
+private:
+
+ struct WStrLess {
+ bool operator()(const std::wstring &LHS, const std::wstring &RHS) const {
+ return _wcsicmp(LHS.c_str(), RHS.c_str()) < 0;
+ }
+ };
+
+
+private:
+
+ boost::shared_ptr<FileRegister> m_FileRegister;
+ boost::shared_ptr<OriginConnection> m_OriginConnection;
+
+ std::wstring m_Name;
+ std::map<std::wstring, FileEntry::Index, WStrLess> m_Files;
+ std::vector<DirectoryEntry*> m_SubDirectories;
+
+ DirectoryEntry *m_Parent;
+ int m_Origin;
+
+ bool m_Populated;
+
+};
+
+
+#endif // DIRECTORYENTRY_H
diff --git a/src/shared/error_report.cpp b/src/shared/error_report.cpp
new file mode 100644
index 00000000..49fdba85
--- /dev/null
+++ b/src/shared/error_report.cpp
@@ -0,0 +1,99 @@
+/*
+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 <http://www.gnu.org/licenses/>.
+*/
+
+#include "error_report.h"
+#include <sstream>
+#include <stdio.h>
+
+
+void reportError(LPCSTR format, ...)
+{
+ char buffer[1025];
+ memset(buffer, 0, sizeof(char) * 1025);
+
+ va_list argList;
+ va_start(argList, format);
+
+ vsnprintf(buffer, 1024, format, argList);
+ va_end(argList);
+
+ MessageBoxA(NULL, buffer, "Error", MB_OK | MB_ICONERROR);
+ LocalFree(buffer);
+}
+
+void reportError(LPCWSTR format, ...)
+{
+ WCHAR buffer[1025];
+ memset(buffer, 0, sizeof(WCHAR) * 1025);
+
+ va_list argList;
+ va_start(argList, format);
+
+
+ _vsnwprintf(buffer, 1024, format, argList);
+ va_end(argList);
+
+ MessageBoxW(NULL, buffer, L"Error", MB_OK | MB_ICONERROR);
+ LocalFree(buffer);
+}
+
+
+std::string getCurrentErrorStringA()
+{
+ LPSTR buffer = NULL;
+
+ DWORD errorCode = ::GetLastError();
+
+ if (FormatMessageA(FORMAT_MESSAGE_ALLOCATE_BUFFER | FORMAT_MESSAGE_FROM_SYSTEM,
+ NULL, errorCode, MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT), (LPSTR)&buffer, 0, NULL) == 0) {
+ ::SetLastError(errorCode);
+ return std::string();
+ } else {
+ LPSTR lastChar = buffer + strlen(buffer) - 2;
+ *lastChar = '\0';
+
+ std::string result(buffer);
+
+ LocalFree(buffer);
+ ::SetLastError(errorCode);
+ return result;
+ }
+}
+
+std::wstring getCurrentErrorStringW()
+{
+ LPWSTR buffer = NULL;
+
+ DWORD errorCode = ::GetLastError();
+
+ if (FormatMessage(FORMAT_MESSAGE_ALLOCATE_BUFFER | FORMAT_MESSAGE_FROM_SYSTEM,
+ NULL, errorCode, MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT), (LPWSTR)&buffer, 0, NULL) == 0) {
+ ::SetLastError(errorCode);
+ return std::wstring();
+ } else {
+ LPWSTR lastChar = buffer + wcslen(buffer) - 2;
+ *lastChar = '\0';
+
+ std::wstring result(buffer);
+
+ LocalFree(buffer);
+ ::SetLastError(errorCode);
+ return result;
+ }
+}
diff --git a/src/shared/error_report.h b/src/shared/error_report.h
new file mode 100644
index 00000000..6ee296df
--- /dev/null
+++ b/src/shared/error_report.h
@@ -0,0 +1,47 @@
+/*
+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 <http://www.gnu.org/licenses/>.
+*/
+
+#pragma once
+
+#include <tchar.h>
+#define WIN32_LEAN_AND_MEAN
+#include <Windows.h>
+#include <string>
+
+namespace std {
+#ifdef UNICODE
+typedef wstring tstring;
+#else
+typedef string tstring;
+#endif
+}
+
+void reportError(LPCSTR format, ...);
+void reportError(LPCWSTR format, ...);
+
+extern void log(const char* format, ...);
+
+std::string getCurrentErrorStringA();
+std::wstring getCurrentErrorStringW();
+
+#ifdef UNICODE
+#define getCurrentErrorString getCurrentErrorStringW
+#else
+#define getCurrentErrorString getCurrentErrorStringA
+#endif
diff --git a/src/shared/fallout3info.cpp b/src/shared/fallout3info.cpp
new file mode 100644
index 00000000..f9699145
--- /dev/null
+++ b/src/shared/fallout3info.cpp
@@ -0,0 +1,244 @@
+/*
+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 <http://www.gnu.org/licenses/>.
+*/
+
+#include "fallout3info.h"
+#include "util.h"
+#include <tchar.h>
+#include <ShlObj.h>
+#include <sstream>
+#include "windows_error.h"
+#include "error_report.h"
+#define WIN32_LEAN_AND_MEAN
+#include <Windows.h>
+#include <boost/assign.hpp>
+
+Fallout3Info::Fallout3Info(const std::wstring &omoDirectory, const std::wstring &gameDirectory)
+ : GameInfo(omoDirectory, gameDirectory)
+{
+ identifyMyGamesDirectory(L"fallout3");
+}
+
+bool Fallout3Info::identifyGame(const std::wstring &searchPath)
+{
+ return FileExists(searchPath, L"Fallout3.exe") &&
+ FileExists(searchPath, L"FalloutLauncher.exe");
+}
+
+unsigned long Fallout3Info::getBSAVersion()
+{
+ return 0x68;
+}
+
+std::wstring Fallout3Info::getRegPathStatic()
+{
+ HKEY key;
+ LONG errorcode = ::RegOpenKeyEx(HKEY_LOCAL_MACHINE, L"Software\\Bethesda Softworks\\Fallout3",
+ 0, KEY_QUERY_VALUE, &key);
+
+ if (errorcode != ERROR_SUCCESS) {
+ return L"";
+ }
+
+ WCHAR temp[MAX_PATH];
+ DWORD bufferSize = MAX_PATH;
+
+ errorcode = ::RegQueryValueExW(key, L"Installed Path", NULL, NULL, (LPBYTE)temp, &bufferSize);
+
+ return std::wstring(temp);
+}
+
+std::wstring Fallout3Info::getInvalidationBSA()
+{
+ return L"Fallout - Invalidation.bsa";
+}
+
+bool Fallout3Info::isInvalidationBSA(const std::wstring &bsaName)
+{
+ static LPCWSTR invalidation[] = { L"Fallout - AI!.bsa", L"Fallout - Invalidation.bsa", NULL };
+
+ for (int i = 0; invalidation[i] != NULL; ++i) {
+ if (wcscmp(bsaName.c_str(), invalidation[i]) == 0) {
+ return true;
+ }
+ }
+ return false;
+}
+
+std::wstring Fallout3Info::getDocumentsDir()
+{
+ std::wostringstream temp;
+ temp << getMyGamesDirectory() << L"\\Fallout3";
+
+ return temp.str();
+}
+
+std::wstring Fallout3Info::getSaveGameDir()
+{
+ std::wostringstream temp;
+ temp << getDocumentsDir() << L"\\Saves";
+ return temp.str();
+}
+
+std::vector<std::wstring> Fallout3Info::getPrimaryPlugins()
+{
+ return boost::assign::list_of(L"fallout3.esm");
+}
+
+std::vector<std::wstring> Fallout3Info::getIniFileNames()
+{
+ return boost::assign::list_of(L"fallout.ini")(L"falloutprefs.ini");
+}
+
+std::wstring Fallout3Info::getSaveGameExtension()
+{
+ return L"*.fos";
+}
+
+std::wstring Fallout3Info::getReferenceDataFile()
+{
+ return L"Fallout - Meshes.bsa";
+}
+
+
+std::wstring Fallout3Info::getOMODExt()
+{
+ return L"fomod";
+}
+
+
+std::wstring Fallout3Info::getSteamAPPId()
+{
+ return L"22300";
+}
+
+
+std::wstring Fallout3Info::getSEName()
+{
+ return L"fose";
+}
+
+
+std::wstring Fallout3Info::getNexusPage()
+{
+ return L"http://fallout3.nexusmods.com";
+}
+
+
+std::wstring Fallout3Info::getNexusInfoUrlStatic()
+{
+ return L"http://fallout3.nexusmods.com";
+}
+
+
+int Fallout3Info::getNexusModIDStatic()
+{
+ return 16348;
+}
+
+
+void Fallout3Info::createProfile(const std::wstring &directory, bool useDefaults)
+{
+ std::wostringstream target;
+
+ // copy plugins.txt
+ target << directory << "\\plugins.txt";
+
+ if (!FileExists(target.str())) {
+ std::wostringstream source;
+ source << getLocalAppFolder() << "\\Fallout3\\plugins.txt";
+ if (!::CopyFileW(source.str().c_str(), target.str().c_str(), true)) {
+ HANDLE file = ::CreateFileW(target.str().c_str(), GENERIC_WRITE, 0, NULL, CREATE_NEW, FILE_ATTRIBUTE_NORMAL, NULL);
+ ::CloseHandle(file);
+ }
+ }
+
+ // copy ini-file
+ target.str(L""); target.clear();
+ target << directory << L"\\fallout.ini";
+
+ if (!FileExists(target.str())) {
+ std::wostringstream source;
+ if (useDefaults) {
+ source << getGameDirectory() << L"\\fallout_default.ini";
+ } else {
+ source << getMyGamesDirectory() << L"\\Fallout3";
+ if (::FileExists(source.str(), L"fallout.ini")) {
+ source << L"\\fallout.ini";
+ } else {
+ source.str(L"");
+ source << getGameDirectory() << L"\\fallout_default.ini";
+ }
+ }
+
+ if (!::CopyFileW(source.str().c_str(), target.str().c_str(), true)) {
+ if (::GetLastError() != ERROR_FILE_EXISTS) {
+ std::ostringstream stream;
+ stream << "failed to copy ini file: " << ToString(source.str(), false);
+ throw windows_error(stream.str());
+ }
+ }
+ }
+ { // copy falloutprefs.ini-file
+ std::wstring target = directory.substr().append(L"\\falloutprefs.ini");
+ if (!FileExists(target)) {
+ std::wostringstream source;
+ source << getMyGamesDirectory() << L"\\Fallout3\\falloutprefs.ini";
+ if (!::CopyFileW(source.str().c_str(), target.c_str(), true)) {
+ if (::GetLastError() != ERROR_FILE_EXISTS) {
+ std::ostringstream stream;
+ stream << "failed to copy ini file: " << ToString(source.str(), false);
+ throw windows_error(stream.str());
+ }
+ }
+ }
+ }
+}
+
+
+void Fallout3Info::repairProfile(const std::wstring &directory)
+{
+ createProfile(directory, false);
+}
+
+
+bool Fallout3Info::rerouteToProfile(const wchar_t *fileName, const wchar_t*)
+{
+ static LPCWSTR profileFiles[] = { L"fallout.ini", L"falloutprefs.ini", L"plugins.txt", NULL };
+
+ for (int i = 0; profileFiles[i] != NULL; ++i) {
+ if (_wcsicmp(fileName, profileFiles[i]) == 0) {
+ return true;
+ }
+ }
+ return false;
+}
+
+
+std::vector<ExecutableInfo> Fallout3Info::getExecutables()
+{
+ std::vector<ExecutableInfo> result;
+ result.push_back(ExecutableInfo(L"FOSE", L"fose_loader.exe", L"", L"", DEFAULT_CLOSE));
+ result.push_back(ExecutableInfo(L"Fallout 3", L"fallout3.exe", L"", L"", DEFAULT_CLOSE));
+ result.push_back(ExecutableInfo(L"Fallout Mod Manager", L"fomm/fomm.exe", L"", L"", DEFAULT_CLOSE));
+ result.push_back(ExecutableInfo(L"Construction Kit", L"geck.exe", L"", L"", DEFAULT_CLOSE));
+ result.push_back(ExecutableInfo(L"Fallout Launcher", L"FalloutLauncher.exe", L"", L"", DEFAULT_CLOSE));
+ result.push_back(ExecutableInfo(L"BOSS", L"BOSS/BOSS.exe", L"", L"", NEVER_CLOSE));
+
+ return result;
+}
diff --git a/src/shared/fallout3info.h b/src/shared/fallout3info.h
new file mode 100644
index 00000000..bc9ab82d
--- /dev/null
+++ b/src/shared/fallout3info.h
@@ -0,0 +1,94 @@
+/*
+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 <http://www.gnu.org/licenses/>.
+*/
+
+#ifndef FALLOUT3INFO_H
+#define FALLOUT3INFO_H
+
+
+#include "gameinfo.h"
+
+
+class Fallout3Info : public GameInfo
+{
+
+ friend class GameInfo;
+
+public:
+
+ virtual ~Fallout3Info() {}
+
+ virtual unsigned long getBSAVersion();
+
+ static std::wstring getRegPathStatic();
+ virtual std::wstring getRegPath() { return Fallout3Info::getRegPathStatic(); }
+ virtual std::wstring getBinaryName() { return L"Fallout3.exe"; }
+
+ virtual GameInfo::Type getType() { return TYPE_FALLOUT3; }
+
+ virtual std::wstring getGameName() const { return L"Fallout 3"; }
+ virtual std::wstring getGameShortName() const { return L"Fallout3"; }
+
+ virtual std::wstring getInvalidationBSA();
+
+ virtual bool isInvalidationBSA(const std::wstring &bsaName);
+
+ // full path to this games "My Games"-directory
+ virtual std::wstring getDocumentsDir();
+
+ virtual std::wstring getSaveGameDir();
+
+ virtual std::vector<std::wstring> getPrimaryPlugins();
+
+ // file name of this games ini (no path)
+ virtual std::vector<std::wstring> getIniFileNames();
+
+ virtual std::wstring getSaveGameExtension();
+ virtual std::wstring getReferenceDataFile();
+ virtual std::wstring getOMODExt();
+
+ virtual std::wstring getSteamAPPId();
+
+ virtual std::wstring getSEName();
+
+ virtual std::wstring getNexusPage();
+ static std::wstring getNexusInfoUrlStatic();
+ virtual std::wstring getNexusInfoUrl() { return Fallout3Info::getNexusInfoUrlStatic(); }
+ static int getNexusModIDStatic();
+ virtual int getNexusModID() { return Fallout3Info::getNexusModIDStatic(); }
+
+ virtual void createProfile(const std::wstring &directory, bool useDefaults);
+ virtual void repairProfile(const std::wstring &directory);
+
+ virtual bool rerouteToProfile(const wchar_t *fileName, const wchar_t *fullPath);
+
+ // get a list of executables (game binary and known-to-work 3rd party tools). All of these are relative to
+ // the game directory
+ virtual std::vector<ExecutableInfo> getExecutables();
+
+ virtual std::wstring archiveListKey() { return L"SArchiveList"; }
+
+private:
+
+ Fallout3Info(const std::wstring &omoDirectory, const std::wstring &gameDirectory);
+
+ static bool identifyGame(const std::wstring &searchPath);
+
+};
+
+#endif // FALLOUT3INFO_H
diff --git a/src/shared/falloutnvinfo.cpp b/src/shared/falloutnvinfo.cpp
new file mode 100644
index 00000000..380940b7
--- /dev/null
+++ b/src/shared/falloutnvinfo.cpp
@@ -0,0 +1,246 @@
+/*
+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 <http://www.gnu.org/licenses/>.
+*/
+
+#include "falloutnvinfo.h"
+#include "util.h"
+#include <tchar.h>
+#include <ShlObj.h>
+#include <sstream>
+#include "windows_error.h"
+#include "error_report.h"
+#define WIN32_LEAN_AND_MEAN
+#include <Windows.h>
+#include <boost/assign.hpp>
+
+
+FalloutNVInfo::FalloutNVInfo(const std::wstring &omoDirectory, const std::wstring &gameDirectory)
+ : GameInfo(omoDirectory, gameDirectory)
+{
+ identifyMyGamesDirectory(L"falloutnv");
+}
+
+bool FalloutNVInfo::identifyGame(const std::wstring &searchPath)
+{
+ return FileExists(searchPath, L"FalloutNV.exe") &&
+ FileExists(searchPath, L"FalloutNVLauncher.exe");
+}
+
+unsigned long FalloutNVInfo::getBSAVersion()
+{
+ return 0x68;
+}
+
+std::wstring FalloutNVInfo::getRegPathStatic()
+{
+ HKEY key;
+ LONG errorcode = ::RegOpenKeyEx(HKEY_LOCAL_MACHINE, L"Software\\Bethesda Softworks\\FalloutNV",
+ 0, KEY_QUERY_VALUE, &key);
+
+ if (errorcode != ERROR_SUCCESS) {
+ return L"";
+ }
+
+ WCHAR temp[MAX_PATH];
+ DWORD bufferSize = MAX_PATH;
+
+ errorcode = ::RegQueryValueExW(key, L"Installed Path", NULL, NULL, (LPBYTE)temp, &bufferSize);
+
+ return std::wstring(temp);
+}
+
+std::wstring FalloutNVInfo::getInvalidationBSA()
+{
+ return L"Fallout - Invalidation.bsa";
+}
+
+bool FalloutNVInfo::isInvalidationBSA(const std::wstring &bsaName)
+{
+ static LPCWSTR invalidation[] = { L"Fallout - AI!.bsa", L"Fallout - Invalidation.bsa", NULL };
+
+ for (int i = 0; invalidation[i] != NULL; ++i) {
+ if (wcscmp(bsaName.c_str(), invalidation[i]) == 0) {
+ return true;
+ }
+ }
+ return false;
+}
+
+std::wstring FalloutNVInfo::getDocumentsDir()
+{
+ std::wostringstream temp;
+ temp << getMyGamesDirectory() << L"\\FalloutNV";
+
+ return temp.str();
+}
+
+std::wstring FalloutNVInfo::getSaveGameDir()
+{
+ std::wostringstream temp;
+ temp << getDocumentsDir() << L"\\Saves";
+ return temp.str();
+}
+
+std::vector<std::wstring> FalloutNVInfo::getPrimaryPlugins()
+{
+ return boost::assign::list_of(L"falloutnv.esm");
+}
+
+std::vector<std::wstring> FalloutNVInfo::getIniFileNames()
+{
+ return boost::assign::list_of(L"fallout.ini")(L"falloutprefs.ini");
+}
+
+
+std::wstring FalloutNVInfo::getSaveGameExtension()
+{
+ return L"*.fos";
+}
+
+std::wstring FalloutNVInfo::getReferenceDataFile()
+{
+ return L"Fallout - Meshes.bsa";
+}
+
+
+std::wstring FalloutNVInfo::getOMODExt()
+{
+ return L"fomod";
+}
+
+
+std::wstring FalloutNVInfo::getSteamAPPId()
+{
+ return L"22380";
+}
+
+
+void FalloutNVInfo::createProfile(const std::wstring &directory, bool useDefaults)
+{
+ std::wostringstream target;
+
+ // copy plugins.txt
+ target << directory << "\\plugins.txt";
+
+ if (!FileExists(target.str())) {
+ std::wostringstream source;
+ source << getLocalAppFolder() << "\\FalloutNV\\plugins.txt";
+ if (!::CopyFileW(source.str().c_str(), target.str().c_str(), true)) {
+ HANDLE file = ::CreateFileW(target.str().c_str(), GENERIC_WRITE, 0, NULL, CREATE_NEW, FILE_ATTRIBUTE_NORMAL, NULL);
+ ::CloseHandle(file);
+ }
+ }
+
+ // copy ini-file
+ target.str(L""); target.clear();
+ target << directory << L"\\fallout.ini";
+
+ if (!FileExists(target.str())) {
+ std::wostringstream source;
+ if (useDefaults) {
+ source << getGameDirectory() << L"\\fallout_default.ini";
+ } else {
+ source << getMyGamesDirectory() << L"\\FalloutNV";
+ if (::FileExists(source.str(), L"fallout.ini")) {
+ source << L"\\fallout.ini";
+ } else {
+ source.str(L"");
+ source << getGameDirectory() << L"\\fallout_default.ini";
+ }
+ }
+
+ if (!::CopyFileW(source.str().c_str(), target.str().c_str(), true)) {
+ if (::GetLastError() != ERROR_FILE_EXISTS) {
+ std::ostringstream stream;
+ stream << "failed to copy ini file: " << ToString(source.str(), false);
+ throw windows_error(stream.str());
+ }
+ }
+ }
+ { // copy falloutprefs.ini-file
+ std::wstring target = directory.substr().append(L"\\falloutprefs.ini");
+ if (!FileExists(target)) {
+ std::wostringstream source;
+ source << getMyGamesDirectory() << L"\\FalloutNV\\falloutprefs.ini";
+ if (!::CopyFileW(source.str().c_str(), target.c_str(), true)) {
+ if (::GetLastError() != ERROR_FILE_EXISTS) {
+ std::ostringstream stream;
+ stream << "failed to copy ini file: " << ToString(source.str(), false);
+ throw windows_error(stream.str());
+ }
+ }
+ }
+ }
+}
+
+
+std::wstring FalloutNVInfo::getSEName()
+{
+ return L"nvse";
+}
+
+
+std::wstring FalloutNVInfo::getNexusPage()
+{
+ return L"http://newvegas.nexusmods.com";
+}
+
+
+std::wstring FalloutNVInfo::getNexusInfoUrlStatic()
+{
+ return L"http://newvegas.nexusmods.com";
+}
+
+
+int FalloutNVInfo::getNexusModIDStatic()
+{
+ return 42572;
+}
+
+
+void FalloutNVInfo::repairProfile(const std::wstring &directory)
+{
+ createProfile(directory, false);
+}
+
+
+bool FalloutNVInfo::rerouteToProfile(const wchar_t *fileName, const wchar_t*)
+{
+ static LPCWSTR profileFiles[] = { L"fallout.ini", L"falloutprefs.ini", L"plugins.txt", NULL };
+
+ for (int i = 0; profileFiles[i] != NULL; ++i) {
+ if (_wcsicmp(fileName, profileFiles[i]) == 0) {
+ return true;
+ }
+ }
+ return false;
+}
+
+
+std::vector<ExecutableInfo> FalloutNVInfo::getExecutables()
+{
+ std::vector<ExecutableInfo> result;
+ result.push_back(ExecutableInfo(L"NVSE", L"nvse_loader.exe", L"", L"", DEFAULT_CLOSE));
+ result.push_back(ExecutableInfo(L"New Vegas", L"falloutnv.exe", L"", L"", DEFAULT_CLOSE));
+ result.push_back(ExecutableInfo(L"Fallout Mod Manager", L"fomm/fomm.exe", L"", L"", DEFAULT_CLOSE));
+ result.push_back(ExecutableInfo(L"Construction Kit", L"geck.exe", L"", L"", DEFAULT_CLOSE));
+ result.push_back(ExecutableInfo(L"Fallout Launcher", L"FalloutNVLauncher.exe", L"", L"", DEFAULT_CLOSE));
+ result.push_back(ExecutableInfo(L"BOSS", L"BOSS/BOSS.exe", L"", L"", NEVER_CLOSE));
+
+ return result;
+}
diff --git a/src/shared/falloutnvinfo.h b/src/shared/falloutnvinfo.h
new file mode 100644
index 00000000..aa02ac30
--- /dev/null
+++ b/src/shared/falloutnvinfo.h
@@ -0,0 +1,95 @@
+/*
+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 <http://www.gnu.org/licenses/>.
+*/
+
+#ifndef FALLOUTNVINFO_H
+#define FALLOUTNVINFO_H
+
+
+#include "gameinfo.h"
+
+
+class FalloutNVInfo : public GameInfo
+{
+
+ friend class GameInfo;
+
+public:
+
+ virtual ~FalloutNVInfo() {}
+
+ virtual unsigned long getBSAVersion();
+
+ static std::wstring getRegPathStatic();
+ virtual std::wstring getRegPath() { return FalloutNVInfo::getRegPathStatic(); }
+ virtual std::wstring getBinaryName() { return L"FalloutNV.exe"; }
+
+ virtual GameInfo::Type getType() { return TYPE_FALLOUTNV; }
+
+ virtual std::wstring getGameName() const { return L"New Vegas"; }
+ virtual std::wstring getGameShortName() const { return L"FalloutNV"; }
+
+// virtual bool requiresSteam() const { return true; }
+
+ virtual std::wstring getInvalidationBSA();
+
+ virtual bool isInvalidationBSA(const std::wstring &bsaName);
+
+ // full path to this games "My Games"-directory
+ virtual std::wstring getDocumentsDir();
+
+ virtual std::wstring getSaveGameDir();
+
+ virtual std::vector<std::wstring> getPrimaryPlugins();
+
+ // file name of this games ini (no path)
+ virtual std::vector<std::wstring> getIniFileNames();
+
+ virtual std::wstring getSaveGameExtension();
+ virtual std::wstring getReferenceDataFile();
+ virtual std::wstring getOMODExt();
+
+ virtual std::wstring getSteamAPPId();
+
+ virtual std::wstring getSEName();
+
+ virtual std::wstring getNexusPage();
+ static std::wstring getNexusInfoUrlStatic();
+ virtual std::wstring getNexusInfoUrl() { return FalloutNVInfo::getNexusInfoUrlStatic(); }
+ static int getNexusModIDStatic();
+ virtual int getNexusModID() { return FalloutNVInfo::getNexusModIDStatic(); }
+
+ virtual void createProfile(const std::wstring &directory, bool useDefaults);
+ virtual void repairProfile(const std::wstring &directory);
+
+ virtual bool rerouteToProfile(const wchar_t *fileName, const wchar_t *fullPath);
+
+ // get a list of executables (game binary and known-to-work 3rd party tools). All of these are relative to
+ // the game directory
+ virtual std::vector<ExecutableInfo> getExecutables();
+
+ virtual std::wstring archiveListKey() { return L"SArchiveList"; }
+
+private:
+
+ FalloutNVInfo(const std::wstring &omoDirectory, const std::wstring &gameDirectory);
+
+ static bool identifyGame(const std::wstring &searchPath);
+};
+
+#endif // FALLOUTNVINFO_H
diff --git a/src/shared/gameinfo.cpp b/src/shared/gameinfo.cpp
new file mode 100644
index 00000000..6054df8b
--- /dev/null
+++ b/src/shared/gameinfo.cpp
@@ -0,0 +1,204 @@
+/*
+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 <http://www.gnu.org/licenses/>.
+*/
+
+#include "gameinfo.h"
+
+#include "windows_error.h"
+
+#include "oblivioninfo.h"
+#include "fallout3info.h"
+#include "falloutnvinfo.h"
+#include "skyriminfo.h"
+#include "util.h"
+
+#include <shlobj.h>
+#include <sstream>
+#include <cassert>
+
+
+GameInfo* GameInfo::s_Instance = NULL;
+
+
+GameInfo::GameInfo(const std::wstring &omoDirectory, const std::wstring &gameDirectory)
+ : m_GameDirectory(gameDirectory), m_OrganizerDirectory(omoDirectory)
+{
+}
+
+
+void GameInfo::identifyMyGamesDirectory(const std::wstring &file)
+{
+ wchar_t myDocuments[MAX_PATH];
+ memset(myDocuments, '\0', MAX_PATH);
+
+ ::SHGetFolderPathW(NULL, CSIDL_PERSONAL, NULL, SHGFP_TYPE_CURRENT, myDocuments);
+
+ m_MyGamesDirectory.assign(myDocuments).append(L"\\My Games");
+ if (!FileExists(m_MyGamesDirectory.substr().append(L"/").append(file))) {
+ if (::SHGetFolderPathW(NULL, CSIDL_PERSONAL, NULL, SHGFP_TYPE_DEFAULT, myDocuments) != S_OK) {
+ m_MyGamesDirectory.assign(myDocuments).append(L"\\My Games");
+ }
+ }
+ if (!FileExists(m_MyGamesDirectory.substr().append(L"/").append(file))) {
+ m_MyGamesDirectory.assign(getSpecialPath(L"Personal")).append(L"\\My Games");
+ }
+}
+
+
+bool GameInfo::identifyGame(const std::wstring &omoDirectory, const std::wstring &searchPath)
+{
+ if (OblivionInfo::identifyGame(searchPath)) {
+ s_Instance = new OblivionInfo(omoDirectory, searchPath);
+ } else if (Fallout3Info::identifyGame(searchPath)) {
+ s_Instance = new Fallout3Info(omoDirectory, searchPath);
+ } else if (FalloutNVInfo::identifyGame(searchPath)) {
+ s_Instance = new FalloutNVInfo(omoDirectory, searchPath);
+ } else if (SkyrimInfo::identifyGame(searchPath)) {
+ s_Instance = new SkyrimInfo(omoDirectory, searchPath);
+ }
+
+ return s_Instance != NULL;
+}
+
+
+bool GameInfo::init(const std::wstring &omoDirectory, const std::wstring &gamePath)
+{
+ if (s_Instance == NULL) {
+ if (gamePath.length() == 0) {
+ // search upward in the directory until a recognized game-binary is found
+ std::wstring searchPath(omoDirectory);
+ while (!identifyGame(omoDirectory, searchPath)) {
+ size_t lastSep = searchPath.find_last_of(L"/\\");
+ if (lastSep == std::string::npos) {
+ return false;
+ }
+ searchPath.erase(lastSep);
+ }
+ } else if (!identifyGame(omoDirectory, gamePath)) {
+ return false;
+ }
+ }
+ return true;
+}
+
+
+GameInfo &GameInfo::instance()
+{
+ assert(s_Instance != NULL);
+ return *s_Instance;
+}
+
+std::wstring GameInfo::getGameDirectory() const
+{
+ return m_GameDirectory;
+}
+
+std::wstring GameInfo::getModsDir() const
+{
+ std::wostringstream temp;
+ temp << m_OrganizerDirectory << L"\\mods";
+ return temp.str();
+}
+
+std::wstring GameInfo::getProfilesDir() const
+{
+ std::wostringstream temp;
+ temp << m_OrganizerDirectory << L"\\profiles";
+ return temp.str();
+}
+
+std::wstring GameInfo::getIniFilename() const
+{
+ std::wostringstream temp;
+ temp << m_OrganizerDirectory << L"\\ModOrganizer.ini";
+ return temp.str();
+}
+
+
+std::wstring GameInfo::getDownloadDir() const
+{
+ std::wostringstream temp;
+ temp << m_OrganizerDirectory << L"\\downloads";
+ return temp.str();
+}
+
+
+std::wstring GameInfo::getCacheDir() const
+{
+ std::wostringstream temp;
+ temp << m_OrganizerDirectory << L"\\webcache";
+ return temp.str();
+}
+
+
+std::wstring GameInfo::getOverwriteDir() const
+{
+ std::wostringstream temp;
+ temp << m_OrganizerDirectory << "\\overwrite";
+ return temp.str();
+}
+
+
+std::wstring GameInfo::getLogDir() const
+{
+ std::wostringstream temp;
+ temp << m_OrganizerDirectory << "\\logs";
+ return temp.str();
+}
+
+
+std::wstring GameInfo::getTutorialDir() const
+{
+ std::wostringstream temp;
+ temp << m_OrganizerDirectory << "\\tutorials";
+ return temp.str();
+}
+
+
+bool GameInfo::requiresSteam() const
+{
+ return FileExists(getGameDirectory().append(L"\\steam_api.dll"));
+}
+
+
+std::wstring GameInfo::getLocalAppFolder() const
+{
+ return getSpecialPath(L"Local AppData");
+}
+
+std::wstring GameInfo::getSpecialPath(LPCWSTR name) const
+{
+ HKEY key;
+ LONG errorcode = ::RegOpenKeyEx(HKEY_CURRENT_USER, L"Software\\Microsoft\\Windows\\CurrentVersion\\Explorer\\User Shell Folders",
+ 0, KEY_QUERY_VALUE, &key);
+
+ if (errorcode != ERROR_SUCCESS) {
+ throw windows_error("failed to look up special folder", errorcode);
+ }
+
+ WCHAR temp[MAX_PATH];
+ DWORD bufferSize = MAX_PATH;
+
+ errorcode = ::RegQueryValueExW(key, name, NULL, NULL, (LPBYTE)temp, &bufferSize);
+ if (errorcode != ERROR_SUCCESS) {
+ throw windows_error("failed to look up special folder", errorcode);
+ }
+
+ return temp;
+}
+
diff --git a/src/shared/gameinfo.h b/src/shared/gameinfo.h
new file mode 100644
index 00000000..8828a2fc
--- /dev/null
+++ b/src/shared/gameinfo.h
@@ -0,0 +1,187 @@
+/*
+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 <http://www.gnu.org/licenses/>.
+*/
+
+#ifndef GAMEINFO_H
+#define GAMEINFO_H
+
+#include <string>
+#include <vector>
+#include <wchar.h>
+
+#define WIN32_LEAN_AND_MEAN
+#include <Windows.h>
+
+enum CloseMOStyle {
+ DEFAULT_CLOSE,
+ DEFAULT_STAY,
+ NEVER_CLOSE
+};
+
+struct ExecutableInfo {
+
+ ExecutableInfo(const std::wstring &aTitle, const std::wstring &aBinary,
+ const std::wstring &aArguments, const std::wstring &aWorkingDirectory, CloseMOStyle aCloseMO)
+ : title(aTitle), binary(aBinary), arguments(aArguments), workingDirectory(aWorkingDirectory),
+ closeMO(aCloseMO), steamAppID(L"") {}
+ ExecutableInfo(const std::wstring &aTitle, const std::wstring &aBinary,
+ const std::wstring &aArguments, const std::wstring &aWorkingDirectory,
+ CloseMOStyle aCloseMO, const std::wstring &aSteamAppID)
+ : title(aTitle), binary(aBinary), arguments(aArguments), workingDirectory(aWorkingDirectory),
+ closeMO(aCloseMO), steamAppID(aSteamAppID) {}
+ std::wstring title;
+ std::wstring binary;
+ std::wstring arguments;
+ std::wstring workingDirectory;
+ CloseMOStyle closeMO;
+ std::wstring steamAppID;
+};
+
+
+/**
+ Class to manage information that depends on the used game type. The intention is to keep
+ as much of the rest of omo is game-agnostic. It is mostly concerned with figuring out the
+ correct paths, filenames and data types.
+*/
+class GameInfo
+{
+
+public:
+
+ enum Type {
+ TYPE_OBLIVION,
+ TYPE_FALLOUT3,
+ TYPE_FALLOUTNV,
+ TYPE_SKYRIM
+ };
+
+ enum LoadOrderMechanism {
+ TYPE_FILETIME,
+ TYPE_PLUGINSTXT
+ };
+
+public:
+
+ virtual ~GameInfo() {}
+
+ std::wstring getOrganizerDirectory() { return m_OrganizerDirectory; }
+
+ virtual std::wstring getRegPath() = 0;
+ virtual std::wstring getBinaryName() = 0;
+
+ virtual unsigned long getBSAVersion() = 0;
+
+ virtual GameInfo::Type getType() = 0;
+
+ virtual std::wstring getGameName() const = 0;
+ virtual std::wstring getGameShortName() const = 0;
+
+ // full path to this games "My Games"-directory
+ virtual std::wstring getDocumentsDir() = 0;
+
+ virtual std::wstring getSaveGameDir() = 0;
+
+ /// determine the load order mechanism used by this game. this may throw an
+ /// exception if the mechanism can't be determined
+ virtual LoadOrderMechanism getLoadOrderMechanism() const { return TYPE_FILETIME; }
+
+ virtual std::wstring getGameDirectory() const;
+ // get absolute path to the directory where omo stores its mods
+ virtual std::wstring getModsDir() const;
+ // get absolute path to the directory where omo stores its profiles
+ virtual std::wstring getProfilesDir() const;
+
+ virtual std::wstring getIniFilename() const;
+ virtual std::wstring getDownloadDir() const;
+ virtual std::wstring getCacheDir() const;
+ virtual std::wstring getOverwriteDir() const;
+ virtual std::wstring getLogDir() const;
+ virtual std::wstring getTutorialDir() const;
+
+ virtual bool requiresBSAInvalidation() const { return true; }
+ virtual bool requiresSteam() const;
+
+ virtual std::wstring getInvalidationBSA() = 0;
+
+ virtual bool isInvalidationBSA(const std::wstring &bsaName) = 0;
+
+ // the key in the game's ini-file that defines the list of bsas to load
+ virtual std::wstring archiveListKey() = 0;
+
+ virtual std::vector<std::wstring> getPrimaryPlugins() = 0;
+
+ // file name of this games ini file(s)
+ virtual std::vector<std::wstring> getIniFileNames() = 0;
+
+ virtual std::wstring getSaveGameExtension() = 0;
+ virtual std::wstring getReferenceDataFile() = 0;
+
+ virtual std::wstring getOMODExt() = 0;
+
+ virtual std::wstring getSteamAPPId() = 0;
+
+ virtual std::wstring getSEName() = 0;
+
+ virtual std::wstring getNexusPage() = 0;
+ virtual std::wstring getNexusInfoUrl() = 0;
+ virtual int getNexusModID() = 0;
+
+ // clone relevant files to the specified directory
+ virtual void createProfile(const std::wstring &directory, bool useDefaults) = 0;
+ virtual void repairProfile(const std::wstring &directory) = 0;
+
+ virtual bool rerouteToProfile(const wchar_t *fileName, const wchar_t *fullPath) = 0;
+
+ // get a list of executables (game binary and known-to-work 3rd party tools). All of these are relative to
+ // the game directory. the boolean says whether omo should be closed when the executable is started
+ virtual std::vector<ExecutableInfo> getExecutables() = 0;
+
+public:
+
+ // initialise with the path to the omo directory (needs to be where hook.dll is stored). This
+ // needs to be called before the instance can be retrieved
+ static bool init(const std::wstring &omoDirectory, const std::wstring &gamePath = L"");
+
+ static GameInfo& instance();
+
+protected:
+
+ GameInfo(const std::wstring &omoDirectory, const std::wstring &gameDirectory);
+
+ std::wstring getLocalAppFolder() const;
+ const std::wstring &getMyGamesDirectory() const { return m_MyGamesDirectory; }
+ void identifyMyGamesDirectory(const std::wstring &file);
+
+private:
+
+ static bool identifyGame(const std::wstring &omoDirectory, const std::wstring &searchPath);
+ std::wstring getSpecialPath(LPCWSTR name) const;
+
+private:
+
+ static GameInfo* s_Instance;
+
+ std::wstring m_MyGamesDirectory;
+
+ std::wstring m_GameDirectory;
+ std::wstring m_OrganizerDirectory;
+
+};
+
+
+#endif // GAMEINFO_H
diff --git a/src/shared/inject.cpp b/src/shared/inject.cpp
new file mode 100644
index 00000000..8cedd247
--- /dev/null
+++ b/src/shared/inject.cpp
@@ -0,0 +1,142 @@
+/*
+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 <http://www.gnu.org/licenses/>.
+*/
+
+#include "inject.h"
+/*#if defined UNICODE && !defined _UNICODE
+#define _UNICODE 1
+#endif*/
+#include <wchar.h>
+#include <cstdio>
+#include <tchar.h>
+#include <cstdlib>
+#include <exception>
+#include <stdexcept>
+#include <string>
+#include "windows_error.h"
+#include "error_report.h"
+
+
+struct TParameters {
+ char dllname[MAX_PATH];
+ wchar_t profileName[101];
+ char initstr[5];
+ int logLevel;
+};
+
+
+typedef HMODULE (WINAPI *TLoadLibraryType)(LPCTSTR);
+typedef FARPROC (WINAPI *TGetProcAddressType)(HMODULE, LPCSTR);
+
+
+
+void injectDLL(HANDLE processHandle, HANDLE threadHandle, const std::string &dllname, const std::wstring &profileName, int logLevel)
+{
+ // prepare parameters that are to be passed to the injected dll
+ TParameters parameters;
+ memset(&parameters, '\0', sizeof(TParameters));
+ strncpy(parameters.dllname, dllname.c_str(), MAX_PATH - 1);
+ wcsncpy(parameters.profileName, profileName.c_str(), 100);
+ _snprintf(parameters.initstr, 5, "Init"); //this is the name of thie initialisation function we want to call in the target process
+
+ HMODULE k32mod = ::LoadLibrary(__TEXT("kernel32.dll"));
+ TLoadLibraryType loadLibraryFunc = NULL;
+ TGetProcAddressType getProcAddressFunc = NULL;
+ // ansi binaries
+ if (k32mod != NULL) {
+ loadLibraryFunc = reinterpret_cast<TLoadLibraryType>(::GetProcAddress(k32mod, "LoadLibraryA"));
+ getProcAddressFunc = reinterpret_cast<TGetProcAddressType>(::GetProcAddress(k32mod, "GetProcAddress"));
+ if ((loadLibraryFunc == NULL) || (getProcAddressFunc == NULL)) {
+ throw windows_error("failed to determine address for required functions");
+ }
+ } else {
+ throw windows_error("kernel32.dll not loaded??");
+ }
+
+ // allocate memory in the target process and write the parameter-block there
+ LPVOID remoteMem = ::VirtualAllocEx(processHandle, NULL, sizeof(TParameters), MEM_COMMIT | MEM_RESERVE, PAGE_READWRITE);
+ if (remoteMem == NULL) {
+ throw windows_error("failed to allocate memory in target process");
+ }
+ SIZE_T written;
+ if (!::WriteProcessMemory(processHandle, remoteMem, &parameters, sizeof(TParameters), &written) ||
+ (written != sizeof(TParameters))) {
+ throw windows_error("failed to write parameters to target process");
+ }
+
+ // now for the interesting part: write a stub into the target process that is run before any code of the original binary. This code will load
+ // our injected dll into the process and run its Init-function which takes the profile name as its parameter
+ // obviously this code is ultra-hacky
+
+ // construct the stub in beautiful assembler with placeholders
+ BYTE stubLocal[] = { 0x60, // PUSHAD
+ 0xB8, 0xBA, 0xAD, 0xF0, 0x0D, // MOV EAX, imm32 (LoadLibrary)
+ 0x68, 0xBA, 0xAD, 0xF0, 0x0D, // PUSH imm32 (dllname)
+ 0xFF, 0xD0, // CALL EAX (=LoadLibrary, leaves module handle of our dll in eax)
+ 0x68, 0xBA, 0xAD, 0xF0, 0x0D, // PUSH imm32 ("Init")
+ 0x50, // PUSH EAX
+ 0xB8, 0xBA, 0xAD, 0xF0, 0x0D, // MOVE EAX, imm32 (GetProcAddress)
+ 0xFF, 0xD0, // CALL EAX (GetProcAddress)
+ 0x68, 0xBA, 0xAD, 0xF0, 0x0D, // PUSH imm32 (profile name)
+ 0x68, 0xBA, 0xAD, 0xF0, 0x0D, // PUSH imm32 (log level)
+ 0xFF, 0xD0, // CALL EAX (=InitFunction)
+ 0x58, // POP EAX (init function is defined cdecl)
+ 0x58, // POP EAX (init function is defined cdecl)
+ 0x61, // POPAD
+ 0xE9, 0xBA, 0xAD, 0xF0, 0x0D // JMP near, relative (=original entry point)
+ };
+
+ // reserve memory for the stub
+ PBYTE stubRemote = reinterpret_cast<PBYTE>(::VirtualAllocEx(processHandle, NULL, sizeof(stubLocal), MEM_COMMIT | MEM_RESERVE, PAGE_EXECUTE_READWRITE));
+ if (stubRemote == NULL) {
+ throw windows_error("failed to allocate memory for stub");
+ }
+ TParameters *remoteParams = reinterpret_cast<TParameters*>(remoteMem);
+ // determine the remote addresses of each of the parameters
+// ULONG remoteDLLName = reinterpret_cast<ULONG>(remoteMem);
+// ULONG remoteInitString = remoteDLLName + MAX_PATH * sizeof(char);
+
+ // dizzy yet? we still have to calculate the entry point as an address relative to our stub
+ ULONG entryPoint = 0;
+
+ CONTEXT threadContext;
+ threadContext.ContextFlags = CONTEXT_CONTROL;
+ if (::GetThreadContext(threadHandle, &threadContext) == 0) {
+ throw windows_error("failed to access thread context. Please note that Mod Organizer does not support 64bit binaries!");
+ } else {
+ entryPoint = threadContext.Eip - (reinterpret_cast<ULONG>(stubRemote) + sizeof(stubLocal));
+ }
+ // now replace each baadf00d by the correct value. The pointers need to be the pointers in the REMOTE memory of course, that's why we copied them there
+ *(PULONG)&stubLocal[2] = reinterpret_cast<ULONG>(*loadLibraryFunc);
+ *(PULONG)&stubLocal[7] = reinterpret_cast<ULONG>(remoteParams->dllname);
+ *(PULONG)&stubLocal[14] = reinterpret_cast<ULONG>(remoteParams->initstr);
+ *(PULONG)&stubLocal[20] = reinterpret_cast<ULONG>(*getProcAddressFunc);
+ *(PULONG)&stubLocal[27] = reinterpret_cast<ULONG>(remoteParams->profileName);
+ *(PULONG)&stubLocal[32] = logLevel;
+ *(PULONG)&stubLocal[42] = entryPoint;
+ // almost there. copy stub to target process
+ if (!::WriteProcessMemory(processHandle, stubRemote, reinterpret_cast<LPCVOID>(stubLocal), sizeof(stubLocal), &written) ||
+ (written != sizeof(stubLocal))) {
+ throw windows_error("failed to write stub to target process");
+ }
+ // finally, make the stub the new next thing for the thread to execute
+ threadContext.Eip = (ULONG)stubRemote;
+ if (::SetThreadContext(threadHandle, &threadContext) == 0) {
+ throw windows_error("failed to overwrite thread context");
+ }
+}
diff --git a/src/shared/inject.h b/src/shared/inject.h
new file mode 100644
index 00000000..69c56cc6
--- /dev/null
+++ b/src/shared/inject.h
@@ -0,0 +1,27 @@
+/*
+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 <http://www.gnu.org/licenses/>.
+*/
+
+#pragma once
+
+#define WIN32_LEAN_AND_MEAN
+#include <windows.h>
+
+#include <string>
+
+void injectDLL(HANDLE processHandle, HANDLE threadHandle, const std::string &dllname, const std::wstring &profileName, int logLevel);
diff --git a/src/shared/oblivioninfo.cpp b/src/shared/oblivioninfo.cpp
new file mode 100644
index 00000000..c34b2202
--- /dev/null
+++ b/src/shared/oblivioninfo.cpp
@@ -0,0 +1,252 @@
+/*
+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 <http://www.gnu.org/licenses/>.
+*/
+
+#include "oblivioninfo.h"
+#include <tchar.h>
+#include <ShlObj.h>
+#include "util.h"
+#include <sstream>
+#include "windows_error.h"
+#include "error_report.h"
+#define WIN32_LEAN_AND_MEAN
+#include <Windows.h>
+#include <boost/assign.hpp>
+
+
+OblivionInfo::OblivionInfo(const std::wstring &omoDirectory, const std::wstring &gameDirectory)
+ : GameInfo(omoDirectory, gameDirectory)
+{
+ identifyMyGamesDirectory(L"oblivion");
+}
+
+bool OblivionInfo::identifyGame(const std::wstring &searchPath)
+{
+ return FileExists(searchPath, L"Oblivion.exe") &&
+ FileExists(searchPath, L"OblivionLauncher.exe");
+}
+
+unsigned long OblivionInfo::getBSAVersion()
+{
+ return 0x67;
+}
+
+std::wstring OblivionInfo::getRegPathStatic()
+{
+ HKEY key;
+ LONG errorcode = ::RegOpenKeyEx(HKEY_LOCAL_MACHINE, L"Software\\Bethesda Softworks\\Oblivion",
+ 0, KEY_QUERY_VALUE, &key);
+
+ if (errorcode != ERROR_SUCCESS) {
+ return L"";
+ }
+
+ WCHAR temp[MAX_PATH];
+ DWORD bufferSize = MAX_PATH;
+
+ errorcode = ::RegQueryValueExW(key, L"Installed Path", NULL, NULL, (LPBYTE)temp, &bufferSize);
+
+ return std::wstring(temp);
+}
+
+std::wstring OblivionInfo::getInvalidationBSA()
+{
+ return L"Oblivion - Invalidation.bsa";
+}
+
+bool OblivionInfo::isInvalidationBSA(const std::wstring &bsaName)
+{
+ static LPCWSTR invalidation[] = { L"Oblivion - Invalidation.bsa", L"ArchiveInvalidationInvalidated!.bsa",
+ L"BSARedirection.bsa", NULL };
+
+ for (int i = 0; invalidation[i] != NULL; ++i) {
+ if (wcscmp(bsaName.c_str(), invalidation[i]) == 0) {
+ return true;
+ }
+ }
+ return false;
+}
+
+std::wstring OblivionInfo::getDocumentsDir()
+{
+ std::wostringstream temp;
+ temp << getMyGamesDirectory() << L"\\Oblivion";
+
+ return temp.str();
+}
+
+std::wstring OblivionInfo::getSaveGameDir()
+{
+ std::wostringstream temp;
+ temp << getDocumentsDir() << L"\\Saves";
+ return temp.str();
+}
+
+
+std::vector<std::wstring> OblivionInfo::getPrimaryPlugins()
+{
+ return boost::assign::list_of(L"oblivion.esm");
+}
+
+
+std::vector<std::wstring> OblivionInfo::getIniFileNames()
+{
+ return boost::assign::list_of(L"oblivion.ini")(L"oblivionprefs.ini");
+}
+
+
+void OblivionInfo::createProfile(const std::wstring &directory, bool useDefaults)
+{
+ std::wostringstream target;
+
+ // copy plugins.txt
+ target << directory << "\\plugins.txt";
+
+ if (!FileExists(target.str())) {
+ std::wostringstream source;
+ source << getLocalAppFolder() << "\\Oblivion\\plugins.txt";
+ if (!::CopyFileW(source.str().c_str(), target.str().c_str(), true)) {
+ HANDLE file = ::CreateFileW(target.str().c_str(), GENERIC_WRITE, 0, NULL, CREATE_NEW, FILE_ATTRIBUTE_NORMAL, NULL);
+ ::CloseHandle(file);
+ }
+ }
+
+ // copy ini-file
+ target.str(L""); target.clear();
+ target << directory << L"\\oblivion.ini";
+
+ if (!FileExists(target.str())) {
+ std::wostringstream source;
+ source << getMyGamesDirectory() << L"\\Oblivion\\oblivion.ini";
+ if (useDefaults) {
+ source << getGameDirectory() << L"\\oblivion_default.ini";
+ } else {
+ source << getMyGamesDirectory() << L"Oblivion";
+ if (::FileExists(source.str(), L"oblivion.ini")) {
+ source << L"\\oblivion.ini";
+ } else {
+ source.str(L"");
+ source << getGameDirectory() << L"\\oblivion_default.ini";
+ }
+ }
+ if (!::CopyFileW(source.str().c_str(), target.str().c_str(), true)) {
+ if (::GetLastError() != ERROR_FILE_EXISTS) {
+ std::ostringstream stream;
+ stream << "failed to copy ini file: " << ToString(source.str(), false);
+ throw windows_error(stream.str());
+ }
+ }
+ }
+
+ { // copy oblivionprefs.ini-file
+ std::wstring target = directory.substr().append(L"\\oblivionprefs.ini");
+ if (!FileExists(target)) {
+ std::wostringstream source;
+ source << getMyGamesDirectory() << L"\\Oblivion\\oblivionprefs.ini";
+ if (!::CopyFileW(source.str().c_str(), target.c_str(), true)) {
+ if ((::CreateFileW(target.c_str(), GENERIC_WRITE, 0, NULL, CREATE_NEW, FILE_ATTRIBUTE_NORMAL, NULL) == INVALID_HANDLE_VALUE) &&
+ (::GetLastError() != ERROR_FILE_EXISTS)) {
+ std::ostringstream stream;
+ stream << "failed to create ini file: " << ToString(target.c_str(), false);
+ throw windows_error(stream.str());
+ }
+ }
+ }
+ }
+}
+
+
+std::wstring OblivionInfo::getSEName()
+{
+ return L"obse";
+}
+
+
+std::wstring OblivionInfo::getNexusPage()
+{
+ return L"http://oblivion.nexusmods.com";
+}
+
+
+std::wstring OblivionInfo::getNexusInfoUrlStatic()
+{
+ return L"http://oblivion.nexusmods.com";
+}
+
+
+int OblivionInfo::getNexusModIDStatic()
+{
+ return 38277;
+}
+
+
+void OblivionInfo::repairProfile(const std::wstring &directory)
+{
+ createProfile(directory, false);
+}
+
+
+bool OblivionInfo::rerouteToProfile(const wchar_t *fileName, const wchar_t*)
+{
+ static LPCWSTR profileFiles[] = { L"oblivion.ini", L"oblivionprefs.ini", L"plugins.txt", NULL };
+
+ for (int i = 0; profileFiles[i] != NULL; ++i) {
+ if (_wcsicmp(fileName, profileFiles[i]) == 0) {
+ return true;
+ }
+ }
+ return false;
+}
+
+
+std::wstring OblivionInfo::getSaveGameExtension()
+{
+ return L"*.ess";
+}
+
+std::wstring OblivionInfo::getReferenceDataFile()
+{
+ return L"Oblivion - Meshes.bsa";
+}
+
+
+std::wstring OblivionInfo::getOMODExt()
+{
+ return L"omod";
+}
+
+
+std::wstring OblivionInfo::getSteamAPPId()
+{
+ return L"22330";
+}
+
+
+std::vector<ExecutableInfo> OblivionInfo::getExecutables()
+{
+ std::vector<ExecutableInfo> result;
+ result.push_back(ExecutableInfo(L"OBSE", L"obse_loader.exe", L"", L"", DEFAULT_CLOSE));
+ result.push_back(ExecutableInfo(L"Oblivion", L"oblivion.exe", L"", L"", DEFAULT_CLOSE));
+ result.push_back(ExecutableInfo(L"Oblivion Mod Manager", L"OblivionModManager.exe", L"", L"", DEFAULT_CLOSE));
+ result.push_back(ExecutableInfo(L"Construction Set", L"TESConstructionSet.exe", L"", L"", DEFAULT_CLOSE));
+ result.push_back(ExecutableInfo(L"Oblivion Launcher", L"OblivionLauncher.exe", L"", L"", DEFAULT_CLOSE));
+ result.push_back(ExecutableInfo(L"BOSS", L"BOSS/BOSS.exe", L"", L"", NEVER_CLOSE));
+ result.push_back(ExecutableInfo(L"BOSS (old)", L"Data/BOSS.exe", L"", L"", NEVER_CLOSE));
+
+ return result;
+}
diff --git a/src/shared/oblivioninfo.h b/src/shared/oblivioninfo.h
new file mode 100644
index 00000000..0d6bba65
--- /dev/null
+++ b/src/shared/oblivioninfo.h
@@ -0,0 +1,92 @@
+/*
+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 <http://www.gnu.org/licenses/>.
+*/
+
+#ifndef OBLIVIONINFO_H
+#define OBLIVIONINFO_H
+
+#include "gameinfo.h"
+
+class OblivionInfo : public GameInfo
+{
+
+ friend class GameInfo;
+
+public:
+
+ virtual ~OblivionInfo() {}
+
+ virtual unsigned long getBSAVersion();
+
+ static std::wstring getRegPathStatic();
+ virtual std::wstring getRegPath() { return OblivionInfo::getRegPathStatic(); }
+ virtual std::wstring getBinaryName() { return L"Oblivion.exe"; }
+
+ virtual GameInfo::Type getType() { return TYPE_OBLIVION; }
+
+ virtual std::wstring getGameName() const { return L"Oblivion"; }
+ virtual std::wstring getGameShortName() const { return L"Oblivion"; }
+
+ virtual std::wstring getInvalidationBSA();
+
+ virtual bool isInvalidationBSA(const std::wstring &bsaName);
+
+ // full path to this games "My Games"-directory
+ virtual std::wstring getDocumentsDir();
+
+ virtual std::wstring getSaveGameDir();
+
+ virtual std::vector<std::wstring> getPrimaryPlugins();
+
+ // file name of this games ini (no path)
+ virtual std::vector<std::wstring> getIniFileNames();
+
+ virtual std::wstring getSaveGameExtension();
+ virtual std::wstring getReferenceDataFile();
+ virtual std::wstring getOMODExt();
+
+ virtual std::wstring getSteamAPPId();
+
+ virtual std::wstring getSEName();
+
+ virtual std::wstring getNexusPage();
+ static std::wstring getNexusInfoUrlStatic();
+ virtual std::wstring getNexusInfoUrl() { return OblivionInfo::getNexusInfoUrlStatic(); }
+ static int getNexusModIDStatic();
+ virtual int getNexusModID() { return OblivionInfo::getNexusModIDStatic(); }
+
+ virtual void createProfile(const std::wstring &directory, bool useDefaults);
+ virtual void repairProfile(const std::wstring &directory);
+
+ virtual bool rerouteToProfile(const wchar_t *fileName, const wchar_t *fullPath);
+
+ // get a list of executables (game binary and known-to-work 3rd party tools). All of these are relative to
+ // the game directory
+ virtual std::vector<ExecutableInfo> getExecutables();
+
+ virtual std::wstring archiveListKey() { return L"SArchiveList"; }
+
+private:
+
+ OblivionInfo(const std::wstring &omoDirectory, const std::wstring &gameDirectory);
+
+ static bool identifyGame(const std::wstring &searchPath);
+
+};
+
+#endif // OBLIVIONINFO_H
diff --git a/src/shared/shared.pro b/src/shared/shared.pro
new file mode 100644
index 00000000..3e1283e6
--- /dev/null
+++ b/src/shared/shared.pro
@@ -0,0 +1,56 @@
+#-------------------------------------------------
+#
+# Project created by QtCreator 2011-05-03T18:35:56
+#
+#-------------------------------------------------
+
+QT -= gui core
+#QT += core
+
+TARGET = mo_shared
+TEMPLATE = lib
+CONFIG += staticlib
+
+INCLUDEPATH += ../../../bsatk "$(BOOSTPATH)"
+
+CONFIG(debug, debug|release) {
+ LIBS += -L../../../bsatk-build/debug
+
+} else {
+ LIBS += -L../../../bsatk-build/release
+}
+
+LIBS += -lbsatk
+
+DEFINES += UNICODE _UNICODE _CRT_SECURE_NO_WARNINGS
+
+DEFINES += BOOST_DISABLE_ASSERTS NDEBUG
+
+# QMAKE_CXXFLAGS += /analyze
+
+SOURCES += \
+ inject.cpp \
+ windows_error.cpp \
+ error_report.cpp \
+ directoryentry.cpp \
+ gameinfo.cpp \
+ oblivioninfo.cpp \
+ fallout3info.cpp \
+ falloutnvinfo.cpp \
+ util.cpp \
+ skyriminfo.cpp \
+ appconfig.cpp
+
+HEADERS += \
+ inject.h \
+ windows_error.h \
+ error_report.h \
+ directoryentry.h \
+ gameinfo.h \
+ oblivioninfo.h \
+ fallout3info.h \
+ falloutnvinfo.h \
+ util.h \
+ skyriminfo.h \
+ appconfig.h \
+ appconfig.inc
diff --git a/src/shared/skyriminfo.cpp b/src/shared/skyriminfo.cpp
new file mode 100644
index 00000000..f3043c28
--- /dev/null
+++ b/src/shared/skyriminfo.cpp
@@ -0,0 +1,278 @@
+/*
+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 <http://www.gnu.org/licenses/>.
+*/
+
+#include "skyriminfo.h"
+
+#include "util.h"
+#include <tchar.h>
+#include <ShlObj.h>
+#include <sstream>
+#include "windows_error.h"
+#include "error_report.h"
+#define WIN32_LEAN_AND_MEAN
+#include <Windows.h>
+#include <boost/assign.hpp>
+
+
+SkyrimInfo::SkyrimInfo(const std::wstring &omoDirectory, const std::wstring &gameDirectory)
+ : GameInfo(omoDirectory, gameDirectory)
+{
+ identifyMyGamesDirectory(L"skyrim");
+}
+
+bool SkyrimInfo::identifyGame(const std::wstring &searchPath)
+{
+ return FileExists(searchPath, L"TESV.exe") &&
+ FileExists(searchPath, L"SkyrimLauncher.exe");
+}
+
+unsigned long SkyrimInfo::getBSAVersion()
+{
+ return 0x68;
+}
+
+
+std::wstring SkyrimInfo::getRegPathStatic()
+{
+ HKEY key;
+ LONG errorcode = ::RegOpenKeyEx(HKEY_LOCAL_MACHINE, L"Software\\Bethesda Softworks\\Skyrim",
+ 0, KEY_QUERY_VALUE, &key);
+
+ if (errorcode != ERROR_SUCCESS) {
+ return L"";
+ }
+
+ WCHAR temp[MAX_PATH];
+ DWORD bufferSize = MAX_PATH;
+
+ errorcode = ::RegQueryValueExW(key, L"Installed Path", NULL, NULL, (LPBYTE)temp, &bufferSize);
+
+ return std::wstring(temp);
+}
+
+
+std::wstring SkyrimInfo::getInvalidationBSA()
+{
+ return L"Skyrim - Invalidation.bsa";
+}
+
+bool SkyrimInfo::isInvalidationBSA(const std::wstring &bsaName)
+{
+ static LPCWSTR invalidation[] = { L"Skyrim - Invalidation.bsa", NULL };
+
+ for (int i = 0; invalidation[i] != NULL; ++i) {
+ if (wcscmp(bsaName.c_str(), invalidation[i]) == 0) {
+ return true;
+ }
+ }
+ return false;
+}
+
+
+GameInfo::LoadOrderMechanism SkyrimInfo::getLoadOrderMechanism() const
+{
+ std::wstring fileName = getGameDirectory().append(L"\\TESV.exe");
+
+ try {
+ VS_FIXEDFILEINFO versionInfo = GetFileVersion(fileName);
+ if ((versionInfo.dwFileVersionMS > 0x10004) || // version >= 1.5.x?
+ ((versionInfo.dwFileVersionMS == 0x10004) && (versionInfo.dwFileVersionLS >= 0x1A0000))) { // version >= ?.4.26
+ return TYPE_PLUGINSTXT;
+ } else {
+ return TYPE_FILETIME;
+ }
+ } catch (const std::exception &e) {
+ reportError("TESV.exe is invalid: %s", e.what());
+ return TYPE_FILETIME;
+ }
+}
+
+std::wstring SkyrimInfo::getDocumentsDir()
+{
+ std::wostringstream temp;
+ temp << getMyGamesDirectory() << L"\\Skyrim";
+
+ return temp.str();
+}
+
+std::wstring SkyrimInfo::getSaveGameDir()
+{
+ std::wostringstream temp;
+ temp << getDocumentsDir() << L"\\Saves";
+ return temp.str();
+}
+
+std::vector<std::wstring> SkyrimInfo::getPrimaryPlugins()
+{
+ return boost::assign::list_of(L"skyrim.esm")(L"update.esm");
+}
+
+std::vector<std::wstring> SkyrimInfo::getIniFileNames()
+{
+ return boost::assign::list_of(L"skyrim.ini")(L"skyrimprefs.ini");
+}
+
+std::wstring SkyrimInfo::getSaveGameExtension()
+{
+ return L"*.ess";
+}
+
+std::wstring SkyrimInfo::getReferenceDataFile()
+{
+ return L"Skyrim - Meshes.bsa";
+}
+
+
+std::wstring SkyrimInfo::getOMODExt()
+{
+ return L"fomod";
+}
+
+
+std::wstring SkyrimInfo::getSteamAPPId()
+{
+ return L"72850";
+}
+
+
+std::wstring SkyrimInfo::getSEName()
+{
+ return L"skse";
+}
+
+
+std::wstring SkyrimInfo::getNexusPage()
+{
+ return L"http://skyrim.nexusmods.com";
+}
+
+
+std::wstring SkyrimInfo::getNexusInfoUrlStatic()
+{
+ return L"http://skyrim.nexusmods.com";
+}
+
+
+int SkyrimInfo::getNexusModIDStatic()
+{
+ return 1334;
+}
+
+
+void SkyrimInfo::createProfile(const std::wstring &directory, bool useDefaults)
+{
+ { // copy plugins.txt
+ std::wstring target = directory.substr().append(L"\\plugins.txt");
+ if (!FileExists(target)) {
+ std::wostringstream source;
+ source << getLocalAppFolder() << "\\Skyrim\\plugins.txt";
+ if (!::CopyFileW(source.str().c_str(), target.c_str(), true)) {
+ HANDLE file = ::CreateFileW(target.c_str(), GENERIC_WRITE, 0, NULL, CREATE_NEW, FILE_ATTRIBUTE_NORMAL, NULL);
+ ::CloseHandle(file);
+ }
+ }
+ target = directory.substr().append(L"\\loadorder.txt");
+ if (!FileExists(target)) {
+ std::wostringstream source;
+ source << getLocalAppFolder() << "\\Skyrim\\loadorder.txt";
+ if (!::CopyFileW(source.str().c_str(), target.c_str(), true)) {
+ HANDLE file = ::CreateFileW(target.c_str(), GENERIC_WRITE, 0, NULL, CREATE_NEW, FILE_ATTRIBUTE_NORMAL, NULL);
+ ::CloseHandle(file);
+ }
+ }
+ }
+
+ { // copy skyrim.ini-file
+ std::wstring target = directory.substr().append(L"\\skyrim.ini");
+ if (!FileExists(target)) {
+ std::wostringstream source;
+ if (useDefaults) {
+ source << getGameDirectory() << L"\\skyrim_default.ini";
+ } else {
+ source << getMyGamesDirectory() << L"\\Skyrim";
+ if (::FileExists(source.str(), L"skyrim.ini")) {
+ source << L"\\skyrim.ini";
+ } else {
+ source.str(L"");
+ source << getGameDirectory() << L"\\skyrim_default.ini";
+ }
+ }
+ if (!::CopyFileW(source.str().c_str(), target.c_str(), true)) {
+ if (::GetLastError() != ERROR_FILE_EXISTS) {
+ std::ostringstream stream;
+ stream << "failed to copy ini file: " << ToString(source.str(), false);
+ throw windows_error(stream.str());
+ }
+ }
+ }
+ }
+
+ { // copy skyrimprefs.ini-file
+ std::wstring target = directory.substr().append(L"\\skyrimprefs.ini");
+ if (!FileExists(target)) {
+ std::wostringstream source;
+ source << getMyGamesDirectory() << L"\\Skyrim\\skyrimprefs.ini";
+ if (!::CopyFileW(source.str().c_str(), target.c_str(), true)) {
+ if (::GetLastError() != ERROR_FILE_EXISTS) {
+ std::ostringstream stream;
+ stream << "failed to copy ini file: " << ToString(source.str(), false);
+ throw windows_error(stream.str());
+ }
+ }
+ }
+ }
+}
+
+
+void SkyrimInfo::repairProfile(const std::wstring &directory)
+{
+ createProfile(directory, false);
+}
+
+
+bool SkyrimInfo::rerouteToProfile(const wchar_t *fileName, const wchar_t *fullPath)
+{
+ static LPCWSTR profileFiles[] = { L"skyrim.ini", L"skyrimprefs.ini", L"loadorder.txt", NULL };
+
+ for (int i = 0; profileFiles[i] != NULL; ++i) {
+ if (_wcsicmp(fileName, profileFiles[i]) == 0) {
+ return true;
+ }
+ }
+
+ if ((_wcsicmp(fileName, L"plugins.txt") == 0) &&
+ (wcsstr(fullPath, L"AppData") != NULL)){
+ return true;
+ }
+
+ return false;
+}
+
+
+std::vector<ExecutableInfo> SkyrimInfo::getExecutables()
+{
+ std::vector<ExecutableInfo> result;
+ result.push_back(ExecutableInfo(L"SKSE", L"skse_loader.exe", L"", L"", DEFAULT_CLOSE));
+ result.push_back(ExecutableInfo(L"SBW", L"SBW.exe", L"", L"", DEFAULT_CLOSE));
+ result.push_back(ExecutableInfo(L"Skyrim", L"TESV.exe", L"", L"", DEFAULT_CLOSE));
+ result.push_back(ExecutableInfo(L"BOSS", L"BOSS/BOSS.exe", L"", L"", DEFAULT_STAY));
+ result.push_back(ExecutableInfo(L"Creation Kit", L"CreationKit.exe", L"", L"", DEFAULT_STAY, L"202480"));
+
+ return result;
+}
diff --git a/src/shared/skyriminfo.h b/src/shared/skyriminfo.h
new file mode 100644
index 00000000..25de4029
--- /dev/null
+++ b/src/shared/skyriminfo.h
@@ -0,0 +1,100 @@
+/*
+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 <http://www.gnu.org/licenses/>.
+*/
+
+#ifndef SKYRIMINFO_H
+#define SKYRIMINFO_H
+
+
+#include "gameinfo.h"
+
+
+class SkyrimInfo : public GameInfo
+{
+
+ friend class GameInfo;
+
+public:
+
+ virtual ~SkyrimInfo() {}
+
+ virtual unsigned long getBSAVersion();
+
+ static std::wstring getRegPathStatic();
+ virtual std::wstring getRegPath() { return SkyrimInfo::getRegPathStatic(); }
+ virtual std::wstring getBinaryName() { return L"TESV.exe"; }
+
+ virtual GameInfo::Type getType() { return TYPE_SKYRIM; }
+
+ virtual std::wstring getGameName() const { return L"Skyrim"; }
+ virtual std::wstring getGameShortName() const { return L"Skyrim"; }
+
+ virtual LoadOrderMechanism getLoadOrderMechanism() const;
+
+ virtual bool requiresBSAInvalidation() const { return true; }
+// virtual bool requiresSteam() const { return true; }
+
+ virtual std::wstring getInvalidationBSA();
+
+ virtual bool isInvalidationBSA(const std::wstring &bsaName);
+
+ // full path to this games "My Games"-directory
+ virtual std::wstring getDocumentsDir();
+
+ virtual std::wstring getSaveGameDir();
+
+ virtual std::vector<std::wstring> getPrimaryPlugins();
+
+ // file name of this games ini (no path)
+ virtual std::vector<std::wstring> getIniFileNames();
+
+ virtual std::wstring getSaveGameExtension();
+ virtual std::wstring getReferenceDataFile();
+ virtual std::wstring getOMODExt();
+
+ virtual std::wstring getSteamAPPId();
+
+ virtual std::wstring getSEName();
+
+ virtual std::wstring getNexusPage();
+
+ static std::wstring getNexusInfoUrlStatic();
+ virtual std::wstring getNexusInfoUrl() { return SkyrimInfo::getNexusInfoUrlStatic(); }
+ static int getNexusModIDStatic();
+ virtual int getNexusModID() { return SkyrimInfo::getNexusModIDStatic(); }
+
+ virtual void createProfile(const std::wstring &directory, bool useDefaults);
+ virtual void repairProfile(const std::wstring &directory);
+
+ virtual bool rerouteToProfile(const wchar_t *fileName, const wchar_t *fullPath);
+
+ // get a list of executables (game binary and known-to-work 3rd party tools). All of these are relative to
+ // the game directory
+ virtual std::vector<ExecutableInfo> getExecutables();
+
+ virtual std::wstring archiveListKey() { return L"SResourceArchiveList"; }
+
+private:
+
+ SkyrimInfo(const std::wstring &omoDirectory, const std::wstring &gameDirectory);
+
+ static bool identifyGame(const std::wstring &searchPath);
+
+};
+
+#endif // SKYRIMINFO_H
diff --git a/src/shared/util.cpp b/src/shared/util.cpp
new file mode 100644
index 00000000..ec52dd38
--- /dev/null
+++ b/src/shared/util.cpp
@@ -0,0 +1,119 @@
+/*
+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 <http://www.gnu.org/licenses/>.
+*/
+
+#include "util.h"
+#include "windows_error.h"
+#include <sstream>
+#include <algorithm>
+
+
+bool FileExists(const std::wstring &filename)
+{
+ WIN32_FIND_DATAW findData;
+ ZeroMemory(&findData, sizeof(WIN32_FIND_DATAW));
+ HANDLE search = ::FindFirstFileExW(filename.c_str(), FindExInfoStandard, &findData, FindExSearchNameMatch, NULL, 0);
+ if (search == INVALID_HANDLE_VALUE) {
+ return false;
+ } else {
+ FindClose(search);
+ return true;
+ }
+}
+
+bool FileExists(const std::wstring &searchPath, const std::wstring &filename)
+{
+ std::wstringstream stream;
+ stream << searchPath << "\\" << filename;
+ return FileExists(stream.str());
+}
+
+std::string ToString(const std::wstring &source, bool utf8)
+{
+ char buffer[MAX_PATH];
+ if (utf8) {
+ ::WideCharToMultiByte(CP_UTF8, 0, source.c_str(), -1, buffer, MAX_PATH, NULL, NULL);
+ } else {
+ ::WideCharToMultiByte(GetACP(), 0, source.c_str(), -1, buffer, MAX_PATH, NULL, NULL);
+// wcstombs(buffer, source.c_str(), MAX_PATH);
+ }
+ return std::string(buffer);
+}
+
+std::wstring ToWString(const std::string &source, bool utf8)
+{
+ wchar_t buffer[MAX_PATH];
+ if (utf8) {
+ ::MultiByteToWideChar(CP_UTF8, 0, source.c_str(), -1, buffer, MAX_PATH);
+ } else {
+ // codepage encoding
+ ::MultiByteToWideChar(GetACP(), 0, source.c_str(), -1, buffer, MAX_PATH);
+// mbstowcs(buffer, source.c_str(), MAX_PATH);
+ }
+ return std::wstring(buffer);
+}
+
+std::string &ToLower(std::string &text)
+{
+ std::transform(text.begin(), text.end(), text.begin(), tolower);
+ return text;
+}
+
+
+std::wstring &ToLower(std::wstring &text)
+{
+ std::transform(text.begin(), text.end(), text.begin(), towlower);
+ return text;
+}
+
+std::wstring ToLower(const std::wstring &text)
+{
+ std::wstring temp = text;
+
+ std::transform(temp.begin(), temp.end(), temp.begin(), towlower);
+ return temp;
+}
+
+VS_FIXEDFILEINFO GetFileVersion(const std::wstring &fileName)
+{
+ DWORD size = ::GetFileVersionInfoSizeW(fileName.c_str(), NULL);
+ if (size == 0) {
+ throw windows_error("failed to determine file version info size");
+ }
+
+ void *buffer = new char[size];
+ try {
+ if (!::GetFileVersionInfoW(fileName.c_str(), 0UL, size, buffer)) {
+ throw windows_error("failed to determine file version info");
+ }
+
+ void *versionInfoPtr = NULL;
+ UINT versionInfoLength = 0;
+ if (!::VerQueryValue(buffer, L"\\", &versionInfoPtr, &versionInfoLength)) {
+ throw windows_error("failed to determine file version");
+ }
+
+ VS_FIXEDFILEINFO result = *(VS_FIXEDFILEINFO*)versionInfoPtr;
+ delete [] buffer;
+ return result;
+ } catch (...) {
+ delete [] buffer;
+ throw;
+ }
+}
+
diff --git a/src/shared/util.h b/src/shared/util.h
new file mode 100644
index 00000000..7f44b55d
--- /dev/null
+++ b/src/shared/util.h
@@ -0,0 +1,46 @@
+/*
+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 <http://www.gnu.org/licenses/>.
+*/
+
+#ifndef UTIL_H
+#define UTIL_H
+
+
+#include <string>
+#define WIN32_LEAN_AND_MEAN
+#include <Windows.h>
+
+
+static const int MAXPATH_UNICODE = 32767;
+
+
+bool FileExists(const std::wstring &filename);
+
+bool FileExists(const std::wstring &searchPath, const std::wstring &filename);
+
+std::string ToString(const std::wstring &source, bool utf8);
+std::wstring ToWString(const std::string &source, bool utf8);
+
+std::string &ToLower(std::string &text);
+std::wstring &ToLower(std::wstring &text);
+
+std::wstring ToLower(const std::wstring &text);
+
+VS_FIXEDFILEINFO GetFileVersion(const std::wstring &fileName);
+
+#endif // UTIL_H
diff --git a/src/shared/windows_error.cpp b/src/shared/windows_error.cpp
new file mode 100644
index 00000000..ad91db44
--- /dev/null
+++ b/src/shared/windows_error.cpp
@@ -0,0 +1,45 @@
+/*
+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 <http://www.gnu.org/licenses/>.
+*/
+
+#include "windows_error.h"
+#include <sstream>
+
+std::string windows_error::constructMessage(const std::string& input, int inErrorCode)
+{
+ std::ostringstream finalMessage;
+ finalMessage << input;
+
+ LPSTR buffer = NULL;
+
+ DWORD errorCode = inErrorCode != -1 ? inErrorCode : ::GetLastError();
+
+ // TODO: the message is not english?
+ if (FormatMessageA(FORMAT_MESSAGE_ALLOCATE_BUFFER | FORMAT_MESSAGE_FROM_SYSTEM,
+ NULL, errorCode, MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT), (LPSTR)&buffer, 0, NULL) == 0) {
+ finalMessage << " (errorcode " << errorCode << ")";
+ } else {
+ LPSTR lastChar = buffer + strlen(buffer) - 2;
+ *lastChar = '\0';
+ finalMessage << " (" << buffer << " [" << errorCode << "])";
+ LocalFree(buffer); // allocated by FormatMessage
+ }
+
+ ::SetLastError(errorCode); // restore error code because FormatMessage might have modified it
+ return finalMessage.str();
+}
diff --git a/src/shared/windows_error.h b/src/shared/windows_error.h
new file mode 100644
index 00000000..879ee1d2
--- /dev/null
+++ b/src/shared/windows_error.h
@@ -0,0 +1,40 @@
+/*
+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 <http://www.gnu.org/licenses/>.
+*/
+
+#ifndef WINDOWS_ERROR_H
+#define WINDOWS_ERROR_H
+
+
+#include <stdexcept>
+#define WIN32_LEAN_AND_MEAN
+#include <windows.h>
+
+class windows_error : public std::runtime_error {
+public:
+ windows_error(const std::string& message, int errorcode = -1)
+ : runtime_error(constructMessage(message, errorcode)), m_ErrorCode(errorcode)
+ {}
+ int getErrorCode() const { return m_ErrorCode; }
+private:
+ std::string constructMessage(const std::string& input, int errorcode);
+private:
+ int m_ErrorCode;
+};
+
+#endif // WINDOWS_ERROR_H