diff --git a/CarbonLauncher.sln b/CarbonLauncher.sln
index 1ce08f2..aec51f1 100644
--- a/CarbonLauncher.sln
+++ b/CarbonLauncher.sln
@@ -1,13 +1,9 @@
Microsoft Visual Studio Solution File, Format Version 12.00
# Visual Studio Version 17
-VisualStudioVersion = 17.13.35507.96
+VisualStudioVersion = 17.13.35507.96 d17.13
MinimumVisualStudioVersion = 10.0.40219.1
-Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "CarbonLauncher", "CarbonLauncher\CarbonLauncher.vcxproj", "{E5A80EF0-1C60-4D13-8569-03CF1794CDB6}"
-EndProject
-Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "CarbonSupervisor", "CarbonSupervisor\CarbonSupervisor.vcxproj", "{1F14E774-5A60-4742-9534-712E254FAA60}"
-EndProject
-Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "DummyGame", "DummyGame\DummyGame.vcxproj", "{A56AC389-85AD-4E6F-9E64-E859DD676099}"
+Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "CarbonLauncher", "CarbonLauncher\CarbonLauncher.vcxproj", "{F55718C8-63D5-4CCA-8499-07500910C9BC}"
EndProject
Global
GlobalSection(SolutionConfigurationPlatforms) = preSolution
@@ -17,35 +13,19 @@ Global
Release|x86 = Release|x86
EndGlobalSection
GlobalSection(ProjectConfigurationPlatforms) = postSolution
- {E5A80EF0-1C60-4D13-8569-03CF1794CDB6}.Debug|x64.ActiveCfg = Debug|x64
- {E5A80EF0-1C60-4D13-8569-03CF1794CDB6}.Debug|x64.Build.0 = Debug|x64
- {E5A80EF0-1C60-4D13-8569-03CF1794CDB6}.Debug|x86.ActiveCfg = Debug|Win32
- {E5A80EF0-1C60-4D13-8569-03CF1794CDB6}.Debug|x86.Build.0 = Debug|Win32
- {E5A80EF0-1C60-4D13-8569-03CF1794CDB6}.Release|x64.ActiveCfg = Release|x64
- {E5A80EF0-1C60-4D13-8569-03CF1794CDB6}.Release|x64.Build.0 = Release|x64
- {E5A80EF0-1C60-4D13-8569-03CF1794CDB6}.Release|x86.ActiveCfg = Release|Win32
- {E5A80EF0-1C60-4D13-8569-03CF1794CDB6}.Release|x86.Build.0 = Release|Win32
- {1F14E774-5A60-4742-9534-712E254FAA60}.Debug|x64.ActiveCfg = Debug|x64
- {1F14E774-5A60-4742-9534-712E254FAA60}.Debug|x64.Build.0 = Debug|x64
- {1F14E774-5A60-4742-9534-712E254FAA60}.Debug|x86.ActiveCfg = Debug|Win32
- {1F14E774-5A60-4742-9534-712E254FAA60}.Debug|x86.Build.0 = Debug|Win32
- {1F14E774-5A60-4742-9534-712E254FAA60}.Release|x64.ActiveCfg = Release|x64
- {1F14E774-5A60-4742-9534-712E254FAA60}.Release|x64.Build.0 = Release|x64
- {1F14E774-5A60-4742-9534-712E254FAA60}.Release|x86.ActiveCfg = Release|Win32
- {1F14E774-5A60-4742-9534-712E254FAA60}.Release|x86.Build.0 = Release|Win32
- {A56AC389-85AD-4E6F-9E64-E859DD676099}.Debug|x64.ActiveCfg = Debug|x64
- {A56AC389-85AD-4E6F-9E64-E859DD676099}.Debug|x64.Build.0 = Debug|x64
- {A56AC389-85AD-4E6F-9E64-E859DD676099}.Debug|x86.ActiveCfg = Debug|Win32
- {A56AC389-85AD-4E6F-9E64-E859DD676099}.Debug|x86.Build.0 = Debug|Win32
- {A56AC389-85AD-4E6F-9E64-E859DD676099}.Release|x64.ActiveCfg = Release|x64
- {A56AC389-85AD-4E6F-9E64-E859DD676099}.Release|x64.Build.0 = Release|x64
- {A56AC389-85AD-4E6F-9E64-E859DD676099}.Release|x86.ActiveCfg = Release|Win32
- {A56AC389-85AD-4E6F-9E64-E859DD676099}.Release|x86.Build.0 = Release|Win32
+ {F55718C8-63D5-4CCA-8499-07500910C9BC}.Debug|x64.ActiveCfg = Debug|x64
+ {F55718C8-63D5-4CCA-8499-07500910C9BC}.Debug|x64.Build.0 = Debug|x64
+ {F55718C8-63D5-4CCA-8499-07500910C9BC}.Debug|x86.ActiveCfg = Debug|Win32
+ {F55718C8-63D5-4CCA-8499-07500910C9BC}.Debug|x86.Build.0 = Debug|Win32
+ {F55718C8-63D5-4CCA-8499-07500910C9BC}.Release|x64.ActiveCfg = Release|x64
+ {F55718C8-63D5-4CCA-8499-07500910C9BC}.Release|x64.Build.0 = Release|x64
+ {F55718C8-63D5-4CCA-8499-07500910C9BC}.Release|x86.ActiveCfg = Release|Win32
+ {F55718C8-63D5-4CCA-8499-07500910C9BC}.Release|x86.Build.0 = Release|Win32
EndGlobalSection
GlobalSection(SolutionProperties) = preSolution
HideSolutionNode = FALSE
EndGlobalSection
GlobalSection(ExtensibilityGlobals) = postSolution
- SolutionGuid = {0197DFC4-D5CF-4C67-AEF4-436F47231050}
+ SolutionGuid = {5C1163F1-0D28-415C-BAC3-80138FA2F668}
EndGlobalSection
EndGlobal
diff --git a/CarbonLauncher/CarbonLauncher.vcxproj b/CarbonLauncher/CarbonLauncher.vcxproj
index ae91bcc..dcb13db 100644
--- a/CarbonLauncher/CarbonLauncher.vcxproj
+++ b/CarbonLauncher/CarbonLauncher.vcxproj
@@ -18,17 +18,10 @@
x64
-
-
-
-
-
-
-
17.0
Win32Proj
- {e5a80ef0-1c60-4d13-8569-03cf1794cdb6}
+ {f55718c8-63d5-4cca-8499-07500910c9bc}
CarbonLauncher
10.0
@@ -37,27 +30,27 @@
Application
true
v143
- MultiByte
+ Unicode
Application
false
v143
true
- MultiByte
+ Unicode
Application
true
v143
- MultiByte
+ Unicode
Application
false
v143
true
- MultiByte
+ Unicode
@@ -91,7 +84,7 @@
/utf-8 %(AdditionalOptions)
- Console
+ Windows
true
@@ -108,7 +101,7 @@
/utf-8 %(AdditionalOptions)
- Console
+ Windows
true
true
true
@@ -125,7 +118,7 @@
/utf-8 %(AdditionalOptions)
- Console
+ Windows
true
@@ -142,12 +135,27 @@
/utf-8 %(AdditionalOptions)
- Console
+ Windows
true
true
true
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/CarbonLauncher/include/discordmanager.h b/CarbonLauncher/include/discordmanager.h
new file mode 100644
index 0000000..73d18b2
--- /dev/null
+++ b/CarbonLauncher/include/discordmanager.h
@@ -0,0 +1,24 @@
+#pragma once
+
+#include
+#include
+
+#include
+
+namespace Carbon {
+ class DiscordManager {
+ public:
+ DiscordManager();
+ ~DiscordManager();
+
+ void UpdateActivity() const;
+ discord::Activity& GetActivity();
+
+ void Update();
+
+ discord::User currentUser = discord::User{};
+ discord::Activity currentActivity = discord::Activity{};
+
+ std::unique_ptr core = nullptr;
+ };
+}; // namespace Carbon
diff --git a/CarbonLauncher/include/gamemanager.h b/CarbonLauncher/include/gamemanager.h
new file mode 100644
index 0000000..ec58e5d
--- /dev/null
+++ b/CarbonLauncher/include/gamemanager.h
@@ -0,0 +1,31 @@
+#pragma once
+
+#define WIN32_LEAN_AND_MEAN
+#define GLFW_INCLUDE_NONE
+
+#include
+#include
+
+namespace Carbon {
+ class GameManager {
+ public:
+ GameManager();
+ ~GameManager();
+
+ bool IsGameRunning();
+
+ void StartGame();
+ void KillGame();
+
+ // TODO: Send a message to the
+ // supervisor to kill the game
+ // void KillGame();
+
+ private:
+ // Checks every 1s if the game is running
+ std::thread gameStatusThread;
+ std::mutex gameStatusMutex;
+
+ bool gameRunning = false;
+ };
+}; // namespace Carbon
diff --git a/CarbonLauncher/include/guimanager.h b/CarbonLauncher/include/guimanager.h
new file mode 100644
index 0000000..ef22944
--- /dev/null
+++ b/CarbonLauncher/include/guimanager.h
@@ -0,0 +1,29 @@
+#pragma once
+
+#define WIN32_LEAN_AND_MEAN
+#define GLFW_INCLUDE_NONE
+
+#include
+
+#include
+
+#include
+
+namespace Carbon {
+ class GUIManager {
+ public:
+ GUIManager(HINSTANCE hInstance);
+ ~GUIManager();
+
+ void RenderCallback(std::function callback) {
+ this->renderCallback = callback;
+ }
+
+ void Run();
+
+ GLFWwindow* window;
+ std::function renderCallback;
+ };
+}; // namespace Carbon
+
+void _GUI();
diff --git a/CarbonLauncher/include/pipemanager.h b/CarbonLauncher/include/pipemanager.h
new file mode 100644
index 0000000..f424b17
--- /dev/null
+++ b/CarbonLauncher/include/pipemanager.h
@@ -0,0 +1,45 @@
+#pragma once
+
+#define WIN32_LEAN_AND_MEAN
+#include
+
+#include
+#include
+#include
+#include
+#include
+#include
+
+namespace Carbon {
+ enum class PacketType {
+ LOADED, // Sent when the game is loaded
+ STATECHANGE, // Sent when the game state changes (e.g. menu -> game)
+ LOG, // (e.g.game log [will be implemented later])
+ UNKNOWNTYPE // Unknown packet type
+ };
+
+ class Packet {
+ public:
+ PacketType type = PacketType::UNKNOWNTYPE;
+ std::optional data;
+ };
+
+ class PipeManager {
+ public:
+ PipeManager();
+ ~PipeManager();
+
+ bool pipeInitialized = false;
+
+ std::mutex pipeMutex;
+ std::queue packets = {};
+
+ std::queue& GetPackets() {
+ std::lock_guard lock(this->pipeMutex);
+ return this->packets;
+ }
+
+ private:
+ std::thread pipeReader;
+ };
+}; // namespace Carbon
diff --git a/CarbonLauncher/include/state.h b/CarbonLauncher/include/state.h
new file mode 100644
index 0000000..8dd4046
--- /dev/null
+++ b/CarbonLauncher/include/state.h
@@ -0,0 +1,18 @@
+#pragma once
+
+#include "guimanager.h"
+#include "discordmanager.h"
+#include "gamemanager.h"
+#include "pipemanager.h"
+
+namespace Carbon {
+ class CarbonState_t {
+ public:
+ Carbon::GUIManager* guiManager;
+ Carbon::DiscordManager* discordManager;
+ Carbon::GameManager* gameManager;
+ Carbon::PipeManager* pipeManager;
+ };
+}; // namespace Carbon
+
+extern Carbon::CarbonState_t C;
diff --git a/CarbonLauncher/include/utils.h b/CarbonLauncher/include/utils.h
deleted file mode 100644
index 33e437b..0000000
--- a/CarbonLauncher/include/utils.h
+++ /dev/null
@@ -1,55 +0,0 @@
-#pragma once
-
-#include
-
-#define WIN32_LEAN_AND_MEAN
-#include
-
-#include
-#include
-
-#undef CreateWindow
-
-static HWND g_hWnd = nullptr;
-
-/*
- * Get the last error message as a string.
- *
- * @return The last error message as a string.
- */
-std::string GetLastErrorAsString();
-
-/*
- * Get the process ID of a process by name.
- *
- * @param processName The name of the process to get the ID of.
- * @return The process ID of the process with the given name, or 0 if the process was not found.
- */
-DWORD GetProcID(const std::string& processName);
-
-/*
- * Inject a DLL into a process.
- *
- * @param targetPID The process ID of the target process.
- * @param window The window to set as the foreground window after injection.
- * @return True if the DLL was successfully injected, false otherwise.
- */
-[[nodiscard]] bool Inject(DWORD targetPID, HWND window, const std::string& toLoad);
-
-/*
- * Enumerate windows callback function.
- *
- * @param hWnd The window handle.
- * @param lParam The process ID to compare against.
- * @return False if the process ID matches, true otherwise.
- */
-BOOL CALLBACK EnumWindowsProc(HWND hWnd, LPARAM lParam);
-
-std::string GetExeDirectory();
-
-GLFWwindow* CreateWindow(int width, int height, const char* title);
-void InitImGui(GLFWwindow* window);
-
-void Render(GLFWwindow* window, std::function renderFunc);
-
-bool IsGameOpen(const char* name);
diff --git a/CarbonLauncher/src/discordmanager.cpp b/CarbonLauncher/src/discordmanager.cpp
new file mode 100644
index 0000000..6ac9d6e
--- /dev/null
+++ b/CarbonLauncher/src/discordmanager.cpp
@@ -0,0 +1,89 @@
+#include "discordmanager.h"
+#include "guimanager.h"
+
+#include
+
+constexpr auto discordClientId = 1315436867545595904;
+
+using namespace Carbon;
+
+DiscordManager::DiscordManager() {
+ discord::Core* core{};
+ auto result = discord::Core::Create(discordClientId, DiscordCreateFlags_NoRequireDiscord, &core);
+
+ this->core.reset(core);
+
+ if (!this->core) {
+ spdlog::warn("Failed to create Discord core");
+ }
+
+ else {
+ auto dCore = this->core.get();
+
+ dCore->SetLogHook(discord::LogLevel::Debug, [](discord::LogLevel level, const char* message) {
+ spdlog::debug("[Discord] {}", message);
+ });
+
+ dCore->SetLogHook(discord::LogLevel::Info, [](discord::LogLevel level, const char* message) {
+ spdlog::info("[Discord] {}", message);
+ });
+
+ dCore->SetLogHook(discord::LogLevel::Warn, [](discord::LogLevel level, const char* message) {
+ spdlog::warn("[Discord] {}", message);
+ });
+
+ dCore->SetLogHook(discord::LogLevel::Error, [](discord::LogLevel level, const char* message) {
+ spdlog::error("[Discord] {}", message);
+ });
+
+ this->core->ActivityManager().RegisterCommand("carbonlauncher://run");
+
+ this->currentActivity = {};
+ auto& activity = this->GetActivity();
+
+ activity.SetDetails("The latest modded launcher for Scrap Mechanic!");
+ activity.SetState("Not in game... https://github.com/ScrappySM/CarbonLauncher!");
+
+ activity.GetAssets().SetLargeImage("carbonlauncher");
+ activity.GetAssets().SetLargeText("Carbon Launcher");
+ activity.GetAssets().SetSmallImage("carbonlauncher");
+ activity.GetAssets().SetSmallText("Carbon Launcher");
+
+ activity.SetType(discord::ActivityType::Playing);
+
+ activity.SetSupportedPlatforms((uint32_t)discord::ActivitySupportedPlatformFlags::Desktop);
+
+ this->core->ActivityManager().UpdateActivity(activity, [](discord::Result result) {
+ if (result != discord::Result::Ok) {
+ spdlog::error("Failed to update Discord RPC (error: {})", (int)result);
+ }
+ });
+ }
+
+
+ spdlog::info("Created Discord instance");
+}
+
+DiscordManager::~DiscordManager() {
+ spdlog::info("Destroying Discord instance");
+}
+
+void DiscordManager::UpdateActivity() const {
+ spdlog::info("Updating activity");
+
+ this->core->ActivityManager().UpdateActivity(this->currentActivity, [](discord::Result result) {
+ if (result != discord::Result::Ok) {
+ spdlog::error("Failed to update activity");
+ }
+ });
+}
+
+discord::Activity& DiscordManager::GetActivity() {
+ spdlog::info("Getting activity");
+
+ return this->currentActivity;
+}
+
+void DiscordManager::Update() {
+ this->core->RunCallbacks();
+}
\ No newline at end of file
diff --git a/CarbonLauncher/src/gamemanager.cpp b/CarbonLauncher/src/gamemanager.cpp
new file mode 100644
index 0000000..a04e57e
--- /dev/null
+++ b/CarbonLauncher/src/gamemanager.cpp
@@ -0,0 +1,80 @@
+#include "gamemanager.h"
+
+#define WIN32_LEAN_AND_MEAN
+#include
+
+#include
+#include
+
+using namespace Carbon;
+
+GameManager::GameManager() {
+ this->gameStatusThread = std::thread([this]() {
+ while (true) {
+ std::this_thread::sleep_for(std::chrono::seconds(1));
+
+ auto snapshot = CreateToolhelp32Snapshot(TH32CS_SNAPPROCESS, 0);
+ if (snapshot == INVALID_HANDLE_VALUE) {
+ continue;
+ }
+
+ PROCESSENTRY32 entry{};
+ entry.dwSize = sizeof(entry);
+
+ if (!Process32First(snapshot, &entry)) {
+ CloseHandle(snapshot);
+ continue;
+ }
+
+ bool found = false;
+ do {
+ if (std::wstring(entry.szExeFile) == L"ScrapMechanic.exe") {
+ found = true;
+ break;
+ }
+ } while (Process32Next(snapshot, &entry));
+
+ CloseHandle(snapshot);
+
+ std::lock_guard lock(this->gameStatusMutex);
+ this->gameRunning = found;
+ }
+ });
+}
+
+GameManager::~GameManager() {
+ this->gameStatusThread.join();
+}
+
+bool GameManager::IsGameRunning() {
+ std::lock_guard lock(this->gameStatusMutex);
+ return this->gameRunning;
+}
+
+void GameManager::StartGame() {
+ // steam://rungameid/387990
+ ShellExecute(NULL, L"open", L"steam://rungameid/387990", NULL, NULL, SW_SHOWNORMAL);
+}
+
+void GameManager::KillGame() {
+ auto snapshot = CreateToolhelp32Snapshot(TH32CS_SNAPPROCESS, 0);
+ if (snapshot == INVALID_HANDLE_VALUE) {
+ return;
+ }
+ PROCESSENTRY32 entry{};
+ entry.dwSize = sizeof(entry);
+ if (!Process32First(snapshot, &entry)) {
+ CloseHandle(snapshot);
+ return;
+ }
+ do {
+ if (std::wstring(entry.szExeFile) == L"ScrapMechanic.exe") {
+ HANDLE process = OpenProcess(PROCESS_TERMINATE, FALSE, entry.th32ProcessID);
+ if (process) {
+ TerminateProcess(process, 0);
+ CloseHandle(process);
+ }
+ }
+ } while (Process32Next(snapshot, &entry));
+ CloseHandle(snapshot);
+}
diff --git a/CarbonLauncher/src/guimanager.cpp b/CarbonLauncher/src/guimanager.cpp
new file mode 100644
index 0000000..a1457d2
--- /dev/null
+++ b/CarbonLauncher/src/guimanager.cpp
@@ -0,0 +1,157 @@
+#pragma comment(lib, "dwmapi.lib")
+#define GLFW_INCLUDE_NONE
+#define GLFW_EXPOSE_NATIVE_WIN32
+#define WIN32_LEAN_AND_MEAN
+
+#include "guimanager.h"
+#include "state.h"
+
+#include
+
+#include
+
+#include
+#include
+#include
+
+#include
+
+#include
+#include
+#include
+
+using namespace Carbon;
+
+GUIManager::GUIManager(HINSTANCE hInstance) {
+ // Initialize GLFW
+ if (!glfwInit()) {
+ MessageBox(NULL, L"GLFW Initialization Failed!", L"Error!", MB_ICONEXCLAMATION | MB_OK);
+ return;
+ }
+
+ glfwSetErrorCallback([](int error, const char* description) {
+ spdlog::error("GLFW Error {}: {}", error, description);
+ });
+
+ // Create the OpenGL context
+ glfwWindowHint(GLFW_CONTEXT_VERSION_MAJOR, 4);
+ glfwWindowHint(GLFW_CONTEXT_VERSION_MINOR, 6);
+ glfwWindowHint(GLFW_OPENGL_PROFILE, GLFW_OPENGL_CORE_PROFILE);
+
+ // Create the GLFW window
+ this->window = glfwCreateWindow(1280, 720, "Carbon Launcher", NULL, NULL);
+ if (!this->window) {
+ spdlog::error("GLFW Window Creation Failed!");
+ glfwTerminate();
+ return;
+ }
+
+ glfwMakeContextCurrent(this->window);
+
+ // Initialize GLAD
+ if (!gladLoadGLLoader((GLADloadproc)glfwGetProcAddress)) {
+ spdlog::error("GLAD Initialization Failed!");
+ glfwTerminate();
+ return;
+ }
+
+ // Enable dark mode (Windows only)
+ BOOL darkMode = TRUE;
+ HWND hWnd = glfwGetWin32Window(this->window);
+ DwmSetWindowAttribute(hWnd, DWMWA_USE_IMMERSIVE_DARK_MODE, &darkMode, sizeof(darkMode));
+
+ // Initialize ImGui
+ IMGUI_CHECKVERSION();
+ ImGui::CreateContext();
+ ImGuiIO& io = ImGui::GetIO();
+ (void)io;
+ ImGui::StyleColorsDark();
+
+ io.Fonts->AddFontFromFileTTF("C:\\Windows\\Fonts\\seguisb.ttf", 18.0f);
+
+ ImGui_ImplGlfw_InitForOpenGL(this->window, true);
+ ImGui_ImplOpenGL3_Init("#version 460");
+ glfwSwapInterval(1);
+}
+
+GUIManager::~GUIManager() {
+ // Terminate GLFW
+ glfwTerminate();
+
+ // Terminate ImGui
+ ImGui_ImplOpenGL3_Shutdown();
+ ImGui_ImplGlfw_Shutdown();
+ ImGui::DestroyContext();
+}
+
+void GUIManager::Run() {
+ while (!glfwWindowShouldClose(window)) {
+ glfwPollEvents();
+
+ glClearColor(0.1f, 0.1f, 0.1f, 1.0f);
+ glClear(GL_COLOR_BUFFER_BIT);
+
+ ImGui_ImplOpenGL3_NewFrame();
+ ImGui_ImplGlfw_NewFrame();
+ ImGui::NewFrame();
+
+ if (this->renderCallback) {
+ this->renderCallback();
+ }
+
+ ImGui::Render();
+ ImGui_ImplOpenGL3_RenderDrawData(ImGui::GetDrawData());
+
+ glfwSwapBuffers(window);
+ }
+}
+
+void _GUI() {
+ using namespace Carbon;
+
+ C.discordManager->Update();
+
+ // Begin main menu bar
+ if (ImGui::BeginMainMenuBar()) {
+ if (ImGui::BeginMenu("File")) {
+ if (ImGui::MenuItem("Exit")) {
+ glfwSetWindowShouldClose(C.guiManager->window, GLFW_TRUE);
+ }
+ ImGui::EndMenu();
+ }
+ ImGui::EndMainMenuBar();
+ }
+
+ int w, h;
+ glfwGetWindowSize(C.guiManager->window, &w, &h);
+
+ ImGui::SetNextWindowPos(ImVec2(0, 0));
+ ImGui::SetNextWindowSize(ImVec2((float)w, (float)h));
+ ImGui::Begin("Carbon Launcher", nullptr, ImGuiWindowFlags_NoTitleBar | ImGuiWindowFlags_NoResize | ImGuiWindowFlags_NoMove | ImGuiWindowFlags_NoCollapse | ImGuiWindowFlags_NoBackground | ImGuiWindowFlags_NoBringToFrontOnFocus | ImGuiWindowFlags_NoNavFocus | ImGuiWindowFlags_NoNav | ImGuiWindowFlags_MenuBar);
+
+ // Begin tabs
+ if (ImGui::BeginTabBar("CarbonTabs", ImGuiTabBarFlags_None)) {
+ // Begin the first tab
+ if (ImGui::BeginTabItem("Home")) {
+ if (ImGui::Button("Start Game")) {
+ C.gameManager->StartGame();
+ }
+ if (ImGui::Button("Kill Game")) {
+ C.gameManager->KillGame();
+ }
+
+ ImGui::Text("Game Running: %s", C.gameManager->IsGameRunning() ? "true" : "false");
+ ImGui::Text("Packets received: %d", C.pipeManager->GetPackets().size());
+
+ ImGui::EndTabItem();
+ }
+ // Begin the second tab
+ if (ImGui::BeginTabItem("Settings")) {
+ ImGui::Text("Settings");
+ ImGui::EndTabItem();
+ }
+ ImGui::EndTabBar();
+ }
+
+ ImGui::End();
+}
diff --git a/CarbonLauncher/src/main.cpp b/CarbonLauncher/src/main.cpp
index a819f69..89da2af 100644
--- a/CarbonLauncher/src/main.cpp
+++ b/CarbonLauncher/src/main.cpp
@@ -1,481 +1,28 @@
-#define GLFW_INCLUDE_NONE
-#define GLFW_EXPOSE_NATIVE_WIN32
-#define WIN32_LEAN_AND_MEAN
-#pragma comment(lib, "dwmapi.lib")
-
-#include
-#include
-#include
-#include
-#include
-#include
-#include
-
-#include
-
-#include
-#include
-#include
-
-#include
-#include
-#include
-
-#undef CreateWindow
-#include "utils.h"
-
-// Discord game SDK
-#include
-#include
-
-#define USE_DUMMY_GAME
-
-struct State_t {
- std::vector enabledMods;
-
-struct DiscordState_t {
- discord::User currentUser;
- discord::Activity currentActivity;
-
- std::unique_ptr core;
-} DiscordState;
-};
-
-State_t State;
-
-constexpr auto discordClientId = 1315436867545595904;
-
-
-int main(int argc, char* argv[]) {
- // Check for other processes (CarbonLauncher.exe)
- auto snap = CreateToolhelp32Snapshot(TH32CS_SNAPPROCESS, 0);
- if (snap == INVALID_HANDLE_VALUE) {
- spdlog::error("Failed to create snapshot");
- return -1;
- }
-
- PROCESSENTRY32 pe32 = { sizeof(PROCESSENTRY32) };
- if (!Process32First(snap, &pe32)) {
- spdlog::error("Failed to get first process");
- return -1;
- }
-
- do {
- if (strcmp(pe32.szExeFile, "CarbonLauncher.exe") == 0) {
- static int count = 0;
- count++;
-
- if (count > 1) {
- spdlog::error("Another instance of Carbon Launcher is already running");
- return -1;
- }
- }
- } while (Process32Next(snap, &pe32));
-
- State.DiscordState = {};
-
- discord::Core* core{};
- auto result = discord::Core::Create(discordClientId, DiscordCreateFlags_NoRequireDiscord, &core);
- State.DiscordState.core.reset(core);
- if (!State.DiscordState.core) {
- spdlog::warn("Failed to create Discord core");
- //std::exit(-1);
- }
- else {
- auto dCore = State.DiscordState.core.get();
-
- dCore->SetLogHook(discord::LogLevel::Debug, [](discord::LogLevel level, const char* message) {
- spdlog::debug("[Discord] {}", message);
- });
-
- dCore->SetLogHook(discord::LogLevel::Info, [](discord::LogLevel level, const char* message) {
- spdlog::info("[Discord] {}", message);
- });
-
- dCore->SetLogHook(discord::LogLevel::Warn, [](discord::LogLevel level, const char* message) {
- spdlog::warn("[Discord] {}", message);
- });
-
- dCore->SetLogHook(discord::LogLevel::Error, [](discord::LogLevel level, const char* message) {
- spdlog::error("[Discord] {}", message);
- });
-
- State.DiscordState.core->ActivityManager().RegisterCommand("carbonlauncher://run");
-
- // Show Carbon Launcher as RPC
- State.DiscordState.currentActivity = {};
- auto& activity = State.DiscordState.currentActivity;
- activity.SetDetails("In the launcher");
- activity.SetState("The latest SM modded launcher");
- activity.GetAssets().SetLargeText("Carbon Launcher");
- activity.GetAssets().SetSmallImage("carbon_launcher");
- activity.SetType(discord::ActivityType::Playing);
-
- activity.SetSupportedPlatforms((uint32_t)discord::ActivitySupportedPlatformFlags::Desktop);
-
- State.DiscordState.core->ActivityManager().UpdateActivity(activity, [](discord::Result result) {
- if (result == discord::Result::Ok) {
- spdlog::info("Discord RPC updated");
- }
- else {
- spdlog::error("Failed to update Discord RPC");
- }
- });
- }
-
- GLFWwindow* window = CreateWindow(1280, 720, "Carbon Launcher");
- InitImGui(window);
-
- HWND hWnd = glfwGetWin32Window(window);
- BOOL compositionEnabled = TRUE;
- DwmSetWindowAttribute(hWnd, DWMWA_USE_IMMERSIVE_DARK_MODE, &compositionEnabled, sizeof(BOOL));
-
- // Create the `mods` directory if it doesn't exist
- std::string modsDir = GetExeDirectory() + "\\mods";
- std::filesystem::create_directory(modsDir);
-
- // Create the `logs` directory if it doesn't exist
- std::string logsDir = GetExeDirectory() + "\\logs";
- std::filesystem::create_directory(logsDir);
-
- // Create the `settings` directory if it doesn't exist
- std::string settingsDir = GetExeDirectory() + "\\settings";
- std::filesystem::create_directory(settingsDir);
-
- ImGui::GetIO().IniFilename = nullptr;
-
- // Load from settings/enabled.txt
- auto loadEnabledMods = [&]() {
- std::ifstream file(settingsDir + "\\enabled.txt");
- if (file.is_open()) {
- std::string line;
- while (std::getline(file, line)) {
- State.enabledMods.push_back(line);
- }
- }
- };
-
- auto saveEnabledMods = [&]() {
- std::ofstream file(settingsDir + "\\enabled.txt");
- if (file.is_open()) {
- for (const auto& mod : State.enabledMods) {
- file << mod.string() << std::endl;
- }
- }
- };
-
- loadEnabledMods();
-
- Render(window, [&]() {
- if (State.DiscordState.core)
- State.DiscordState.core->RunCallbacks();
-
- int w, h = 0;
- glfwGetWindowSize(window, &w, &h);
-
- ImGui::SetNextWindowPos(ImVec2(0, 0));
- ImGui::SetNextWindowSize(ImVec2((float)w, (float)h));
- bool popen = true;
- ImGui::Begin("Carbon Launcher", &popen, ImGuiWindowFlags_NoTitleBar | ImGuiWindowFlags_NoResize | ImGuiWindowFlags_NoMove | ImGuiWindowFlags_NoCollapse | ImGuiWindowFlags_NoBringToFrontOnFocus | ImGuiWindowFlags_NoSavedSettings | ImGuiWindowFlags_MenuBar);
-
- bool shouldOpenPopup = false;
- if (ImGui::BeginMainMenuBar()) {
- if (ImGui::BeginMenu("File")) {
- if (ImGui::MenuItem("Exit"))
- glfwSetWindowShouldClose(window, GLFW_TRUE);
- ImGui::EndMenu();
- }
-
- if (ImGui::BeginMenu("Help")) {
- if (ImGui::MenuItem("About")) {
- shouldOpenPopup = true;
- }
-
- ImGui::EndMenu();
- }
-
- ImGui::EndMainMenuBar();
- }
-
- static std::chrono::time_point popupOpenTime;
-
- if (shouldOpenPopup) {
- ImGui::OpenPopup("Info");
- popupOpenTime = std::chrono::system_clock::now();
- }
-
- bool isInfoPopupOpen = true;
- if (ImGui::BeginPopupModal("Info", &isInfoPopupOpen, ImGuiWindowFlags_AlwaysAutoResize)) {
- ImGui::Text("Carbon Launcher");
- ImGui::Separator();
- ImGui::Text("Version 0.1.0");
- ImGui::Text("By Ben McAvoy");
-
- if (!isInfoPopupOpen)
- ImGui::CloseCurrentPopup();
-
- ImGui::EndPopup();
- }
-
- // Create a childwindow with three tabs, "Mods" and "Settings" and "Logs".
- if (ImGui::BeginTabBar("Tabs")) {
- if (ImGui::BeginTabItem("Mods")) {
- auto dirIter = std::filesystem::directory_iterator(modsDir);
- for (const auto& entry : dirIter) {
- ImGui::BeginChild(entry.path().string().c_str(), ImVec2(0, 150), true);
- ImGui::TextWrapped(entry.path().filename().string().c_str());
-
- bool isModEnabled = std::find(State.enabledMods.begin(), State.enabledMods.end(), entry.path()) != State.enabledMods.end();
- if (ImGui::Checkbox("Enabled", &isModEnabled)) {
- if (isModEnabled) {
- State.enabledMods.push_back(entry.path());
- }
- else {
- State.enabledMods.erase(std::remove(State.enabledMods.begin(), State.enabledMods.end(), entry.path()), State.enabledMods.end());
- }
-
- saveEnabledMods();
- }
-
- ImGui::EndChild();
- }
-
- if (dirIter == std::filesystem::directory_iterator())
- ImGui::Text("No mods found :(");
-
- ImGui::EndTabItem();
- }
- if (ImGui::BeginTabItem("Settings")) {
- ImGui::BeginChild("Directories", ImVec2(0, 0), false);
- if (ImGui::TreeNode("Directories")) {
- if (ImGui::Button("Open Mods Directory")) {
- ShellExecuteA(NULL, "open", modsDir.c_str(), NULL, NULL, SW_SHOWNORMAL);
- }
-
- if (ImGui::Button("Open Logs Directory")) {
- ShellExecuteA(NULL, "open", logsDir.c_str(), NULL, NULL, SW_SHOWNORMAL);
- }
-
- if (ImGui::Button("Open Settings Directory")) {
- ShellExecuteA(NULL, "open", settingsDir.c_str(), NULL, NULL, SW_SHOWNORMAL);
- }
-
- ImGui::TreePop();
- }
-
- ImGui::EndChild();
-
- ImGui::EndTabItem();
- }
- if (ImGui::BeginTabItem("Logs")) {
- ImGui::Text("Logs");
- ImGui::EndTabItem();
- }
- ImGui::EndTabBar();
- }
-
- static bool oldIsGameRunning = false;
- static bool isGameRunning = false;
- static std::chrono::time_point timeSinceCheckedOpen = std::chrono::system_clock::now() - std::chrono::seconds(2);
- if (std::chrono::duration_cast(std::chrono::system_clock::now() - timeSinceCheckedOpen).count() > 1) {
- timeSinceCheckedOpen = std::chrono::system_clock::now();
-
-#ifdef USE_DUMMY_GAME
- isGameRunning = IsGameOpen("DummyGame.exe");
-#else
- isGameRunning = IsGameOpen("ScrapMechanic.exe");
-#endif
- }
-
- // If isGameRunning changes, update the activity
- if (oldIsGameRunning != isGameRunning) {
- //activity.SetDetails(isGameRunning ? "Playing Scrap Mechanic" : "In the launcher"); This gets set by game state anyway
-
- std::string modCount = fmt::format("{} mods enabled", State.enabledMods.size());
- //activity.SetState(isGameRunning ? modCount.c_str() : "The latest SM modded launcher");
-
- if (State.DiscordState.core) {
- auto& activity = State.DiscordState.currentActivity;
- if (!isGameRunning) {
- activity.SetState("The latest SM modded launcher");
- }
-
- State.DiscordState.core->ActivityManager().UpdateActivity(activity, [](discord::Result result) {
- if (result == discord::Result::Ok) {
- spdlog::info("Discord RPC updated");
- }
- else {
- spdlog::error("Failed to update Discord RPC");
- }
- });
- }
-
- oldIsGameRunning = isGameRunning;
- }
-
- static float buttonSize = ImGui::CalcItemWidth();
- ImVec2 size = ImVec2(buttonSize, 20);
- const char* text = isGameRunning ? "Stop" : "Start";
- ImVec2 pos = ImVec2((w - size.x) / 2, size.y);
- ImGui::SetCursorPosX(pos.x);
- ImGui::SetCursorPosY(ImGui::GetCursorPosY() + 10);
- if (ImGui::Button(text, ImVec2(size.x + 20, size.y + 10))) {
- if (isGameRunning) {
- //ShellExecuteA(NULL, "open", "taskkill", "/F /IM ScrapMechanic.exe", NULL, SW_SHOWNORMAL);
-
-#ifdef USE_DUMMY_GAME
- ShellExecuteA(NULL, "open", "taskkill", "/F /IM DummyGame.exe", NULL, SW_SHOWMINIMIZED);
-#else
- ShellExecuteA(NULL, "open", "taskkill", "/F /IM ScrapMechanic.exe", NULL, SW_SHOWMINIMIZED);
-#endif
- }
- else {
- // ShellExecuteA(NULL, "open", "steam://run/387990/-dev", NULL, NULL, SW_SHOWMINIMIZED);
-
-#ifdef USE_DUMMY_GAME
- std::string dummyGamePath = GetExeDirectory() + "\\DummyGame.exe";
- ShellExecuteA(NULL, "open", dummyGamePath.c_str(), NULL, NULL, SW_SHOWMINIMIZED);
-#else
- ShellExecuteA(NULL, "open", "steam://run/387990/-dev", NULL, NULL, SW_SHOWMINIMIZED);
-#endif
-
- // Management of injection after message from CarbonSupervisor.
- std::thread([&] {
- //DWORD targetPID = GetProcID("ScrapMechanic.exe");
-
-#ifdef USE_DUMMY_GAME
- DWORD targetPID = GetProcID("DummyGame.exe");
-#else
- DWORD targetPID = GetProcID("ScrapMechanic.exe");
-#endif
-
- while (targetPID == 0) {
- spdlog::info("Waiting for ScrapMechanic.exe to start");
- std::this_thread::sleep_for(std::chrono::seconds(2));
- //targetPID = GetProcID("ScrapMechanic.exe");
-
-#ifdef USE_DUMMY_GAME
- targetPID = GetProcID("DummyGame.exe");
-#else
- targetPID = GetProcID("ScrapMechanic.exe");
-#endif
- }
-
- std::string supervisorPath = GetExeDirectory() + "\\CarbonSupervisor.dll";
- if (!Inject(targetPID, hWnd, supervisorPath)) {
- spdlog::error("Failed to inject CarbonSupervisor.dll");
- return 1;
- }
-
- // Allow some initialization time
- std::this_thread::sleep_for(std::chrono::seconds(1));
-
- // *Connect* to the supervisor pipe
- HANDLE hPipe = CreateFileA("\\\\.\\pipe\\CarbonSupervisor", GENERIC_READ | GENERIC_WRITE, 0, nullptr, OPEN_EXISTING, 0, nullptr);
- if (hPipe == INVALID_HANDLE_VALUE) {
- spdlog::error("Failed to open named pipe: {}", GetLastErrorAsString());
- return 1;
- }
-
- // Infinite loop receiving messages from the supervisor
- char buffer[1024];
- DWORD bytesRead;
-
- // Infinitely try to read from the pipe
- while (true) {
- memset(buffer, 0, sizeof(buffer));
- if (!ReadFile(hPipe, buffer, sizeof(buffer), &bytesRead, nullptr)) {
- spdlog::error("Failed to read from named pipe: {}", GetLastErrorAsString());
- return 1;
- }
- buffer[bytesRead] = '\0'; // Null-terminate after each read.
- std::string message = buffer;
-
- spdlog::info("Received message: {}", message);
-
- if (message == "loaded") {
- // Inject mods
- std::vector enabledModsClone = State.enabledMods;
- for (auto& mod : enabledModsClone) {
- spdlog::info("Injecting mod: {}", mod.string());
- if (!Inject(targetPID, hWnd, mod.string())) {
- spdlog::error("Failed to inject mod: {}", mod.string());
- continue;
- }
- }
- }
-
- if (message.length() == 1 && isdigit(message[0])) {
- if (message == "2") {
- spdlog::info("TODO: Say in game");
- }
- else if (message == "3") {
- spdlog::info("TODO: Say in menu");
- }
- else if (message == "1") {
- spdlog::info("TODO: Say in loading screen");
- }
- }
-
- std::this_thread::sleep_for(std::chrono::milliseconds(100));
- }
-
- return 0;
- }).detach();
- }
- }
-
- // Drop up menu for the kill button, should only be shown when the game is running
- if (isGameRunning) {
- ImGui::SetCursorPosX(pos.x + size.x + 24);
- ImGui::SetCursorPosY(h - pos.y - 24);
-
- bool isKillPopupOpen = false;
- if (ImGui::Button("^", ImVec2(24, size.y + 10))) {
- isKillPopupOpen = true;
- }
-
- if (isKillPopupOpen)
- ImGui::OpenPopup("KillGame");
-
- if (ImGui::BeginPopupModal("KillGame", NULL, ImGuiWindowFlags_AlwaysAutoResize)) {
- ImGui::Text("Are you sure you want to kill the game?");
- ImGui::Separator();
-
- if (ImGui::Button("Yes")) {
- //ShellExecuteA(NULL, "open", "taskkill", "/F /IM ScrapMechanic.exe", NULL, SW_SHOWMINIMIZED);
-
-#ifdef USE_DUMMY_GAME
- ShellExecuteA(NULL, "open", "taskkill", "/F /IM DummyGame.exe", NULL, SW_SHOWMINIMIZED);
-#else
- ShellExecuteA(NULL, "open", "taskkill", "/F /IM ScrapMechanic.exe", NULL, SW_SHOWMINIMIZED);
-#endif
-
- ImGui::CloseCurrentPopup();
- }
-
- ImGui::SameLine();
-
- if (ImGui::Button("No")) {
- ImGui::CloseCurrentPopup();
- }
-
- ImGui::EndPopup();
- }
- }
-
- ImGui::End();
-
- if (!popen)
- glfwSetWindowShouldClose(window, GLFW_TRUE);
- });
-
- ImGui_ImplOpenGL3_Shutdown();
- ImGui_ImplGlfw_Shutdown();
- ImGui::DestroyContext();
-
- glfwDestroyWindow(window);
- glfwTerminate();
+#include "guimanager.h"
+#include "discordmanager.h"
+#include "gamemanager.h"
+#include "pipemanager.h"
+#include "state.h"
+
+using namespace Carbon;
+
+/*
+ * Main entry point for the Carbon Launcher
+ *
+ * @param hInstance The instance of the application
+ * @param hPrevInstance The previous instance of the application
+ * @param lpCmdLine The command line arguments
+ * @param nCmdShow The command show
+ * @return The exit code of the application
+ */
+int WINAPI WinMain(_In_ HINSTANCE hInstance, _In_opt_ HINSTANCE hPrevInstance, _In_ LPSTR lpCmdLine, _In_ int nCmdShow) {
+ C.guiManager = new GUIManager(hInstance);
+ C.discordManager = new DiscordManager();
+ C.gameManager = new GameManager();
+ C.pipeManager = new PipeManager();
+
+ C.guiManager->RenderCallback(_GUI);
+ C.guiManager->Run();
return 0;
}
\ No newline at end of file
diff --git a/CarbonLauncher/src/pipemanager.cpp b/CarbonLauncher/src/pipemanager.cpp
new file mode 100644
index 0000000..b4c22d7
--- /dev/null
+++ b/CarbonLauncher/src/pipemanager.cpp
@@ -0,0 +1,83 @@
+#include "pipemanager.h"
+
+#include
+
+using namespace Carbon;
+
+PipeManager::PipeManager() {
+ this->pipeReader = std::thread([this]() {
+ // Create a pipe that *other* processes can connect to
+ auto pipe = CreateNamedPipe(
+ L"\\\\.\\pipe\\CarbonPipe",
+ PIPE_ACCESS_DUPLEX,
+ PIPE_TYPE_MESSAGE | PIPE_READMODE_MESSAGE | PIPE_WAIT,
+ PIPE_UNLIMITED_INSTANCES,
+ 4096,
+ 4096,
+ 0,
+ NULL
+ );
+
+ if (pipe == INVALID_HANDLE_VALUE) {
+ spdlog::error("Failed to create pipe");
+ return;
+ }
+
+ // Wait for a connection
+ if (!ConnectNamedPipe(pipe, NULL)) {
+ spdlog::error("Failed to connect to pipe");
+ return;
+ }
+
+ while (true) {
+ std::this_thread::sleep_for(std::chrono::milliseconds(100));
+
+ // Read the message
+ char buffer[4096];
+ DWORD bytesRead = 0;
+ if (!ReadFile(pipe, buffer, sizeof(buffer), &bytesRead, NULL)) {
+ spdlog::error("Failed to read from pipe");
+ return;
+ }
+
+ // Parse the message
+ Packet packet;
+
+ // Split on `-:-`
+ std::string data(buffer, bytesRead);
+ auto split = data.find("-:-");
+ if (split == std::string::npos) {
+ spdlog::error("Invalid packet received");
+ continue;
+ }
+
+ auto type = data.substr(0, split);
+ auto payload = data.substr(split + 3);
+
+ if (type == "LOADED") {
+ packet.type = PacketType::LOADED;
+ }
+ else if (type == "STATECHANGE") {
+ packet.type = PacketType::STATECHANGE;
+ }
+ else if (type == "LOG") {
+ packet.type = PacketType::LOG;
+ }
+ else {
+ spdlog::error("Invalid packet type received");
+ continue;
+ }
+
+ packet.data = payload;
+
+ // Lock the mutex and add the packet
+ std::lock_guard lock(this->pipeMutex);
+ this->packets.push(packet);
+ }
+ });
+}
+
+PipeManager::~PipeManager() {
+ this->pipeReader.join();
+ this->packets = {};
+}
\ No newline at end of file
diff --git a/CarbonLauncher/src/state.cpp b/CarbonLauncher/src/state.cpp
new file mode 100644
index 0000000..1fcddf1
--- /dev/null
+++ b/CarbonLauncher/src/state.cpp
@@ -0,0 +1,3 @@
+#include "state.h"
+using namespace Carbon;
+CarbonState_t C;
\ No newline at end of file
diff --git a/CarbonLauncher/src/utils.cpp b/CarbonLauncher/src/utils.cpp
deleted file mode 100644
index 4b485df..0000000
--- a/CarbonLauncher/src/utils.cpp
+++ /dev/null
@@ -1,248 +0,0 @@
-#define GLFW_INCLUDE_NONE
-#define GLFW_EXPOSE_NATIVE_WIN32
-
-#include "utils.h"
-
-#include
-#include
-#include
-
-#include
-
-#include
-#include
-
-#include
-#include
-#include
-
-#include
-#include
-
-#undef CreateWindow
-
-#include
-
-/*
- * Get the last error message as a string.
- *
- * @return The last error message as a string.
- */
-std::string GetLastErrorAsString() {
- DWORD errorMessageID = GetLastError();
- if (errorMessageID == 0) {
- return std::string();
- }
- LPSTR messageBuffer = nullptr;
- size_t size = FormatMessageA(FORMAT_MESSAGE_ALLOCATE_BUFFER | FORMAT_MESSAGE_FROM_SYSTEM | FORMAT_MESSAGE_IGNORE_INSERTS,
- NULL, errorMessageID, MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT), (LPSTR)&messageBuffer, 0, NULL);
- std::string message(messageBuffer, size);
- LocalFree(messageBuffer);
- return message;
-}
-
-/*
- * Get the process ID of a process by name.
- *
- * @param processName The name of the process to get the ID of.
- * @return The process ID of the process with the given name, or 0 if the process was not found.
- */
-DWORD GetProcID(const std::string& processName) {
- PROCESSENTRY32 processEntry = { sizeof(PROCESSENTRY32) };
- HANDLE snapshot = CreateToolhelp32Snapshot(TH32CS_SNAPPROCESS, 0);
-
- if (snapshot == INVALID_HANDLE_VALUE) {
- spdlog::error("Failed to create snapshot: {}", GetLastErrorAsString());
- return 0;
- }
-
- if (!Process32First(snapshot, &processEntry)) {
- spdlog::error("Failed to get first process: {}", GetLastErrorAsString());
- CloseHandle(snapshot);
- return 0;
- }
-
- while (Process32Next(snapshot, &processEntry)) {
- if (processName == processEntry.szExeFile) {
- CloseHandle(snapshot);
- return processEntry.th32ProcessID;
- }
- }
-
- CloseHandle(snapshot);
- return 0;
-}
-
-/*
- * Inject a DLL into a process.
- *
- * @param targetPID The process ID of the target process.
- * @param window The window to set as the foreground window after injection.
- * @return True if the DLL was successfully injected, false otherwise.
- */
-[[nodiscard]] bool Inject(DWORD targetPID, HWND window, const std::string& toLoad) {
- HANDLE process = OpenProcess(PROCESS_ALL_ACCESS, FALSE, targetPID);
- if (process == NULL) {
- spdlog::error("Failed to open process: {}", GetLastErrorAsString());
- return false;
- }
-
- //char buffer[MAX_PATH];
- //GetModuleFileName(NULL, buffer, MAX_PATH);
- //std::string dllPath = std::string(buffer, strlen(buffer));
- //dllPath = dllPath.substr(0, dllPath.find_last_of('\\') + 1) + toLoad;
-
- std::string dllPath = toLoad;
-
- if (!std::filesystem::exists(dllPath)) {
- spdlog::error("DLL does not exist: {}", dllPath);
- CloseHandle(process);
- return false;
- }
-
- spdlog::info("Injecting DLL: {}", dllPath);
-
- LPVOID remoteMemory = VirtualAllocEx(process, NULL, dllPath.size() + 1, MEM_COMMIT | MEM_RESERVE, PAGE_READWRITE);
- if (remoteMemory == NULL) {
- spdlog::error("Failed to allocate memory in target process: {}", GetLastErrorAsString());
- CloseHandle(process);
- return false;
- }
-
- if (!WriteProcessMemory(process, remoteMemory, dllPath.c_str(), dllPath.size() + 1, NULL)) {
- spdlog::error("Failed to write memory in target process: {}", GetLastErrorAsString());
- CloseHandle(process);
- return false;
- }
-
- HANDLE thread = CreateRemoteThread(process, NULL, 0, (LPTHREAD_START_ROUTINE)LoadLibraryA, remoteMemory, 0, NULL);
- if (thread == NULL) {
- spdlog::error("Failed to create remote thread: {}", GetLastErrorAsString());
- CloseHandle(process);
- return false;
- }
-
- WaitForSingleObject(thread, INFINITE);
-
- CloseHandle(thread);
- CloseHandle(process);
-
- return true;
-}
-
-/*
- * Enumerate windows callback function.
- *
- * @param hWnd The window handle.
- * @param lParam The process ID to compare against.
- * @return False if the process ID matches, true otherwise.
- */
-BOOL CALLBACK EnumWindowsProc(HWND hWnd, LPARAM lParam) {
- DWORD pid = 0;
- GetWindowThreadProcessId(hWnd, &pid);
-
- if (pid == lParam) {
- g_hWnd = hWnd;
- return FALSE;
- }
-
- return TRUE;
-}
-
-std::string GetExeDirectory() {
- char buffer[MAX_PATH];
- GetModuleFileNameA(NULL, buffer, MAX_PATH);
- std::string::size_type pos = std::string(buffer).find_last_of("\\/");
- return std::string(buffer).substr(0, pos);
-}
-
-GLFWwindow* CreateWindow(int width, int height, const char* title) {
- if (!glfwInit()) {
- return nullptr;
- }
- glfwWindowHint(GLFW_CONTEXT_VERSION_MAJOR, 4);
- glfwWindowHint(GLFW_CONTEXT_VERSION_MINOR, 6);
- glfwWindowHint(GLFW_OPENGL_PROFILE, GLFW_OPENGL_CORE_PROFILE);
- glfwWindowHint(GLFW_RESIZABLE, GLFW_TRUE);
- glfwWindowHint(GLFW_VISIBLE, GLFW_TRUE);
-
- GLFWwindow* window = glfwCreateWindow(width, height, title, NULL, NULL);
- if (!window) {
- glfwTerminate();
- return nullptr;
- }
-
- glfwMakeContextCurrent(window);
-
- if (!gladLoadGLLoader((GLADloadproc)glfwGetProcAddress)) {
- glfwDestroyWindow(window);
- glfwTerminate();
- return nullptr;
- }
-
- return window;
-}
-
-
-void InitImGui(GLFWwindow* window) {
- IMGUI_CHECKVERSION();
- ImGui::CreateContext();
- ImGuiIO& io = ImGui::GetIO();
- io.ConfigFlags |= ImGuiConfigFlags_NavEnableKeyboard;
-
- // Load Segoe UI font
- //io.Fonts->AddFontFromFileTTF("C:\\Windows\\Fonts\\CascadiaCode.ttf", 16.0f);
- io.Fonts->AddFontFromFileTTF("C:\\Windows\\Fonts\\seguisb.ttf", 18.0f);
-
- ImGui::StyleColorsDark();
-
- ImGuiStyle& style = ImGui::GetStyle();
- style.WindowRounding = 6.0f;
- style.FrameRounding = 4.0f;
- style.GrabRounding = 4.0f;
- style.TabRounding = 4.0f;
- style.ScrollbarRounding = 4.0f;
- style.WindowBorderSize = 0.0f;
- style.FrameBorderSize = 0.0f;
- style.PopupBorderSize = 0.0f;
- style.GrabMinSize = 8.0f;
- style.ChildRounding = 4.0f;
-
- ImGui_ImplGlfw_InitForOpenGL(window, true);
- ImGui_ImplOpenGL3_Init("#version 460");
-}
-
-bool IsGameOpen(const char* name) {
- HANDLE hSnap = CreateToolhelp32Snapshot(TH32CS_SNAPPROCESS, 0);
- if (hSnap == INVALID_HANDLE_VALUE) {
- return false;
- }
- PROCESSENTRY32 pe32;
- pe32.dwSize = sizeof(PROCESSENTRY32);
- if (!Process32First(hSnap, &pe32)) {
- CloseHandle(hSnap);
- return false;
- }
- do {
- if (strcmp(pe32.szExeFile, name) == 0) {
- CloseHandle(hSnap);
- return true;
- }
- } while (Process32Next(hSnap, &pe32));
- CloseHandle(hSnap);
- return false;
-}
-
-void Render(GLFWwindow* window, std::function renderFunc) {
- while (!glfwWindowShouldClose(window)) {
- ImGui_ImplOpenGL3_NewFrame();
- ImGui_ImplGlfw_NewFrame();
- ImGui::NewFrame();
- renderFunc();
- ImGui::Render();
- ImGui_ImplOpenGL3_RenderDrawData(ImGui::GetDrawData());
- glfwMakeContextCurrent(window);
- glfwSwapBuffers(window);
- glfwPollEvents();
- }
-}
diff --git a/CarbonLauncher/vcpkg.json b/CarbonLauncher/vcpkg.json
index 42242e0..a0fd874 100644
--- a/CarbonLauncher/vcpkg.json
+++ b/CarbonLauncher/vcpkg.json
@@ -17,4 +17,4 @@
"spdlog",
"discord-game-sdk"
]
-}
+}
\ No newline at end of file
diff --git a/CarbonSupervisor/CarbonSupervisor.vcxproj b/CarbonSupervisor/CarbonSupervisor.vcxproj
deleted file mode 100644
index 0379183..0000000
--- a/CarbonSupervisor/CarbonSupervisor.vcxproj
+++ /dev/null
@@ -1,139 +0,0 @@
-
-
-
-
- Debug
- Win32
-
-
- Release
- Win32
-
-
- Debug
- x64
-
-
- Release
- x64
-
-
-
- 17.0
- Win32Proj
- {1f14e774-5a60-4742-9534-712e254faa60}
- CarbonSupervisor
- 10.0
-
-
-
- DynamicLibrary
- true
- v143
- Unicode
-
-
- DynamicLibrary
- false
- v143
- true
- Unicode
-
-
- DynamicLibrary
- true
- v143
- Unicode
-
-
- DynamicLibrary
- false
- v143
- true
- Unicode
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
- Level3
- true
- WIN32;_DEBUG;_CONSOLE;%(PreprocessorDefinitions)
- true
- stdcpp20
-
-
- Console
- true
-
-
-
-
- Level3
- true
- true
- true
- WIN32;NDEBUG;_CONSOLE;%(PreprocessorDefinitions)
- true
- stdcpp20
-
-
- Console
- true
- true
- true
-
-
-
-
- Level3
- true
- _DEBUG;_CONSOLE;%(PreprocessorDefinitions)
- true
- stdcpp20
-
-
- Console
- true
-
-
-
-
- Level3
- true
- true
- true
- NDEBUG;_CONSOLE;%(PreprocessorDefinitions)
- true
- stdcpp20
-
-
- Console
- true
- true
- true
-
-
-
-
-
-
-
-
-
\ No newline at end of file
diff --git a/CarbonSupervisor/src/main.cpp b/CarbonSupervisor/src/main.cpp
deleted file mode 100644
index 058e408..0000000
--- a/CarbonSupervisor/src/main.cpp
+++ /dev/null
@@ -1,126 +0,0 @@
-#define WIN32_LEAN_AND_MEAN
-#include
-
-#include
-#include
-#include
-#include
-
-typedef unsigned int uint4_t;
-
-struct Contraption {
- /* 0x000 */ char pad_056[0x17C];
- /* 0x17C */ uint4_t gameStateType;
- /* 0x180 */ char pad_004[0x20];
- /* 0x1A0 */ HWND hWnd;
-};
-
-template
-T FetchClass(uintptr_t address) {
- return *reinterpret_cast(address);
-}
-
-/*
- * Thread entry point for the DLL.
- * @param lpParameter The parameter passed to the thread.
- */
-DWORD WINAPI ThreadProc(LPVOID lpParameter) {
- HMODULE hModule = static_cast(lpParameter);
-
- // *Create* a named pipe for other processes to connect to
- HANDLE hPipe = CreateNamedPipeA("\\\\.\\pipe\\CarbonSupervisor", PIPE_ACCESS_DUPLEX, PIPE_TYPE_BYTE | PIPE_READMODE_BYTE | PIPE_WAIT, 1, 1024, 1024, 0, nullptr);
- if (hPipe == INVALID_HANDLE_VALUE) {
- MessageBoxA(nullptr, "Failed to open named pipe", "CarbonSupervisor", MB_OK);
- return 1;
- }
-
- uintptr_t contraptionAddr = (uintptr_t)GetModuleHandle(nullptr) + 0x12674B8;
- Contraption* contraption = FetchClass(contraptionAddr);
-
- while (contraption == nullptr)
- contraption = FetchClass(contraptionAddr);
-
- while (contraption->gameStateType < 1 || contraption->gameStateType > 3 || contraption == nullptr || contraption->hWnd == nullptr) {
- contraption = FetchClass(contraptionAddr);
- std::this_thread::sleep_for(std::chrono::milliseconds(100));
- }
-
- while (contraption->gameStateType == 1) {
- std::this_thread::sleep_for(std::chrono::milliseconds(100));
- }
-
- std::this_thread::sleep_for(std::chrono::seconds(1));
-
- // Open the supervisor pipe and send `loaded`
- char buffer[] = "loaded";
- DWORD bytesWritten;
-
- if (!WriteFile(hPipe, buffer, sizeof(buffer), &bytesWritten, nullptr)) {
- MessageBoxA(nullptr, "Failed to write to named pipe", "CarbonSupervisor", MB_OK);
- return 1;
- }
-
- for (;;) {
- static uint4_t lastGameStateType = contraption->gameStateType;
-
- if (contraption->gameStateType != lastGameStateType) {
- lastGameStateType = contraption->gameStateType;
-
- std::cout << "\n\n\n\n\nChanged game state type: " << contraption->gameStateType << "\n\n\n\n\n";
-
- std::cout << "Game state type: " << contraption->gameStateType << std::endl;
-
- // Send the game state type to the supervisor
- memset(buffer, 0, sizeof(buffer));
- //buffer[0] = contraption->gameStateType;
- //_itoa(contraption->gameStateType, buffer, 10);
- _itoa_s(contraption->gameStateType, buffer, 10);
- // buffer -> "1" -> 0x31
-
- if (!WriteFile(hPipe, buffer, sizeof(buffer), &bytesWritten, nullptr)) {
- MessageBoxA(nullptr, "Failed to write to named pipe", "CarbonSupervisor", MB_OK);
- return 1;
- }
-
- if (contraption->gameStateType == 3) {
- break;
- }
-
- std::this_thread::sleep_for(std::chrono::seconds(1));
- }
-
- std::this_thread::sleep_for(std::chrono::seconds(1));
- }
-
- // TODO: Send log messages
-
- FreeLibraryAndExitThread(hModule, 0);
- return 0;
-}
-
-
-/*
- * DLL Entry Point.
- *
- * @param hModule The handle to the DLL module.
- * @param dwReason The reason for the DLL entry point being called.
- * @param lpReserved Reserved.
- * @return True if the DLL was successfully loaded, false otherwise.
- */
-BOOL APIENTRY DllMain(HMODULE hModule, DWORD dwReason, LPVOID lpReserved) {
- switch (dwReason) {
- case DLL_PROCESS_ATTACH:
- DisableThreadLibraryCalls(hModule);
- CreateThread(nullptr, 0, ThreadProc, hModule, 0, nullptr);
-
- break;
- case DLL_PROCESS_DETACH:
- break;
- case DLL_THREAD_ATTACH:
- break;
- case DLL_THREAD_DETACH:
- break;
- }
-
- return TRUE;
-}
diff --git a/CarbonSupervisor/vcpkg-configuration.json b/CarbonSupervisor/vcpkg-configuration.json
deleted file mode 100644
index c94aa08..0000000
--- a/CarbonSupervisor/vcpkg-configuration.json
+++ /dev/null
@@ -1,14 +0,0 @@
-{
- "default-registry": {
- "kind": "git",
- "baseline": "b2a47d316de1f3625ea43a7ca3e42dd28c52ece7",
- "repository": "https://github.com/microsoft/vcpkg"
- },
- "registries": [
- {
- "kind": "artifact",
- "location": "https://github.com/microsoft/vcpkg-ce-catalog/archive/refs/heads/main.zip",
- "name": "microsoft"
- }
- ]
-}
diff --git a/CarbonSupervisor/vcpkg.json b/CarbonSupervisor/vcpkg.json
deleted file mode 100644
index 0967ef4..0000000
--- a/CarbonSupervisor/vcpkg.json
+++ /dev/null
@@ -1 +0,0 @@
-{}
diff --git a/DummyGame/DummyGame.vcxproj b/DummyGame/DummyGame.vcxproj
deleted file mode 100644
index 4917232..0000000
--- a/DummyGame/DummyGame.vcxproj
+++ /dev/null
@@ -1,150 +0,0 @@
-
-
-
-
- Debug
- Win32
-
-
- Release
- Win32
-
-
- Debug
- x64
-
-
- Release
- x64
-
-
-
- 17.0
- Win32Proj
- {a56ac389-85ad-4e6f-9e64-e859dd676099}
- DummyGame
- 10.0
-
-
-
- Application
- true
- v143
- MultiByte
-
-
- Application
- false
- v143
- true
- MultiByte
-
-
- Application
- true
- v143
- MultiByte
-
-
- Application
- false
- v143
- true
- MultiByte
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
- true
-
-
-
- Level3
- true
- WIN32;_DEBUG;_CONSOLE;%(PreprocessorDefinitions)
- true
- stdcpp20
- include
- /utf-8 %(AdditionalOptions)
-
-
- Console
- true
-
-
-
-
- Level3
- true
- true
- true
- WIN32;NDEBUG;_CONSOLE;%(PreprocessorDefinitions)
- true
- stdcpp20
- include
- /utf-8 %(AdditionalOptions)
-
-
- Console
- true
- true
- true
-
-
-
-
- Level3
- true
- _DEBUG;_CONSOLE;%(PreprocessorDefinitions)
- true
- stdcpp20
- include
- /utf-8 %(AdditionalOptions)
-
-
- Console
- true
-
-
-
-
- Level3
- true
- true
- true
- NDEBUG;_CONSOLE;%(PreprocessorDefinitions)
- true
- stdcpp20
- include
- /utf-8 %(AdditionalOptions)
-
-
- Console
- true
- true
- true
-
-
-
-
-
-
-
-
-
\ No newline at end of file
diff --git a/DummyGame/src/main.cpp b/DummyGame/src/main.cpp
deleted file mode 100644
index dcf0f72..0000000
--- a/DummyGame/src/main.cpp
+++ /dev/null
@@ -1,100 +0,0 @@
-#define WIN32_LEAN_AND_MEAN
-#include
-#include
-#include
-
-#include
-
-#include
-#include
-
-#include
-#include
-#include
-
-typedef unsigned int uint4_t;
-
-// LoadLibraryA function pointer
-typedef HMODULE(WINAPI* LoadLibraryA_t)(LPCSTR lpLibFileName);
-
-struct Contraption_t {
- /* 0x000 */ char pad_056[0x17C]; // this is genuinely random in the game
- /* 0x17C */ uint4_t gameStateType = 0; // This is random at first but managed
- /* 0x180 */ char pad_004[0x20]; // this is genuinely random in the game
- /* 0x1A0 */ HWND hWnd; // this is random in the game at first but managed
-};
-
-#ifdef NDEBUG
-static char pad[0x12674B8 - 0x1DC30] = { 0 }; // offset it to replicate the game's memory layout
-#else
-static_assert(false, "This is only for the release build");
-#endif
-
-static Contraption_t* Contraption = new Contraption_t();
-
-int main() {
- spdlog::set_level(spdlog::level::trace);
-
- // Stop the compiler optimizing the pad array
- if (pad[0] == 1) {
- spdlog::critical("This should never be printed");
- }
-
- // Get a pointer to the pointer and print it so we can adjust it to be the same as the game
- Contraption_t** ContraptionPtr = &Contraption;
-
- spdlog::info("Contraption: {}", reinterpret_cast((uintptr_t)ContraptionPtr - (uintptr_t)GetModuleHandle(nullptr)));
-
- // Wait 5s
- std::this_thread::sleep_for(std::chrono::seconds(5));
-
- HINSTANCE hInst = GetModuleHandle(nullptr);
- HWND newHwnd = CreateWindowExA(0, "STATIC", "Dummy Game", WS_OVERLAPPEDWINDOW, CW_USEDEFAULT, CW_USEDEFAULT, 1280, 720, NULL, NULL, hInst, NULL);
-
- // Save hwnd to the Contraption struct and set the game state type to 1 (loading screen)
- Contraption->hWnd = (HWND)newHwnd;
- Contraption->gameStateType = 1;
-
- // Wait 2s
- std::this_thread::sleep_for(std::chrono::seconds(2));
-
- // Set the game state type to 2 (in-game)
- Contraption->gameStateType = 2;
-
- // Hook LoadLibraryA so we can detect and block DLL injection (we need to block it
- // because since we aren't the real game it will crash, this is only for testing so seeing
- // the mods injecting isn't important)
- MH_Initialize();
- MH_CreateHook(&LoadLibraryA, (LPVOID)(LoadLibraryA_t)[](LPCSTR lpLibFileName) -> HMODULE {
- std::string libFileName = lpLibFileName;
- spdlog::trace("Blocked DLL injection: {}", libFileName);
- return NULL;
- }, nullptr);
-
- MH_EnableHook(&LoadLibraryA);
-
- while (!(GetAsyncKeyState(VK_HOME) & 1 && GetAsyncKeyState(VK_END)) & 1) {
- // Fx sets contraption game state type to x
- if (GetAsyncKeyState(VK_F1) & 1) {
- Contraption->gameStateType = 1;
- spdlog::trace("Game state type is now 1");
- }
-
- if (GetAsyncKeyState(VK_F2) & 1) {
- Contraption->gameStateType = 2;
- spdlog::trace("Game state type is now 2");
- }
-
- if (GetAsyncKeyState(VK_F3) & 1) {
- Contraption->gameStateType = 3;
- spdlog::trace("Game state type is now 3");
- }
-
- std::this_thread::sleep_for(std::chrono::milliseconds(100));
- }
-
- MH_DisableHook(&LoadLibraryA);
- MH_Uninitialize();
-
- return 0;
-}
diff --git a/DummyGame/vcpkg-configuration.json b/DummyGame/vcpkg-configuration.json
deleted file mode 100644
index ba06dad..0000000
--- a/DummyGame/vcpkg-configuration.json
+++ /dev/null
@@ -1,14 +0,0 @@
-{
- "default-registry": {
- "kind": "git",
- "baseline": "2960d7d80e8d09c84ae8abf15c12196c2ca7d39a",
- "repository": "https://github.com/microsoft/vcpkg"
- },
- "registries": [
- {
- "kind": "artifact",
- "location": "https://github.com/microsoft/vcpkg-ce-catalog/archive/refs/heads/main.zip",
- "name": "microsoft"
- }
- ]
-}
diff --git a/DummyGame/vcpkg.json b/DummyGame/vcpkg.json
deleted file mode 100644
index fe2b23d..0000000
--- a/DummyGame/vcpkg.json
+++ /dev/null
@@ -1,6 +0,0 @@
-{
- "dependencies": [
- "spdlog",
- "minhook"
- ]
-}