Esempio n. 1
0
Common::WriteStream *TestbedConfigManager::getConfigWriteStream() const {
	// Look for config file in game-path
	const Common::String &path = ConfMan.get("path");
	Common::WriteStream *ws;
	Common::FSNode gameRoot(path);
	Common::FSNode config = gameRoot.getChild(_configFileName);
	ws = config.createWriteStream();
	return ws;
}
Esempio n. 2
0
TestbedEngine::TestbedEngine(OSystem *syst)
 : Engine(syst) {
	// Put your engine in a sane state, but do nothing big yet;
	// in particular, do not load data from files; rather, if you
	// need to do such things, do them from init().

	// Do not initialize graphics here

	// However this is the place to specify all default directories
	// Put game-data dir in search path
	Common::FSNode gameRoot(ConfMan.get("path"));
	if (gameRoot.exists()) {
		SearchMan.addDirectory(gameRoot.getDisplayName(), gameRoot);
	}

	DebugMan.addDebugChannel(kTestbedLogOutput, "LOG", "Log of test results generated by testbed");
	DebugMan.addDebugChannel(kTestbedEngineDebug, "Debug", "Engine-specific debug statements");
	DebugMan.enableDebugChannel("LOG");

	// Initialize testsuites here
	// GFX
	Testsuite *ts = new GFXTestSuite();
	_testsuiteList.push_back(ts);
	// FS
	ts = new FSTestSuite();
	_testsuiteList.push_back(ts);
	// Savegames
	ts = new SaveGameTestSuite();
	_testsuiteList.push_back(ts);
	// Misc.
	ts = new MiscTestSuite();
	_testsuiteList.push_back(ts);
	// Events
	ts = new EventTestSuite();
	_testsuiteList.push_back(ts);
	// Sound
	ts = new SoundSubsystemTestSuite();
	_testsuiteList.push_back(ts);
	// Midi
	ts = new MidiTestSuite();
	_testsuiteList.push_back(ts);
#ifdef USE_LIBCURL
	// Cloud
	ts = new CloudTestSuite();
	_testsuiteList.push_back(ts);
#endif
#ifdef USE_SDL_NET
	// Webserver
	ts = new WebserverTestSuite();
	_testsuiteList.push_back(ts);
#endif
}
Esempio n. 3
0
FSTestSuite::FSTestSuite() {
	// FS tests depend on Game Data files.
	// If those are not found. Disable this testsuite.
	const Common::String &path = ConfMan.get("path");
	Common::FSNode gameRoot(path);

	Common::FSNode gameIdentificationFile = gameRoot.getChild("TESTBED");
	if (!gameIdentificationFile.exists()) {
		logPrintf("WARNING! : Game Data not found. Skipping FS tests\n");
		ConfParams.setGameDataFound(false);
		Testsuite::enable(false);
	}
	addTest("ReadingFile", &FStests::testReadFile, false);
	addTest("WritingFile", &FStests::testWriteFile, false);
}
Esempio n. 4
0
TestExitStatus CloudTests::testFolderDownloading() {
	ConfParams.setCloudTestCallbackCalled(false);
	ConfParams.setCloudTestErrorCallbackCalled(false);

	if (CloudMan.getCurrentStorage() == nullptr) {
		Testsuite::logPrintf("Couldn't find connected Storage\n");
		return kTestFailed;
	}

	Common::String info = "Testing Cloud Storage API downloadFolder() method.\n"
		"In this test we'll try to download remote 'testbed/' directory.";

	if (Testsuite::handleInteractiveInput(info, "OK", "Skip", kOptionRight)) {
		Testsuite::logPrintf("Info! Skipping test : downloadFolder()\n");
		return kTestSkipped;
	}

	const Common::String &path = ConfMan.get("path");
	Common::FSDirectory gameRoot(path);
	Common::FSNode node = gameRoot.getFSNode().getChild("downloaded_directory");
	Common::String filepath = node.getPath();
	if (CloudMan.downloadFolder(
			getRemoteTestPath(),
			filepath.c_str(),
			new Common::GlobalFunctionCallback<Cloud::Storage::FileArrayResponse>(&directoryDownloadedCallback),
			new Common::GlobalFunctionCallback<Networking::ErrorResponse>(&errorCallback)
		) == nullptr) {
		Testsuite::logPrintf("Warning! No Request is returned!\n");
	}

	if (!waitForCallbackMore()) return kTestSkipped;
	Testsuite::clearScreen();

	if (ConfParams.isCloudTestErrorCallbackCalled()) {
		Testsuite::logPrintf("Error callback was called\n");
		return kTestFailed;
	}

	if (Testsuite::handleInteractiveInput("Was the CloudMan able to download into 'testbed/downloaded_directory'?", "Yes", "No", kOptionRight)) {
		Testsuite::logDetailedPrintf("Error! Directory was not downloaded!\n");
		return kTestFailed;
	}

	Testsuite::logDetailedPrintf("Directory was downloaded\n");
	return kTestPassed;
}
Esempio n. 5
0
TestExitStatus CloudTests::testSavesSync() {
	ConfParams.setCloudTestCallbackCalled(false);
	ConfParams.setCloudTestErrorCallbackCalled(false);

	if (CloudMan.getCurrentStorage() == nullptr) {
		Testsuite::logPrintf("Couldn't find connected Storage\n");
		return kTestFailed;
	}

	Common::String info = "Testing Cloud Storage API syncSaves() method.\n"
		"In this test we'll try to sync your saves.";

	if (Testsuite::handleInteractiveInput(info, "OK", "Skip", kOptionRight)) {
		Testsuite::logPrintf("Info! Skipping test : syncSaves()\n");
		return kTestSkipped;
	}

	const Common::String &path = ConfMan.get("path");
	Common::FSDirectory gameRoot(path);
	Common::FSNode node = gameRoot.getFSNode().getChild("downloaded_directory");
	Common::String filepath = node.getPath();
	if (CloudMan.syncSaves(
			new Common::GlobalFunctionCallback<Cloud::Storage::BoolResponse>(&savesSyncedCallback),
			new Common::GlobalFunctionCallback<Networking::ErrorResponse>(&errorCallback)
		) == nullptr) {
		Testsuite::logPrintf("Warning! No Request is returned!\n");
	}

	if (!waitForCallbackMore()) return kTestSkipped;
	Testsuite::clearScreen();

	if (ConfParams.isCloudTestErrorCallbackCalled()) {
		Testsuite::logPrintf("Error callback was called\n");
		return kTestFailed;
	}

	if (Testsuite::handleInteractiveInput("Was the CloudMan able to sync saves?", "Yes", "No", kOptionRight)) {
		Testsuite::logDetailedPrintf("Error! Saves were not synced!\n");
		return kTestFailed;
	}

	Testsuite::logDetailedPrintf("Saves were synced successfully\n");
	return kTestPassed;
}
Esempio n. 6
0
/**
 * This test creates a file testbed.out, writes a sample data and confirms if
 * it is same by reading the file again.
 */
TestExitStatus FStests::testWriteFile() {
	const Common::String &path = ConfMan.get("path");
	Common::FSNode gameRoot(path);
	if (!gameRoot.exists()) {
		Testsuite::logPrintf("Couldn't open the game data directory %s", path.c_str());
		 return kTestFailed;
	}

	Common::FSNode fileToWrite = gameRoot.getChild("testbed.out");

	Common::WriteStream *ws = fileToWrite.createWriteStream();

	if (!ws) {
		Testsuite::logDetailedPrintf("Can't open writable file in game data dir\n");
		 return kTestFailed;
	}

	ws->writeString("ScummVM Rocks!");
	ws->flush();
	delete ws;

	Common::SeekableReadStream *rs = fileToWrite.createReadStream();
	if (!rs) {
		Testsuite::logDetailedPrintf("Can't open recently written file testbed.out in game data dir\n");
		 return kTestFailed;
	}
	Common::String readFromFile = rs->readLine();
	delete rs;

	if (readFromFile.equals("ScummVM Rocks!")) {
		// All good
		Testsuite::logDetailedPrintf("Data written and read correctly\n");
		 return kTestPassed;
	}

	 return kTestFailed;
}
Esempio n. 7
0
TestExitStatus FStests::testReadFile() {
	const Common::String &path = ConfMan.get("path");
	Common::FSDirectory gameRoot(path);
	int numFailed = 0;

	if (!gameRoot.getFSNode().exists() || !gameRoot.getFSNode().isDirectory()) {
		Testsuite::logDetailedPrintf("game Path should be an existing directory");
		 return kTestFailed;
	}

	const char *dirList[] = {"test1" ,"Test2", "TEST3" , "tEST4", "test5"};
	const char *file[] = {"file.txt", "File.txt", "FILE.txt", "fILe.txt", "file"};

	for (unsigned int i = 0; i < ARRAYSIZE(dirList); i++) {
		Common::String dirName = dirList[i];
		Common::String fileName = file[i];
		Common::FSDirectory *directory = gameRoot.getSubDirectory(dirName);

		if (!directory) {
			Testsuite::logDetailedPrintf("Failed to open directory %s during FS tests\n", dirName.c_str());
			 return kTestFailed;
		}

		if (!readDataFromFile(directory, fileName.c_str())) {
			Testsuite::logDetailedPrintf("Reading from %s/%s failed\n", dirName.c_str(), fileName.c_str());
			numFailed++;
		}

		dirName.toLowercase();
		fileName.toLowercase();
		delete directory;
		directory = gameRoot.getSubDirectory(dirName);

		if (!directory) {
			Testsuite::logDetailedPrintf("Failed to open directory %s during FS tests\n", dirName.c_str());
			 return kTestFailed;
		}

		if (!readDataFromFile(directory, fileName.c_str())) {
			Testsuite::logDetailedPrintf("Reading from %s/%s failed\n", dirName.c_str(), fileName.c_str());
			numFailed++;
		}

		dirName.toUppercase();
		fileName.toUppercase();
		delete directory;
		directory = gameRoot.getSubDirectory(dirName);

		if (!directory) {
			Testsuite::logDetailedPrintf("Failed to open directory %s during FS tests\n", dirName.c_str());
			 return kTestFailed;
		}

		if (!readDataFromFile(directory, fileName.c_str())) {
			Testsuite::logDetailedPrintf("Reading from %s/%s failed\n", dirName.c_str(), fileName.c_str());
			numFailed++;
		}
		delete directory;
	}

	Testsuite::logDetailedPrintf("Failed %d out of 15\n", numFailed);
	if (numFailed) {
		return kTestFailed;
	} else {
		return kTestPassed;
	}
}
    // /////////////////////////////////////////////////////////////////
    //
    // /////////////////////////////////////////////////////////////////
    bool LuaStateManager::Init(char const * const pInitFileName)
    {
        // Create our global actor table.
        // This table will hold context for all actors created in the game world.
        LuaPlus::LuaObject globals = m_GlobalState->GetGlobals();
        LuaPlus::LuaObject actorTable = globals.CreateTable("ActorList");

        // Execute the init file and setup some useful global variables for the lua scripts to know.
        if(DoFile(pInitFileName)) {
            std::string luaCommand;                     // The LUA command to execute.
            std::string currOpVal;                      // The current option value retrieved.
            boost::shared_ptr<GameOptions> opPtr = g_appPtr->GetGameOptions();

            // Write out the location of the game root directory.
            boost::filesystem::path gameRoot(g_appPtr->GetGameRootDir());
            //if(GameHalloran::RetrieveAndConvertOption<std::string>(opPtr, std::string("GameRoot"), GameHalloran::GameOptions::PROGRAMMER, currOpVal))
            //{
            luaCommand = std::string("INIT_GAME_ROOT_PATH = \"") + gameRoot.generic_string() + std::string("\";");
            if(!ExecuteString(luaCommand.c_str())) {
                return (false);
            }
            currOpVal.clear();
            luaCommand.clear();
            //}

            // Write out what type of build is running.
#ifdef DEBUG
            luaCommand = std::string("INIT_RUNNING_DEBUG_BUILD = true;");
#else
            luaCommand = std::string("INIT_RUNNING_DEBUG_BUILD = false;");
#endif
            if(!ExecuteString(luaCommand.c_str())) {
                return (false);
            }
            luaCommand.clear();

            // Write out the various player options so they are available to the lua UI setup scripts.
            luaCommand = std::string("INIT_PLAYER_OPTIONS = {};");
            if(!ExecuteString(luaCommand.c_str())) {
                return (false);
            }
            luaCommand.clear();

            // Sound options.
            if(GameHalloran::RetrieveAndConvertOption<std::string>(opPtr, std::string("MasterVolume"), GameHalloran::GameOptions::PLAYER, currOpVal)) {
                luaCommand = std::string("INIT_PLAYER_OPTIONS.MasterVolume = ") + currOpVal + std::string(";");
                if(!ExecuteString(luaCommand.c_str())) {
                    return (false);
                }
                currOpVal.clear();
                luaCommand.clear();
            }
            bool flag;
            if(GameHalloran::RetrieveAndConvertOption<bool>(opPtr, std::string("Music"), GameHalloran::GameOptions::PLAYER, flag)) {
                luaCommand = std::string("INIT_PLAYER_OPTIONS.Music = ") + (flag ? std::string("true") : std::string("false")) + std::string(";");
                if(!ExecuteString(luaCommand.c_str())) {
                    return (false);
                }
                luaCommand.clear();
            }
            if(GameHalloran::RetrieveAndConvertOption<bool>(opPtr, std::string("SoundFx"), GameHalloran::GameOptions::PLAYER, flag)) {
                luaCommand = std::string("INIT_PLAYER_OPTIONS.SoundFx = ") + (flag ? std::string("true") : std::string("false")) + std::string(";");
                if(!ExecuteString(luaCommand.c_str())) {
                    return (false);
                }
                luaCommand.clear();
            }

            // Graphics options.
            if(GameHalloran::RetrieveAndConvertOption<bool>(opPtr, std::string("RenderShadows"), GameHalloran::GameOptions::PLAYER, flag)) {
                luaCommand = std::string("INIT_PLAYER_OPTIONS.RenderShadows = ") + (flag ? std::string("true") : std::string("false")) + std::string(";");
                if(!ExecuteString(luaCommand.c_str())) {
                    return (false);
                }
                luaCommand.clear();
            }
            if(GameHalloran::RetrieveAndConvertOption<std::string>(opPtr, std::string("ScreenWidth"), GameHalloran::GameOptions::PLAYER, currOpVal)) {
                luaCommand = std::string("INIT_PLAYER_OPTIONS.ScreenResolution = \"") + currOpVal + std::string("*");
                boost::shared_ptr<GameOptions> opPtr = g_appPtr->GetGameOptions();
                if(GameHalloran::RetrieveAndConvertOption<std::string>(opPtr, std::string("ScreenHeight"), GameHalloran::GameOptions::PLAYER, currOpVal)) {
                    luaCommand += currOpVal + std::string("\";");
                    if(!ExecuteString(luaCommand.c_str())) {
                        return (false);
                    }
                }
                currOpVal.clear();
                luaCommand.clear();
            }
            if(GameHalloran::RetrieveAndConvertOption<std::string>(opPtr, std::string("Multisampling"), GameHalloran::GameOptions::PLAYER, currOpVal)) {
                currOpVal = std::string("x") + currOpVal;

                luaCommand = std::string("INIT_PLAYER_OPTIONS.Multisampling = \"") + currOpVal + std::string("\";");
                if(!ExecuteString(luaCommand.c_str())) {
                    return (false);
                }
                currOpVal.clear();
                luaCommand.clear();
            }
            if(GameHalloran::RetrieveAndConvertOption<std::string>(opPtr, std::string("TextureFilteringType"), GameHalloran::GameOptions::PLAYER, currOpVal)) {
                luaCommand = std::string("INIT_PLAYER_OPTIONS.TextureFilteringType = \"") + currOpVal + std::string("\";");
                if(!ExecuteString(luaCommand.c_str())) {
                    return (false);
                }
                currOpVal.clear();
                luaCommand.clear();
            }

            return (true);
        }

        return (false);
    }
Esempio n. 9
0
TestExitStatus CloudTests::testUploading() {
	ConfParams.setCloudTestCallbackCalled(false);
	ConfParams.setCloudTestErrorCallbackCalled(false);

	if (CloudMan.getCurrentStorage() == nullptr) {
		Testsuite::logPrintf("Couldn't find connected Storage\n");
		return kTestFailed;
	}

	Common::String info = "Testing Cloud Storage API upload() method.\n"
		"In this test we'll try to upload a 'test1/file.txt' file.";

	if (Testsuite::handleInteractiveInput(info, "OK", "Skip", kOptionRight)) {
		Testsuite::logPrintf("Info! Skipping test : upload()\n");
		return kTestSkipped;
	}

	if (!ConfParams.isGameDataFound()) {
		Testsuite::logPrintf("Info! Couldn't find the game data, so skipping test : upload()\n");
		return kTestSkipped;
	}

	const Common::String &path = ConfMan.get("path");
	Common::FSDirectory gameRoot(path);
	Common::FSDirectory *directory = gameRoot.getSubDirectory("test1");
	Common::FSNode node = directory->getFSNode().getChild("file.txt");
	delete directory;

	if (CloudMan.getCurrentStorage()->uploadStreamSupported()) {
		if (CloudMan.getCurrentStorage()->upload(
				Common::String(getRemoteTestPath()) + "/testfile.txt",
				node.createReadStream(),
				new Common::GlobalFunctionCallback<Cloud::Storage::UploadResponse>(&fileUploadedCallback),
				new Common::GlobalFunctionCallback<Networking::ErrorResponse>(&errorCallback)
			) == nullptr) {
			Testsuite::logPrintf("Warning! No Request is returned!\n");
		}
	} else {
		Common::String filepath = node.getPath();
		if (CloudMan.getCurrentStorage()->upload(
				Common::String(getRemoteTestPath()) + "/testfile.txt",
				filepath.c_str(),
				new Common::GlobalFunctionCallback<Cloud::Storage::UploadResponse>(&fileUploadedCallback),
				new Common::GlobalFunctionCallback<Networking::ErrorResponse>(&errorCallback)
			) == nullptr) {
			Testsuite::logPrintf("Warning! No Request is returned!\n");
		}
	}

	if (!waitForCallbackMore()) return kTestSkipped;
	Testsuite::clearScreen();

	if (ConfParams.isCloudTestErrorCallbackCalled()) {
		Testsuite::logPrintf("Error callback was called\n");
		return kTestFailed;
	}

	Common::String info2 = "upload() is finished. Do you want to list '/testbed' directory?";

	if (!Testsuite::handleInteractiveInput(info2, "Yes", "No", kOptionRight)) {
		ConfParams.setCloudTestCallbackCalled(false);

		if (CloudMan.listDirectory(
				getRemoteTestPath(),
				new Common::GlobalFunctionCallback<Cloud::Storage::FileArrayResponse>(&directoryListedCallback),
				new Common::GlobalFunctionCallback<Networking::ErrorResponse>(&errorCallback)
			) == nullptr) {
			Testsuite::logPrintf("Warning! No Request is returned!\n");
		}

		if (!waitForCallbackMore()) return kTestSkipped;
		Testsuite::clearScreen();

		if (ConfParams.isCloudTestErrorCallbackCalled()) {
			Testsuite::logPrintf("Error callback was called\n");
			return kTestFailed;
		}
	}

	if (Testsuite::handleInteractiveInput("Was the CloudMan able to upload into 'testbed/testfile.txt' file?", "Yes", "No", kOptionRight)) {
		Testsuite::logDetailedPrintf("Error! File was not uploaded!\n");
		return kTestFailed;
	}

	Testsuite::logDetailedPrintf("File was uploaded\n");
	return kTestPassed;
}
Esempio n. 10
0
void EnsureGamePath()
{
	std::wstring fpath = MakeRelativeCitPath(L"CitizenFX.ini");
	const wchar_t* pathKey = L"IVPath";

	if (wcsstr(GetCommandLine(), L"cl2"))
	{
		pathKey = L"PathCL2";
	}

	if (GetFileAttributes(fpath.c_str()) != INVALID_FILE_ATTRIBUTES)
	{
		wchar_t path[256];

		GetPrivateProfileString(L"Game", pathKey, L"", path, _countof(path), fpath.c_str());

		if (path[0] != L'\0')
		{
			return;
		}
	}

	ScopedCoInitialize coInit(COINIT_APARTMENTTHREADED);

	if (!coInit)
	{
		MessageBox(nullptr, va(L"CoInitializeEx failed. HRESULT = 0x%08x.", coInit.GetResult()), L"Error", MB_OK | MB_ICONERROR);

		ExitProcess(coInit.GetResult());
	}

	WRL::ComPtr<IFileDialog> fileDialog;
	HRESULT hr = CoCreateInstance(CLSID_FileOpenDialog, NULL, CLSCTX_INPROC_SERVER, IID_IFileDialog, (void**)fileDialog.GetAddressOf());

	if (FAILED(hr))
	{
		MessageBox(nullptr, va(L"CoCreateInstance(IFileDialog) failed. HRESULT = 0x%08x.", hr), L"Error", MB_OK | MB_ICONERROR);

		ExitProcess(hr);
	}

	FILEOPENDIALOGOPTIONS opts;
	fileDialog->GetOptions(&opts);

	opts |= FOS_FORCEFILESYSTEM | FOS_PICKFOLDERS;

	fileDialog->SetOptions(opts);

#ifndef GTA_FIVE
	fileDialog->SetTitle(L"Select the folder containing " GAME_EXECUTABLE);
#else
	fileDialog->SetTitle(L"Select the folder containing Grand Theft Auto V");

	// set the default folder, if we can find one
	{
		wchar_t gameRootBuf[1024];
		DWORD gameRootLength = sizeof(gameRootBuf);

		// 5 is the amount of characters to strip off the end
		const std::tuple<std::wstring, std::wstring, int> folderAttempts[] = {
			{ L"InstallFolderSteam", L"SOFTWARE\\WOW6432Node\\Rockstar Games\\GTAV", 5 },
			{ L"InstallFolder", L"SOFTWARE\\WOW6432Node\\Rockstar Games\\Grand Theft Auto V", 0 }
		};

		for (const auto& folder : folderAttempts)
		{
			if (RegGetValue(HKEY_LOCAL_MACHINE,
				std::get<1>(folder).c_str(), std::get<0>(folder).c_str(),
				RRF_RT_REG_SZ, nullptr, gameRootBuf, &gameRootLength) == ERROR_SUCCESS)
			{
				WRL::ComPtr<IShellItem> item;

				std::wstring gameRoot(gameRootBuf);

				// strip \GTAV if needed
				gameRoot = gameRoot.substr(0, gameRoot.length() - std::get<int>(folder));

				if (SUCCEEDED(SHCreateItemFromParsingName(gameRoot.c_str(), nullptr, IID_IShellItem, (void**)item.GetAddressOf())))
				{
					auto checkFile = [&](const std::wstring& path)
					{
						return GetFileAttributesW((gameRoot + (L"\\" + path)).c_str()) != INVALID_FILE_ATTRIBUTES;
					};

					fileDialog->SetFolder(item.Get());

					if (checkFile(L"x64a.rpf") && checkFile(L"x64b.rpf") &&
						checkFile(L"x64g.rpf") && checkFile(L"common.rpf") &&
						checkFile(L"bink2w64.dll") && checkFile(L"x64\\audio\\audio_rel.rpf") &&
						checkFile(L"GTA5.exe"))
					{
						WritePrivateProfileString(L"Game", pathKey, gameRoot.c_str(), fpath.c_str());

						return;
					}
				}
			}
		}
	}
#endif

	hr = fileDialog->Show(nullptr);

	if (FAILED(hr))
	{
		if (hr != HRESULT_FROM_WIN32(ERROR_CANCELLED))
		{
			MessageBox(nullptr, va(L"Could not show game folder selection window: IFileDialog::Show failed. HRESULT = 0x%08x.", hr), L"Error", MB_OK | MB_ICONERROR);
		}

		ExitProcess(0);
	}

	WRL::ComPtr<IShellItem> result;
	hr = fileDialog->GetResult(result.GetAddressOf());

	if (!result)
	{
		MessageBox(nullptr, va(L"You did not select a game folder: IFileDialog::GetResult failed. HRESULT = 0x%08x.", hr), L"Error", MB_OK | MB_ICONERROR);
		ExitProcess(0);
	}

	PWSTR resultPath;

	if (FAILED(hr = result->GetDisplayName(SIGDN_FILESYSPATH, &resultPath)))
	{
		MessageBox(nullptr, va(L"Could not get game directory: IShellItem::GetDisplayName failed. HRESULT = 0x%08x.", hr), L"Error", MB_OK | MB_ICONERROR);
		ExitProcess(0);
	}

	// check if there's a game EXE in the path
	std::wstring gamePath = std::wstring(resultPath) + L"\\" GAME_EXECUTABLE;

	if (GetFileAttributes(gamePath.c_str()) == INVALID_FILE_ATTRIBUTES)
	{
#if defined(GTA_NY)
		std::wstring eflcPath = std::wstring(resultPath) + L"\\EFLC.exe";

		if (GetFileAttributes(eflcPath.c_str()) != INVALID_FILE_ATTRIBUTES)
		{
			MessageBox(nullptr, L"The selected path does not contain a GTAIV.exe file. As this is an EFLC installation, placing a GTAIV.exe (version 1.0.7.0) from any source will work as well.", PRODUCT_NAME, MB_OK | MB_ICONWARNING);
		}
		else
#endif
		{
			MessageBox(nullptr, L"The selected path does not contain a " GAME_EXECUTABLE L" file.", PRODUCT_NAME, MB_OK | MB_ICONWARNING);
		}

		ExitProcess(0);
	}

	WritePrivateProfileString(L"Game", pathKey, resultPath, fpath.c_str());

	CoTaskMemFree(resultPath);
}