Exemple #1
0
void init() {
    CLOG(L"Initializing...");

    delete vOSD;
    delete eOSD;

    Settings *settings = Settings::Instance();
    settings->Load();

    SkinManager::Instance()->LoadSkin(settings->SkinXML());

    /* TODO: Detect monitor changes, update this map, and reload/reorg OSDs */
    DisplayManager::UpdateMonitorMap();

    /* OSDs */
    eOSD = new EjectOSD();
    vOSD = new VolumeOSD();

    /* Hotkey setup */
    if (hkManager != NULL) {
        hkManager->Shutdown();
    }
    hkManager = HotkeyManager::Instance(mainWnd);

    hotkeys = Settings::Instance()->Hotkeys();
    for (auto it = hotkeys.begin(); it != hotkeys.end(); ++it) {
        int combination = it->first;
        hkManager->Register(combination);
    }

    WTSRegisterSessionNotification(mainWnd, NOTIFY_FOR_THIS_SESSION);
}
Exemple #2
0
Settings* GetSettings()
{
  static std::auto_ptr<Settings> settings;
  if (settings.get() == NULL)
  {
    Settings* newSettings = new Settings();
    newSettings->Load();
    settings.reset(newSettings);
  }
  return settings.get();
}
Exemple #3
0
// Konstruktor i destruktor
Dump::Dump():
    _window(new sf::RenderWindow),
    _title("Dump"),
    _version("v0.1")
{
    Settings *s = Settings::Create();
    s->Load();
    if (!s->AreLoaded())
        s->SetToDefaults();
    _window->Create(sf::VideoMode(s->GetScreenWidth(), s->GetScreenHeight(), 32), (_title + ' ') + _version);
    // TODO:
    // Obsługa fullscreena.
}
Exemple #4
0
void SettingsTest(String packageName)
{
	bool b = false;
	int i = 1;
	float f = 2.0f;
	double d = 3.0;
	char* cstr = strdup("cstring");
	String str("string");

	String appFileStoragePath = "/data/data/";
	appFileStoragePath += packageName;
	appFileStoragePath += "/files/";

	String FilePath = appFileStoragePath + "settingstest.json";

	LOG("Opening file");
	Settings* s = new Settings(FilePath);

	LOG("Deleting variables");
	s->DeleteVar("testbool");
	s->DeleteVar("testint");
	s->DeleteVar("testfloat");
	s->DeleteVar("testdouble");
	s->DeleteVar("testcstr");
	s->DeleteVar("teststr");

	LOG("Defining variables");
	s->Define("testbool",&b);
	s->Define("testint",&i);
	s->Define("testfloat",&f);
	s->Define("testdouble",&d);
	s->Define("testcstr",&cstr);
	s->Define("teststr",&str);

	LOG("Changing values");
	b = true;
	i = 5;
	cstr = strdup("changed cstr");

	LOG("Saving changes");
	s->SaveChanged();

	LOG("resetting values");
	b = false;
	i = 4;
	f = 6.0f;
	str.AssignString("changed String",strlen("changed String"));

	Array<const char*> onlysave;
	onlysave.PushBack("teststr");
	LOG("Saving only string value");
	s->SaveOnly(onlysave);

	LOG("Changing string value");
	cstr = strdup("changed cstr 2");
	str.AssignString("changed String 2",strlen("changed String 2"));

	LOG("Closing settings");
	delete(s);

	LOG("Opening file again");
	s = new Settings(FilePath);

	LOG("Defining stuff");
	s->Define("testint",&i);
	s->Define("testfloat",&f);
	s->Define("testdouble",&d);
	s->Define("testcstr",&cstr);
	s->Define("teststr",&str);

	LOG("Loading variables");
	s->Load();

	LOG("Checking values");

	assert( b == false ); // didn't define it
	assert( i == 5 ); // should revert
	assert( f == 6.0f ); // shouldn't have been saved
	assert( d == 3.0 ); // should stay same
	assert( strcmp(cstr,"changed cstr") == 0 ); // C-Strings revert ok?
	LOG("Test %s",str.ToCStr());
	assert( str == "changed String" ); // Saved on its own?
}
Exemple #5
0
int main(int argc, char *argv[]) {
	Settings ini;
	Game game;
	string gameStr;							// allow for autodetection override
	string bosslogFormat;
	fs::path sortfile;						//modlist/masterlist to sort plugins using.


	///////////////////////////////
	// Set up initial conditions
	///////////////////////////////

	LOG_INFO("BOSS starting...");

	LOG_INFO("Parsing Ini...");
	//Parse ini file if found. Can't just use BOOST's program options ini parser because of the CSS syntax and spaces.
	if (fs::exists(ini_path)) {
		try {
			ini.Load(ini_path);
		} catch (boss_error &e) {
			LOG_ERROR("Error: %s", e.getString().c_str());
			//Error will be added to log once format has been set.
		}
	} else {
		try {
			ini.Save(ini_path, AUTODETECT);
		} catch (boss_error &e) {
			ini.ErrorBuffer(ParsingError("Error: " + e.getString()));
		}
	}

	//Specify location of language dictionaries
	boost::locale::generator gen;
    gen.add_messages_path(l10n_path.string());
	gen.add_messages_domain("messages");

	//Set the locale to get encoding and language conversions working correctly.
	string localeId = "";
	if (gl_language == ENGLISH)
		localeId = "en.UTF-8";
	else if (gl_language == SPANISH)
		localeId = "es.UTF-8";
	else if (gl_language == GERMAN)
		localeId = "de.UTF-8";
	else if (gl_language == RUSSIAN)
		localeId = "ru.UTF-8";
	else if (gl_language == SIMPCHINESE)
		localeId = "zh.UTF-8";

	try {
		locale::global(gen(localeId));
		cout.imbue(locale());
	} catch(exception &e) {
		LOG_ERROR("could not implement translation: %s", e.what());
		cout << e.what() << endl;
	}
	locale global_loc = locale();
	locale loc(global_loc, new boost::filesystem::detail::utf8_codecvt_facet());
	boost::filesystem::path::imbue(loc);

	//////////////////////////////
	// Handle Command Line Args
	//////////////////////////////

	// declare the supported options
	po::options_description opts("Options");
    opts.add_options()
        ("help,h", translate("produces this help message").str().c_str())
        ("version,V", translate("prints the version banner").str().c_str())
        ("update,u", po::value(&gl_update)->zero_tokens(),
        translate("automatically update the local copy of the"
        " masterlist to the latest version"
        " available on the web before sorting").str().c_str())
        ("no-update,U", translate("inhibit the automatic masterlist updater").str().c_str())
        ("only-update,o", po::value(&gl_update_only)->zero_tokens(),
        translate("automatically update the local copy of the"
        " masterlist to the latest version"
        " available on the web but don't sort right"
        " now").str().c_str())
        ("silent,s", po::value(&gl_silent)->zero_tokens(),
        translate("don't launch a browser to show the HTML log"
        " at program completion").str().c_str())
        ("revert,r", po::value(&gl_revert)->implicit_value(1, ""),
        translate("revert to a previous load order.  this"
        " parameter optionally accepts values of 1 or"
        " 2, indicating how many undo steps to apply."
        "  if no option value is specified, it"
        " defaults to 1").str().c_str())
        ("verbose,v", po::value(&gl_debug_verbosity)->implicit_value(1, ""),
        translate("specify verbosity level (0-3) of the debugging output.  0 is the"
        " default, showing only WARN and ERROR messges."
        " 1 (INFO and above) is implied if this option"
        " is specified without an argument.  higher"
        " values increase the verbosity further").str().c_str())
        ("game,g", po::value(&gameStr),
        translate("override game autodetection.  valid values"
        " are: 'Oblivion', 'Nehrim', 'Fallout3',"
        " 'FalloutNV', and 'Skyrim'").str().c_str())
        ("crc-display,c", po::value(&gl_show_CRCs)->zero_tokens(),
        translate("show mod file CRCs, so that a file's CRC can be"
        " added to the masterlist in a conditional").str().c_str())
        ("format,f", po::value(&bosslogFormat),
        translate("select output format. valid values"
        " are: 'html', 'text'").str().c_str())
        ("trial-run,t", po::value(&gl_trial_run)->zero_tokens(),
        translate("run BOSS without actually making any changes to load order").str().c_str());

	// parse command line arguments
	po::variables_map vm;
	try{
		po::store(po::command_line_parser(argc, argv).options(opts).run(), vm);
		po::notify(vm);
	}catch (po::multiple_occurrences &){
		LOG_ERROR("cannot specify options multiple times; please use the '--help' option to see usage instructions");
		Fail();
	}catch (exception & e){
		LOG_ERROR("%s; please use the '--help' option to see usage instructions", e.what());
		Fail();
	}

	// set alternative output stream for logger and whether to track log statement origins
    if (gl_debug_verbosity > 0)
	    g_logger.setStream(debug_log_path.string().c_str());
	if (gl_debug_verbosity < 0) {
		LOG_ERROR("invalid option for 'verbose' parameter: %d", gl_debug_verbosity);
		Fail();
	}
	g_logger.setVerbosity(static_cast<LogVerbosity>(LV_WARN + gl_debug_verbosity));  // it's ok if this number is too high.  setVerbosity will handle it

	if (vm.count("help")) {
		ShowUsage(opts);
		exit(0);
	}
	if (vm.count("version")) {
		ShowVersion();
		exit(0);
	}
	if (vm.count("no-update")) {
		gl_update = false;
	}
	if ((vm.count("update")) && (vm.count("no-update"))) {
		LOG_ERROR("invalid options: --update,-u and --no-update,-U cannot both be given.");
		Fail();
	}
	if (vm.count("revert") && (gl_revert < 1 || gl_revert > 2)) {
		LOG_ERROR("invalid option for 'revert' parameter: %d", gl_revert);
		Fail();
	}
	if (vm.count("game")) {
		// sanity check and parse argument
		if      (boost::iequals("Oblivion",   gameStr))
			gl_game = OBLIVION;
		else if (boost::iequals("Fallout3",   gameStr))
			gl_game = FALLOUT3;
		else if (boost::iequals("Nehrim",     gameStr))
			gl_game = NEHRIM;
		else if (boost::iequals("FalloutNV", gameStr))
			gl_game = FALLOUTNV;
		else if (boost::iequals("Skyrim", gameStr))
			gl_game = SKYRIM;
		else {
			LOG_ERROR("invalid option for 'game' parameter: '%s'", gameStr.c_str());
			Fail();
		}
		LOG_DEBUG("game ini setting overridden with: '%s' (%d)", gameStr.c_str(), gl_game);
	}
	if (vm.count("format")) {
		// sanity check and parse argument
		string bosslogFormat = vm["format"].as<string>();
		if (bosslogFormat == "html")
			gl_log_format = HTML;
		else if (bosslogFormat == "text")
			gl_log_format = PLAINTEXT;
		else {
			LOG_ERROR("invalid option for 'format' parameter: '%s'", bosslogFormat.c_str());
			Fail();
		}
		LOG_DEBUG("BOSSlog format set to: '%s'", bosslogFormat.c_str());
	}


	/////////////////////////////////////////
	// Check for critical error conditions
	/////////////////////////////////////////

	//Game checks.
	LOG_DEBUG("Detecting game...");
	try {
		gl_last_game = AUTODETECT;  //Clear this setting in case the GUI was run.
		vector<uint32_t> detected, undetected;
		uint32_t detectedGame = DetectGame(detected, undetected);
		if (detectedGame == AUTODETECT) {
			//Now check what games were found.
			if (detected.empty())
				throw boss_error(BOSS_ERROR_NO_GAME_DETECTED);
			else if (detected.size() == 1)
				detectedGame = detected.front();
			else {
				size_t ans;
				//Ask user to choose game.
				cout << endl << translate("Please pick which game to run BOSS for:") << endl;
				for (size_t i=0; i < detected.size(); i++)
					cout << i << " : " << Game(detected[i], "", true).Name() << endl;

				cin >> ans;
				if (ans < 0 || ans >= detected.size()) {
					cout << translate("Invalid selection.") << endl;
					throw boss_error(BOSS_ERROR_NO_GAME_DETECTED);
				}
				detectedGame = detected[ans];
			}
		}
		game = Game(detectedGame);
		game.CreateBOSSGameFolder();
		LOG_INFO("Game detected: %s", game.Name().c_str());
	} catch (boss_error &e) {
		LOG_ERROR("Critical Error: %s", e.getString().c_str());
		game.bosslog.SetFormat(gl_log_format);
		game.bosslog.criticalError << LIST_ITEM_CLASS_ERROR << translate("Critical Error: ") << e.getString() << LINE_BREAK
			<< translate("Check the Troubleshooting section of the ReadMe for more information and possible solutions.") << LINE_BREAK
			<< translate("Utility will end now.");
		try {
			game.bosslog.Save(game.Log(gl_log_format), true);
		} catch (boss_error &e) {
			LOG_ERROR("Critical Error: %s", e.getString().c_str());
		}
		if ( !gl_silent )
			Launch(game.Log(gl_log_format).string());	//Displays the BOSSlog.txt.
		exit (1); //fail in screaming heap.
	}
	game.bosslog.SetFormat(gl_log_format);
	game.bosslog.parsingErrors.push_back(ini.ErrorBuffer());


	/////////////////////////////////////////////////////////
	// Update masterlist
	/////////////////////////////////////////////////////////

	if (gl_revert < 1 && (gl_update || gl_update_only)) {
		cout << endl << translate("Updating to the latest masterlist from the online repository...") << endl;
		LOG_DEBUG("Updating masterlist...");
		try {
            string revision = UpdateMasterlist(game, progress, NULL);
            string message = (boost::format(translate("Masterlist updated; at revision: %1%.")) % revision).str();
			game.bosslog.updaterOutput << LIST_ITEM_CLASS_SUCCESS << message;
			cout << endl << message << endl;
		} catch (boss_error &e) {
            game.bosslog.updaterOutput << LIST_ITEM_CLASS_ERROR << translate("Error: masterlist update failed.") << LINE_BREAK
                << (boost::format(translate("Details: %1%")) % e.getString()).str() << LINE_BREAK;
			LOG_ERROR("Error: masterlist update failed. Details: %s", e.getString().c_str());
		}
    }
    else {
        string revision = GetMasterlistVersion(game);
        string message = (boost::format(translate("Masterlist updating disabled; at revision: %1%.")) % revision).str();
        game.bosslog.updaterOutput << LIST_ITEM_CLASS_SUCCESS << message;
    }

	//If true, exit BOSS now. Flush earlyBOSSlogBuffer to the bosslog and exit.
	if (gl_update_only) {
		try {
			game.bosslog.Save(game.Log(gl_log_format), true);
		} catch (boss_error &e) {
			LOG_ERROR("Critical Error: %s", e.getString().c_str());
		}
		if ( !gl_silent )
			Launch(game.Log(gl_log_format).string());	//Displays the BOSSlog.
		return (0);
	}


	///////////////////////////////////
	// Resume Error Condition Checks
	///////////////////////////////////

	cout << endl << translate("BOSS working...") << endl;

	//Build and save modlist.
	try {
		game.modlist.Load(game, game.DataFolder());
		if (gl_revert < 1)
			game.modlist.Save(game.Modlist(), game.OldModlist());
	} catch (boss_error &e) {
		LOG_ERROR("Failed to load/save modlist, error was: %s", e.getString().c_str());
		game.bosslog.criticalError << LIST_ITEM_CLASS_ERROR << (boost::format(translate("Critical Error: %1%")) % e.getString()).str() << LINE_BREAK
			<< translate("Check the Troubleshooting section of the ReadMe for more information and possible solutions.") << LINE_BREAK
			<< translate("Utility will end now.");
		try {
			game.bosslog.Save(game.Log(gl_log_format), true);
		} catch (boss_error &e) {
			LOG_ERROR("Critical Error: %s", e.getString().c_str());
		}
		if ( !gl_silent )
			Launch(game.Log(gl_log_format).string());	//Displays the BOSSlog.txt.
		exit (1); //fail in screaming heap.
	}


	/////////////////////////////////
	// Parse Master- and Userlists
	/////////////////////////////////
	//masterlist parse errors are critical, ini and userlist parse errors are not.

	//Set masterlist path to be used.
	if (gl_revert == 1)
		sortfile = game.Modlist();
	else if (gl_revert == 2)
		sortfile = game.OldModlist();
	else
		sortfile = game.Masterlist();
	LOG_INFO("Using sorting file: %s", sortfile.string().c_str());

	//Parse masterlist/modlist backup into data structure.
	try {
		LOG_INFO("Starting to parse sorting file: %s", sortfile.string().c_str());
		game.masterlist.Load(game, sortfile);
		LOG_INFO("Starting to parse conditionals from sorting file: %s", sortfile.string().c_str());
		game.masterlist.EvalConditions(game);
		game.masterlist.EvalRegex(game);
		game.bosslog.globalMessages = game.masterlist.GlobalMessageBuffer();
		game.bosslog.parsingErrors.push_back(game.masterlist.ErrorBuffer());
	} catch (boss_error &e) {
		LOG_ERROR("Critical Error: %s", e.getString().c_str());
        if (e.getCode() == BOSS_ERROR_FILE_PARSE_FAIL)
			game.bosslog.criticalError << game.masterlist.ErrorBuffer();
		else if (e.getCode() == BOSS_ERROR_CONDITION_EVAL_FAIL)
			game.bosslog.criticalError << LIST_ITEM_CLASS_ERROR << e.getString();
		else
			game.bosslog.criticalError << LIST_ITEM_CLASS_ERROR << (boost::format(translate("Critical Error: %1%")) % e.getString()).str() << LINE_BREAK
				<< translate("Check the Troubleshooting section of the ReadMe for more information and possible solutions.") << LINE_BREAK
				<< translate("Utility will end now.");
		try {
			game.bosslog.Save(game.Log(gl_log_format), true);
		} catch (boss_error &e) {
			LOG_ERROR("Critical Error: %s", e.getString().c_str());
		}
		if ( !gl_silent )
                Launch(game.Log(gl_log_format).string());  //Displays the BOSSlog.txt.
        exit (1); //fail in screaming heap.
	}

	LOG_INFO("Starting to parse userlist.");
	try {
		game.userlist.Load(game, game.Userlist());
		vector<ParsingError> errs = game.userlist.ErrorBuffer();
		game.bosslog.parsingErrors.insert(game.bosslog.parsingErrors.end(), errs.begin(), errs.end());
	} catch (boss_error &e) {
		vector<ParsingError> errs = game.userlist.ErrorBuffer();
		game.bosslog.parsingErrors.insert(game.bosslog.parsingErrors.end(), errs.begin(), errs.end());
		game.userlist.Clear();  //If userlist has parsing errors, empty it so no rules are applied.
		LOG_ERROR("Error: %s", e.getString().c_str());
	}

	//////////////////////////////////
	// Perform sorting functionality
	//////////////////////////////////

	try {
		game.ApplyMasterlist();
		LOG_INFO("masterlist now filled with ordered mods and modlist filled with unknowns.");
		game.ApplyUserlist();
		LOG_INFO("userlist sorting process finished.");
		game.ScanSEPlugins();
		game.SortPlugins();
		game.bosslog.Save(game.Log(gl_log_format), true);
	} catch (boss_error &e) {
		LOG_ERROR("Critical Error: %s", e.getString().c_str());
		game.bosslog.criticalError << LIST_ITEM_CLASS_ERROR << (boost::format(translate("Critical Error: %1%")) % e.getString()).str() << LINE_BREAK
			<< translate("Check the Troubleshooting section of the ReadMe for more information and possible solutions.") << LINE_BREAK
			<< translate("Utility will end now.");
		try {
			game.bosslog.Save(game.Log(gl_log_format), true);
		} catch (boss_error &e) {
			LOG_ERROR("Critical Error: %s", e.getString().c_str());
		}
		if ( !gl_silent )
                Launch(game.Log(gl_log_format).string());  //Displays the BOSSlog.txt.
        exit (1); //fail in screaming heap.
	}

	LOG_INFO("Launching boss log in browser.");
	if ( !gl_silent )
		Launch(game.Log(gl_log_format).string());	//Displays the BOSSlog.txt.
	LOG_INFO("BOSS finished.");
	return (0);
}
Exemple #6
0
int APIENTRY wWinMain(
        _In_ HINSTANCE hInstance,
        _In_opt_ HINSTANCE hPrevInstance,
        _In_ LPTSTR lpCmdLine,
        _In_ int nCmdShow) {

	UNREFERENCED_PARAMETER(hPrevInstance);
	UNREFERENCED_PARAMETER(lpCmdLine);

    Logger::Start();
    CLOG(L"Starting SettingsUI...");

    mutex = CreateMutex(NULL, FALSE, L"Local\\3RVXSettings");
    if (GetLastError() == ERROR_ALREADY_EXISTS) {
        if (mutex) {
            ReleaseMutex(mutex);
        }

        HWND settingsWnd = FindWindow(CLASS_3RVX_SETTINGS, CLASS_3RVX_SETTINGS);

        CLOG(L"A settings instance is already running. Moving window [%d] "
            L"to the foreground.", (int) settingsWnd);
        SetForegroundWindow(settingsWnd);
        SendMessage(settingsWnd, WM_3RVX_SETTINGSCTRL, MSG_ACTIVATE, NULL);

#if defined(ENABLE_3RVX_LOG) && (defined(ENABLE_3RVX_LOGTOFILE) == FALSE)
        CLOG(L"Press [enter] to terminate");
        std::cin.get();
#endif

        return EXIT_SUCCESS;
    }

    Settings *settings = Settings::Instance();
    settings->Load();

    WNDCLASSEX wcex = { 0 };
    wcex.cbSize = sizeof(WNDCLASSEX);
    wcex.lpfnWndProc = WndProc;
    wcex.hInstance = hInstance;
    wcex.hIcon = LoadIcon(NULL, MAKEINTRESOURCE(IDI_SETTINGS));
    wcex.hbrBackground = (HBRUSH) (COLOR_WINDOW);
    wcex.lpszClassName = CLASS_3RVX_SETTINGS;

    if (RegisterClassEx(&wcex) == 0) {
        CLOG(L"Could not register class: %d", GetLastError());
        return EXIT_FAILURE;
    }

    mainWnd = CreateWindowEx(
        NULL, CLASS_3RVX_SETTINGS, CLASS_3RVX_SETTINGS, NULL,
        0, 0, 0, 0, NULL, NULL, hInstance, NULL);

    PROPSHEETPAGE psp[4];

    LanguageTranslator *lt = settings->Translator();
    std::wstring genTitle = lt->Translate(std::wstring(L"General"));
    std::wstring dispTitle = lt->Translate(std::wstring(L"Display"));
    std::wstring hkTitle = lt->Translate(std::wstring(L"Hotkeys"));
    std::wstring aboutTitle = lt->Translate(std::wstring(L"About"));

    psp[0] = { 0 };
    psp[0].dwSize = sizeof(PROPSHEETPAGE);
    psp[0].dwFlags = PSP_USETITLE;
    psp[0].hInstance = hInstance;
    psp[0].pszTemplate = MAKEINTRESOURCE(IDD_GENERAL);
    psp[0].pszIcon = NULL;
    psp[0].pfnDlgProc = (DLGPROC) GeneralTabProc;
    psp[0].pszTitle = &genTitle[0];
    psp[0].lParam = NULL;

    psp[1] = { 0 };
    psp[1].dwSize = sizeof(PROPSHEETPAGE);
    psp[1].dwFlags = PSP_USETITLE;
    psp[1].hInstance = hInstance;
    psp[1].pszTemplate = MAKEINTRESOURCE(IDD_DISPLAY);
    psp[1].pszIcon = NULL;
    psp[1].pfnDlgProc = (DLGPROC) DisplayTabProc;
    psp[1].pszTitle = &dispTitle[0];
    psp[1].lParam = 0;

    psp[2] = { 0 };
    psp[2].dwSize = sizeof(PROPSHEETPAGE);
    psp[2].dwFlags = PSP_USETITLE;
    psp[2].hInstance = hInstance;
    psp[2].pszTemplate = MAKEINTRESOURCE(IDD_HOTKEYS);
    psp[2].pszIcon = NULL;
    psp[2].pfnDlgProc = (DLGPROC) HotkeyTabProc;
    psp[2].pszTitle = &hkTitle[0];
    psp[2].lParam = 0;

    psp[3] = { 0 };
    psp[3].dwSize = sizeof(PROPSHEETPAGE);
    psp[3].dwFlags = PSP_USETITLE;
    psp[3].hInstance = hInstance;
    psp[3].pszTemplate = MAKEINTRESOURCE(IDD_ABOUT);
    psp[3].pszIcon = NULL;
    psp[3].pfnDlgProc = (DLGPROC) AboutTabProc;
    psp[3].pszTitle = &aboutTitle[0];
    psp[3].lParam = 0;

    PROPSHEETHEADER psh = { 0 };
    psh.dwSize = sizeof(PROPSHEETHEADER);
    psh.dwFlags = PSH_PROPSHEETPAGE | PSH_USEICONID | PSH_USECALLBACK;
    psh.hwndParent = mainWnd;
    psh.hInstance = hInstance;
    psh.pszIcon = MAKEINTRESOURCE(IDI_SETTINGS);
    psh.pszCaption = L"3RVX Settings";
    psh.nStartPage = 0;
    psh.nPages = sizeof(psp) / sizeof(PROPSHEETPAGE);
    psh.ppsp = (LPCPROPSHEETPAGE) &psp;
    psh.pfnCallback = (PFNPROPSHEETCALLBACK) PropSheetProc;

    /* Position the window */
    POINT pt = { 0 };
    GetCursorPos(&pt);
    MoveWindow(mainWnd, pt.x - XOFFSET, pt.y - YOFFSET, 0, 0, TRUE);

    CLOG(L"Launching modal property sheet.");
    return PropertySheet(&psh);
}