mirror of
https://github.com/ModOrganizer2/modorganizer.git
synced 2026-07-27 13:58:24 -07:00
Compare commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
41249036e5 | ||
|
|
d69289e081 | ||
|
|
0adf8a9f1b | ||
|
|
05d03eec68 | ||
|
|
592fa73104 | ||
|
|
64ba6cae1e | ||
|
|
f71accc1ad | ||
|
|
44332c778d | ||
|
|
6a1d57fe0c | ||
|
|
777229f28f | ||
|
|
2eeaa0e31a | ||
|
|
9d3281fd58 | ||
|
|
1a468d66db | ||
|
|
19fe01b60d | ||
|
|
b6fac68b32 | ||
|
|
50d883079e | ||
|
|
e379610805 | ||
|
|
6dc736ca37 | ||
|
|
7791ea0bd5 |
@@ -0,0 +1,27 @@
|
||||
---
|
||||
name: Game support Request
|
||||
about: Request support for a new game
|
||||
title: Add support for game [GAME NAME]
|
||||
labels: 'additional games support, Feature Request, area: mo2 game plugins'
|
||||
assignees: ''
|
||||
|
||||
---
|
||||
|
||||
**This template is useful to add basic support using https://github.com/ModOrganizer2/modorganizer-basic_games.**
|
||||
|
||||
**If you are vaguely familiar with programming you can try following the instructions on that link to get something working yourself. If you have trouble with that please fill in this template.**
|
||||
|
||||
**Please provide the following information for the game (remove the optional lines you do not use):**
|
||||
|
||||
- **Name:** Name of the game
|
||||
- **Short name:** Name of the game in Nexus URL (e.g. skyrimse for Skyrim SE).
|
||||
- **Nexus ID [optional]:** ID of the game on Nexus (you can usually find this in Nexus URL).
|
||||
- **Executable:** Name of the main executable for the game (relative to the game folder).
|
||||
- **Launcher [optional]:** Name of the game launcher (relative to the game folder).
|
||||
- **Data path:** Path to the data folder of the game (relative to the game folder).
|
||||
- **Documents path:** Path to folder containing INI files, etc., for the game (usually under "My Games", or the game folder itself).
|
||||
- **Saves directory [optional]:** Path to the folder containing save games (this default to the path above).
|
||||
- **Save extension [optional]:** Extension of the saves
|
||||
- **Steam ID [optional]:** ID of the game on Steam (if the game is on Steam).
|
||||
|
||||
**Provide additional details regarding the game that could be required for the plugin.**
|
||||
+2
-1
@@ -1,6 +1,7 @@
|
||||
#ifndef ENV_ENVFS_H
|
||||
#define ENV_ENVFS_H
|
||||
|
||||
#include "thread_utils.h"
|
||||
#include <thread>
|
||||
|
||||
namespace env
|
||||
@@ -125,7 +126,7 @@ private:
|
||||
ThreadInfo()
|
||||
: busy(true), ready(false), stop(false)
|
||||
{
|
||||
thread = std::thread([&]{ run(); });
|
||||
thread = MOShared::startSafeThread([&]{ run(); });
|
||||
}
|
||||
|
||||
~ThreadInfo()
|
||||
|
||||
@@ -40,7 +40,7 @@ const QString& directoryFileType()
|
||||
const QString& cachedFileTypeNoExtension()
|
||||
{
|
||||
static const QString name = [] {
|
||||
const DWORD flags = SHGFI_TYPENAME;
|
||||
const DWORD flags = SHGFI_TYPENAME | SHGFI_USEFILEATTRIBUTES;
|
||||
SHFILEINFOW sfi = {};
|
||||
|
||||
// dummy filename with no extension
|
||||
|
||||
+2
-1
@@ -1,4 +1,5 @@
|
||||
#include "iconfetcher.h"
|
||||
#include "thread_utils.h"
|
||||
#include "shared/util.h"
|
||||
|
||||
void IconFetcher::Waiter::wait()
|
||||
@@ -25,7 +26,7 @@ IconFetcher::IconFetcher()
|
||||
m_quickCache.file = getPixmapIcon(QFileIconProvider::File);
|
||||
m_quickCache.directory = getPixmapIcon(QFileIconProvider::Folder);
|
||||
|
||||
m_thread = std::thread([&]{ threadFun(); });
|
||||
m_thread = MOShared::startSafeThread([&]{ threadFun(); });
|
||||
}
|
||||
|
||||
IconFetcher::~IconFetcher()
|
||||
|
||||
+33
-4
@@ -127,9 +127,10 @@ bool bootstrap()
|
||||
return true;
|
||||
}
|
||||
|
||||
LPTOP_LEVEL_EXCEPTION_FILTER prevUnhandledExceptionFilter = nullptr;
|
||||
thread_local LPTOP_LEVEL_EXCEPTION_FILTER prevUnhandledExceptionFilter = nullptr;
|
||||
thread_local std::terminate_handler prevTerminateHandler = nullptr;
|
||||
|
||||
static LONG WINAPI MyUnhandledExceptionFilter(struct _EXCEPTION_POINTERS *exceptionPtrs)
|
||||
LONG WINAPI MyUnhandledExceptionFilter(struct _EXCEPTION_POINTERS *exceptionPtrs)
|
||||
{
|
||||
const std::wstring& dumpPath = OrganizerCore::crashDumpsPath();
|
||||
int dumpRes =
|
||||
@@ -139,12 +140,39 @@ static LONG WINAPI MyUnhandledExceptionFilter(struct _EXCEPTION_POINTERS *except
|
||||
else
|
||||
log::error("ModOrganizer has crashed, CreateMiniDump failed ({}, error {}).", dumpRes, GetLastError());
|
||||
|
||||
if (prevUnhandledExceptionFilter)
|
||||
if (prevUnhandledExceptionFilter && exceptionPtrs)
|
||||
return prevUnhandledExceptionFilter(exceptionPtrs);
|
||||
else
|
||||
return EXCEPTION_CONTINUE_SEARCH;
|
||||
}
|
||||
|
||||
void terminateHandler() noexcept
|
||||
{
|
||||
__try
|
||||
{
|
||||
// force an exception to get a valid stack trace for this thread
|
||||
*(int*)0 = 42;
|
||||
}
|
||||
__except
|
||||
(
|
||||
MyUnhandledExceptionFilter(GetExceptionInformation()), EXCEPTION_EXECUTE_HANDLER
|
||||
)
|
||||
{
|
||||
}
|
||||
|
||||
if (prevTerminateHandler) {
|
||||
prevTerminateHandler();
|
||||
} else {
|
||||
std::abort();
|
||||
}
|
||||
}
|
||||
|
||||
void setUnhandledExceptionHandler()
|
||||
{
|
||||
prevUnhandledExceptionFilter = SetUnhandledExceptionFilter(MyUnhandledExceptionFilter);
|
||||
prevTerminateHandler = std::set_terminate(terminateHandler);
|
||||
}
|
||||
|
||||
// Parses the first parseArgCount arguments of the current process command line and returns
|
||||
// them in parsedArgs, the rest of the command line is returned untouched.
|
||||
LPCWSTR UntouchedCommandLineArguments(int parseArgCount, std::vector<std::wstring>& parsedArgs)
|
||||
@@ -171,6 +199,7 @@ LPCWSTR UntouchedCommandLineArguments(int parseArgCount, std::vector<std::wstrin
|
||||
return cmd;
|
||||
}
|
||||
|
||||
|
||||
static int SpawnWaitProcess(LPCWSTR workingDirectory, LPCWSTR commandLine) {
|
||||
PROCESS_INFORMATION pi{ 0 };
|
||||
STARTUPINFO si{ 0 };
|
||||
@@ -947,7 +976,7 @@ int main(int argc, char *argv[])
|
||||
application.setProperty("dataPath", dataPath);
|
||||
|
||||
// initialize dump collection only after "dataPath" since the crashes are stored under it
|
||||
prevUnhandledExceptionFilter = SetUnhandledExceptionFilter(MyUnhandledExceptionFilter);
|
||||
setUnhandledExceptionHandler();
|
||||
|
||||
const auto logFile =
|
||||
qApp->property("dataPath").toString() + "/logs/mo_interface.log";
|
||||
|
||||
@@ -221,7 +221,7 @@ void OrganizerCore::updateExecutablesList()
|
||||
void OrganizerCore::updateModInfoFromDisc() {
|
||||
ModInfo::updateFromDisc(
|
||||
m_Settings.paths().mods(), &m_DirectoryStructure,
|
||||
m_PluginContainer, m_Settings.interface().displayForeign(),
|
||||
m_PluginContainer, m_Settings.interface().displayForeign(),
|
||||
m_Settings.refreshThreadCount(), managedGame());
|
||||
}
|
||||
|
||||
@@ -371,7 +371,7 @@ void OrganizerCore::downloadRequestedNXM(const QString &url)
|
||||
}
|
||||
}
|
||||
|
||||
void OrganizerCore::userInterfaceInitialized()
|
||||
void OrganizerCore::userInterfaceInitialized()
|
||||
{
|
||||
m_UserInterfaceInitialized(m_UserInterface->mainWindow());
|
||||
}
|
||||
@@ -1139,7 +1139,7 @@ void OrganizerCore::refreshModList(bool saveChanges)
|
||||
|
||||
ModInfo::updateFromDisc(
|
||||
m_Settings.paths().mods(), &m_DirectoryStructure,
|
||||
m_PluginContainer, m_Settings.interface().displayForeign(),
|
||||
m_PluginContainer, m_Settings.interface().displayForeign(),
|
||||
m_Settings.refreshThreadCount(), managedGame());
|
||||
|
||||
m_CurrentProfile->refreshModStatus();
|
||||
@@ -1473,7 +1473,7 @@ void OrganizerCore::directory_refreshed()
|
||||
m_StructureDeleter.join();
|
||||
}
|
||||
|
||||
m_StructureDeleter = std::thread([=]{
|
||||
m_StructureDeleter = MOShared::startSafeThread([=]{
|
||||
log::debug("structure deleter thread start");
|
||||
delete newStructure;
|
||||
log::debug("structure deleter thread done");
|
||||
|
||||
@@ -65,7 +65,7 @@ private:
|
||||
OrganizerCore *m_Proxied;
|
||||
PluginContainer *m_PluginContainer;
|
||||
|
||||
const QString &m_PluginName;
|
||||
QString m_PluginName;
|
||||
|
||||
};
|
||||
|
||||
|
||||
+22
-3
@@ -1,12 +1,31 @@
|
||||
#ifndef MO2_THREAD_UTILS_H
|
||||
#define MO2_THREAD_UTILS_H
|
||||
|
||||
#include <log.h>
|
||||
#include <functional>
|
||||
#include <mutex>
|
||||
#include <thread>
|
||||
|
||||
// in main.cpp
|
||||
void setUnhandledExceptionHandler();
|
||||
LONG WINAPI MyUnhandledExceptionFilter(struct _EXCEPTION_POINTERS *exceptionPtrs);
|
||||
|
||||
|
||||
namespace MOShared {
|
||||
|
||||
// starts an std::thread with an unhandled exception handler for core dumps
|
||||
// and a top-level catch
|
||||
//
|
||||
template <class F>
|
||||
std::thread startSafeThread(F&& f)
|
||||
{
|
||||
return std::thread([f=std::forward<F>(f)] {
|
||||
setUnhandledExceptionHandler();
|
||||
f();
|
||||
});
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Class that can be used to perform thread-safe memoization.
|
||||
*
|
||||
@@ -26,7 +45,7 @@ struct MemoizedLocked {
|
||||
template <class Callable>
|
||||
MemoizedLocked(Callable &&callable, T value = {}) :
|
||||
m_Fn{ std::forward<Callable>(callable) }, m_Value{ std::move(value) } { }
|
||||
|
||||
|
||||
template <class... Args>
|
||||
T& value(Args&&... args) const {
|
||||
if (m_NeedUpdating) {
|
||||
@@ -66,7 +85,7 @@ private:
|
||||
*
|
||||
*/
|
||||
template <class It, class Callable>
|
||||
void parallelMap(It begin, It end, Callable callable, std::size_t nThreads)
|
||||
void parallelMap(It begin, It end, Callable callable, std::size_t nThreads)
|
||||
{
|
||||
std::mutex m;
|
||||
std::vector<std::thread> threads(nThreads);
|
||||
@@ -75,7 +94,7 @@ void parallelMap(It begin, It end, Callable callable, std::size_t nThreads)
|
||||
// - The mutex is only used to fetch/increment the iterator.
|
||||
// - The callable is copied in each thread to avoid conflicts.
|
||||
for (auto &thread: threads) {
|
||||
thread = std::thread([&m, &begin, end, callable]() {
|
||||
thread = startSafeThread([&m, &begin, end, callable]() {
|
||||
while (true) {
|
||||
decltype(begin) it;
|
||||
{
|
||||
|
||||
+2
-2
@@ -3,8 +3,8 @@
|
||||
// If VS_FF_PRERELEASE is not set, MO labels the build as a release and uses VER_FILEVERSION to determine version number.
|
||||
// Otherwise, if letters are used in VER_FILEVERSION_STR, uses the full MOBase::VersionInfo parser
|
||||
// Otherwise, uses the numbers from VER_FILEVERSION and sets the release type as pre-alpha
|
||||
#define VER_FILEVERSION 2,3,0
|
||||
#define VER_FILEVERSION_STR "2.3.0\0"
|
||||
#define VER_FILEVERSION 2,3,1
|
||||
#define VER_FILEVERSION_STR "2.3.1\0"
|
||||
|
||||
VS_VERSION_INFO VERSIONINFO
|
||||
FILEVERSION VER_FILEVERSION
|
||||
|
||||
Reference in New Issue
Block a user