mirror of
https://github.com/absoluteSpacehead/MercuryLauncher.git
synced 2026-01-13 11:02:22 +01:00
Compare commits
17 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
fb0dd1c9c4 | ||
|
|
e02a559c2b | ||
|
|
5d0464e8bf | ||
|
|
b6635b028a | ||
|
|
50147c2d22 | ||
|
|
ad180c57ab | ||
|
|
099478360f | ||
|
|
20492f45b8 | ||
|
|
ab6cccfd84 | ||
|
|
cd561c6721 | ||
|
|
9237818eb9 | ||
|
|
d274be0842 | ||
|
|
2687eaddf3 | ||
|
|
b138c34b8c | ||
|
|
68650aab7d | ||
|
|
3eab3b0e2c | ||
|
|
650f169232 |
@@ -39,7 +39,8 @@ void SetSubGameHook(void* thisref, uint8_t SubGame)
|
|||||||
|
|
||||||
if (!write)
|
if (!write)
|
||||||
{
|
{
|
||||||
std::cerr << "DLL failed to inject (WriteProcessMemory failed).\n";
|
MessageBoxA(NULL, "DLL failed to inject (WriteProcessMemory failed).", NULL, MB_OK);
|
||||||
|
std::cerr << "\nDLL failed to inject (WriteProcessMemory failed).\n";
|
||||||
|
|
||||||
MH_DisableHook(Offset<uintptr_t>(SetSubGameOffset));
|
MH_DisableHook(Offset<uintptr_t>(SetSubGameOffset));
|
||||||
SetSubGame(thisref, SubGame);
|
SetSubGame(thisref, SubGame);
|
||||||
@@ -51,7 +52,8 @@ void SetSubGameHook(void* thisref, uint8_t SubGame)
|
|||||||
|
|
||||||
if (!thread)
|
if (!thread)
|
||||||
{
|
{
|
||||||
std::cerr << "DLL failed to inject (CreateRemoteThread failed).\n";
|
MessageBoxA(NULL, "DLL failed to inject (CreateRemoteThread failed).", NULL, MB_OK);
|
||||||
|
std::cerr << "\nDLL failed to inject (CreateRemoteThread failed).\n";
|
||||||
|
|
||||||
MH_DisableHook(Offset<uintptr_t>(SetSubGameOffset));
|
MH_DisableHook(Offset<uintptr_t>(SetSubGameOffset));
|
||||||
SetSubGame(thisref, SubGame);
|
SetSubGame(thisref, SubGame);
|
||||||
|
|||||||
@@ -10,26 +10,32 @@
|
|||||||
#define LAWIN_URL "https://github.com/Lawin0129/LawinServer/zipball/master"
|
#define LAWIN_URL "https://github.com/Lawin0129/LawinServer/zipball/master"
|
||||||
|
|
||||||
#define BINARY_PATH_OT L".\\FortniteGame\\Binaries\\Win32\\FortniteClient-Win32-Shipping.exe"
|
#define BINARY_PATH_OT L".\\FortniteGame\\Binaries\\Win32\\FortniteClient-Win32-Shipping.exe"
|
||||||
|
#define BINARY_PATH_OT_FUCKBLK L".\\FortniteGame\\Binaries\\Win32\\FortniteClient-Win64-Shipping.exe" // what the actual fuck is wrong with this guy
|
||||||
#define CONTENT_PATH_OT L".\\FortniteGame\\Content\\"
|
#define CONTENT_PATH_OT L".\\FortniteGame\\Content\\"
|
||||||
#define CONFIG_PATH_OT L".\\FortniteGame\\Config\\"
|
#define CONFIG_PATH_OT L".\\FortniteGame\\Config\\"
|
||||||
#define DEFAULTENGINE_URL "https://github.com/absoluteSpacehead/testtesttest/raw/refs/heads/main/DefaultEngine.ini"
|
#define DEFAULTENGINE_URL "https://github.com/absoluteSpacehead/MercuryLauncher-Assets/raw/refs/heads/main/DefaultEngine.ini"
|
||||||
#define DEFAULTGAME_URL "https://github.com/absoluteSpacehead/testtesttest/raw/refs/heads/main/DefaultGame.ini"
|
#define DEFAULTGAME_URL "https://github.com/absoluteSpacehead/MercuryLauncher-Assets/raw/refs/heads/main/DefaultGame.ini"
|
||||||
#define ABILITIES_URL "https://github.com/absoluteSpacehead/testtesttest/raw/refs/heads/main/GE_AllAbilities.uasset"
|
#define ABILITIES_URL "https://github.com/absoluteSpacehead/MercuryLauncher-Assets/raw/refs/heads/main/GE_AllAbilities.uasset"
|
||||||
#define ACTOR_URL "https://github.com/absoluteSpacehead/testtesttest/raw/refs/heads/main/InGame_Actor.uasset"
|
#define ACTOR_URL "https://github.com/absoluteSpacehead/MercuryLauncher-Assets/raw/refs/heads/main/InGame_Actor.uasset"
|
||||||
#define GAMEMODE_URL "https://github.com/absoluteSpacehead/testtesttest/raw/refs/heads/main/InGame_Gamemode.uasset"
|
#define GAMEMODE_URL "https://github.com/absoluteSpacehead/MercuryLauncher-Assets/raw/refs/heads/main/InGame_Gamemode.uasset"
|
||||||
|
#define PAK_URL_OT "https://github.com/absoluteSpacehead/MercuryLauncher-Assets/raw/refs/heads/main/FortniteGame-WindowsClient_p.pak"
|
||||||
|
|
||||||
#define BINARY_PATH_1_8 L".\\FortniteGame\\Binaries\\Win64\\FortniteClient-Win64-Shipping.exe"
|
#define BINARY_PATH_1_8 L".\\FortniteGame\\Binaries\\Win64\\FortniteClient-Win64-Shipping.exe"
|
||||||
#define PAKS_PATH_1_8 L".\\FortniteGame\\Content\\Paks\\"
|
#define PAKS_PATH_1_8 L".\\FortniteGame\\Content\\Paks\\"
|
||||||
#define PRODUCT_VERSION_1_8 L"3724489"
|
#define PRODUCT_VERSION_1_8 L"3724489"
|
||||||
#define DLL_URL "https://github.com/absoluteSpacehead/testtesttest/raw/refs/heads/main/Mercury-1.8.dll"
|
#define DLL_URL "https://github.com/absoluteSpacehead/MercuryLauncher-Assets/raw/refs/heads/main/Mercury-1.8.dll"
|
||||||
#define PAK_URL "https://github.com/absoluteSpacehead/testtesttest/raw/refs/heads/main/zzz_LawinServer.pak"
|
#define INJECTOR_URL "https://github.com/absoluteSpacehead/MercuryLauncher/releases/latest/download/MercuryInjector.dll"
|
||||||
#define SIG_URL "https://github.com/absoluteSpacehead/testtesttest/raw/refs/heads/main/zzz_LawinServer.sig"
|
#define PAK_URL_18 "https://github.com/absoluteSpacehead/MercuryLauncher-Assets/raw/refs/heads/main/zzz_LawinServer.pak"
|
||||||
|
#define SIG_URL_18 "https://github.com/absoluteSpacehead/MercuryLauncher-Assets/raw/refs/heads/main/zzz_LawinServer.sig"
|
||||||
|
|
||||||
HANDLE job;
|
HANDLE job;
|
||||||
|
|
||||||
|
bool verbosecURL;
|
||||||
|
|
||||||
void Exit()
|
void Exit()
|
||||||
{
|
{
|
||||||
std::cout << "Press any key to exit.\n";
|
std::cout << "Press any key to exit.\n";
|
||||||
|
TerminateJobObject(job, 0);
|
||||||
_getch();
|
_getch();
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -49,7 +55,7 @@ int DownloadFile(const char* URL, const wchar_t outName[FILENAME_MAX])
|
|||||||
|
|
||||||
if (!curl)
|
if (!curl)
|
||||||
{
|
{
|
||||||
std::cout << "cURL init failed.\n";
|
std::cout << "\ncURL init failed.\n";
|
||||||
return 1;
|
return 1;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -58,7 +64,7 @@ int DownloadFile(const char* URL, const wchar_t outName[FILENAME_MAX])
|
|||||||
{
|
{
|
||||||
char out[128];
|
char out[128];
|
||||||
strerror_s(out, fpOpen);
|
strerror_s(out, fpOpen);
|
||||||
std::cout << "_wfopen_s failed (" << out << ") .\n";
|
std::cout << "\n_wfopen_s failed (" << out << ") .\n";
|
||||||
return 2;
|
return 2;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -66,11 +72,15 @@ int DownloadFile(const char* URL, const wchar_t outName[FILENAME_MAX])
|
|||||||
curl_easy_setopt(curl, CURLOPT_URL, URL);
|
curl_easy_setopt(curl, CURLOPT_URL, URL);
|
||||||
curl_easy_setopt(curl, CURLOPT_WRITEFUNCTION, WriteData);
|
curl_easy_setopt(curl, CURLOPT_WRITEFUNCTION, WriteData);
|
||||||
curl_easy_setopt(curl, CURLOPT_WRITEDATA, fp);
|
curl_easy_setopt(curl, CURLOPT_WRITEDATA, fp);
|
||||||
|
|
||||||
|
if (verbosecURL)
|
||||||
|
curl_easy_setopt(curl, CURLOPT_VERBOSE, 1);
|
||||||
|
|
||||||
res = curl_easy_perform(curl);
|
res = curl_easy_perform(curl);
|
||||||
|
|
||||||
if (res != 0)
|
if (res != 0)
|
||||||
{
|
{
|
||||||
std::cout << "cURL failed (" << curl_easy_strerror(res) << ").\n";
|
std::cout << "\ncURL failed (" << curl_easy_strerror(res) << ").\n";
|
||||||
return 3;
|
return 3;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -122,7 +132,7 @@ int RunLawin()
|
|||||||
|
|
||||||
if (DownloadFile(LAWIN_URL, (localAppData + L"\\LawinServer.zip").c_str()) != 0)
|
if (DownloadFile(LAWIN_URL, (localAppData + L"\\LawinServer.zip").c_str()) != 0)
|
||||||
{
|
{
|
||||||
std::cerr << "LawinServer failed to download.\n";
|
std::cerr << "\nLawinServer failed to download.\n";
|
||||||
return 1;
|
return 1;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -135,14 +145,14 @@ int RunLawin()
|
|||||||
zip_source_t* src = zip_source_win32w_create((localAppData + L"\\LawinServer.zip").c_str(), 0, -1, &err);
|
zip_source_t* src = zip_source_win32w_create((localAppData + L"\\LawinServer.zip").c_str(), 0, -1, &err);
|
||||||
if (!src)
|
if (!src)
|
||||||
{
|
{
|
||||||
std::cerr << "Source creation failed (" << zip_error_strerror(&err) << ").\n";
|
std::cerr << "\nSource creation failed (" << zip_error_strerror(&err) << ").\n";
|
||||||
return 2;
|
return 2;
|
||||||
}
|
}
|
||||||
|
|
||||||
zip* file = zip_open_from_source(src, 0, &err);
|
zip* file = zip_open_from_source(src, 0, &err);
|
||||||
if (!file)
|
if (!file)
|
||||||
{
|
{
|
||||||
std::cerr << "File opening failed (" << zip_error_strerror(&err) << ").\n";
|
std::cerr << "\nFile opening failed (" << zip_error_strerror(&err) << ").\n";
|
||||||
zip_source_free(src);
|
zip_source_free(src);
|
||||||
return 3;
|
return 3;
|
||||||
}
|
}
|
||||||
@@ -175,7 +185,7 @@ int RunLawin()
|
|||||||
zip_file* zFile = zip_fopen_index(file, i, 0);
|
zip_file* zFile = zip_fopen_index(file, i, 0);
|
||||||
if (!zFile)
|
if (!zFile)
|
||||||
{
|
{
|
||||||
std::cerr << "Failed to open file " << name << " (file may be corrupt).\n";
|
std::cerr << "\nFailed to open file " << name << " (file may be corrupt).\n";
|
||||||
return 4;
|
return 4;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -183,7 +193,7 @@ int RunLawin()
|
|||||||
|
|
||||||
if (fileHandle == INVALID_HANDLE_VALUE)
|
if (fileHandle == INVALID_HANDLE_VALUE)
|
||||||
{
|
{
|
||||||
std::wcerr << "Failed to create file " << targ << ".\n";
|
std::wcerr << "\nFailed to create file " << targ << ".\n";
|
||||||
return 5;
|
return 5;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -194,7 +204,7 @@ int RunLawin()
|
|||||||
{
|
{
|
||||||
if (!WriteFile(fileHandle, buf, (DWORD)read, &written, nullptr))
|
if (!WriteFile(fileHandle, buf, (DWORD)read, &written, nullptr))
|
||||||
{
|
{
|
||||||
std::wcerr << "Error writing file " << targ << ".\n";
|
std::wcerr << "\nError writing file " << targ << ".\n";
|
||||||
return 6;
|
return 6;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -220,7 +230,7 @@ int RunLawin()
|
|||||||
startupInfo.cb = sizeof(STARTUPINFO);
|
startupInfo.cb = sizeof(STARTUPINFO);
|
||||||
PROCESS_INFORMATION processInformation = { 0 };
|
PROCESS_INFORMATION processInformation = { 0 };
|
||||||
std::wstring cmd = L"cmd /c npm i";
|
std::wstring cmd = L"cmd /c npm i";
|
||||||
CreateProcessW(nullptr, &cmd[0], nullptr, nullptr, FALSE, CREATE_NO_WINDOW, nullptr, (localAppData + L"\\LawinServer").c_str(), &startupInfo, &processInformation);
|
CreateProcessW(nullptr, &cmd[0], nullptr, nullptr, FALSE, 0x0, nullptr, (localAppData + L"\\LawinServer").c_str(), &startupInfo, &processInformation);
|
||||||
|
|
||||||
WaitForSingleObject(processInformation.hProcess, INFINITE);
|
WaitForSingleObject(processInformation.hProcess, INFINITE);
|
||||||
|
|
||||||
@@ -230,7 +240,7 @@ int RunLawin()
|
|||||||
// make sure that it actually. installed the packages
|
// make sure that it actually. installed the packages
|
||||||
if (!std::filesystem::exists(localAppData + L"\\LawinServer\\node_modules"))
|
if (!std::filesystem::exists(localAppData + L"\\LawinServer\\node_modules"))
|
||||||
{
|
{
|
||||||
std::cerr << "Packages were not installed correctly (node_modules doesn't exist).\n";
|
std::cerr << "\nPackages were not installed correctly (node_modules doesn't exist).\n\nnpm may either not be installed or not be added to PATH. Ensure node.js and npm have been installed correctly.\nIf node.js has not yet been installed, visit https://nodejs.org to download and install it.\n";
|
||||||
return 7;
|
return 7;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -242,7 +252,11 @@ int RunLawin()
|
|||||||
startupInfo.cb = sizeof(STARTUPINFO);
|
startupInfo.cb = sizeof(STARTUPINFO);
|
||||||
processInformation = { 0 };
|
processInformation = { 0 };
|
||||||
cmd = L"node index.js";
|
cmd = L"node index.js";
|
||||||
CreateProcessW(nullptr, &cmd[0], nullptr, nullptr, FALSE, 0x0, nullptr, (localAppData + L"\\LawinServer").c_str(), &startupInfo, &processInformation);
|
if (!CreateProcessW(nullptr, &cmd[0], nullptr, nullptr, FALSE, 0x0, nullptr, (localAppData + L"\\LawinServer").c_str(), &startupInfo, &processInformation))
|
||||||
|
{
|
||||||
|
std::cerr << "\nLawinServer failed to start.\n\nnode.js may either not be installed or not be added to PATH. Ensure node.js and npm have been installed correctly.\nIf node.js has not yet been installed, visit https://nodejs.org to download and install it.\n";
|
||||||
|
return 7;
|
||||||
|
}
|
||||||
|
|
||||||
AssignProcessToJobObject(job, processInformation.hProcess);
|
AssignProcessToJobObject(job, processInformation.hProcess);
|
||||||
|
|
||||||
@@ -256,19 +270,31 @@ int RunLawin()
|
|||||||
|
|
||||||
// The setup for OT / 1.8 are *completely* different, to the point where theres no code shared between them
|
// The setup for OT / 1.8 are *completely* different, to the point where theres no code shared between them
|
||||||
|
|
||||||
int SetupOT()
|
int SetupOTPak()
|
||||||
{
|
{
|
||||||
int lawinStatus = RunLawin();
|
std::wstring pathAsWstring(CONTENT_PATH_OT);
|
||||||
if (lawinStatus != 0)
|
pathAsWstring += L"Paks\\";
|
||||||
return 1;
|
|
||||||
|
|
||||||
// are we pakless?
|
// https://forums.unrealengine.com/t/overriding-or-swapping-out-asset-in-a-pak-file/472404
|
||||||
if (!std::filesystem::exists(CONTENT_PATH_OT))
|
// despite arbitrary filenames working in 1.8, OT6.5 seemingly doesnt accept that?
|
||||||
|
if (!std::filesystem::exists(pathAsWstring + L"FortniteGame-WindowsClient_p.pak"))
|
||||||
{
|
{
|
||||||
std::cerr << "Build may not be pakless (Content folder not found). Ensure you downloaded the build from the Mercury server.\n";
|
std::cout << "Required Mercury file is missing. Downloading...\n";
|
||||||
return 2;
|
|
||||||
|
if (DownloadFile(PAK_URL_OT, (pathAsWstring + L"FortniteGame-WindowsClient_p.pak").c_str()) != 0)
|
||||||
|
{
|
||||||
|
std::cerr << "\nFailed to download FortniteGame-WindowsClient_p.pak.\n";
|
||||||
|
return 3;
|
||||||
|
}
|
||||||
|
|
||||||
|
std::cout << "File downloaded.\n";
|
||||||
}
|
}
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
int SetupOTPakless()
|
||||||
|
{
|
||||||
std::wstring pathAsWstring(CONTENT_PATH_OT);
|
std::wstring pathAsWstring(CONTENT_PATH_OT);
|
||||||
|
|
||||||
// check if we have InGame_Gamemode.uasset. we download this last so if we're missing this we can download everything just to be safe
|
// check if we have InGame_Gamemode.uasset. we download this last so if we're missing this we can download everything just to be safe
|
||||||
@@ -279,44 +305,89 @@ int SetupOT()
|
|||||||
pathAsWstring = CONFIG_PATH_OT;
|
pathAsWstring = CONFIG_PATH_OT;
|
||||||
if (DownloadFile(DEFAULTENGINE_URL, (pathAsWstring + L"DefaultEngine.ini").c_str()) != 0)
|
if (DownloadFile(DEFAULTENGINE_URL, (pathAsWstring + L"DefaultEngine.ini").c_str()) != 0)
|
||||||
{
|
{
|
||||||
std::cerr << "Failed to download DefaultEngine.ini.\n";
|
std::cerr << "\nFailed to download DefaultEngine.ini.\n";
|
||||||
return 3;
|
return 3;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (DownloadFile(DEFAULTGAME_URL, (pathAsWstring + L"DefaultGame.ini").c_str()) != 0)
|
if (DownloadFile(DEFAULTGAME_URL, (pathAsWstring + L"DefaultGame.ini").c_str()) != 0)
|
||||||
{
|
{
|
||||||
std::cerr << "Failed to download DefaultGame.ini.\n";
|
std::cerr << "\nFailed to download DefaultGame.ini.\n";
|
||||||
return 3;
|
return 3;
|
||||||
}
|
}
|
||||||
|
|
||||||
pathAsWstring = CONTENT_PATH_OT;
|
pathAsWstring = CONTENT_PATH_OT;
|
||||||
if (DownloadFile(ABILITIES_URL, (pathAsWstring + L"GE_AllAbilities.uasset").c_str()) != 0)
|
if (DownloadFile(ABILITIES_URL, (pathAsWstring + L"GE_AllAbilities.uasset").c_str()) != 0)
|
||||||
{
|
{
|
||||||
std::cerr << "Failed to download GE_AllAbilities.uasset.\n";
|
std::cerr << "\nFailed to download GE_AllAbilities.uasset.\n";
|
||||||
return 3;
|
return 3;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (DownloadFile(ACTOR_URL, (pathAsWstring + L"InGame_Actor.uasset").c_str()) != 0)
|
if (DownloadFile(ACTOR_URL, (pathAsWstring + L"InGame_Actor.uasset").c_str()) != 0)
|
||||||
{
|
{
|
||||||
std::cerr << "Failed to download InGame_Actor.uasset.\n";
|
std::cerr << "\nFailed to download InGame_Actor.uasset.\n";
|
||||||
return 3;
|
return 3;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (DownloadFile(GAMEMODE_URL, (pathAsWstring + L"InGame_Gamemode.uasset").c_str()) != 0)
|
if (DownloadFile(GAMEMODE_URL, (pathAsWstring + L"InGame_Gamemode.uasset").c_str()) != 0)
|
||||||
{
|
{
|
||||||
std::cerr << "Failed to download InGame_Gamemode.uasset.\n";
|
std::cerr << "\nFailed to download InGame_Gamemode.uasset.\n";
|
||||||
return 3;
|
return 3;
|
||||||
}
|
}
|
||||||
|
|
||||||
std::cout << "All files downloaded.\n";
|
std::cout << "All files downloaded.\n";
|
||||||
}
|
}
|
||||||
|
|
||||||
std::cout << "Starting Fortnite...\nLoading may take a while. Enter anything on the login screen.\n";
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
int SetupOT()
|
||||||
|
{
|
||||||
|
int lawinStatus = RunLawin();
|
||||||
|
if (lawinStatus != 0)
|
||||||
|
return 1;
|
||||||
|
|
||||||
|
// i just cant fucking believe it
|
||||||
|
if (std::filesystem::exists(BINARY_PATH_OT_FUCKBLK))
|
||||||
|
{
|
||||||
|
std::cout << "Looks like this build was downloaded from blk. Fixing your build files...\n";
|
||||||
|
std::filesystem::rename(BINARY_PATH_OT_FUCKBLK, BINARY_PATH_OT);
|
||||||
|
std::filesystem::remove(L".\\FortniteGame\\Binaries\\Win32\\FortniteLauncher.exe");
|
||||||
|
std::filesystem::remove(L".\\FortniteGame\\Binaries\\Win32\\Launcher.bat");
|
||||||
|
}
|
||||||
|
|
||||||
|
// are we pakless?
|
||||||
|
if (!std::filesystem::exists(CONFIG_PATH_OT))
|
||||||
|
{
|
||||||
|
if (SetupOTPak() != 0)
|
||||||
|
{
|
||||||
|
std::cerr << "\nFailed to set up Mercury. (OT6.5 pakked)\n";
|
||||||
|
return 2;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
if (SetupOTPakless() != 0)
|
||||||
|
{
|
||||||
|
std::cerr << "\nFailed to set up Mercury. (OT6.5 pakless)\n";
|
||||||
|
return 2;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
std::cout << "Starting Fortnite...\nLoading may take a while.\n";
|
||||||
|
|
||||||
STARTUPINFOW startupInfo = { 0 };
|
STARTUPINFOW startupInfo = { 0 };
|
||||||
startupInfo.cb = sizeof(STARTUPINFO);
|
startupInfo.cb = sizeof(STARTUPINFO);
|
||||||
PROCESS_INFORMATION processInformation = { 0 };
|
PROCESS_INFORMATION processInformation = { 0 };
|
||||||
CreateProcessW((LPWSTR)BINARY_PATH_OT, nullptr, nullptr, nullptr, FALSE, 0x0, nullptr, nullptr, &startupInfo, &processInformation);
|
std::wstring cmd(BINARY_PATH_OT);
|
||||||
|
cmd += L" -log -AUTH_LOGIN=unknown -AUTH_PASSWORD=5001 -AUTH_TYPE=exchangecode";
|
||||||
|
|
||||||
|
bool success = CreateProcessW(BINARY_PATH_OT, &cmd[0], nullptr, nullptr, FALSE, 0x0, nullptr, nullptr, &startupInfo, &processInformation);
|
||||||
|
|
||||||
|
if (!success)
|
||||||
|
{
|
||||||
|
std::cerr << "\nFailed to launch Fortnite. (" << GetLastError() << ")\n";
|
||||||
|
return 3;
|
||||||
|
}
|
||||||
|
|
||||||
AssignProcessToJobObject(job, processInformation.hProcess);
|
AssignProcessToJobObject(job, processInformation.hProcess);
|
||||||
|
|
||||||
@@ -335,37 +406,36 @@ int Setup18()
|
|||||||
return 1;
|
return 1;
|
||||||
|
|
||||||
std::wstring localAppData;
|
std::wstring localAppData;
|
||||||
|
|
||||||
GetLocalAppData(localAppData);
|
GetLocalAppData(localAppData);
|
||||||
|
|
||||||
// check that we have our dll. this isn't required for OT so it's not linked, means you only need one file in the OT dir
|
// check that we have the mercury dlls AND the sig. someone could be using the same build folder but have wiped their appdata, etc
|
||||||
if (!std::filesystem::exists(".\\MercuryInjector.dll"))
|
// check for injector for backcomp
|
||||||
{
|
|
||||||
std::cerr << "Required DLL (MercuryInjector.dll) is missing. Ensure all files were extracted properly.\n";
|
|
||||||
return 4;
|
|
||||||
}
|
|
||||||
|
|
||||||
// check that we have the mercury dll AND the pak. someone could be using the same build folder but have wiped their appdata, etc
|
|
||||||
std::wstring pathAsWstring(PAKS_PATH_1_8);
|
std::wstring pathAsWstring(PAKS_PATH_1_8);
|
||||||
if (!std::filesystem::exists(localAppData + L"\\Mercury-1.8.dll") || !std::filesystem::exists(pathAsWstring + L"zzz_LawinServer.pak"))
|
if (!std::filesystem::exists(localAppData + L"\\Mercury-1.8.dll") || !std::filesystem::exists(localAppData + L"\\MercuryInjector.dll") || !std::filesystem::exists(pathAsWstring + L"zzz_LawinServer.sig"))
|
||||||
{
|
{
|
||||||
std::cout << "Required Mercury files are missing. Downloading...\n";
|
std::cout << "Required Mercury files are missing. Downloading...\n";
|
||||||
|
|
||||||
if (DownloadFile(DLL_URL, (localAppData + L"\\Mercury-1.8.dll").c_str()) != 0)
|
if (DownloadFile(DLL_URL, (localAppData + L"\\Mercury-1.8.dll").c_str()) != 0)
|
||||||
{
|
{
|
||||||
std::cerr << "Failed to download Mercury-1.8.dll.\n";
|
std::cerr << "\nFailed to download Mercury-1.8.dll.\n";
|
||||||
return 2;
|
return 2;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (DownloadFile(PAK_URL, (pathAsWstring + L"zzz_LawinServer.pak").c_str()) != 0)
|
if (DownloadFile(INJECTOR_URL, (localAppData + L"\\MercuryInjector.dll").c_str()) != 0)
|
||||||
{
|
{
|
||||||
std::cerr << "Failed to download zzz_LawinServer.pak.\n";
|
std::cerr << "\nFailed to download MercuryInjector.dll.\n";
|
||||||
return 2;
|
return 2;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (DownloadFile(SIG_URL, (pathAsWstring + L"zzz_LawinServer.sig").c_str()) != 0)
|
if (DownloadFile(PAK_URL_18, (pathAsWstring + L"zzz_LawinServer.pak").c_str()) != 0)
|
||||||
{
|
{
|
||||||
std::cerr << "Failed to download zzz_LawinServer.sig.\n";
|
std::cerr << "\nFailed to download zzz_LawinServer.pak.\n";
|
||||||
|
return 2;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (DownloadFile(SIG_URL_18, (pathAsWstring + L"zzz_LawinServer.sig").c_str()) != 0)
|
||||||
|
{
|
||||||
|
std::cerr << "\nFailed to download zzz_LawinServer.sig.\n";
|
||||||
return 2;
|
return 2;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -383,12 +453,17 @@ int Setup18()
|
|||||||
std::wstring cmdl(BINARY_PATH_1_8);
|
std::wstring cmdl(BINARY_PATH_1_8);
|
||||||
cmdl += L" -skippatchcheck -epicportal -HTTP=WinInet -log";
|
cmdl += L" -skippatchcheck -epicportal -HTTP=WinInet -log";
|
||||||
|
|
||||||
CreateProcessW((LPWSTR)BINARY_PATH_1_8, &cmdl[0], nullptr, nullptr, FALSE, 0x0, nullptr, nullptr, &startupInfo, &processInformation);
|
bool success = CreateProcessW(BINARY_PATH_1_8, &cmdl[0], nullptr, nullptr, FALSE, 0x0, nullptr, nullptr, &startupInfo, &processInformation);
|
||||||
|
|
||||||
|
if (!success)
|
||||||
|
{
|
||||||
|
std::cerr << "\nFailed to launch Fortnite. (" << GetLastError() << ")\n";
|
||||||
|
return 5;
|
||||||
|
}
|
||||||
|
|
||||||
AssignProcessToJobObject(job, processInformation.hProcess);
|
AssignProcessToJobObject(job, processInformation.hProcess);
|
||||||
|
|
||||||
std::wstring dllPath(std::filesystem::current_path().c_str());
|
std::wstring dllPath((localAppData + L"\\MercuryInjector.dll").c_str());
|
||||||
dllPath += L"\\MercuryInjector.dll";
|
|
||||||
|
|
||||||
// reference https://github.com/ZeroMemoryEx/Dll-Injector/blob/master/DLL-Injector/Dll-Injector.cpp#L43
|
// reference https://github.com/ZeroMemoryEx/Dll-Injector/blob/master/DLL-Injector/Dll-Injector.cpp#L43
|
||||||
HANDLE fnHandle = OpenProcess(PROCESS_ALL_ACCESS, FALSE, processInformation.dwProcessId);
|
HANDLE fnHandle = OpenProcess(PROCESS_ALL_ACCESS, FALSE, processInformation.dwProcessId);
|
||||||
@@ -397,7 +472,7 @@ int Setup18()
|
|||||||
|
|
||||||
if (!write)
|
if (!write)
|
||||||
{
|
{
|
||||||
std::cerr << "DLL failed to inject (WriteProcessMemory failed).\n";
|
std::cerr << "\nDLL failed to inject (WriteProcessMemory failed).\n";
|
||||||
|
|
||||||
TerminateProcess(processInformation.hProcess, 0);
|
TerminateProcess(processInformation.hProcess, 0);
|
||||||
return 3;
|
return 3;
|
||||||
@@ -407,7 +482,7 @@ int Setup18()
|
|||||||
|
|
||||||
if (!thread)
|
if (!thread)
|
||||||
{
|
{
|
||||||
std::cerr << "DLL failed to inject (CreateRemoteThread failed).\n";
|
std::cerr << "\nDLL failed to inject (CreateRemoteThread failed).\n";
|
||||||
|
|
||||||
TerminateProcess(processInformation.hProcess, 0);
|
TerminateProcess(processInformation.hProcess, 0);
|
||||||
return 3;
|
return 3;
|
||||||
@@ -428,32 +503,22 @@ int Setup18()
|
|||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
int main()
|
int main(int argc, char* argv[])
|
||||||
{
|
{
|
||||||
|
for (int i = 0; i < argc; i++)
|
||||||
|
{
|
||||||
|
if (_stricmp(argv[i], "-verbosecurl") == 0)
|
||||||
|
{
|
||||||
|
verbosecURL = true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
job = CreateJobObjectW(nullptr, nullptr);
|
job = CreateJobObjectW(nullptr, nullptr);
|
||||||
JOBOBJECT_EXTENDED_LIMIT_INFORMATION jobInfo = { 0 };
|
JOBOBJECT_EXTENDED_LIMIT_INFORMATION jobInfo = { 0 };
|
||||||
jobInfo.BasicLimitInformation.LimitFlags = JOB_OBJECT_LIMIT_KILL_ON_JOB_CLOSE;
|
jobInfo.BasicLimitInformation.LimitFlags = JOB_OBJECT_LIMIT_KILL_ON_JOB_CLOSE;
|
||||||
SetInformationJobObject(job, JobObjectExtendedLimitInformation, &jobInfo, sizeof(jobInfo));
|
SetInformationJobObject(job, JobObjectExtendedLimitInformation, &jobInfo, sizeof(jobInfo));
|
||||||
|
|
||||||
// a lil hacky i dont care.
|
if (std::filesystem::exists(BINARY_PATH_OT) || std::filesystem::exists(BINARY_PATH_OT_FUCKBLK))
|
||||||
std::wstring cmd = L"cmd /c npm --version";
|
|
||||||
STARTUPINFOW startupInfo = { 0 };
|
|
||||||
startupInfo.cb = sizeof(STARTUPINFO);
|
|
||||||
PROCESS_INFORMATION processInformation = { 0 };
|
|
||||||
|
|
||||||
if (!CreateProcessW(nullptr, &cmd[0], nullptr, nullptr, FALSE, CREATE_NO_WINDOW, nullptr, nullptr, &startupInfo, &processInformation))
|
|
||||||
{
|
|
||||||
std::cerr << "npm is either not installed or has not been added to PATH. Ensure node.js and npm have been installed correctly.\nIf node.js has not yet been installed, visit https://nodejs.org to download and install it.\n";
|
|
||||||
CloseHandle(processInformation.hProcess);
|
|
||||||
CloseHandle(processInformation.hThread);
|
|
||||||
Exit();
|
|
||||||
return 3;
|
|
||||||
}
|
|
||||||
|
|
||||||
CloseHandle(processInformation.hProcess);
|
|
||||||
CloseHandle(processInformation.hThread);
|
|
||||||
|
|
||||||
if (std::filesystem::exists(BINARY_PATH_OT))
|
|
||||||
{
|
{
|
||||||
// we only have 1 32bit build lol
|
// we only have 1 32bit build lol
|
||||||
int status = SetupOT();
|
int status = SetupOT();
|
||||||
@@ -487,14 +552,14 @@ int main()
|
|||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
std::wcerr << "Binary ProductVersion is wrong (got " << version << ", expected " << PRODUCT_VERSION_1_8 << "). Ensure you're using Fortnite 1.8.\n";
|
std::wcerr << "\nBinary ProductVersion is wrong (got " << version << ", expected " << PRODUCT_VERSION_1_8 << "). Ensure you're using Fortnite 1.8.\n";
|
||||||
Exit();
|
Exit();
|
||||||
return 2;
|
return 2;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
std::cerr << "No binaries could be found. Ensure the launcher is placed alongside the FortniteGame and Content folders.\n";
|
std::cerr << "\nNo binaries could be found. Ensure the launcher is placed alongside the FortniteGame and Engine folders.\n";
|
||||||
Exit();
|
Exit();
|
||||||
return 1;
|
return 1;
|
||||||
}
|
}
|
||||||
|
|||||||
6
README.md
Normal file
6
README.md
Normal file
@@ -0,0 +1,6 @@
|
|||||||
|
Temporary, sloppily written Mercury launcher designed to keep things simple until the next update (never) releases.
|
||||||
|
|
||||||
|
Uses a bunch of libraries that I'm too lazy to write here, notably MinHook, libcurl & libzip
|
||||||
|
|
||||||
|
# Usage
|
||||||
|
<img src="https://github.com/user-attachments/assets/3e063e39-95a5-43df-b942-7bf90f1496e4" />
|
||||||
Reference in New Issue
Block a user