obs/Source/Main.cpp
jp9000 70cb064d32 Implement scene collections in menu only
This hopefully resolves some conflicting opinions about how scene
collections should be handled, and removes it from the general settings
panel, where it felt a bit cluttered.

Actually, I would kind of like it if profiles were handled this way too.
This feels a lot cleaner than how profiles are handled in the general
settings.  The profiles thing in the general settings definitely feels
clunky.

In the main menu, the scene collections menu now has the following menu
items:

- New
- Duplicate
- Rename
- Remove
- Import
- Export

Duplicate was something I personally added - I felt like it might be a
nice thing to have for the sake of convenience.  Import/Export was
requested by dodgepong, and is also definitely nice to have.  Due to how
easy new scene collections are to set up it didn't take very long to put
in.
2014-07-30 19:28:27 -07:00

752 lines
23 KiB
C++
Raw Blame History

This file contains invisible Unicode characters

This file contains invisible Unicode characters that are indistinguishable to humans but may be processed differently by a computer. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

/********************************************************************************
Copyright (C) 2012 Hugh Bailey <obs.jim@gmail.com>
This program 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 2 of the License, or
(at your option) any later version.
This program 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 this program; if not, write to the Free Software
Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307, USA.
********************************************************************************/
#include "Main.h"
#include <shellapi.h>
#include <shlobj.h>
#include <dwmapi.h>
#include <intrin.h>
#include <inttypes.h>
#include <gdiplus.h>
//----------------------------
HWND hwndMain = NULL;
HWND hwndRenderFrame = NULL;
HWND hwndLogWindow = NULL;
HWND hwndLog = NULL;
HINSTANCE hinstMain = NULL;
ConfigFile *GlobalConfig = NULL;
ConfigFile *AppConfig = NULL;
OBS *App = NULL;
bool bIsPortable = false;
bool bStreamOnStart = false;
TCHAR lpAppPath[MAX_PATH];
TCHAR lpAppDataPath[MAX_PATH];
//----------------------------
void InitSockets();
void TerminateSockets();
void LogVideoCardStats();
HANDLE hOBSMutex = NULL;
BOOL LoadSeDebugPrivilege()
{
DWORD err;
HANDLE hToken;
LUID Val;
TOKEN_PRIVILEGES tp;
if (!OpenProcessToken(GetCurrentProcess(), TOKEN_ADJUST_PRIVILEGES | TOKEN_QUERY, &hToken))
{
err = GetLastError();
return FALSE;
}
if (!LookupPrivilegeValue(NULL, SE_DEBUG_NAME, &Val))
{
err = GetLastError();
CloseHandle(hToken);
return FALSE;
}
tp.PrivilegeCount = 1;
tp.Privileges[0].Luid = Val;
tp.Privileges[0].Attributes = SE_PRIVILEGE_ENABLED;
if (!AdjustTokenPrivileges(hToken, FALSE, &tp, sizeof (tp), NULL, NULL))
{
err = GetLastError();
CloseHandle(hToken);
return FALSE;
}
CloseHandle(hToken);
return TRUE;
}
static void LogModule(CTSTR lpModuleName, HMODULE addr)
{
#ifdef _WIN64
Log(TEXT("%016X %s"), addr, lpModuleName);
#else
Log(TEXT("%08X %s"), addr, lpModuleName);
#endif
}
void LogSystemStats()
{
HKEY key;
TCHAR data[1024];
DWORD dwSize, dwSpeed;
zero(data, 1024);
if(RegOpenKey(HKEY_LOCAL_MACHINE, TEXT("HARDWARE\\DESCRIPTION\\System\\CentralProcessor\\0"), &key) != ERROR_SUCCESS)
{
AppWarning(TEXT("Could not open system information registry key"));
return;
}
#ifdef _WIN64
Log(TEXT("%s - 64bit ( ^ω^)"), OBS_VERSION_STRING);
#else
Log(TEXT("%s - 32bit (´・ω・`)"), OBS_VERSION_STRING);
#endif
Log(TEXT("-------------------------------"));
dwSize = 1024;
RegQueryValueEx(key, TEXT("ProcessorNameString"), NULL, NULL, (LPBYTE)data, &dwSize);
Log(TEXT("CPU Name: %s"), sfix(data));
dwSize = 4;
RegQueryValueEx(key, TEXT("~MHz"), NULL, NULL, (LPBYTE)&dwSpeed, &dwSize);
Log(TEXT("CPU Speed: %dMHz"), dwSpeed);
RegCloseKey(key);
MEMORYSTATUS ms;
GlobalMemoryStatus(&ms);
Log(TEXT("Physical Memory: %ldMB Total, %ldMB Free"), (ms.dwTotalPhys/1048576), (ms.dwAvailPhys/1048576));
int cpuInfo[4];
__cpuid(cpuInfo, 1);
BYTE cpuSteppingID = cpuInfo[0] & 0xF;
BYTE cpuModel = ((cpuInfo[0]>>4) & 0xF) + ((cpuInfo[0]>>12) & 0xF0);
BYTE cpuFamily = ((cpuInfo[0]>>8) & 0xF) + ((cpuInfo[0]>>20) & 0xFF);
BYTE cpuType = (cpuInfo[0]>>12) & 0x3;
BYTE cpuExtModel = (cpuInfo[0]>>17) & 0xF;
BYTE cpuExtFamily = (cpuInfo[0]>>21) & 0xFF;
BYTE cpuHTT = (cpuInfo[3]>>28) & 1;
Log(TEXT("stepping id: %u, model %u, family %u, type %u, extmodel %u, extfamily %u, HTT %u, logical cores %u, total cores %u"), cpuSteppingID, cpuModel, cpuFamily, cpuType, cpuExtModel, cpuExtFamily, cpuHTT, OSGetLogicalCores(), OSGetTotalCores());
for(UINT i=0; i<App->NumMonitors(); i++)
{
const MonitorInfo &info = App->GetMonitor(i);
Log(TEXT("monitor %u: pos={%d, %d}, size={%d, %d}"), i+1, info.rect.left, info.rect.top, info.rect.right-info.rect.left, info.rect.bottom-info.rect.top);
}
OSVERSIONINFO osvi;
osvi.dwOSVersionInfoSize = sizeof(osvi);
GetVersionEx(&osvi);
Log(TEXT("Windows Version: %u.%u Build %u %S"), osvi.dwMajorVersion, osvi.dwMinorVersion, osvi.dwBuildNumber, osvi.szCSDVersion);
BOOL bComposition;
DwmIsCompositionEnabled(&bComposition);
Log(TEXT("Aero is %s"), bComposition ? TEXT("Enabled") : TEXT("Disabled"));
HMODULE hOBS = GetModuleHandle(NULL);
HMODULE hOBSApi = GetModuleHandle(TEXT("OBSApi"));
Log(TEXT("-------------------------------"));
Log(TEXT("OBS Modules:"));
Log(TEXT("Base Address Module"));
LogModule(TEXT("OBS.exe"), hOBS);
LogModule(TEXT("OBSApi.dll"), hOBSApi);
for (UINT i=0; i<App->NumPlugins(); i++) {
const PluginInfo *info = App->GetPluginInfo(i);
LogModule(info->strFile, info->hModule);
}
LogVideoCardStats();
}
void InvertPre47Scenes()
{
String strScenesPath;
strScenesPath << lpAppDataPath << TEXT("\\scenes.xconfig");
XConfig scenesConfig;
if(scenesConfig.Open(strScenesPath))
{
XElement *scenes = scenesConfig.GetElement(TEXT("scenes"));
if(!scenes)
return;
UINT numScenes = scenes->NumElements();
for(UINT i=0; i<numScenes; i++)
{
XElement *scene = scenes->GetElementByID(i);
XElement *sources = scene->GetElement(TEXT("sources"));
if(!sources)
continue;
sources->ReverseOrder();
}
scenesConfig.Close(true);
}
}
String FindSceneCollection(String scenecollection)
{
String result = FormattedString(L"%s\\sceneCollection\\%s.xconfig", lpAppDataPath, scenecollection.Array());
if (OSFileExists(result))
return result;
result = FormattedString(L"%s\\%s.xconfig", lpAppDataPath, scenecollection.Array());
if (OSFileExists(result))
return result;
return String();
}
void SetupSceneCollection(CTSTR scenecollection)
{
String strSceneCollection = scenecollection ? scenecollection : GlobalConfig->GetString(TEXT("General"), TEXT("SceneCollection"));
String strXconfig;
if (scenecollection)
GlobalConfig->SetString(TEXT("General"), TEXT("SceneCollection"), scenecollection);
if (!strSceneCollection.IsValid() || FindSceneCollection(strSceneCollection).IsEmpty())
{
OSFindData ofd;
strXconfig.Clear() << lpAppDataPath << TEXT("\\sceneCollection\\*.xconfig");
HANDLE hFind = OSFindFirstFile(strXconfig, ofd);
if (hFind)
{
do
{
if (ofd.bDirectory) continue;
strSceneCollection = GetPathWithoutExtension(ofd.fileName);
GlobalConfig->SetString(TEXT("General"), TEXT("SceneCollection"), strSceneCollection);
break;
} while (OSFindNextFile(hFind, ofd));
OSFindClose(hFind);
}
}
}
void SetupIni(CTSTR profile)
{
//first, find out which profile we're using
String strProfile = profile ? profile : GlobalConfig->GetString(TEXT("General"), TEXT("Profile"));
DWORD lastVersion = GlobalConfig->GetInt(TEXT("General"), TEXT("LastAppVersion"));
String strIni;
if (profile)
GlobalConfig->SetString(TEXT("General"), TEXT("Profile"), profile);
//--------------------------------------------
// 0.47a fix (invert sources in all scenes)
if(lastVersion < 0x470)
InvertPre47Scenes();
//--------------------------------------------
// try to find and open the file, otherwise use the first one available
bool bFoundProfile = false;
if(strProfile.IsValid())
{
strIni << lpAppDataPath << TEXT("\\profiles\\") << strProfile << TEXT(".ini");
bFoundProfile = OSFileExists(strIni) != 0;
}
if(!bFoundProfile)
{
OSFindData ofd;
strIni.Clear() << lpAppDataPath << TEXT("\\profiles\\*.ini");
HANDLE hFind = OSFindFirstFile(strIni, ofd);
if(hFind)
{
do
{
if(ofd.bDirectory) continue;
strProfile = GetPathWithoutExtension(ofd.fileName);
GlobalConfig->SetString(TEXT("General"), TEXT("Profile"), strProfile);
bFoundProfile = true;
break;
} while(OSFindNextFile(hFind, ofd));
OSFindClose(hFind);
}
}
//--------------------------------------------
// open, or if no profile found, create one
if(bFoundProfile)
{
strIni.Clear() << lpAppDataPath << TEXT("\\profiles\\") << strProfile << TEXT(".ini");
if(AppConfig->Open(strIni))
return;
}
strProfile = TEXT("Untitled");
GlobalConfig->SetString(TEXT("General"), TEXT("Profile"), strProfile);
strIni.Clear() << lpAppDataPath << TEXT("\\profiles\\") << strProfile << TEXT(".ini");
if(!AppConfig->Create(strIni))
CrashError(TEXT("Could not create '%s'"), strIni.Array());
AppConfig->SetString(TEXT("Audio"), TEXT("Device"), TEXT("Default"));
AppConfig->SetFloat (TEXT("Audio"), TEXT("MicVolume"), 1.0f);
AppConfig->SetFloat (TEXT("Audio"), TEXT("DesktopVolume"), 1.0f);
AppConfig->SetInt (TEXT("Video"), TEXT("Monitor"), 0);
AppConfig->SetInt (TEXT("Video"), TEXT("FPS"), 30);
AppConfig->SetFloat (TEXT("Video"), TEXT("Downscale"), 1.0f);
AppConfig->SetInt (TEXT("Video"), TEXT("DisableAero"), 0);
AppConfig->SetInt (TEXT("Video Encoding"), TEXT("BufferSize"), 1000);
AppConfig->SetInt (TEXT("Video Encoding"), TEXT("MaxBitrate"), 1000);
AppConfig->SetString(TEXT("Video Encoding"), TEXT("Preset"), TEXT("veryfast"));
AppConfig->SetInt (TEXT("Video Encoding"), TEXT("Quality"), 8);
AppConfig->SetInt (TEXT("Audio Encoding"), TEXT("Format"), 1);
AppConfig->SetString(TEXT("Audio Encoding"), TEXT("Bitrate"), TEXT("128"));
AppConfig->SetInt (TEXT("Audio Encoding"), TEXT("isStereo"), 1);
AppConfig->SetInt (TEXT("Publish"), TEXT("Service"), 0);
AppConfig->SetInt (TEXT("Publish"), TEXT("Mode"), 0);
};
void LoadGlobalIni()
{
GlobalConfig = new ConfigFile;
String strGlobalIni;
strGlobalIni << lpAppDataPath << TEXT("\\global.ini");
if(!GlobalConfig->Open(strGlobalIni))
{
if(!GlobalConfig->Create(strGlobalIni))
CrashError(TEXT("Could not create '%s'"), strGlobalIni.Array());
//----------------------
// first, try to set the app to the system language, defaulting to english if the language doesn't exist
DWORD bufSize = GetLocaleInfoEx(LOCALE_NAME_USER_DEFAULT, LOCALE_SISO639LANGNAME, NULL, 0);
String str639Lang;
str639Lang.SetLength(bufSize);
GetLocaleInfoEx(LOCALE_NAME_USER_DEFAULT, LOCALE_SISO639LANGNAME, str639Lang.Array(), bufSize+1);
String strLangFile;
strLangFile << TEXT("locale/") << str639Lang << TEXT(".txt");
if(!OSFileExists(strLangFile))
str639Lang = TEXT("en");
//----------------------
GlobalConfig->SetString(TEXT("General"), TEXT("Language"), str639Lang);
GlobalConfig->SetInt(TEXT("General"), TEXT("MaxLogs"), 20);
}
}
void WINAPI ProcessEvents()
{
MSG msg;
while(PeekMessage(&msg, NULL, 0, 0, 1))
{
TranslateMessage(&msg);
DispatchMessage(&msg);
}
}
int HasSSE2Support ()
{
int cpuInfo[4];
__cpuid(cpuInfo, 1);
return (cpuInfo[3] & (1<<26)) != 0;
}
typedef BOOL (WINAPI *getUserModeExceptionProc)(LPDWORD);
typedef BOOL (WINAPI *setUserModeExceptionProc)(DWORD);
void InitializeExceptionHandler()
{
HMODULE k32;
//standard app-wide unhandled exception filter
SetUnhandledExceptionFilter(OBSExceptionHandler);
//fix for exceptions being swallowed inside callbacks (see KB976038)
k32 = GetModuleHandle(TEXT("KERNEL32"));
if (k32)
{
DWORD dwFlags;
getUserModeExceptionProc procGetProcessUserModeExceptionPolicy;
setUserModeExceptionProc procSetProcessUserModeExceptionPolicy;
procGetProcessUserModeExceptionPolicy = (getUserModeExceptionProc)GetProcAddress(k32, "GetProcessUserModeExceptionPolicy");
procSetProcessUserModeExceptionPolicy = (setUserModeExceptionProc)GetProcAddress(k32, "SetProcessUserModeExceptionPolicy");
if (procGetProcessUserModeExceptionPolicy && procSetProcessUserModeExceptionPolicy)
{
if (procGetProcessUserModeExceptionPolicy(&dwFlags))
procSetProcessUserModeExceptionPolicy(dwFlags & ~1);
}
}
}
void SetWorkingFolder(void)
{
String modulePath;
if (GetFileAttributes(TEXT("locale\\en.txt")) != INVALID_FILE_ATTRIBUTES)
return;
modulePath.SetLength(MAX_PATH);
if (GetModuleFileName(NULL, modulePath, modulePath.Length()-1))
{
TCHAR *p;
p = srchr(modulePath, '\\');
if (p)
*p = 0;
SetCurrentDirectory(modulePath);
}
}
int WINAPI WinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance, LPSTR lpCmdLine, int nShowCmd)
{
if (!HasSSE2Support())
{
OBSMessageBox (NULL, TEXT("OBS requires an SSE2-compatible CPU."), TEXT("Unsupported CPU"), MB_ICONERROR);
return 1;
}
#if defined _M_X64 && _MSC_VER == 1800
//workaround AVX2 bug in VS2013, http://connect.microsoft.com/VisualStudio/feedback/details/811093
_set_FMA3_enable(0);
#endif
LoadSeDebugPrivilege();
int numArgs;
LPWSTR *args = CommandLineToArgvW(GetCommandLineW(), &numArgs);
LPWSTR profile = NULL;
LPWSTR sceneCollection = NULL;
bool bDisableMutex = false;
for(int i=1; i<numArgs; i++)
{
if(scmpi(args[i], TEXT("-multi")) == 0)
bDisableMutex = true;
else if(scmpi(args[i], TEXT("-portable")) == 0)
bIsPortable = true;
else if (scmpi(args[i], TEXT("-start")) == 0)
bStreamOnStart = true;
else if (scmpi(args[i], L"-profile") == 0)
{
if (++i < numArgs)
profile = args[i];
}
else if (scmpi(args[i], L"-scenecollection") == 0)
{
if (++i < numArgs)
sceneCollection = args[i];
}
}
//------------------------------------------------------------
//make sure only one instance of the application can be open at a time
hOBSMutex = CreateMutex(NULL, TRUE, TEXT("OBSMutex"));
if(!bDisableMutex && GetLastError() == ERROR_ALREADY_EXISTS)
{
hwndMain = FindWindow(OBS_WINDOW_CLASS, NULL);
if(hwndMain)
SetForegroundWindow(hwndMain);
CloseHandle(hOBSMutex);
return 0;
}
//------------------------------------------------------------
hinstMain = hInstance;
HeapSetInformation(NULL, HeapEnableTerminationOnCorruption, NULL, 0);
SetProcessDEPPolicy(PROCESS_DEP_ENABLE | PROCESS_DEP_DISABLE_ATL_THUNK_EMULATION);
InitializeExceptionHandler();
ULONG_PTR gdipToken;
const Gdiplus::GdiplusStartupInput gdipInput;
Gdiplus::GdiplusStartup(&gdipToken, &gdipInput, NULL);
if(InitXT(NULL, TEXT("FastAlloc")))
{
InitSockets();
//CoInitializeEx(NULL, COINIT_MULTITHREADED);
CoInitialize(0);
EnableProfiling(TRUE);
//always make sure we're running inside our app folder so that locale files and plugins work
SetWorkingFolder();
//get current working dir
{
String strDirectory;
UINT dirSize = GetCurrentDirectory(0, 0);
strDirectory.SetLength(dirSize);
GetCurrentDirectory(dirSize, strDirectory.Array());
scpy(lpAppPath, strDirectory);
}
//if -portable isn't specified in command line check if there's a file named "obs_portable_mode" in current working dir, if so, obs goes into portable mode
if(!bIsPortable)
{
String strPMFileName = lpAppPath;
strPMFileName += TEXT("\\obs_portable_mode");
if(OSFileExists(strPMFileName))
bIsPortable = true;
}
TSTR lpAllocator = NULL;
{
if(bIsPortable)
scpy(lpAppDataPath, lpAppPath);
else
{
SHGetFolderPath(NULL, CSIDL_APPDATA, NULL, SHGFP_TYPE_CURRENT, lpAppDataPath);
scat_n(lpAppDataPath, TEXT("\\OBS"), 4);
}
if(!OSFileExists(lpAppDataPath) && !OSCreateDirectory(lpAppDataPath))
CrashError(TEXT("Couldn't create directory '%s'"), lpAppDataPath);
String strAppDataPath = lpAppDataPath;
String strProfilesPath = strAppDataPath + TEXT("\\profiles");
if(!OSFileExists(strProfilesPath) && !OSCreateDirectory(strProfilesPath))
CrashError(TEXT("Couldn't create directory '%s'"), strProfilesPath.Array());
String strSceneCollectionPath = strAppDataPath + TEXT("\\sceneCollection");
if (!OSFileExists(strSceneCollectionPath) && !OSCreateDirectory(strSceneCollectionPath))
CrashError(TEXT("Couldn't create directory '%s'"), strSceneCollectionPath.Array());
String strLogsPath = strAppDataPath + TEXT("\\logs");
if(!OSFileExists(strLogsPath) && !OSCreateDirectory(strLogsPath))
CrashError(TEXT("Couldn't create directory '%s'"), strLogsPath.Array());
String strCrashPath = strAppDataPath + TEXT("\\crashDumps");
if(!OSFileExists(strCrashPath) && !OSCreateDirectory(strCrashPath))
CrashError(TEXT("Couldn't create directory '%s'"), strCrashPath.Array());
String strPluginDataPath = strAppDataPath + TEXT("\\pluginData");
if(!OSFileExists(strPluginDataPath) && !OSCreateDirectory(strPluginDataPath))
CrashError(TEXT("Couldn't create directory '%s'"), strPluginDataPath.Array());
String strUpdatePath = strAppDataPath + TEXT("\\updates");
if(!OSFileExists(strUpdatePath) && !OSCreateDirectory(strUpdatePath))
CrashError(TEXT("Couldn't create directory '%s'"), strUpdatePath.Array());
LoadGlobalIni();
String strAllocator = GlobalConfig->GetString(TEXT("General"), TEXT("Allocator"));
if(strAllocator.IsValid())
{
UINT size = strAllocator.DataLength();
lpAllocator = (TSTR)malloc(size);
mcpy(lpAllocator, strAllocator.Array(), size);
}
}
if(lpAllocator)
{
delete GlobalConfig;
ResetXTAllocator(lpAllocator);
free(lpAllocator);
LoadGlobalIni();
}
//EnableMemoryTracking(true, 8961);
//--------------------------------------------
GlobalConfig->SetString(TEXT("General"), TEXT("LastAppDirectory"), lpAppPath);
//--------------------------------------------
AppConfig = new ConfigFile;
SetupIni(profile);
SetupSceneCollection(sceneCollection);
//--------------------------------------------
DWORD colors[16];
for (int i=0; i<16; i++) {
String strColorIdx = "Color";
strColorIdx << IntString(i);
colors[i] = GlobalConfig->GetInt(TEXT("CustomColors"), strColorIdx, 0xFFFFFF);
}
CCSetCustomColors(colors);
String strLogFileWildcard;
strLogFileWildcard << lpAppDataPath << TEXT("\\logs\\*.log");
OSFindData ofd;
HANDLE hFindLogs = OSFindFirstFile(strLogFileWildcard, ofd);
if(hFindLogs)
{
int numLogs = 0;
String strFirstLog;
do
{
if(ofd.bDirectory) continue;
if(!numLogs++)
strFirstLog << lpAppDataPath << TEXT("\\logs\\") << ofd.fileName;
} while(OSFindNextFile(hFindLogs, ofd));
OSFindClose(hFindLogs);
if(numLogs >= GlobalConfig->GetInt(TEXT("General"), TEXT("MaxLogs"), 20))
OSDeleteFile(strFirstLog);
}
SYSTEMTIME st;
GetLocalTime(&st);
String strLog;
strLog << lpAppDataPath << FormattedString(TEXT("\\logs\\%u-%02u-%02u-%02u%02u-%02u"), st.wYear, st.wMonth, st.wDay, st.wHour, st.wMinute, st.wSecond) << TEXT(".log");
InitXTLog(strLog);
//--------------------------------------------
BOOL bDisableComposition = AppConfig->GetInt(TEXT("Video"), TEXT("DisableAero"), 0);
if(bDisableComposition)
DwmEnableComposition(DWM_EC_DISABLECOMPOSITION);
//--------------------------------------------
String strCaptureHookLog;
strCaptureHookLog << lpAppDataPath << L"\\pluginData\\captureHookLog.txt";
OSFileChangeData *pGCHLogMF = NULL;
pGCHLogMF = OSMonitorFileStart (strCaptureHookLog, true);
App = new OBS;
HACCEL hAccel = LoadAccelerators(hinstMain, MAKEINTRESOURCE(IDR_ACCELERATOR1));
MSG msg;
while(GetMessage(&msg, NULL, 0, 0))
{
if(!TranslateAccelerator(hwndMain, hAccel, &msg) && !IsDialogMessage(hwndMain, &msg))
{
TranslateMessage(&msg);
DispatchMessage(&msg);
}
}
delete App;
//--------------------------------------------
CCGetCustomColors(colors);
for (int i=0; i<16; i++) {
String strColorIdx = "Color";
strColorIdx << IntString(i);
GlobalConfig->SetInt(TEXT("CustomColors"), strColorIdx, colors[i]);
}
GlobalConfig->SetInt(TEXT("General"), TEXT("LastAppVersion"), OBS_VERSION);
delete AppConfig;
delete GlobalConfig;
if(bDisableComposition)
DwmEnableComposition(DWM_EC_ENABLECOMPOSITION);
TerminateSockets();
bool skipGCHLog = false;
if(pGCHLogMF)
{
if(!OSFileHasChanged(pGCHLogMF))
skipGCHLog = true;
OSMonitorFileDestroy(pGCHLogMF);
}
//FIXME: File monitoring needs fixing. Half the time game capture logs are not
//getting attached even when users clearly used it.
if(true) //!skipGCHLog)
{
XFile captureHookLog;
if (captureHookLog.Open(strCaptureHookLog, XFILE_READ|XFILE_SHARED, XFILE_OPENEXISTING))
{
String strContents;
captureHookLog.ReadFileToString(strContents);
LogRaw(L"\r\n\r\nLast game capture log:");
LogRaw(strContents.Array(), strContents.Length());
}
}
}
Gdiplus::GdiplusShutdown(gdipToken);
TerminateXT();
//------------------------------------------------------------
CloseHandle(hOBSMutex);
LocalFree(args);
return 0;
}