853 lines
26 KiB
C++
853 lines
26 KiB
C++
/********************************************************************************
|
||
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
|
||
}
|
||
|
||
typedef DWORD (WINAPI *GETFILEVERSIONINFOSIZEWPROC)(LPCWSTR module, LPDWORD unused);
|
||
typedef BOOL (WINAPI *GETFILEVERSIONINFOWPROC)(LPCWSTR module, DWORD unused, DWORD len, LPVOID data);
|
||
typedef BOOL (WINAPI *VERQUERYVALUEWPROC)(LPVOID data, LPCWSTR subblock, LPVOID *buf, PUINT sizeout);
|
||
|
||
static void LogWindowsVersion()
|
||
{
|
||
HMODULE ver_module = GetModuleHandleW(L"version");
|
||
if (!ver_module)
|
||
{
|
||
ver_module = LoadLibraryW(L"version");
|
||
if (!ver_module)
|
||
{
|
||
Log(L"Couldn't get version module");
|
||
return;
|
||
}
|
||
}
|
||
|
||
GETFILEVERSIONINFOSIZEWPROC pGetFileVersionInfoSizeW = (GETFILEVERSIONINFOSIZEWPROC)GetProcAddress(ver_module, "GetFileVersionInfoSizeW");
|
||
GETFILEVERSIONINFOWPROC pGetFileVersionInfoW = (GETFILEVERSIONINFOWPROC)GetProcAddress(ver_module, "GetFileVersionInfoW");
|
||
VERQUERYVALUEWPROC pVerQueryValueW = (VERQUERYVALUEWPROC)GetProcAddress(ver_module, "VerQueryValueW");
|
||
|
||
if (!pGetFileVersionInfoSizeW || !pGetFileVersionInfoW || !pVerQueryValueW)
|
||
{
|
||
Log(L"Couldn't get version functions");
|
||
return;
|
||
}
|
||
|
||
DWORD size = pGetFileVersionInfoSizeW(L"kernel32", 0);
|
||
VS_FIXEDFILEINFO *info = NULL;
|
||
UINT len;
|
||
|
||
if (!size)
|
||
{
|
||
Log(L"Couldn't get windows version info size");
|
||
return;
|
||
}
|
||
|
||
LPVOID data = malloc(size);
|
||
if (!pGetFileVersionInfoW(L"kernel32", 0, size, data))
|
||
{
|
||
Log(L"Couldn't get windows version info");
|
||
free(data);
|
||
return;
|
||
}
|
||
BOOL success = pVerQueryValueW(data, L"\\", (LPVOID*)&info, &len);
|
||
if (!success || !info || !len)
|
||
{
|
||
Log(L"Couldn't query version value");
|
||
free(data);
|
||
return;
|
||
}
|
||
|
||
WORD major = HIWORD(info->dwFileVersionMS);
|
||
WORD minor = LOWORD(info->dwFileVersionMS);
|
||
WORD build = HIWORD(info->dwFileVersionLS);
|
||
WORD revis = LOWORD(info->dwFileVersionLS);
|
||
|
||
Log(TEXT("Windows Version: %u.%u Build %u (revision %d)"), major, minor, build, revis);
|
||
free(data);
|
||
}
|
||
|
||
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);
|
||
}
|
||
|
||
LogWindowsVersion();
|
||
|
||
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;
|
||
|
||
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);
|
||
}
|
||
|
||
if (strSceneCollection.IsEmpty())
|
||
{
|
||
CopyFile(String() << lpAppDataPath << L"\\scenes.xconfig", String() << lpAppDataPath << L"\\sceneCollection\\scenes.xconfig", true);
|
||
strSceneCollection = L"scenes";
|
||
GlobalConfig->SetString(L"General", L"SceneCollection", strSceneCollection);
|
||
}
|
||
}
|
||
}
|
||
|
||
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;
|
||
LPWSTR userService = 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];
|
||
}
|
||
else if (scmpi(args[i], L"-installservice") == 0)
|
||
{
|
||
if (++i < numArgs)
|
||
{
|
||
bDisableMutex = true;
|
||
userService = 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());
|
||
|
||
String servicesPath = strAppDataPath + L"\\services";
|
||
if (!OSFileExists(servicesPath) && !OSCreateDirectory(servicesPath))
|
||
CrashError(TEXT("Couldn't create directory '%s'"), servicesPath.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);
|
||
}
|
||
|
||
RegisterServiceFileHandler();
|
||
}
|
||
|
||
if(lpAllocator)
|
||
{
|
||
delete GlobalConfig;
|
||
|
||
ResetXTAllocator(lpAllocator);
|
||
free(lpAllocator);
|
||
|
||
LoadGlobalIni();
|
||
}
|
||
|
||
//EnableMemoryTracking(true, 8961);
|
||
|
||
//-----------------------------------------------------
|
||
// load locale
|
||
|
||
if (!locale->LoadStringFile(TEXT("locale/en.txt")))
|
||
AppWarning(TEXT("Could not open locale string file '%s'"), TEXT("locale/en.txt"));
|
||
|
||
String strLanguage = GlobalConfig->GetString(TEXT("General"), TEXT("Language"), TEXT("en"));
|
||
if (!strLanguage.CompareI(TEXT("en")))
|
||
{
|
||
String langFile;
|
||
langFile << TEXT("locale/") << strLanguage << TEXT(".txt");
|
||
|
||
if (!locale->LoadStringFile(langFile))
|
||
AppWarning(TEXT("Could not open locale string file '%s'"), langFile.Array());
|
||
}
|
||
|
||
// install user service here after we've loaded XT and locale
|
||
if (userService)
|
||
{
|
||
if (!InstallUserService(userService))
|
||
return 1;
|
||
return 0;
|
||
}
|
||
|
||
//--------------------------------------------
|
||
|
||
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;
|
||
|
||
App->LoadAllPlugins();
|
||
|
||
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;
|
||
}
|