static inline std::string MakeRelativeNarrowPath(const std::string& path) { #ifdef _WIN32 return ToNarrow(MakeRelativeCitPath(ToWide(path))); #else return MakeRelativeCitPath(path); #endif }
void Component_RunPreInit() { #ifdef _M_AMD64 // again, a Win7 SP1 check (Chromium x64 isn't supported below this operating level) if (!IsWindows7SP1OrGreater()) { FatalError("CitizenFX requires Windows 7 SP1 or higher. Please upgrade to this operating system version to run CitizenFX."); } #endif // CEF keeps loading/unloading this - load it ourselves to make the refcount always 1 LoadLibrary(L"bluetoothapis.dll"); // load Chrome dependencies ourselves so that the system won't try loading from other paths LoadLibrary(MakeRelativeCitPath(L"bin/chrome_elf.dll").c_str()); LoadLibrary(MakeRelativeCitPath(L"bin/libEGL.dll").c_str()); LoadLibrary(MakeRelativeCitPath(L"bin/libGLESv2.dll").c_str()); // load the CEF library HMODULE libcef = LoadLibraryW(MakeRelativeCitPath(L"bin/libcef.dll").c_str()); if (!libcef) { MessageBoxW(NULL, L"Could not load bin/libcef.dll.", L"CitizenFX", MB_ICONSTOP | MB_OK); ExitProcess(0); } __HrLoadAllImportsForDll("libcef.dll"); Instance<NUIApp>::Set(new NUIApp()); // instantiate a NUIApp auto selfApp = Instance<NUIApp>::Get(); CefMainArgs args(GetModuleHandle(NULL)); static CefRefPtr<CefApp> app(selfApp); auto schemeHandlerFactory = new NUISchemeHandlerFactory(); schemeHandlerFactory->AddRef(); Instance<NUISchemeHandlerFactory>::Set(schemeHandlerFactory); InitFunctionBase::RunAll(); OnResumeGame.Connect([] () { FinalizeInitNUI(); }); // try to execute as a CEF process int exitCode = CefExecuteProcess(args, app, nullptr); // and exit if we did if (exitCode >= 0) { ExitProcess(0); } }
void __fastcall ScanGameEpisodes(CEpisodes* episodes) { std::wstring citizenPath = MakeRelativeCitPath(L"citizen"); char citizenPathStr[256]; wcstombs(citizenPathStr, citizenPath.c_str(), sizeof(citizenPathStr)); AddEpisode(episodes, 4, citizenPathStr); HKEY regKey; if (RegOpenKeyEx(HKEY_LOCAL_MACHINE, L"SOFTWARE\\Rockstar Games\\EFLC", 0, KEY_READ, ®Key) == ERROR_SUCCESS) { char installFolder[512]; DWORD numBytes = sizeof(installFolder); if (RegQueryValueExA(regKey, "InstallFolder", 0, nullptr, (LPBYTE)installFolder, &numBytes) == ERROR_SUCCESS) { if (GetFileAttributesA(installFolder) != INVALID_FILE_ATTRIBUTES) { char pathStr[256]; strcpy(pathStr, installFolder); strcat(pathStr, "\\TLAD\\"); AddEpisode(episodes, 2, pathStr); strcpy(pathStr, installFolder); strcat(pathStr, "\\TBoGT\\"); AddEpisode(episodes, 8, pathStr); } } } episodes->unknownScanFlag = 2; }
fwPlatformString GetAbsoluteGamePath() { static fwPlatformString gamePath; if (!gamePath.size()) { std::wstring fpath = MakeRelativeCitPath(L"CitizenFX.ini"); if (GetFileAttributes(fpath.c_str()) == INVALID_FILE_ATTRIBUTES) { return L"null"; } wchar_t path[512]; const wchar_t* pathKey = L"IVPath"; if (wcsstr(GetCommandLine(), L"cl2")) { pathKey = L"PathCL2"; } GetPrivateProfileString(L"Game", pathKey, L"", path, _countof(path), fpath.c_str()); gamePath = path; gamePath += L"\\"; } return gamePath; }
static void ErrorDo(uint32_t error) { if (error == 0xbe0e0aea) // ERR_GEN_INVALID { FatalError("Invalid rage::fiPackfile encryption type specified. If you have any modified game files, please remove or verify them. See http://rsg.ms/verify for more information."); } trace("error function called from %p for code 0x%08x\n", _ReturnAddress(), error); // provide pickup file for minidump handler to use FILE* dbgFile = _wfopen(MakeRelativeCitPath(L"cache\\error_out").c_str(), L"wb"); if (dbgFile) { fwrite(&error, 1, 4, dbgFile); uint64_t retAddr = (uint64_t)_ReturnAddress(); fwrite(&retAddr, 1, 8, dbgFile); fclose(dbgFile); } // NOTE: crashes on this line are supposed to be read based on the exception-write address! *(uint32_t*)(error | 0x1000000000) = 0xDEADBADE; TerminateProcess(GetCurrentProcess(), -1); }
static int CoreClrCallback(const char* imageName) { if (!imageName) { return FALSE; } wchar_t* filePart = nullptr; wchar_t fullPath[512]; if (GetFullPathNameW(ToWide(imageName).c_str(), _countof(fullPath), fullPath, &filePart) == 0) { return FALSE; } if (!filePart) { return FALSE; } *(filePart - 1) = '\0'; std::wstring platformPath = MakeRelativeCitPath(L"citizen\\clr2\\lib"); if (_wcsicmp(platformPath.c_str(), fullPath) != 0) { platformPath = MakeRelativeCitPath(L"citizen\\clr2\\lib\\mono\\4.5"); if (_wcsicmp(platformPath.c_str(), fullPath) != 0) { trace("%s %s is not a platform image.\n", ToNarrow(fullPath), ToNarrow(filePart)); return FALSE; } } for (int i = 0; i < _countof(g_platformAssemblies); i++) { if (!_wcsicmp(filePart, g_platformAssemblies[i])) { return TRUE; } } trace("%s %s is not a platform image (even though the dir matches).\n", ToNarrow(fullPath), ToNarrow(filePart)); return FALSE; }
void GSClient_GetFavorites() { std::ifstream favFile(MakeRelativeCitPath(L"favorites.json")); std::string json; favFile >> json; favFile.close(); nui::ExecuteRootScript(va("citFrames['mpMenu'].contentWindow.postMessage({ type: 'getFavorites', list: JSON.parse('%s') }, '*');", json.c_str())); }
static int SysError(const char* buffer) { #ifdef WIN32 HWND wnd = FindWindow(L"grcWindow", nullptr); #if !defined(COMPILING_LAUNCH) && !defined(COMPILING_CONSOLE) if (CoreIsDebuggerPresent()) #else if (IsDebuggerPresent()) #endif { __debugbreak(); } #if !defined(COMPILING_LAUNCH) && !defined(COMPILING_CONSOLE) && !defined(IS_FXSERVER) json o = json::object(); o["message"] = buffer; o["file"] = std::get<0>(g_thisError); o["line"] = std::get<1>(g_thisError); o["sigHash"] = std::get<2>(g_thisError); FILE* f = _wfopen(MakeRelativeCitPath(L"cache\\error-pickup").c_str(), L"wb"); if (f) { fprintf(f, "%s", o.dump().c_str()); fclose(f); #ifdef _DEBUG assert(!"breakpoint time"); #endif return -1; } #endif MessageBoxA(wnd, buffer, "Fatal Error", MB_OK | MB_ICONSTOP); #ifdef _DEBUG assert(!"breakpoint time"); #endif TerminateProcess(GetCurrentProcess(), 1); #else fprintf(stderr, "%s", buffer); abort(); #endif return 0; }
void GSClient_SaveFavorites(const wchar_t *json) { std::wofstream favFile(MakeRelativeCitPath(L"favorites.json")); favFile << json; favFile.close(); }
void ComponentLoader::Initialize() { // run local initialization functions InitFunctionBase::RunAll(); // set up the root component m_rootComponent = FxGameComponent::Create(); AddComponent(m_rootComponent); // parse and load additional components FILE* componentCache = _pfopen(MakeRelativeCitPath(L"components.json").c_str(), _P("rb")); if (!componentCache) { FatalError("Could not find component cache storage file (components.json)."); } // read component cache file fseek(componentCache, 0, SEEK_END); int length = ftell(componentCache); fseek(componentCache, 0, SEEK_SET); char* cacheBuf = new char[length + 1]; fread(cacheBuf, 1, length, componentCache); cacheBuf[length] = '\0'; fclose(componentCache); // parse the list rapidjson::Document doc; doc.Parse(cacheBuf); delete[] cacheBuf; if (doc.HasParseError()) { FatalError("Error parsing components.json: %d", doc.GetParseError()); } // look through the list for components to load std::vector<std::string> components; for (auto it = doc.Begin(); it != doc.End(); it++) { const char* name = it->GetString(); components.push_back(name); // replace colons with dashes char* nameStr = strdup(name); char* p = nameStr; while (*p) { if (*p == ':') { *p = '-'; } p++; } fwPlatformString nameWide(nameStr); free(nameStr); AddComponent(new DllGameComponent(va(PLATFORM_LIBRARY_STRING, nameWide.c_str()))); } // load the components, but don't instance them std::vector<fwRefContainer<ComponentData>> componentDatas; for (auto& component : components) { auto comp = LoadComponent(component.c_str()); if (!comp.GetRef()) { FatalError("Could not load component %s.", component.c_str()); } componentDatas.push_back(comp); } // sort the list by dependency std::queue<fwRefContainer<ComponentData>> sortedList = SortDependencyList(componentDatas); // clear the loaded list (it'll be added afterwards in order) m_loadedComponents.clear(); while (!sortedList.empty()) { auto comp = sortedList.front(); sortedList.pop(); m_loadedComponents.push_back(comp); // create a component instance if need be if (comp->ShouldAutoInstance()) { trace("Initializing instance of %s.\n", comp->GetName().c_str()); comp->CreateInstance(std::string()); } } }
injectCall.call(); rage::fiDevice::OnInitialMount(); });*/ // patch 2 changed register alloc (2015-04-17) //hook::call(hook::pattern("0F B7 05 ? ? ? ? 48 03 C3 44 88 3C 38 66").count(1).get(0).get<void>(0x15), CallInitialMount); hook::call(hook::pattern("0F B7 05 ? ? ? ? 48 03 C3 44 88 34 38 66").count(1).get(0).get<void>(0x15), CallInitialMount); #if 0 //Crashes everything you could imagine.. // temporarily located here because of lack of glue/such rage::fiDevice::OnInitialMount.Connect([]() { { std::wstring_convert<std::codecvt_utf8_utf16<wchar_t>, wchar_t> converter; std::string narrowPath = converter.to_bytes(MakeRelativeCitPath(L"citizen\\common")); rage::fiDeviceRelative* relativeDevice = new rage::fiDeviceRelative(); relativeDevice->SetPath(narrowPath.c_str(), nullptr, true); relativeDevice->Mount("common:/"); rage::fiDeviceRelative* relativeDeviceCrc = new rage::fiDeviceRelative(); relativeDeviceCrc->SetPath(narrowPath.c_str(), nullptr, true); relativeDeviceCrc->Mount("commoncrc:/"); } { std::wstring_convert<std::codecvt_utf8_utf16<wchar_t>, wchar_t> converter; std::string narrowPath = converter.to_bytes(MakeRelativeCitPath(L"citizen\\platform")); rage::fiDeviceRelative* relativeDevice = new rage::fiDeviceRelative();
static void InitMono() { std::string citizenClrPath = MakeRelativeNarrowPath("citizen/clr2/lib/"); std::string citizenCfgPath = MakeRelativeNarrowPath("citizen/clr2/cfg/"); mono_set_dirs(citizenClrPath.c_str(), citizenCfgPath.c_str()); #ifdef _WIN32 std::wstring citizenClrLibPath = MakeRelativeCitPath(L"citizen/clr2/lib/mono/4.5/"); SetEnvironmentVariable(L"MONO_PATH", citizenClrLibPath.c_str()); mono_set_crash_chaining(true); #else std::string citizenClrLibPath = MakeRelativeNarrowPath("citizen/clr2/lib/mono/4.5/"); putenv(const_cast<char*>(va("MONO_PATH=%s", citizenClrLibPath))); #endif mono_assembly_setrootdir(citizenClrPath.c_str()); putenv("MONO_DEBUG=casts"); #ifndef IS_FXSERVER mono_security_enable_core_clr(); mono_security_core_clr_set_options((MonoSecurityCoreCLROptions)(MONO_SECURITY_CORE_CLR_OPTIONS_RELAX_DELEGATE | MONO_SECURITY_CORE_CLR_OPTIONS_RELAX_REFLECTION)); mono_security_set_core_clr_platform_callback(CoreClrCallback); #endif char* args[1]; #ifdef _WIN32 args[0] = "--soft-breakpoints"; #else args[0] = "--use-fallback-tls"; #endif mono_jit_parse_options(1, args); mono_debug_init(MONO_DEBUG_FORMAT_MONO); g_rootDomain = mono_jit_init_version("Citizen", "v4.0.30319"); mono_domain_set_config(g_rootDomain, ".", "cfx.config"); mono_install_unhandled_exception_hook([] (MonoObject* exc, void*) { OutputExceptionDetails(exc); }, nullptr); mono_set_crash_chaining(true); mono_add_internal_call("CitizenFX.Core.GameInterface::PrintLog", reinterpret_cast<void*>(GI_PrintLogCall)); mono_add_internal_call("CitizenFX.Core.GameInterface::fwFree", reinterpret_cast<void*>(fwFree)); std::string platformPath = MakeRelativeNarrowPath("citizen/clr2/lib/mono/4.5/CitizenFX.Core.dll"); auto scriptManagerAssembly = mono_domain_assembly_open(g_rootDomain, platformPath.c_str()); if (!scriptManagerAssembly) { FatalError("Could not load CitizenFX.Core.dll.\n"); } auto scriptManagerImage = mono_assembly_get_image(scriptManagerAssembly); bool methodSearchSuccess = true; MonoMethodDesc* description; #define method_search(name, method) description = mono_method_desc_new(name, 1); \ method = mono_method_desc_search_in_image(description, scriptManagerImage); \ mono_method_desc_free(description); \ methodSearchSuccess = methodSearchSuccess && method != NULL MonoMethod* rtInitMethod; method_search("CitizenFX.Core.RuntimeManager:Initialize", rtInitMethod); method_search("CitizenFX.Core.RuntimeManager:GetImplementedClasses", g_getImplementsMethod); method_search("CitizenFX.Core.RuntimeManager:CreateObjectInstance", g_createObjectMethod); if (!methodSearchSuccess) { FatalError("Couldn't find one or more CitizenFX.Core methods.\n"); } MonoObject* exc = nullptr; mono_runtime_invoke(rtInitMethod, nullptr, nullptr, &exc); if (exc) { OutputExceptionDetails(exc); return; } }
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); }
void FinalizeInitNUI() { if (getenv("CitizenFX_ToolMode")) { return; } std::wstring cachePath = MakeRelativeCitPath(L"cache\\browser\\"); CreateDirectory(cachePath.c_str(), nullptr); // delete any old CEF logs DeleteFile(MakeRelativeCitPath(L"debug.log").c_str()); auto selfApp = Instance<NUIApp>::Get(); CefMainArgs args(GetModuleHandle(NULL)); CefRefPtr<CefApp> app(selfApp); CefSettings cSettings; cSettings.multi_threaded_message_loop = true; cSettings.remote_debugging_port = 13172; cSettings.pack_loading_disabled = false; // true; cSettings.windowless_rendering_enabled = false; // true; cSettings.log_severity = LOGSEVERITY_DISABLE; CefString(&cSettings.browser_subprocess_path).FromWString(MakeCfxSubProcess(L"ChromeBrowser")); CefString(&cSettings.locale).FromASCII("en-US"); std::wstring resPath = MakeRelativeCitPath(L"bin/cef/"); CefString(&cSettings.resources_dir_path).FromWString(resPath); CefString(&cSettings.locales_dir_path).FromWString(resPath); CefString(&cSettings.cache_path).FromWString(cachePath); // 2014-06-30: sandbox disabled as it breaks scheme handler factories (results in blank page being loaded) CefInitialize(args, cSettings, app.get(), /*cefSandbox*/ nullptr); CefRegisterSchemeHandlerFactory("nui", "", Instance<NUISchemeHandlerFactory>::Get()); CefAddCrossOriginWhitelistEntry("nui://game", "http", "", true); CefAddCrossOriginWhitelistEntry("nui://game", "nui", "", true); HookFunctionBase::RunAll(); #if defined(GTA_NY) OnGrcBeginScene.Connect([] () { Instance<NUIWindowManager>::Get()->ForAllWindows([] (fwRefContainer<NUIWindow> window) { window->UpdateFrame(); }); }); #else #endif //g_hooksDLL->SetHookCallback(StringHash("d3dCreate"), [] (void*) OnGrcCreateDevice.Connect([]() { //int resX = *(int*)0xFDCEAC; //int resY = *(int*)0xFDCEB0; int resX, resY; GetGameResolution(resX, resY); auto rootWindow = NUIWindow::Create(true, resX, resY, "nui://game/ui/root.html"); rootWindow->SetPaintType(NUIPaintTypePostRender); Instance<NUIWindowManager>::Get()->SetRootWindow(rootWindow); }); }