├── .gitattributes ├── .github └── FUNDING.yml ├── .gitignore ├── ErScripts.sln ├── ErScripts ├── AntiAfk.cpp ├── AutoAccept.cpp ├── AutoPistol.cpp ├── AutoStop.cpp ├── BombTime.cpp ├── CS2Binds.cpp ├── ChatSpammer.cpp ├── Config.cpp ├── Config.h ├── Crosshair.cpp ├── ErScripts.aps ├── ErScripts.cpp ├── ErScripts.h ├── ErScripts.rc ├── ErScripts.vcxproj ├── ErScripts.vcxproj.filters ├── ErScripts.vcxproj.user ├── FileMonitor.cpp ├── FileMonitor.h ├── GSIServer.cpp ├── GSIServer.h ├── Globals.cpp ├── Globals.h ├── GradientManager.cpp ├── GradientManager.h ├── ImagesShellCodes.h ├── KillSay.cpp ├── KillSound.cpp ├── KnifeSwitch.cpp ├── Logger.cpp ├── Logger.h ├── Menu.cpp ├── Overlay.cpp ├── Overlay.h ├── OverlayHelper.cpp ├── PixelTrigger.cpp ├── RGBCrosshair.cpp ├── Rebuild.cpp ├── Rebuild.h ├── Render.cpp ├── RoundStartAlert.cpp ├── SimpleSound.cpp ├── SimpleSound.h ├── SoundsShellcodes.h ├── SteamTools.cpp ├── SteamTools.h ├── UIAccess.c ├── UIAccess.h ├── Updater.cpp ├── Updater.h ├── erscripts.ico ├── imgui │ ├── imconfig.h │ ├── imgui.cpp │ ├── imgui.h │ ├── imgui_demo.cpp │ ├── imgui_draw.cpp │ ├── imgui_impl_dx11.cpp │ ├── imgui_impl_dx11.h │ ├── imgui_impl_win32.cpp │ ├── imgui_impl_win32.h │ ├── imgui_internal.h │ ├── imgui_tables.cpp │ ├── imgui_widgets.cpp │ ├── imstb_rectpack.h │ ├── imstb_textedit.h │ └── imstb_truetype.h ├── main.cpp └── resource.h ├── Image ├── bombtimer.gif ├── crosshair.gif ├── keystrokes.gif ├── menu1.png └── menu2.png ├── Include ├── httplib │ └── httplib.h └── nlohmann │ └── json.hpp ├── LICENSE.txt ├── README.md └── docs ├── css └── styles.css ├── img └── icon.ico └── index.html /.gitattributes: -------------------------------------------------------------------------------- 1 | ############################################################################### 2 | # Set default behavior to automatically normalize line endings. 3 | ############################################################################### 4 | * text=auto 5 | 6 | ############################################################################### 7 | # Set default behavior for command prompt diff. 8 | # 9 | # This is need for earlier builds of msysgit that does not have it on by 10 | # default for csharp files. 11 | # Note: This is only used by command line 12 | ############################################################################### 13 | #*.cs diff=csharp 14 | 15 | ############################################################################### 16 | # Set the merge driver for project and solution files 17 | # 18 | # Merging from the command prompt will add diff markers to the files if there 19 | # are conflicts (Merging from VS is not affected by the settings below, in VS 20 | # the diff markers are never inserted). Diff markers may cause the following 21 | # file extensions to fail to load in VS. An alternative would be to treat 22 | # these files as binary and thus will always conflict and require user 23 | # intervention with every merge. To do so, just uncomment the entries below 24 | ############################################################################### 25 | #*.sln merge=binary 26 | #*.csproj merge=binary 27 | #*.vbproj merge=binary 28 | #*.vcxproj merge=binary 29 | #*.vcproj merge=binary 30 | #*.dbproj merge=binary 31 | #*.fsproj merge=binary 32 | #*.lsproj merge=binary 33 | #*.wixproj merge=binary 34 | #*.modelproj merge=binary 35 | #*.sqlproj merge=binary 36 | #*.wwaproj merge=binary 37 | 38 | ############################################################################### 39 | # behavior for image files 40 | # 41 | # image files are treated as binary by default. 42 | ############################################################################### 43 | #*.jpg binary 44 | #*.png binary 45 | #*.gif binary 46 | 47 | ############################################################################### 48 | # diff behavior for common document formats 49 | # 50 | # Convert binary document formats to text before diffing them. This feature 51 | # is only available from the command line. Turn it on by uncommenting the 52 | # entries below. 53 | ############################################################################### 54 | #*.doc diff=astextplain 55 | #*.DOC diff=astextplain 56 | #*.docx diff=astextplain 57 | #*.DOCX diff=astextplain 58 | #*.dot diff=astextplain 59 | #*.DOT diff=astextplain 60 | #*.pdf diff=astextplain 61 | #*.PDF diff=astextplain 62 | #*.rtf diff=astextplain 63 | #*.RTF diff=astextplain 64 | -------------------------------------------------------------------------------- /.github/FUNDING.yml: -------------------------------------------------------------------------------- 1 | github: emp0ry 2 | buy_me_a_coffee: emp0ry 3 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | # Prerequisites 2 | *.d 3 | 4 | # Compiled Object files 5 | *.slo 6 | *.lo 7 | *.o 8 | *.obj 9 | 10 | # Precompiled Headers 11 | *.gch 12 | *.pch 13 | 14 | # Compiled Dynamic libraries 15 | *.so 16 | *.dylib 17 | *.dll 18 | 19 | # Fortran module files 20 | *.mod 21 | *.smod 22 | 23 | # Compiled Static libraries 24 | *.lai 25 | *.la 26 | *.a 27 | *.lib 28 | 29 | # Executables 30 | *.exe 31 | *.out 32 | *.app 33 | /ErScripts/configs 34 | /ErScripts/x64 35 | /x64 36 | /.vs 37 | /docs/.vscode 38 | -------------------------------------------------------------------------------- /ErScripts.sln: -------------------------------------------------------------------------------- 1 |  2 | Microsoft Visual Studio Solution File, Format Version 12.00 3 | # Visual Studio Version 17 4 | VisualStudioVersion = 17.12.35506.116 d17.12 5 | MinimumVisualStudioVersion = 10.0.40219.1 6 | Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "ErScripts", "ErScripts\ErScripts.vcxproj", "{97211613-A2DB-4D8E-93B3-7B5DA48C57DB}" 7 | EndProject 8 | Global 9 | GlobalSection(SolutionConfigurationPlatforms) = preSolution 10 | Debug|x64 = Debug|x64 11 | Debug|x86 = Debug|x86 12 | Release|x64 = Release|x64 13 | Release|x86 = Release|x86 14 | EndGlobalSection 15 | GlobalSection(ProjectConfigurationPlatforms) = postSolution 16 | {97211613-A2DB-4D8E-93B3-7B5DA48C57DB}.Debug|x64.ActiveCfg = Debug|x64 17 | {97211613-A2DB-4D8E-93B3-7B5DA48C57DB}.Debug|x64.Build.0 = Debug|x64 18 | {97211613-A2DB-4D8E-93B3-7B5DA48C57DB}.Debug|x86.ActiveCfg = Debug|Win32 19 | {97211613-A2DB-4D8E-93B3-7B5DA48C57DB}.Debug|x86.Build.0 = Debug|Win32 20 | {97211613-A2DB-4D8E-93B3-7B5DA48C57DB}.Release|x64.ActiveCfg = Release|x64 21 | {97211613-A2DB-4D8E-93B3-7B5DA48C57DB}.Release|x64.Build.0 = Release|x64 22 | {97211613-A2DB-4D8E-93B3-7B5DA48C57DB}.Release|x86.ActiveCfg = Release|Win32 23 | {97211613-A2DB-4D8E-93B3-7B5DA48C57DB}.Release|x86.Build.0 = Release|Win32 24 | EndGlobalSection 25 | GlobalSection(SolutionProperties) = preSolution 26 | HideSolutionNode = FALSE 27 | EndGlobalSection 28 | EndGlobal 29 | -------------------------------------------------------------------------------- /ErScripts/AntiAfk.cpp: -------------------------------------------------------------------------------- 1 | #include "ErScripts.h" 2 | 3 | void ErScripts::AntiAfk() { 4 | std::thread([]() { 5 | bool oldRoundStartState = false; 6 | while (!globals::finish) { 7 | if (cfg->antiAfkState) { 8 | if (ErScripts::GetWindowState() && ErScripts::GetCursorState()) { 9 | if (globals::roundStartState != oldRoundStartState) { 10 | oldRoundStartState = globals::roundStartState; 11 | 12 | if (globals::roundStartState) { 13 | if (!(GetAsyncKeyState('W') & 0x8000)) { 14 | Keyboard('W', true, false); 15 | std::this_thread::sleep_for(std::chrono::milliseconds((std::rand() % 3) + 16)); 16 | Keyboard('W', false, false); 17 | } 18 | } 19 | } 20 | } 21 | } 22 | 23 | std::this_thread::sleep_for(std::chrono::microseconds(15625)); 24 | } 25 | }).detach(); 26 | } -------------------------------------------------------------------------------- /ErScripts/AutoAccept.cpp: -------------------------------------------------------------------------------- 1 | #include "ErScripts.h" 2 | 3 | void ErScripts::AutoAccept() { 4 | std::thread([this]() { 5 | const std::vector targetColor = { 54, 183, 82 }; 6 | 7 | while (!globals::finish) { 8 | if (cfg->autoAcceptState) { 9 | if (foundMatch) { 10 | foundMatch = false; 11 | 12 | POINT mousePos; 13 | GetCursorPos(&mousePos); 14 | 15 | bool isInCS2 = GetWindowState(); 16 | 17 | // Maximize cs2 18 | ShowWindow(hwnd, SW_RESTORE); 19 | SetForegroundWindow(hwnd); 20 | 21 | bool colorState = false; 22 | auto startTime = std::chrono::steady_clock::now(); 23 | 24 | while (std::chrono::duration_cast(std::chrono::steady_clock::now() - startTime).count() <= cfg->autoAcceptWaitingTime) { 25 | int buttonX = static_cast(round(globals::width / 2.f + globals::posX)), buttonY = static_cast(round(globals::height / 2.215f + globals::posY)); 26 | 27 | if (GetWindowState()) { 28 | std::vector color = GetPixelColor(buttonX, buttonY); 29 | 30 | if (isColorSimilar(targetColor, color, 20)) { 31 | colorState = true; 32 | Logger::logInfo("AutoAccept button clicking"); 33 | 34 | // Move cursor to correct position 35 | SetCursorPos(buttonX, buttonY); 36 | std::this_thread::sleep_for(std::chrono::milliseconds(1)); 37 | 38 | // Click accept button 39 | mouse_event(MOUSEEVENTF_LEFTDOWN, 0, 0, 0, 0); 40 | std::this_thread::sleep_for(std::chrono::milliseconds((std::rand() % 32) + 16)); 41 | mouse_event(MOUSEEVENTF_LEFTUP, 0, 0, 0, 0); 42 | } 43 | else if (colorState) { 44 | break; 45 | } 46 | } 47 | std::this_thread::sleep_for(std::chrono::milliseconds(1)); 48 | } 49 | 50 | if (!isInCS2) { 51 | Keyboard(56, true, false); 52 | std::this_thread::sleep_for(std::chrono::milliseconds((std::rand() % 32) + 16)); 53 | Keyboard(15, true, false); 54 | std::this_thread::sleep_for(std::chrono::milliseconds((std::rand() % 32) + 16)); 55 | Keyboard(15, false, false); 56 | std::this_thread::sleep_for(std::chrono::milliseconds((std::rand() % 32) + 16)); 57 | Keyboard(56, false, false); 58 | } 59 | 60 | SetCursorPos(mousePos.x, mousePos.y); 61 | } 62 | } 63 | std::this_thread::sleep_for(std::chrono::microseconds(15625)); 64 | } 65 | }).detach(); 66 | } -------------------------------------------------------------------------------- /ErScripts/AutoPistol.cpp: -------------------------------------------------------------------------------- 1 | #include "ErScripts.h" 2 | 3 | void ErScripts::AutoPistol() { 4 | std::thread([]() { 5 | while (!globals::finish) { 6 | if (cfg->autoPistolState && globals::pistolState && !globals::revolverState && !globals::cz75aState) { 7 | if (ErScripts::GetWindowState() && ErScripts::GetCursorState()) { 8 | if (GetAsyncKeyState(VK_LBUTTON) & 0x8000) { 9 | ErScripts::CommandsSender(6, "attack 1 1 0"); 10 | std::this_thread::sleep_for(std::chrono::milliseconds((rand() % 5) + 1)); 11 | ErScripts::CommandsSender(6, "attack -999 1 0"); 12 | } 13 | } 14 | } 15 | 16 | std::this_thread::sleep_for(std::chrono::microseconds(15625)); 17 | } 18 | }).detach(); 19 | } -------------------------------------------------------------------------------- /ErScripts/AutoStop.cpp: -------------------------------------------------------------------------------- 1 | #include "ErScripts.h" 2 | #include 3 | 4 | inline int GenerateUniqueRandomDelay(std::vector& recentDelays, int minDelay, int maxDelay) { 5 | static std::mt19937 rng(static_cast(std::chrono::high_resolution_clock::now().time_since_epoch().count())); 6 | std::uniform_int_distribution dist(minDelay, maxDelay); 7 | 8 | int delay; 9 | do { 10 | delay = dist(rng); 11 | } while (std::find(recentDelays.begin(), recentDelays.end(), delay) != recentDelays.end()); 12 | 13 | if (recentDelays.size() >= 10) { 14 | recentDelays.erase(recentDelays.begin()); 15 | } 16 | recentDelays.push_back(delay); 17 | 18 | return delay; 19 | } 20 | 21 | inline void PressAndRelease(char key, std::vector& recentDelays) { 22 | int minDelay = 5; // Min delay in ms 23 | int maxDelay = 30; // Max delay in ms 24 | int baseTimeout = 115; // Base timeout (115) 25 | 26 | int delay = GenerateUniqueRandomDelay(recentDelays, minDelay, maxDelay); 27 | 28 | int totalTimeout = baseTimeout - delay; 29 | 30 | std::this_thread::sleep_for(std::chrono::milliseconds(delay)); 31 | ErScripts::Keyboard(key, true, false); 32 | std::this_thread::sleep_for(std::chrono::milliseconds(totalTimeout)); 33 | ErScripts::Keyboard(key, false, false); 34 | } 35 | 36 | void ErScripts::AutoStop() { 37 | std::thread([this]() { 38 | std::vector recentDelays; 39 | bool aWasPressed = false; 40 | bool dWasPressed = false; 41 | 42 | bool state = false; 43 | bool statePrev = false; 44 | 45 | while (!globals::finish) { 46 | if (cfg->autoStopState) { 47 | if (ErScripts::GetWindowState() && ErScripts::GetCursorState()) { 48 | bool bindState = GetAsyncKeyState(cfg->autoStopBind) & 0x8000; 49 | 50 | if (cfg->autoStopToggleState) { 51 | if (bindState && !statePrev) { 52 | state = !state; 53 | } 54 | } 55 | else { 56 | state = bindState; 57 | } 58 | statePrev = bindState; 59 | 60 | if (state) { 61 | bool aPressed = GetAsyncKeyState('A') & 0x8000; 62 | bool dPressed = GetAsyncKeyState('D') & 0x8000; 63 | 64 | // Detect release of A while not pressing D 65 | if (aWasPressed && !aPressed && !dPressed) { 66 | PressAndRelease('D', recentDelays); 67 | } 68 | // Detect release of D while not pressing A 69 | else if (dWasPressed && !dPressed && !aPressed) { 70 | PressAndRelease('A', recentDelays); 71 | } 72 | 73 | aWasPressed = aPressed; 74 | dWasPressed = dPressed; 75 | } 76 | } 77 | } 78 | std::this_thread::sleep_for(std::chrono::microseconds(15625)); 79 | } 80 | }).detach(); 81 | } -------------------------------------------------------------------------------- /ErScripts/BombTime.cpp: -------------------------------------------------------------------------------- 1 | #include "ErScripts.h" 2 | 3 | void ErScripts::BombTimer() { 4 | std::thread([this]() { 5 | while (!globals::finish) { 6 | if (cfg->bombTimerState) { 7 | static bool isbombTimerStarted = false; 8 | static std::chrono::system_clock::time_point bombPlantedTime; 9 | 10 | if (globals::bombState) { 11 | if (!isbombTimerStarted) { 12 | bombPlantedTime = std::chrono::system_clock::now(); 13 | isbombTimerStarted = true; 14 | } 15 | 16 | if (isbombTimerStarted) { 17 | globals::bombTime = 40000 - std::chrono::duration(std::chrono::system_clock::now() - bombPlantedTime).count(); 18 | if (globals::bombTime <= 0) globals::bombTime = 0.0l; 19 | } 20 | } 21 | else { 22 | globals::bombTime = 0.0l; 23 | isbombTimerStarted = false; 24 | } 25 | } 26 | std::this_thread::sleep_for(std::chrono::microseconds(15625)); 27 | } 28 | }).detach(); 29 | } -------------------------------------------------------------------------------- /ErScripts/CS2Binds.cpp: -------------------------------------------------------------------------------- 1 | #include "ErScripts.h" 2 | 3 | void ErScripts::CS2Binds() { 4 | std::thread([]() { 5 | while (!globals::finish) { 6 | if (ErScripts::GetWindowState() && ErScripts::GetCursorState()) { 7 | if (cfg->longJumpBind) { 8 | if (GetAsyncKeyState(cfg->longJumpBind) & 0x8000) { 9 | ErScripts::CommandsSender(7, "jump 1 1 0"); 10 | std::this_thread::sleep_for(std::chrono::milliseconds((rand() % 5) + 1)); 11 | ErScripts::CommandsSender(7, "duck 1 1 0"); 12 | std::this_thread::sleep_for(std::chrono::milliseconds((rand() % 5) + 1)); 13 | ErScripts::CommandsSender(7, "jump -999 1 0"); 14 | while ((GetAsyncKeyState(cfg->longJumpBind) & 0x8000)) { 15 | if (globals::finish) break; 16 | std::this_thread::sleep_for(std::chrono::milliseconds((rand() % 5) + 1)); 17 | } 18 | ErScripts::CommandsSender(7, "duck -999 1 0"); 19 | } 20 | } 21 | 22 | if (cfg->jumpThrowBind) { 23 | if (GetAsyncKeyState(cfg->jumpThrowBind) & 0x8000) { 24 | ErScripts::CommandsSender(7, "jump 1 1 0"); 25 | std::this_thread::sleep_for(std::chrono::milliseconds((rand() % 5) + 1)); 26 | ErScripts::CommandsSender(7, "attack -999 1 0"); 27 | std::this_thread::sleep_for(std::chrono::milliseconds((rand() % 5) + 1)); 28 | ErScripts::CommandsSender(7, "jump -999 1 0"); 29 | } 30 | } 31 | 32 | if (cfg->dropBombBind) { 33 | if (globals::isBombInWeapons) { 34 | if (GetAsyncKeyState(cfg->dropBombBind) & 0x8000) { 35 | ErScripts::CommandsSender(7, "slot5"); 36 | std::this_thread::sleep_for(std::chrono::milliseconds(64)); 37 | ErScripts::CommandsSender(7, "drop;lastinv"); 38 | std::this_thread::sleep_for(std::chrono::milliseconds(128)); 39 | } 40 | } 41 | } 42 | 43 | if (cfg->selfKickBind) { 44 | if (GetAsyncKeyState(cfg->selfKickBind) & 0x8000) { 45 | ErScripts::CommandsSender(7, "status"); 46 | std::this_thread::sleep_for(std::chrono::milliseconds(256)); 47 | ErScripts::CommandsSender(7, std::format("callvote kick {}", globals::localPlayerSlotNumber)); 48 | } 49 | } 50 | 51 | static bool isBindPressed = false; 52 | if (cfg->angleBindState) { 53 | bool isPressed = GetAsyncKeyState(cfg->angleBindBind) & 0x8000; 54 | if (isPressed && !isBindPressed) { 55 | float yaw = 0.0f; 56 | if (globals::isScope) { 57 | yaw = roundf((cfg->angleBindDegree / (globals::config->sensitivity * globals::config->zoomSensitivity * 0.4444444444f) / globals::config->yaw) * 10000.0f) / 10000.0f; 58 | } 59 | else { 60 | yaw = roundf((cfg->angleBindDegree / globals::config->sensitivity / globals::config->yaw) * 10000.0f) / 10000.0f; 61 | } 62 | ErScripts::CommandsSender(7, std::format("yaw {} 1 0", yaw)); 63 | isBindPressed = true; 64 | } 65 | else if (!isPressed && isBindPressed) { 66 | isBindPressed = false; 67 | } 68 | } 69 | } 70 | 71 | std::this_thread::sleep_for(std::chrono::microseconds(15625)); 72 | } 73 | }).detach(); 74 | } -------------------------------------------------------------------------------- /ErScripts/ChatSpammer.cpp: -------------------------------------------------------------------------------- 1 | #include "ErScripts.h" 2 | 3 | void ErScripts::ChatSpammer() { 4 | std::thread([this]() { 5 | bool state = false; 6 | bool statePrev = false; 7 | 8 | while (!globals::finish) { 9 | if (cfg->chatSpammerState) { 10 | bool bindState = GetAsyncKeyState(cfg->chatSpammerBind) & 0x8000; 11 | 12 | if (bindState && !statePrev) { 13 | state = !state; 14 | } 15 | 16 | statePrev = bindState; 17 | 18 | if (state || !cfg->chatSpammerBind) { 19 | CommandsSender(9, std::format("say {}", cfg->chatSpammerText)); 20 | } 21 | } 22 | std::this_thread::sleep_for(std::chrono::milliseconds(50)); 23 | } 24 | }).detach(); 25 | } -------------------------------------------------------------------------------- /ErScripts/Config.cpp: -------------------------------------------------------------------------------- 1 | #include "config.h" 2 | 3 | void Config::load(const std::string& filename) { 4 | if (!std::filesystem::exists("configs")) 5 | std::filesystem::create_directory("configs"); 6 | 7 | std::ifstream file("configs\\" + filename + ".json"); 8 | if (!file.is_open()) return; 9 | 10 | nlohmann::json json; 11 | file >> json; 12 | 13 | /* Pixel Trigger */ 14 | read(json["pixel-trigger"]["state"], triggerState); 15 | read(json["pixel-trigger"]["bind"], triggerBind); 16 | readArray(json["pixel-trigger"]["displacement"], triggerDisplacement); 17 | read(json["pixel-trigger"]["threshold"], triggerThreshold); 18 | read(json["pixel-trigger"]["delay"], triggerDelayMs); 19 | 20 | /* Auto Accept */ 21 | read(json["auto-accept"]["state"], autoAcceptState); 22 | read(json["auto-accept"]["waiting-time"], autoAcceptWaitingTime); 23 | 24 | /* Bomb Timer */ 25 | read(json["bomb-timer"]["state"], bombTimerState); 26 | read(json["bomb-timer"]["scale"], bombTimerScale); 27 | read(json["bomb-timer"]["gradient"]["state"], bombTimerGradientState); 28 | read(json["bomb-timer"]["transparency"], bombTimerTransparency); 29 | readArray(json["bomb-timer"]["pos"], bombTimerPos); 30 | 31 | /* Sniper Crosshair */ 32 | read(json["sniper-crosshair"]["state"], sniperCrosshairState); 33 | 34 | /* Recoil Crosshair */ 35 | read(json["recoil-crosshair"]["state"], recoilCrosshairState); 36 | 37 | /* RGB Crosshair */ 38 | read(json["rgb-crosshair"]["state"], rgbCrosshairState); 39 | 40 | /* Keystrokes */ 41 | read(json["keystrokes"]["state"], keystrokesState); 42 | read(json["keystrokes"]["scale"], keystrokesScale); 43 | read(json["keystrokes"]["gradient"]["state"], keystrokesGradientState); 44 | read(json["keystrokes"]["animation"]["speed"], keystrokesAnimationSpeed); 45 | readRGB(json["keystrokes"]["pressed"]["color"], keystrokesPressedColor); 46 | readRGB(json["keystrokes"]["released"]["color"], keystrokesReleasedColor); 47 | readArray(json["keystrokes"]["pos"], keystrokesPos); 48 | 49 | /* Keystrokes */ 50 | read(json["knife-switch"]["state"], knifeSwitchState); 51 | 52 | /* Auto pistol */ 53 | read(json["auto-pistol"]["state"], autoPistolState); 54 | 55 | /* Anti Afk */ 56 | read(json["anti-afk"]["state"], antiAfkState); 57 | 58 | /* Long Jump */ 59 | read(json["long-jump"]["bind"], longJumpBind); 60 | 61 | /* Jump Throw */ 62 | read(json["jump-throw"]["bind"], jumpThrowBind); 63 | 64 | /* Drop Bomb */ 65 | read(json["drop-bomb"]["bind"], dropBombBind); 66 | 67 | /* Self Kick */ 68 | read(json["self-kick"]["bind"], selfKickBind); 69 | 70 | /* Kill Say */ 71 | read(json["kill-say"]["state"], killSayState); 72 | read(json["kill-say"]["text"], killSayText); 73 | 74 | /* Kill Sound */ 75 | read(json["kill-sound"]["state"], killSoundState); 76 | read(json["kill-sound"]["volume"], killSoundVolume); 77 | read(json["kill-sound"]["file-name"], killSoundFileName); 78 | 79 | /* Round Start Alert */ 80 | read(json["round-start-alert"]["state"], roundStartAlertState); 81 | read(json["round-start-alert"]["volume"], roundStartAlertVolume); 82 | read(json["round-start-alert"]["file-name"], roundStartAlertFileName); 83 | 84 | /* Auto Stop */ 85 | read(json["auto-stop"]["state"], autoStopState); 86 | read(json["auto-stop"]["bind"], autoStopBind); 87 | read(json["auto-stop"]["toggle"]["state"], autoStopToggleState); 88 | 89 | /* Chat Spammer */ 90 | read(json["chat-spammer"]["state"], chatSpammerState); 91 | read(json["chat-spammer"]["bind"], chatSpammerBind); 92 | read(json["chat-spammer"]["text"], chatSpammerText); 93 | 94 | /* Angle Bind */ 95 | read(json["angle-bind"]["state"], angleBindState); 96 | read(json["angle-bind"]["bind"], angleBindBind); 97 | read(json["angle-bind"]["degree"], angleBindDegree); 98 | 99 | /* Watermark */ 100 | read(json["watermark"]["state"], watermarkState); 101 | read(json["watermark"]["gradient"]["state"], watermarkGradientState); 102 | read(json["watermark"]["transparency"], watermarkTransparency); 103 | read(json["watermark"]["ping-update-rate"], watermarkPingUpdateRate); 104 | 105 | /* FPS Limiter */ 106 | read(json["fps-limiter"]["state"], fpsLimiterState); 107 | read(json["fps-limiter"]["fps"], fpsLimiter); 108 | 109 | /* Capture Bypass */ 110 | read(json["capture-bypass"]["state"], captureBypassState); 111 | 112 | /* Gradient Manager */ 113 | read(json["gradient-manager"]["num-steps"], gradient.numSteps); 114 | read(json["gradient-manager"]["delay-ms"], gradient.delayMs); 115 | read(json["gradient-manager"]["start-hue"], gradient.startHue); 116 | read(json["gradient-manager"]["end-hue]"], gradient.endHue); 117 | read(json["gradient-manager"]["saturation"], gradient.saturation); 118 | read(json["gradient-manager"]["value"], gradient.value); 119 | 120 | /* ErScripts Binds */ 121 | read(json["er-scripts"]["menu"]["bind"], erScriptsMenuBind); 122 | read(json["er-scripts"]["exit"]["bind"], erScriptsExitBind); 123 | 124 | /* Vsync */ 125 | read(json["vsync"]["state"], vsyncState); 126 | } 127 | 128 | void Config::save(const std::string& filename) const { 129 | nlohmann::ordered_json json; 130 | 131 | /* Pixel Trigger */ 132 | json["pixel-trigger"]["state"] = triggerState; 133 | json["pixel-trigger"]["bind"] = triggerBind; 134 | json["pixel-trigger"]["displacement"] = triggerDisplacement; 135 | json["pixel-trigger"]["threshold"] = triggerThreshold; 136 | json["pixel-trigger"]["delay"] = triggerDelayMs; 137 | 138 | /* Auto Accept */ 139 | json["auto-accept"]["state"] = autoAcceptState; 140 | json["auto-accept"]["waiting-time"] = autoAcceptWaitingTime; 141 | 142 | /* Bomb Timer */ 143 | json["bomb-timer"]["state"] = bombTimerState; 144 | json["bomb-timer"]["scale"] = bombTimerScale; 145 | json["bomb-timer"]["gradient"]["state"] = bombTimerGradientState; 146 | json["bomb-timer"]["transparency"] = bombTimerTransparency; 147 | json["bomb-timer"]["pos"] = bombTimerPos; 148 | 149 | /* Sniper Crosshair */ 150 | json["sniper-crosshair"]["state"] = sniperCrosshairState; 151 | 152 | /* RGB Crosshair */ 153 | json["rgb-crosshair"]["state"] = rgbCrosshairState; 154 | 155 | /* Recoil Crosshair */ 156 | json["recoil-crosshair"]["state"] = recoilCrosshairState; 157 | 158 | /* Keystrokes */ 159 | json["keystrokes"]["state"] = keystrokesState; 160 | json["keystrokes"]["scale"] = keystrokesScale; 161 | json["keystrokes"]["gradient"]["state"] = keystrokesGradientState; 162 | json["keystrokes"]["animation"]["speed"] = keystrokesAnimationSpeed; 163 | json["keystrokes"]["pressed"]["color"] = { keystrokesPressedColor.r, keystrokesPressedColor.g, keystrokesPressedColor.b }; 164 | json["keystrokes"]["released"]["color"] = { keystrokesReleasedColor.r, keystrokesReleasedColor.g, keystrokesReleasedColor.b }; 165 | json["keystrokes"]["pos"] = keystrokesPos; 166 | 167 | /* Keystrokes */ 168 | json["knife-switch"]["state"] = knifeSwitchState; 169 | 170 | /* Auto pistol */ 171 | json["auto-pistol"]["state"] = autoPistolState; 172 | 173 | /* Anti Afk */ 174 | json["anti-afk"]["state"] = antiAfkState; 175 | 176 | /* Long Jump */ 177 | json["long-jump"]["bind"] = longJumpBind; 178 | 179 | /* Jump Throw */ 180 | json["jump-throw"]["bind"] = jumpThrowBind; 181 | 182 | /* Drop Bomb */ 183 | json["drop-bomb"]["bind"] = dropBombBind; 184 | 185 | /* Self Kick */ 186 | json["self-kick"]["bind"] = selfKickBind; 187 | 188 | /* Kill Say */ 189 | json["kill-say"]["state"] = killSayState; 190 | json["kill-say"]["text"] = killSayText; 191 | 192 | /* Kill Sound */ 193 | json["kill-sound"]["state"] = killSoundState; 194 | json["kill-sound"]["volume"] = killSoundVolume; 195 | json["kill-sound"]["file-name"] = killSoundFileName; 196 | 197 | /* Round Start Alert */ 198 | json["round-start-alert"]["state"] = roundStartAlertState; 199 | json["round-start-alert"]["volume"] = roundStartAlertVolume; 200 | json["round-start-alert"]["file-name"] = roundStartAlertFileName; 201 | 202 | /* Auto Stop */ 203 | json["auto-stop"]["state"] = autoStopState; 204 | json["auto-stop"]["bind"] = autoStopBind; 205 | json["auto-stop"]["toggle"]["state"] = autoStopToggleState; 206 | 207 | /* Chat Spammer */ 208 | json["chat-spammer"]["state"] = chatSpammerState; 209 | json["chat-spammer"]["bind"] = chatSpammerBind; 210 | json["chat-spammer"]["text"] = chatSpammerText; 211 | 212 | /* Angle Bind */ 213 | json["angle-bind"]["state"] = angleBindState; 214 | json["angle-bind"]["bind"] = angleBindBind; 215 | json["angle-bind"]["degree"] = angleBindDegree; 216 | 217 | /* Watermark */ 218 | json["watermark"]["state"] = watermarkState; 219 | json["watermark"]["gradient"]["state"] = watermarkGradientState; 220 | json["watermark"]["transparency"] = watermarkTransparency; 221 | json["watermark"]["ping-update-rate"] = watermarkPingUpdateRate; 222 | 223 | /* FPS Limiter */ 224 | json["fps-limiter"]["state"] = fpsLimiterState; 225 | json["fps-limiter"]["fps"] = fpsLimiter; 226 | 227 | /* Capture Bypass */ 228 | json["capture-bypass"]["state"] = captureBypassState; 229 | 230 | /* Gradient Manager */ 231 | json["gradient-manager"]["num-steps"] = gradient.numSteps; 232 | json["gradient-manager"]["delay-ms"] = gradient.delayMs; 233 | json["gradient-manager"]["start-hue"] = gradient.startHue; 234 | json["gradient-manager"]["end-hue]"] = gradient.endHue; 235 | json["gradient-manager"]["saturation"] = gradient.saturation; 236 | json["gradient-manager"]["value"] = gradient.value; 237 | 238 | /* Vsync */ 239 | json["vsync"]["state"] = vsyncState; 240 | 241 | /* ErScripts Binds */ 242 | json["er-scripts"]["menu"]["bind"] = erScriptsMenuBind; 243 | json["er-scripts"]["exit"]["bind"] = erScriptsExitBind; 244 | 245 | if (!std::filesystem::exists("configs")) 246 | std::filesystem::create_directory("configs"); 247 | 248 | std::ofstream file("configs\\" + filename + ".json"); 249 | if (file.is_open()) { 250 | file << json.dump(4); // Pretty print with 4 spaces indentation 251 | } 252 | } 253 | 254 | void Config::readArray(const nlohmann::json& src, float(&dest)[2]) { 255 | try { 256 | if (!src.is_null() && src.is_array() && src.size() == 2) { 257 | dest[0] = src[0].get(); 258 | dest[1] = src[1].get(); 259 | } 260 | } 261 | catch (const std::exception& e) { 262 | Logger::logWarning(std::format("Config Array Error: {}", e.what())); 263 | } 264 | } 265 | 266 | void Config::readArray(const nlohmann::json& src, int(&dest)[2]) { 267 | try { 268 | if (!src.is_null() && src.is_array() && src.size() == 2) { 269 | dest[0] = src[0].get(); 270 | dest[1] = src[1].get(); 271 | } 272 | } 273 | catch (const std::exception& e) { 274 | Logger::logWarning(std::format("Config Array Error: {}", e.what())); 275 | } 276 | } 277 | 278 | void Config::readRGB(const nlohmann::json& src, RGBColor& dest) { 279 | try { 280 | if (!src.is_null() && src.is_array() && src.size() == 3) { 281 | dest.r = src[0].get(); 282 | dest.g = src[1].get(); 283 | dest.b = src[2].get(); 284 | } 285 | } 286 | catch (const std::exception& e) { 287 | Logger::logWarning(std::format("Config Array Error: {}", e.what())); 288 | } 289 | } -------------------------------------------------------------------------------- /ErScripts/Config.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include "Logger.h" 4 | #include "GradientManager.h" 5 | #include 6 | #include 7 | #include 8 | #include 9 | 10 | class Config { 11 | public: 12 | void load(const std::string& filename); 13 | void save(const std::string& filename) const; 14 | 15 | /* Pixel Trigger */ 16 | bool triggerState{ false }; 17 | int triggerBind{ 0 }; 18 | int triggerDisplacement[2]{ 1, 1 }; 19 | int triggerThreshold{ 20 }; 20 | int triggerDelayMs{ 0 }; 21 | 22 | /* Auto Accept */ 23 | bool autoAcceptState{ false }; 24 | int autoAcceptWaitingTime{ 5 }; 25 | 26 | /* Bomb Timer */ 27 | bool bombTimerState{ false }; 28 | float bombTimerScale{ 1.0f }; 29 | bool bombTimerGradientState{ false }; 30 | float bombTimerTransparency{ 1.0f }; 31 | float bombTimerPos[2]{ 0, 0 }; 32 | 33 | /* Sniper Crosshair */ 34 | bool sniperCrosshairState{ false }; 35 | 36 | /* RGB Crosshair */ 37 | bool rgbCrosshairState{ false }; 38 | 39 | /* Recoil Crosshair */ 40 | bool recoilCrosshairState{ false }; 41 | 42 | /* Keystrokes */ 43 | bool keystrokesState{ false }; 44 | float keystrokesScale{ 1.5f }; 45 | bool keystrokesGradientState{ true }; 46 | float keystrokesAnimationSpeed{ 15.0f }; 47 | RGBColor keystrokesPressedColor{ 15, 15, 15 }; 48 | RGBColor keystrokesReleasedColor{ 20, 20, 20 }; 49 | float keystrokesTransparency{ 0.6f }; 50 | float keystrokesPos[2]{ 0, 0 }; 51 | 52 | /* Keystrokes */ 53 | bool knifeSwitchState{ false }; 54 | 55 | /* Auto pistol */ 56 | bool autoPistolState{ false }; 57 | 58 | /* Anti Afk */ 59 | bool antiAfkState{ false }; 60 | 61 | /* Long Jump */ 62 | int longJumpBind{ 0 }; 63 | 64 | /* Jump Throw */ 65 | int jumpThrowBind{ 0 }; 66 | 67 | /* Drop Bomb */ 68 | int dropBombBind{ 0 }; 69 | 70 | /* Self Kick */ 71 | int selfKickBind{ 0 }; 72 | 73 | /* Kill Say */ 74 | bool killSayState{ false }; 75 | std::string killSayText{ "1" }; 76 | 77 | /* Kill Sound */ 78 | bool killSoundState{ false }; 79 | int killSoundVolume{ 100 }; 80 | std::string killSoundFileName{ "" }; 81 | 82 | /* Round Start Alert */ 83 | bool roundStartAlertState{ false }; 84 | int roundStartAlertVolume{ 100 }; 85 | std::string roundStartAlertFileName{ "" }; 86 | 87 | /* Auto Stop */ 88 | bool autoStopState{ false }; 89 | int autoStopBind{ 0 }; 90 | bool autoStopToggleState{ false }; 91 | 92 | /* Chat Spammer */ 93 | bool chatSpammerState{ false }; 94 | int chatSpammerBind{ 0 }; 95 | std::string chatSpammerText{ "Enhance Your Skills with emp0ry.github.io/cs2-ErScripts/" }; 96 | 97 | /* Angle Bind */ 98 | bool angleBindState{ false }; 99 | int angleBindBind{ 0 }; 100 | float angleBindDegree{ 180.0f }; 101 | 102 | /* Watermark */ 103 | bool watermarkState{ true }; 104 | bool watermarkGradientState{ true }; 105 | float watermarkTransparency{ 1.0f }; 106 | int watermarkPingUpdateRate{ 3 }; 107 | 108 | /* FPS Limiter */ 109 | bool fpsLimiterState{ true }; 110 | int fpsLimiter{ 350 }; 111 | 112 | /* Capture Bypass */ 113 | bool captureBypassState{ false }; 114 | 115 | /* Gradient Manager */ 116 | GradientConfig gradient; 117 | 118 | /* Vsync */ 119 | bool vsyncState{ false }; 120 | 121 | /* ErScripts Binds */ 122 | int erScriptsMenuBind{ VK_INSERT }; 123 | int erScriptsExitBind{ VK_END }; 124 | 125 | private: 126 | template 127 | void read(const nlohmann::json& src, T& dest) { 128 | try { 129 | if (!src.is_null()) { 130 | dest = src.get(); 131 | } 132 | } 133 | catch (const std::exception& e) { 134 | Logger::logWarning(std::format("Config Error Code: {}", e.what())); 135 | } 136 | } 137 | 138 | void readArray(const nlohmann::json& src, float(&dest)[2]); 139 | void readArray(const nlohmann::json& src, int(&dest)[2]); 140 | void readRGB(const nlohmann::json& src, RGBColor& dest); 141 | }; 142 | 143 | inline std::unique_ptr cfg = std::make_unique(); -------------------------------------------------------------------------------- /ErScripts/Crosshair.cpp: -------------------------------------------------------------------------------- 1 | #include "ErScripts.h" 2 | 3 | void ErScripts::Crosshair() { 4 | std::thread([this]() { 5 | std::vector blackColor = { 0, 0, 0 }; 6 | bool oldRecoilCrosshairState = false; 7 | 8 | while (!globals::finish) { 9 | if (ErScripts::GetWindowState() && ErScripts::GetCursorState()) { 10 | 11 | // No longer needed because the print_changed_convars command is used for it 12 | /*if (globals::configUpdaterState) { 13 | ErScripts::CommandsSender("host_writeconfig"); 14 | std::this_thread::sleep_for(std::chrono::milliseconds(200)); 15 | Logger::logInfo("Crosshair Updater"); 16 | globals::crosshair = SteamTools::getCrosshairSettings("730"); 17 | SteamTools::printCrosshairSettings(*globals::crosshair); 18 | globals::configUpdaterState = false; 19 | }*/ 20 | 21 | if (globals::sniperCrosshairState) { 22 | std::vector pixelColor1 = GetPixelColor(globals::posX + 1, globals::posY + 1); 23 | std::vector pixelColor2 = GetPixelColor(globals::posX + 1, globals::posY + globals::height - 6); 24 | 25 | //Logger::logInfo(std::format("Color1: RGB({} {} {}) POS({} {}), Color2: RGB({} {} {}) POS({} {})", pixelColor1[0], pixelColor1[1], pixelColor1[2], globals::posX + globals::width / 2, globals::posY, pixelColor2[0], pixelColor2[1], pixelColor2[2], globals::posX + globals::width / 2, globals::posY + globals::height - 1)); 26 | 27 | globals::isScope = (isColorSimilar(pixelColor1, blackColor, 20) && isColorSimilar(pixelColor2, blackColor, 20)); 28 | } 29 | 30 | if (cfg->recoilCrosshairState != oldRecoilCrosshairState) { 31 | if (cfg->recoilCrosshairState) { 32 | CommandsSender(3, "cl_crosshair_recoil true"); 33 | } 34 | else { 35 | CommandsSender(3, "cl_crosshair_recoil false"); 36 | } 37 | 38 | oldRecoilCrosshairState = cfg->recoilCrosshairState; 39 | } 40 | 41 | //static bool wasPressed = false; 42 | 43 | //if (cfg->recoilCrosshairState) { 44 | // bool isPressed = (GetAsyncKeyState(VK_LBUTTON) & 0x8000); 45 | 46 | // // If just pressed 47 | // if (isPressed && !wasPressed) { 48 | // ErScripts::CommandsSender(3, 49 | // "cl_crosshairgap -5; cl_crosshair_outlinethickness 1; \ 50 | // cl_crosshaircolor_r 255; cl_crosshaircolor_g 0; cl_crosshaircolor_b 0; \ 51 | // cl_crosshairalpha 255; cl_crosshaircolor 5; cl_crosshair_drawoutline 0; \ 52 | // cl_crosshairthickness 0.1; cl_crosshairdot 1; cl_crosshair_t 0; \ 53 | // cl_crosshairstyle 4; cl_crosshairsize 2"); 54 | // wasPressed = true; 55 | // } 56 | // // If just released 57 | // else if (!isPressed && wasPressed) { 58 | // ErScripts::CommandsSender(3, std::format( 59 | // "cl_crosshairgap {}; cl_crosshair_outlinethickness {}; cl_crosshaircolor_r {}; \ 60 | // cl_crosshaircolor_g {}; cl_crosshaircolor_b {}; cl_crosshairalpha {}; \ 61 | // cl_crosshaircolor {}; cl_crosshair_drawoutline {}; cl_crosshairthickness {}; \ 62 | // cl_crosshairdot {}; cl_crosshair_t {}; cl_crosshairstyle {}; cl_crosshairsize {}", 63 | // globals::config->gap, 64 | // globals::config->outlineThickness, 65 | // globals::config->red, 66 | // globals::config->green, 67 | // globals::config->blue, 68 | // globals::config->alpha, 69 | // globals::config->color, 70 | // globals::config->drawOutline, 71 | // globals::config->thickness, 72 | // globals::config->dot, 73 | // globals::config->tStyle, 74 | // globals::config->style, 75 | // globals::config->size)); 76 | // wasPressed = false; 77 | // } 78 | //} 79 | 80 | } 81 | std::this_thread::sleep_for(std::chrono::microseconds(15625)); 82 | } 83 | }).detach(); 84 | } -------------------------------------------------------------------------------- /ErScripts/ErScripts.aps: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/emp0ry/cs2-ErScripts/78ed5dcc81561cc25fc577c8348559754272bcd6/ErScripts/ErScripts.aps -------------------------------------------------------------------------------- /ErScripts/ErScripts.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include "FileMonitor.h" 4 | #include "GradientManager.h" 5 | #include "Logger.h" 6 | #include "Config.h" 7 | #include "Globals.h" 8 | #include 9 | #include 10 | 11 | class ErScripts { 12 | public: 13 | void Initalization(); 14 | void ConsoleLogStream(); 15 | static const bool GetCursorState(); 16 | static const bool GetWindowState(); 17 | static const void GetWindowInfo(int& width, int& height, int& posX, int& posY); 18 | static void CommandsSender(const int num, const std::string& command); 19 | static const void Keyboard(int wScan, bool isPressed, bool useScanCode = true); 20 | 21 | void InitBinds(); 22 | void AutoAccept(); 23 | void PixelTrigger(); 24 | void Crosshair(); 25 | void BombTimer(); 26 | void RGBCrosshair(); 27 | void KnifeSwitch(); 28 | void AutoPistol(); 29 | void AntiAfk(); 30 | void CS2Binds(); 31 | void KillSay(); 32 | void KillSound(); 33 | void RoundStartAlert(); 34 | void AutoStop(); 35 | void ChatSpammer(); 36 | private: 37 | static HWND hwnd; 38 | 39 | std::wstring path; // CS2 installation path 40 | static std::wstring config; // Path to cfg file 41 | std::wstring gsi; // Path to gamestate_integration file 42 | std::wstring consoleLog; // Path to console.log file 43 | 44 | bool foundMatch = false; // For Auto Accept 45 | 46 | static bool bindsInit; // Init Binds 47 | 48 | bool setPaths(); // Returns true if path is found and set 49 | bool configsValidation(); // Configs validation if not exist create 50 | 51 | const std::vector GetPixelColor(int x, int y); 52 | const bool isColorSimilar(const std::vector& color, const std::vector& targetColor, int range); 53 | }; -------------------------------------------------------------------------------- /ErScripts/ErScripts.rc: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/emp0ry/cs2-ErScripts/78ed5dcc81561cc25fc577c8348559754272bcd6/ErScripts/ErScripts.rc -------------------------------------------------------------------------------- /ErScripts/ErScripts.vcxproj: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | Debug 6 | Win32 7 | 8 | 9 | Release 10 | Win32 11 | 12 | 13 | Debug 14 | x64 15 | 16 | 17 | Release 18 | x64 19 | 20 | 21 | 22 | 17.0 23 | Win32Proj 24 | {97211613-a2db-4d8e-93b3-7b5da48c57db} 25 | ErScripts 26 | 10.0 27 | 28 | 29 | 30 | Application 31 | true 32 | v143 33 | Unicode 34 | 35 | 36 | Application 37 | false 38 | v143 39 | true 40 | Unicode 41 | 42 | 43 | Application 44 | true 45 | v143 46 | Unicode 47 | 48 | 49 | Application 50 | false 51 | v143 52 | true 53 | Unicode 54 | x64 55 | 56 | 57 | 58 | 59 | 60 | 61 | 62 | 63 | 64 | 65 | 66 | 67 | 68 | 69 | 70 | 71 | 72 | 73 | 74 | 75 | $(SolutionDir)include;$(VC_IncludePath);$(WindowsSDK_IncludePath); 76 | 77 | 78 | $(SolutionDir)Include;$(IncludePath) 79 | 80 | 81 | false 82 | 83 | 84 | 85 | Level3 86 | true 87 | WIN32;_DEBUG;_CONSOLE;%(PreprocessorDefinitions) 88 | true 89 | 90 | 91 | Console 92 | true 93 | 94 | 95 | 96 | 97 | Level3 98 | true 99 | true 100 | true 101 | WIN32;NDEBUG;_CONSOLE;%(PreprocessorDefinitions) 102 | true 103 | 104 | 105 | Console 106 | true 107 | true 108 | true 109 | 110 | 111 | 112 | 113 | Level3 114 | true 115 | _DEBUG;_CONSOLE;%(PreprocessorDefinitions) 116 | true 117 | stdcpp20 118 | 119 | 120 | Console 121 | true 122 | 123 | 124 | 125 | 126 | Level3 127 | true 128 | true 129 | true 130 | NDEBUG;_CONSOLE;%(PreprocessorDefinitions) 131 | true 132 | stdcpp20 133 | 134 | 135 | MaxSpeed 136 | false 137 | MultiThreaded 138 | 139 | 140 | Neither 141 | 142 | 143 | Console 144 | true 145 | true 146 | true 147 | RequireAdministrator 148 | windowscodecs.lib;d3d11.lib;dxgi.lib;dwmapi.lib;%(AdditionalDependencies) 149 | 150 | 151 | true 152 | 153 | 154 | 155 | 156 | 157 | 158 | 159 | 160 | 161 | 162 | 163 | 164 | 165 | 166 | 167 | 168 | 169 | 170 | 171 | 172 | 173 | 174 | 175 | 176 | 177 | 178 | 179 | 180 | 181 | 182 | 183 | 184 | 185 | 186 | 187 | 188 | 189 | 190 | 191 | 192 | 193 | 194 | 195 | 196 | 197 | 198 | 199 | 200 | 201 | 202 | 203 | 204 | 205 | 206 | 207 | 208 | 209 | 210 | 211 | 212 | 213 | 214 | 215 | 216 | 217 | 218 | 219 | 220 | 221 | 222 | 223 | 224 | 225 | 226 | 227 | 228 | 229 | -------------------------------------------------------------------------------- /ErScripts/ErScripts.vcxproj.filters: -------------------------------------------------------------------------------- 1 |  2 | 3 | 4 | 5 | {67DA6AB6-F800-4c08-8B7A-83BB121AAD01} 6 | rc;ico;cur;bmp;dlg;rc2;rct;bin;rgs;gif;jpg;jpeg;jpe;resx;tiff;tif;png;wav;mfcribbon-ms 7 | 8 | 9 | {4FC737F1-C7A5-4376-A066-2A32D752A2FF} 10 | cpp;c;cc;cxx;c++;cppm;ixx;def;odl;idl;hpj;bat;asm;asmx 11 | 12 | 13 | {1a1e2700-1a05-4258-83e8-738581c8d606} 14 | 15 | 16 | {89053330-8b0e-4c62-aae1-6c35499773f5} 17 | 18 | 19 | {b105e09a-767e-4dbf-aeb3-961973e28b49} 20 | 21 | 22 | {5d0ea2df-c957-4162-afbb-cf7fce4016e1} 23 | 24 | 25 | {0084733d-ae86-42fd-b0e3-395ccdb38569} 26 | 27 | 28 | {072e0560-16c3-4c90-a725-0938fde4f045} 29 | 30 | 31 | {fd6f62fa-be63-43a4-ae4c-78ad90e77355} 32 | 33 | 34 | {e702640f-ba0e-4133-99a6-f0bae39174b3} 35 | 36 | 37 | {acd99bb9-82b3-4177-af97-27c1d80d7a61} 38 | 39 | 40 | {10f7d168-ed61-4380-b292-f6f07c9d0df5} 41 | 42 | 43 | {ae2d8ada-5318-41b4-a128-a5ced24f6531} 44 | 45 | 46 | {efe16123-d2de-41b9-9192-11a75d28ae09} 47 | 48 | 49 | {7aeba3ba-c87b-467e-9c41-14592fe7a0bc} 50 | 51 | 52 | {241ff238-ae5d-4fff-aabf-574da5f5e6ce} 53 | 54 | 55 | {1ff65aba-bfb7-4875-8138-ecfe24c3d9d6} 56 | 57 | 58 | {8e37d140-a614-43b2-a3a4-89e025f3878f} 59 | 60 | 61 | 62 | 63 | Main 64 | 65 | 66 | Functions\Logger 67 | 68 | 69 | Config 70 | 71 | 72 | Render 73 | 74 | 75 | Functions\ErScripts 76 | 77 | 78 | Functions\GSI Server 79 | 80 | 81 | Functions\Globals 82 | 83 | 84 | Render\Imgui 85 | 86 | 87 | Render\Imgui 88 | 89 | 90 | Render\Imgui 91 | 92 | 93 | Render\Imgui 94 | 95 | 96 | Render\Imgui 97 | 98 | 99 | Render\Imgui 100 | 101 | 102 | Render\Imgui 103 | 104 | 105 | Render 106 | 107 | 108 | Render 109 | 110 | 111 | Functions\Steam Tools 112 | 113 | 114 | Functions\File Monitor 115 | 116 | 117 | Functions\Functions 118 | 119 | 120 | Render\UIAccess 121 | 122 | 123 | Render 124 | 125 | 126 | Functions\Functions 127 | 128 | 129 | Functions\Functions 130 | 131 | 132 | Functions\Functions 133 | 134 | 135 | Functions\Functions 136 | 137 | 138 | Functions\GradientManager 139 | 140 | 141 | Functions\Functions 142 | 143 | 144 | Functions\Functions 145 | 146 | 147 | Functions\Functions 148 | 149 | 150 | Functions\Functions 151 | 152 | 153 | Functions\Functions 154 | 155 | 156 | Functions\Functions 157 | 158 | 159 | Functions\Simple Sound 160 | 161 | 162 | Functions\Functions 163 | 164 | 165 | Functions\Simple Sound 166 | 167 | 168 | Functions\Updater 169 | 170 | 171 | Functions\Functions 172 | 173 | 174 | Functions\Functions 175 | 176 | 177 | Rebuild 178 | 179 | 180 | 181 | 182 | Functions\Logger 183 | 184 | 185 | Config 186 | 187 | 188 | Render 189 | 190 | 191 | Functions\ErScripts 192 | 193 | 194 | Functions\GSI Server 195 | 196 | 197 | Functions\Globals 198 | 199 | 200 | Render\Imgui 201 | 202 | 203 | Render\Imgui 204 | 205 | 206 | Render\Imgui 207 | 208 | 209 | Render\Imgui 210 | 211 | 212 | Render\Imgui 213 | 214 | 215 | Render\Imgui 216 | 217 | 218 | Render\Imgui 219 | 220 | 221 | Render\Imgui 222 | 223 | 224 | Functions\Steam Tools 225 | 226 | 227 | Functions\File Monitor 228 | 229 | 230 | Render\UIAccess 231 | 232 | 233 | Render 234 | 235 | 236 | Functions\GradientManager 237 | 238 | 239 | 240 | Functions\Simple Sound 241 | 242 | 243 | Functions\Updater 244 | 245 | 246 | Rebuild 247 | 248 | 249 | 250 | 251 | Resource Files 252 | 253 | 254 | 255 | 256 | Resource Files 257 | 258 | 259 | -------------------------------------------------------------------------------- /ErScripts/ErScripts.vcxproj.user: -------------------------------------------------------------------------------- 1 |  2 | 3 | 4 | false 5 | 6 | -------------------------------------------------------------------------------- /ErScripts/FileMonitor.cpp: -------------------------------------------------------------------------------- 1 | #include "FileMonitor.h" 2 | #include "Logger.h" 3 | #include 4 | #include 5 | 6 | FileMonitor::FileMonitor(std::wstring filePath) : filePath_(std::move(filePath)) { 7 | // Start at the end of the file 8 | std::wifstream file(filePath_, std::ios::in); 9 | if (file.is_open()) { 10 | file.seekg(0, std::ios::end); 11 | lastOffset_ = file.tellg(); 12 | file.close(); 13 | } 14 | } 15 | 16 | FileMonitor::~FileMonitor() { 17 | stop(); 18 | } 19 | 20 | void FileMonitor::start() { 21 | if (running_.exchange(true)) { 22 | return; 23 | } 24 | monitorThread_ = std::thread(&FileMonitor::pollFile, this); 25 | } 26 | 27 | void FileMonitor::stop() { 28 | running_ = false; 29 | if (monitorThread_.joinable()) { 30 | monitorThread_.join(); 31 | } 32 | } 33 | 34 | std::optional> FileMonitor::getNewLines() { 35 | std::lock_guard lock(linesMutex_); 36 | if (newLines_.empty()) { 37 | return std::nullopt; 38 | } 39 | auto result = std::move(newLines_); 40 | newLines_.clear(); 41 | return result; 42 | } 43 | 44 | void FileMonitor::pollFile() { 45 | while (running_) { 46 | std::wifstream file(filePath_, std::ios::in); 47 | if (!file.is_open()) { 48 | Logger::logWarning(std::format(L"Failed to open CS2 log: {}", filePath_)); 49 | std::this_thread::sleep_for(std::chrono::milliseconds(1000)); 50 | continue; 51 | } 52 | 53 | // Move to last known offset 54 | file.seekg(lastOffset_, std::ios::beg); 55 | 56 | std::deque tempLines; 57 | std::wstring line; 58 | 59 | // Read all new lines from current position 60 | while (std::getline(file, line)) { 61 | if (!line.empty()) { 62 | tempLines.push_back(std::move(line)); 63 | } 64 | } 65 | 66 | // Update offset to current position 67 | lastOffset_ = file.tellg(); 68 | if (lastOffset_ == -1) { // Handle potential EOF or error 69 | file.clear(); 70 | file.seekg(0, std::ios::end); 71 | lastOffset_ = file.tellg(); 72 | } 73 | 74 | if (!tempLines.empty()) { 75 | std::lock_guard lock(linesMutex_); 76 | newLines_.insert(newLines_.end(), 77 | std::move_iterator(tempLines.begin()), 78 | std::move_iterator(tempLines.end())); 79 | } 80 | 81 | file.close(); 82 | std::this_thread::sleep_for(std::chrono::milliseconds(50)); // Fast but light 83 | } 84 | } -------------------------------------------------------------------------------- /ErScripts/FileMonitor.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include 4 | #include 5 | #include 6 | #include 7 | #include 8 | #include 9 | #include 10 | 11 | class FileMonitor { 12 | public: 13 | explicit FileMonitor(std::wstring filePath); 14 | ~FileMonitor(); 15 | 16 | FileMonitor(const FileMonitor&) = delete; 17 | FileMonitor& operator=(const FileMonitor&) = delete; 18 | 19 | void start(); 20 | void stop(); 21 | [[nodiscard]] std::optional> getNewLines(); 22 | 23 | private: 24 | std::wstring filePath_; 25 | std::atomic running_{ false }; 26 | std::thread monitorThread_; 27 | std::mutex linesMutex_; 28 | std::deque newLines_; 29 | std::streamoff lastOffset_{ 0 }; 30 | 31 | void pollFile(); 32 | }; -------------------------------------------------------------------------------- /ErScripts/GSIServer.cpp: -------------------------------------------------------------------------------- 1 | #include "GSIServer.h" 2 | #include "Logger.h" 3 | #include "Globals.h" 4 | #include 5 | #include 6 | 7 | // Helper function to check if player is local player 8 | std::optional GSIServer::getLocalPlayerData(const nlohmann::json& data) { 9 | if (!data.contains("player") || !data.contains("provider")) { 10 | return std::nullopt; 11 | } 12 | if (!data["player"].contains("steamid") || !data["provider"].contains("steamid")) { 13 | return std::nullopt; 14 | } 15 | if (data["player"]["steamid"].get() != data["provider"]["steamid"].get()) { 16 | return std::nullopt; 17 | } 18 | return data["player"]; 19 | } 20 | 21 | // Helper to check weapon states 22 | bool GSIServer::hasActiveWeaponType(const nlohmann::json& playerData, const std::string& weaponType) { 23 | if (!playerData.contains("weapons") || !playerData["weapons"].is_object()) { 24 | return false; 25 | } 26 | 27 | for (const auto& [_, weapon] : playerData["weapons"].items()) { 28 | if (weapon.contains("type") && weapon.contains("state")) { 29 | std::string type = weapon["type"].get(); 30 | std::string state = weapon["state"].get(); 31 | if (type == weaponType && (state == "active" || weaponType == "C4")) { 32 | return true; 33 | } 34 | } 35 | } 36 | return false; 37 | } 38 | 39 | void GSIServer::gsiServer() { 40 | server.Post("/", [this](const httplib::Request& req, httplib::Response& res) { 41 | try { 42 | //system("cls"); std::cout << req.body << std::endl; 43 | nlohmann::json jsonData = nlohmann::json::parse(req.body); 44 | handleJsonPayload(jsonData); 45 | res.set_content("OK", "text/plain"); 46 | } 47 | catch (const nlohmann::json::exception& e) { 48 | Logger::logWarning(std::format("JSON parsing error: {}", e.what())); 49 | res.status = 400; 50 | res.set_content("Invalid JSON", "text/plain"); 51 | } 52 | }); 53 | 54 | Logger::logInfo(std::format("Starting GSI server on 127.0.0.1:{}", port)); 55 | if (!server.listen("127.0.0.1", port)) { 56 | Logger::logWarning(std::format("Failed to start GSI server on 127.0.0.1:{}", port)); 57 | } 58 | } 59 | 60 | void GSIServer::run() { 61 | std::thread serverThread(&GSIServer::gsiServer, this); 62 | serverThread.detach(); 63 | } 64 | 65 | void GSIServer::stop() { 66 | Logger::logInfo("Stopping GSI server"); 67 | server.stop(); 68 | } 69 | 70 | void GSIServer::handleJsonPayload(const nlohmann::json& data) { 71 | auto playerData = getLocalPlayerData(data); 72 | 73 | globals::sniperCrosshairState = handleSniperCrosshairState(data); 74 | globals::bombState = handleBombState(data); 75 | globals::defusekitState = handleDefuseKitState(data); 76 | globals::roundStartState = handleRoundStartState(data); 77 | globals::knifeState = handleKnifeState(playerData); 78 | globals::pistolState = handlePistolState(playerData); 79 | globals::isBombInWeapons = handleIsBombInWeapons(playerData); 80 | globals::localPlayerKills = handleLocalPlayerKills(playerData); 81 | globals::localPlayerIsActivityPlaying = handleIsLocalPlayerActivityPlaying(playerData); 82 | globals::revolverState = handlActiveWeaponState(playerData, "weapon_revolver"); 83 | globals::cz75aState = handlActiveWeaponState(playerData, "weapon_cz75a"); 84 | } 85 | 86 | bool GSIServer::handleSniperCrosshairState(const nlohmann::json& data) { 87 | if (!data.contains("player") || !data["player"].contains("weapons")) { 88 | return false; 89 | } 90 | return hasActiveWeaponType(data["player"], "SniperRifle"); 91 | } 92 | 93 | bool GSIServer::handleBombState(const nlohmann::json& data) { 94 | return data.contains("round") && 95 | data["round"].contains("bomb") && 96 | data["round"]["bomb"].get() == "planted"; 97 | } 98 | 99 | bool GSIServer::handleDefuseKitState(const nlohmann::json& data) { 100 | return data.contains("player") && 101 | data["player"].contains("state") && 102 | data["player"]["state"].contains("defusekit"); 103 | } 104 | 105 | bool GSIServer::handleRoundStartState(const nlohmann::json& data) { 106 | return data.contains("round") && 107 | data["round"].contains("phase") && 108 | data["round"]["phase"].get() == "live"; 109 | } 110 | 111 | int GSIServer::handleLocalPlayerKills(const std::optional& playerData) { 112 | if (!playerData || !playerData->contains("match_stats") || !(*playerData)["match_stats"].contains("kills")) { 113 | return 0; 114 | } 115 | return (*playerData)["match_stats"]["kills"].get(); 116 | } 117 | 118 | std::string GSIServer::handleSteamID(const nlohmann::json& data) { 119 | if (data.contains("provider") && data["provider"].contains("steamid")) { 120 | return data["provider"]["steamid"].get(); 121 | } 122 | return ""; 123 | } 124 | 125 | bool GSIServer::handleKnifeState(const std::optional& playerData) { 126 | return playerData && hasActiveWeaponType(*playerData, "Knife"); 127 | } 128 | 129 | bool GSIServer::handlePistolState(const std::optional& playerData) { 130 | return playerData && hasActiveWeaponType(*playerData, "Pistol"); 131 | } 132 | 133 | bool GSIServer::handleIsBombInWeapons(const std::optional& playerData) { 134 | return playerData && hasActiveWeaponType(*playerData, "C4"); 135 | } 136 | 137 | bool GSIServer::handleIsLocalPlayerActivityPlaying(const std::optional& playerData) { 138 | return playerData && playerData->contains("activity") && (*playerData)["activity"].get() == "playing"; 139 | } 140 | 141 | bool GSIServer::handlActiveWeaponState(const std::optional& playerData, const std::string weaponName) { 142 | if (!playerData && !playerData->contains("weapons") || !(*playerData)["weapons"].is_object()) { 143 | return false; 144 | } 145 | 146 | for (const auto& [_, weapon] : (*playerData)["weapons"].items()) { 147 | if (weapon.contains("name") && weapon.contains("state")) { 148 | std::string name = weapon["name"].get(); 149 | std::string state = weapon["state"].get(); 150 | if (name == weaponName && (state == "active")) { 151 | return true; 152 | } 153 | } 154 | } 155 | 156 | return false; 157 | } -------------------------------------------------------------------------------- /ErScripts/GSIServer.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include 4 | #include 5 | #include "ErScripts.h" 6 | #include 7 | #include 8 | 9 | class GSIServer { 10 | private: 11 | const int port = 23561; // Server port 12 | httplib::Server server; // HTTP server instance 13 | 14 | // Helper function to check if player is local player 15 | std::optional getLocalPlayerData(const nlohmann::json& data); 16 | 17 | // Helper to check weapon states 18 | bool hasActiveWeaponType(const nlohmann::json& playerData, const std::string& weaponType); 19 | 20 | void gsiServer(); // Runs the server 21 | void handleJsonPayload(const nlohmann::json& data); // Processes JSON data 22 | bool handleSniperCrosshairState(const nlohmann::json& data); // Checks for sniper rifle 23 | bool handleBombState(const nlohmann::json& data); // Checks if bomb is planted 24 | bool handleDefuseKitState(const nlohmann::json& data); // Checks for defuse kit 25 | std::string handleSteamID(const nlohmann::json& data); // Gets SteamID 26 | bool handleKnifeState(const std::optional& playerData); // Checks for knife 27 | bool handlePistolState(const std::optional& playerData); // Checks for pistol 28 | bool handleRoundStartState(const nlohmann::json& data); // Checks if round is live 29 | bool handleIsBombInWeapons(const std::optional& playerData); // Checks for bomb in inventory 30 | int handleLocalPlayerKills(const std::optional& playerData); // Gets local player kills 31 | bool handleIsLocalPlayerActivityPlaying(const std::optional& playerData); // Checks if player activity is "playing" 32 | bool handlActiveWeaponState(const std::optional& playerData, const std::string weaponName); // Checks for active weapon 33 | 34 | public: 35 | void run(); // Starts server in a thread 36 | void stop(); // Stops server 37 | }; -------------------------------------------------------------------------------- /ErScripts/Globals.cpp: -------------------------------------------------------------------------------- 1 | #include "Globals.h" 2 | 3 | namespace globals { 4 | int width = GetSystemMetrics(0), height = GetSystemMetrics(1), posX = 0, posY = 0; 5 | bool finish = false; 6 | bool menuState = true; 7 | 8 | bool sniperCrosshairState = false, isScope = false; 9 | bool bombState = false; 10 | bool defusekitState = false; 11 | double bombTime = 0.0l; 12 | bool knifeState = false; 13 | bool pistolState = false; 14 | bool revolverState = false, cz75aState = false; 15 | bool roundStartState = false; 16 | bool isBombInWeapons = false; 17 | int localPlayerKills = 0, localPlayerSlotNumber = 0; 18 | bool localPlayerIsActivityPlaying = false; 19 | std::string steamid = "", nickname = ""; 20 | 21 | std::optional config; 22 | 23 | int cs2_ping = 0; 24 | } -------------------------------------------------------------------------------- /ErScripts/Globals.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include "SteamTools.h" 4 | #include 5 | 6 | namespace globals { 7 | extern int width, height, posX, posY; 8 | extern bool finish; 9 | extern bool menuState; 10 | 11 | extern bool sniperCrosshairState, isScope; 12 | extern bool bombState; 13 | extern bool defusekitState; 14 | extern double bombTime; 15 | extern bool knifeState; 16 | extern bool pistolState; 17 | extern bool revolverState, cz75aState; 18 | extern bool roundStartState; 19 | extern bool isBombInWeapons; 20 | extern int localPlayerKills, localPlayerSlotNumber; 21 | extern bool localPlayerIsActivityPlaying; 22 | extern std::string steamid, nickname; 23 | 24 | extern std::optional config; 25 | 26 | extern int cs2_ping; 27 | } -------------------------------------------------------------------------------- /ErScripts/GradientManager.cpp: -------------------------------------------------------------------------------- 1 | #include "GradientManager.h" 2 | #include 3 | #include 4 | 5 | GradientManager::GradientManager() { 6 | generateGradient(); 7 | std::thread(&GradientManager::runGradientLoop, this).detach(); 8 | } 9 | 10 | RGBColor GradientManager::hsvToRgb(float h, float s, float v) { 11 | float r, g, b; 12 | int i = static_cast(h * 6); 13 | float f = h * 6 - i; 14 | float p = v * (1 - s); 15 | float q = v * (1 - f * s); 16 | float t = v * (1 - (1 - f) * s); 17 | 18 | switch (i % 6) { 19 | case 0: r = v; g = t; b = p; break; 20 | case 1: r = q; g = v; b = p; break; 21 | case 2: r = p; g = v; b = t; break; 22 | case 3: r = p; g = q; b = v; break; 23 | case 4: r = t; g = p; b = v; break; 24 | case 5: r = v; g = p; b = q; break; 25 | default: r = v; g = t; b = p; break; 26 | } 27 | 28 | return { static_cast(r * 255), static_cast(g * 255), static_cast(b * 255) }; 29 | } 30 | 31 | void GradientManager::generateGradient() { 32 | colors.clear(); 33 | float hueRange = config.endHue - config.startHue; 34 | for (int i = 0; i < config.numSteps; ++i) { 35 | float t = static_cast(i) / (config.numSteps - 1); // Normalized step 36 | float h = config.startHue + (hueRange * t); // Interpolate hue 37 | colors.push_back(hsvToRgb(h, config.saturation, config.value)); 38 | } 39 | } 40 | 41 | void GradientManager::runGradientLoop() { 42 | while (!globals::finish) { 43 | currentIndex = (currentIndex + 1) % colors.size(); 44 | std::this_thread::sleep_for(std::chrono::milliseconds(config.delayMs)); 45 | } 46 | } 47 | 48 | RGBColor GradientManager::getCurrentColor() const { 49 | return colors[currentIndex]; 50 | } 51 | 52 | void GradientManager::setConfig(const GradientConfig& newConfig) { 53 | config = newConfig; 54 | generateGradient(); // Regenerate gradient with new settings 55 | } 56 | 57 | GradientManager gradient; // Definition of global instance -------------------------------------------------------------------------------- /ErScripts/GradientManager.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include "Globals.h" 4 | #include 5 | #include 6 | 7 | struct RGBColor { 8 | int r, g, b; 9 | }; 10 | 11 | struct GradientConfig { 12 | int numSteps = 500; // Number of steps in the gradient (smoothness) 13 | int delayMs = 10; // Delay between color changes in milliseconds 14 | float startHue = 0.0f; // Start hue (0.0 to 1.0) 15 | float endHue = 1.0f; // End hue (0.0 to 1.0) 16 | float saturation = 1.0f; // Saturation (0.0 to 1.0) 17 | float value = 1.0f; // Value/Brightness (0.0 to 1.0) 18 | }; 19 | 20 | class GradientManager { 21 | public: 22 | GradientManager(); 23 | 24 | RGBColor getCurrentColor() const; 25 | void setConfig(const GradientConfig& newConfig); 26 | 27 | private: 28 | GradientConfig config; 29 | std::vector colors; 30 | size_t currentIndex = 0; 31 | 32 | RGBColor hsvToRgb(float h, float s, float v); 33 | void generateGradient(); 34 | void runGradientLoop(); 35 | }; 36 | 37 | extern GradientManager gradient; // Global instance -------------------------------------------------------------------------------- /ErScripts/KillSay.cpp: -------------------------------------------------------------------------------- 1 | #include "ErScripts.h" 2 | 3 | void ErScripts::KillSay() { 4 | std::thread([this]() { 5 | int oldKills = globals::localPlayerKills; 6 | while (!globals::finish) { 7 | if (cfg->killSayState) { 8 | if (globals::localPlayerKills != oldKills) { 9 | if (globals::localPlayerKills > oldKills) 10 | CommandsSender(8, std::format("say {}", cfg->killSayText)); 11 | oldKills = globals::localPlayerKills; 12 | } 13 | } 14 | 15 | std::this_thread::sleep_for(std::chrono::microseconds(15625)); 16 | } 17 | }).detach(); 18 | } -------------------------------------------------------------------------------- /ErScripts/KillSound.cpp: -------------------------------------------------------------------------------- 1 | #include "ErScripts.h" 2 | #include "SimpleSound.h" 3 | #include "SoundsShellcodes.h" 4 | 5 | void ErScripts::KillSound() { 6 | std::thread([this]() { 7 | SimpleSound sound(hitSound, hitSoundLen); 8 | sound.secondBuffer(cfg->killSoundFileName); 9 | 10 | std::string lastCustomFile = cfg->killSoundFileName; 11 | 12 | int oldKills = globals::localPlayerKills; 13 | 14 | while (!globals::finish) { 15 | if (cfg->killSoundState) { 16 | if (cfg->killSoundFileName != lastCustomFile) { 17 | sound.secondBuffer(cfg->killSoundFileName); 18 | 19 | lastCustomFile = cfg->killSoundFileName; 20 | } 21 | 22 | if (globals::localPlayerKills != oldKills) { 23 | if (globals::localPlayerKills > oldKills && globals::localPlayerKills) { 24 | sound.setVolume(cfg->killSoundVolume); 25 | sound.play(); 26 | } 27 | oldKills = globals::localPlayerKills; 28 | } 29 | } 30 | 31 | std::this_thread::sleep_for(std::chrono::microseconds(15625)); 32 | } 33 | }).detach(); 34 | } -------------------------------------------------------------------------------- /ErScripts/KnifeSwitch.cpp: -------------------------------------------------------------------------------- 1 | #include "ErScripts.h" 2 | 3 | void ErScripts::KnifeSwitch() { 4 | std::thread([this]() { 5 | bool oldKnifeState = false; 6 | while (!globals::finish) { 7 | if (cfg->knifeSwitchState) { 8 | if (ErScripts::GetWindowState() && ErScripts::GetCursorState()) { 9 | if (globals::knifeState != oldKnifeState) { 10 | CommandsSender(4, "switchhands"); 11 | oldKnifeState = globals::knifeState; 12 | } 13 | } 14 | } 15 | 16 | std::this_thread::sleep_for(std::chrono::microseconds(15625)); 17 | } 18 | }).detach(); 19 | } -------------------------------------------------------------------------------- /ErScripts/Logger.cpp: -------------------------------------------------------------------------------- 1 | #include "Logger.h" 2 | 3 | #define COLOR_RED "\033[31m" 4 | #define COLOR_GREEN "\033[32m" 5 | #define COLOR_WHITE "\033[37m" 6 | #define COLOR_RESET "\033[0m" 7 | 8 | namespace { 9 | std::string to_utf8(std::wstring_view wstr) { 10 | if (wstr.empty()) return {}; 11 | int size_needed = WideCharToMultiByte(CP_UTF8, 0, wstr.data(), (int)wstr.size(), nullptr, 0, nullptr, nullptr); 12 | std::string result(size_needed, 0); 13 | WideCharToMultiByte(CP_UTF8, 0, wstr.data(), (int)wstr.size(), result.data(), size_needed, nullptr, nullptr); 14 | return result; 15 | } 16 | } 17 | 18 | // Narrow string implementations 19 | void Logger::logWarning(std::string_view message) { 20 | std::cerr << std::format("{}[!] {}{}\n", COLOR_RED, message, COLOR_RESET); 21 | } 22 | 23 | void Logger::logInfo(std::string_view message) { 24 | std::cout << std::format("{}[*] {}{}\n", COLOR_WHITE, message, COLOR_RESET); 25 | } 26 | 27 | void Logger::logSuccess(std::string_view message) { 28 | std::cout << std::format("{}[+] {}{}\n", COLOR_GREEN, message, COLOR_RESET); 29 | } 30 | 31 | // Wide string implementations (convert to UTF-8 and call narrow overloads) 32 | void Logger::logWarning(std::wstring_view message) { 33 | logWarning(to_utf8(message)); 34 | } 35 | 36 | void Logger::logInfo(std::wstring_view message) { 37 | logInfo(to_utf8(message)); 38 | } 39 | 40 | void Logger::logSuccess(std::wstring_view message) { 41 | logSuccess(to_utf8(message)); 42 | } 43 | 44 | void Logger::EnableANSIColors() { 45 | HANDLE hOut = GetStdHandle(STD_OUTPUT_HANDLE); 46 | if (hOut == INVALID_HANDLE_VALUE) return; 47 | 48 | DWORD dwMode = 0; 49 | if (!GetConsoleMode(hOut, &dwMode)) return; 50 | 51 | dwMode |= ENABLE_VIRTUAL_TERMINAL_PROCESSING; 52 | SetConsoleMode(hOut, dwMode); 53 | } -------------------------------------------------------------------------------- /ErScripts/Logger.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include 4 | #include 5 | #include 6 | #include 7 | 8 | class Logger { 9 | public: 10 | // Enable ANSI color support 11 | static void EnableANSIColors(); 12 | 13 | // Overloads for narrow strings (std::string) 14 | static void logWarning(std::string_view message); 15 | static void logInfo(std::string_view message); 16 | static void logSuccess(std::string_view message); 17 | 18 | // Overloads for wide strings (std::wstring) 19 | static void logWarning(std::wstring_view message); 20 | static void logInfo(std::wstring_view message); 21 | static void logSuccess(std::wstring_view message); 22 | }; -------------------------------------------------------------------------------- /ErScripts/Overlay.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #pragma comment(lib,"d3d11.lib") 4 | #pragma comment(lib,"dwmapi.lib") 5 | 6 | #include "imgui/imgui.h" 7 | #include "imgui/imgui_internal.h" // for ImGui::GetKeyData() 8 | #include "imgui/imgui_impl_win32.h" 9 | #include "imgui/imgui_impl_dx11.h" 10 | 11 | #include "ErScripts.h" 12 | #include "ImagesShellCodes.h" 13 | 14 | #include 15 | #include 16 | #include 17 | #include 18 | #include 19 | #include 20 | #include 21 | #include 22 | 23 | class Overlay { 24 | public: 25 | void run(); 26 | private: 27 | HWND window_handle = nullptr; 28 | ID3D11Device* g_pd3dDevice = nullptr; 29 | ID3D11DeviceContext* g_pd3dDeviceContext = nullptr; 30 | IDXGISwapChain* g_pSwapChain = nullptr; 31 | bool g_SwapChainOccluded = false; 32 | ID3D11RenderTargetView* g_mainRenderTargetView = nullptr; 33 | 34 | ImDrawList* draw_list = nullptr; 35 | ImFont* menu_font = new ImFont(); 36 | ImFont* arial_font = new ImFont(); 37 | ImFont* bold_font = new ImFont(); 38 | //ImFont* weapon_font = new ImFont(); 39 | 40 | ID3D11ShaderResourceView* bombTexture = nullptr; 41 | ID3D11ShaderResourceView* settingsTexture = nullptr; 42 | ID3D11ShaderResourceView* reloadTexture = nullptr; 43 | 44 | bool isFinish = true; 45 | 46 | template 47 | inline VOID SafeRelease(T*& p) noexcept; 48 | 49 | bool CreateDeviceD3D() noexcept; 50 | void CleanupDeviceD3D() noexcept; 51 | void CreateRenderTarget() noexcept; 52 | void CleanupRenderTarget() noexcept; 53 | 54 | void Handler() noexcept; 55 | void Render() noexcept; 56 | void Menu() noexcept; 57 | 58 | void AutoAcceptMenu() noexcept; 59 | void TriggerMenu() noexcept; 60 | void BombTimerMenu() noexcept; 61 | void SniperCrosshairMenu() noexcept; 62 | void RecoilCrosshairMenu() noexcept; 63 | void RGBCrosshairMenu() noexcept; 64 | void KeystrokesMenu() noexcept; 65 | void KnifeSwitchMenu() noexcept; 66 | void AutoPistolMenu() noexcept; 67 | void AntiAfkMenu() noexcept; 68 | void CustomBindsMenu() noexcept; 69 | void KillSayMenu() noexcept; 70 | void KillSoundMenu() noexcept; 71 | void RoundStartAlertMenu() noexcept; 72 | void AutoStopMenu() noexcept; 73 | void ChatSpammerMenu() noexcept; 74 | void AngleBindMenu() noexcept; 75 | void GradientManagerMenu() noexcept; 76 | void WatermarkMenu() noexcept; 77 | void FPSLimitMenu() noexcept; 78 | void CaptureBypassMenu() noexcept; 79 | 80 | //void RenderEsp() noexcept; 81 | 82 | FLOAT RenderText(ImFont* font, const std::string& text, const ImVec2& position, const float size, const ImColor& color, const bool centerX, const bool centerY, const bool outline, const bool background) noexcept; 83 | void RenderCrosshair(const std::optional& ch); 84 | void CircularTimer(const ImVec2& pos, const float min, const float max, const float& time, const int points_count, const float radius, const float thickness, const bool top, const ImColor& color); 85 | ID3D11ShaderResourceView* LoadTextureFromPNGShellcode(unsigned char* icon_png, unsigned int icon_png_size); 86 | bool ImageButton(const char* str_id, ImTextureID imageTexture, const ImVec2& size); 87 | 88 | void OverlayLoop() noexcept; 89 | }; -------------------------------------------------------------------------------- /ErScripts/OverlayHelper.cpp: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/emp0ry/cs2-ErScripts/78ed5dcc81561cc25fc577c8348559754272bcd6/ErScripts/OverlayHelper.cpp -------------------------------------------------------------------------------- /ErScripts/PixelTrigger.cpp: -------------------------------------------------------------------------------- 1 | #include "ErScripts.h" 2 | 3 | void ErScripts::PixelTrigger() { 4 | std::thread([this]() { 5 | HDC hScreenDC = GetDC(nullptr); 6 | if (!hScreenDC) return; 7 | 8 | // Initial screen dimensions 9 | int width = globals::width; 10 | int height = globals::height; 11 | 12 | while (!globals::finish) { 13 | if (!cfg->triggerState) { 14 | Sleep(1); // Minimal sleep when inactive 15 | continue; 16 | } 17 | 18 | // Update dimensions if changed 19 | if (width != globals::width || height != globals::height) { 20 | width = globals::width; 21 | height = globals::height; 22 | } 23 | 24 | // First coordinate 25 | int targetX1 = (width / 2) + globals::posX + cfg->triggerDisplacement[0]; 26 | int targetY1 = (height / 2) + globals::posY + cfg->triggerDisplacement[1]; 27 | 28 | // Second coordinate (inverted displacements) 29 | int targetX2 = (width / 2) + globals::posX - cfg->triggerDisplacement[0]; 30 | int targetY2 = (height / 2) + globals::posY - cfg->triggerDisplacement[1]; 31 | 32 | if (cfg->triggerBind) { 33 | if (GetAsyncKeyState(cfg->triggerBind) & 0x8000) { 34 | // Get initial colors for both points 35 | COLORREF baseColor1 = GetPixel(hScreenDC, targetX1, targetY1); 36 | BYTE baseR1 = GetRValue(baseColor1); 37 | BYTE baseG1 = GetGValue(baseColor1); 38 | BYTE baseB1 = GetBValue(baseColor1); 39 | 40 | COLORREF baseColor2 = GetPixel(hScreenDC, targetX2, targetY2); 41 | BYTE baseR2 = GetRValue(baseColor2); 42 | BYTE baseG2 = GetGValue(baseColor2); 43 | BYTE baseB2 = GetBValue(baseColor2); 44 | 45 | while (GetAsyncKeyState(cfg->triggerBind) & 0x8000) { 46 | // Get current colors for both points 47 | COLORREF currentColor1 = GetPixel(hScreenDC, targetX1, targetY1); 48 | BYTE currentR1 = GetRValue(currentColor1); 49 | BYTE currentG1 = GetGValue(currentColor1); 50 | BYTE currentB1 = GetBValue(currentColor1); 51 | 52 | COLORREF currentColor2 = GetPixel(hScreenDC, targetX2, targetY2); 53 | BYTE currentR2 = GetRValue(currentColor2); 54 | BYTE currentG2 = GetGValue(currentColor2); 55 | BYTE currentB2 = GetBValue(currentColor2); 56 | 57 | // Check color difference for either point 58 | bool colorChanged = 59 | (abs(baseR1 - currentR1) > cfg->triggerThreshold || 60 | abs(baseG1 - currentG1) > cfg->triggerThreshold || 61 | abs(baseB1 - currentB1) > cfg->triggerThreshold) || 62 | (abs(baseR2 - currentR2) > cfg->triggerThreshold || 63 | abs(baseG2 - currentG2) > cfg->triggerThreshold || 64 | abs(baseB2 - currentB2) > cfg->triggerThreshold); 65 | 66 | if (colorChanged) { 67 | if (cfg->triggerDelayMs) std::this_thread::sleep_for(std::chrono::milliseconds(cfg->triggerDelayMs)); 68 | mouse_event(MOUSEEVENTF_LEFTDOWN, 0, 0, 0, 0); 69 | std::this_thread::sleep_for(std::chrono::milliseconds((rand() % 32) + 16)); 70 | mouse_event(MOUSEEVENTF_LEFTUP, 0, 0, 0, 0); 71 | } 72 | } 73 | } 74 | } 75 | 76 | std::this_thread::sleep_for(std::chrono::microseconds(15625)); 77 | } 78 | 79 | ReleaseDC(nullptr, hScreenDC); 80 | }).detach(); 81 | } -------------------------------------------------------------------------------- /ErScripts/RGBCrosshair.cpp: -------------------------------------------------------------------------------- 1 | #include "ErScripts.h" 2 | 3 | void ErScripts::RGBCrosshair() { 4 | std::thread([this]() { 5 | while (!globals::finish) { 6 | if (cfg->rgbCrosshairState) { 7 | RGBColor color = gradient.getCurrentColor(); 8 | CommandsSender(2, std::format("cl_crosshaircolor 5; cl_crosshaircolor_r {}; cl_crosshaircolor_g {}; cl_crosshaircolor_b {}", color.r, color.g, color.b)); 9 | } 10 | std::this_thread::sleep_for(std::chrono::microseconds(15625)); 11 | } 12 | }).detach(); 13 | } -------------------------------------------------------------------------------- /ErScripts/Rebuild.cpp: -------------------------------------------------------------------------------- 1 | #include "Rebuild.h" 2 | #include "Logger.h" 3 | #include 4 | #include 5 | #include 6 | #include 7 | #include 8 | #include 9 | 10 | #pragma comment(lib, "shlwapi.lib") 11 | 12 | std::string Rebuild::random_suffix_; // Initialize static member 13 | 14 | Rebuild::Rebuild() { 15 | // Generate suffix once per run 16 | if (random_suffix_.empty()) { 17 | random_suffix_ = generateRandomString(8); 18 | } 19 | } 20 | 21 | std::string Rebuild::generateRandomString(size_t length) { 22 | static const char charset[] = "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ"; 23 | std::random_device rd; 24 | std::mt19937 gen(rd()); 25 | std::uniform_int_distribution<> dis(0, sizeof(charset) - 2); 26 | std::string result; 27 | result.reserve(length); 28 | for (size_t i = 0; i < length; ++i) { 29 | result += charset[dis(gen)]; 30 | } 31 | return result; 32 | } 33 | 34 | bool Rebuild::isValidExecutable(const std::string& filePath) { 35 | std::ifstream file(filePath, std::ios::binary); 36 | if (!file) { 37 | Logger::logWarning(std::format("Cannot open file for validation: {}", filePath)); 38 | return false; 39 | } 40 | 41 | char header[2]; 42 | file.read(header, 2); 43 | file.close(); 44 | bool isValid = (header[0] == 'M' && header[1] == 'Z'); 45 | if (!isValid) { 46 | Logger::logWarning(std::format("File is not a valid PE executable: {}", filePath)); 47 | } 48 | return isValid; 49 | } 50 | 51 | std::string Rebuild::getCurrentExeName() { 52 | char exePath[MAX_PATH]; 53 | DWORD pathLength = GetModuleFileNameA(NULL, exePath, MAX_PATH); 54 | if (pathLength == 0) { 55 | Logger::logWarning(std::format("Failed to get module filename: {}", GetLastError())); 56 | return "ErScripts.exe"; 57 | } 58 | 59 | std::string fullPath(exePath, pathLength); 60 | size_t lastSlash = fullPath.find_last_of("\\/"); 61 | if (lastSlash != std::string::npos) { 62 | return fullPath.substr(lastSlash + 1); 63 | } 64 | return fullPath; 65 | } 66 | 67 | std::string Rebuild::getCurrentExePath() { 68 | char exePath[MAX_PATH]; 69 | DWORD pathLength = GetModuleFileNameA(NULL, exePath, MAX_PATH); 70 | if (pathLength == 0) { 71 | Logger::logWarning(std::format("Failed to get module path: {}", GetLastError())); 72 | return "ErScripts.exe"; 73 | } 74 | return std::string(exePath, pathLength); 75 | } 76 | 77 | bool Rebuild::unpackIfNeeded() { 78 | std::string current_exe = getCurrentExePath(); 79 | 80 | // Read binary 81 | std::ifstream in_file(current_exe, std::ios::binary); 82 | if (!in_file) { 83 | Logger::logWarning(std::format("Failed to read binary for unpacking: {}", current_exe)); 84 | return false; 85 | } 86 | std::vector binary_data((std::istreambuf_iterator(in_file)), std::istreambuf_iterator()); 87 | in_file.close(); 88 | 89 | // Check for marker (2 bytes: random byte + 0xFF) 90 | if (binary_data.size() < 2 || binary_data[binary_data.size() - 1] != 0xFF) { 91 | return true; // Not packed 92 | } 93 | 94 | // Remove random byte and marker 95 | binary_data.pop_back(); // Marker (0xFF) 96 | binary_data.pop_back(); // Random byte 97 | 98 | // Write unpacked binary 99 | for (int retry = 0; retry < 3; ++retry) { 100 | std::ofstream out_file(current_exe, std::ios::binary); 101 | if (out_file) { 102 | out_file.write(binary_data.data(), binary_data.size()); 103 | out_file.close(); 104 | return true; 105 | } 106 | Logger::logWarning(std::format("Retry {}: Failed to write unpacked binary: {}", retry + 1, GetLastError())); 107 | std::this_thread::sleep_for(std::chrono::milliseconds(100)); 108 | } 109 | Logger::logWarning("Failed to unpack binary after retries"); 110 | return false; 111 | } 112 | 113 | bool Rebuild::createPolymorphicBinary(std::string& output_path) { 114 | // Get TEMP directory 115 | char temp_dir[MAX_PATH]; 116 | if (GetTempPathA(MAX_PATH, temp_dir) == 0) { 117 | Logger::logWarning(std::format("Failed to get TEMP path: {}", GetLastError())); 118 | return false; 119 | } 120 | 121 | // Use stored suffix 122 | output_path = std::string(temp_dir) + "ErScripts_temp_" + random_suffix_ + ".exe"; 123 | 124 | // Check disk space (~10MB) 125 | ULARGE_INTEGER freeBytes; 126 | if (GetDiskFreeSpaceExA(temp_dir, &freeBytes, NULL, NULL)) { 127 | if (freeBytes.QuadPart < 10 * 1024 * 1024) { 128 | Logger::logWarning("Insufficient disk space for rebuild"); 129 | return false; 130 | } 131 | } 132 | 133 | // Read current binary 134 | std::string current_exe = getCurrentExePath(); 135 | std::ifstream in_file(current_exe, std::ios::binary); 136 | if (!in_file) { 137 | Logger::logWarning("Failed to read current binary for rebuild"); 138 | return false; 139 | } 140 | std::vector binary_data((std::istreambuf_iterator(in_file)), std::istreambuf_iterator()); 141 | in_file.close(); 142 | 143 | // Append one random byte 144 | std::random_device rd; 145 | std::mt19937 gen(rd()); 146 | std::uniform_int_distribution byte_dis(0, 255); 147 | binary_data.push_back(static_cast(byte_dis(gen))); 148 | 149 | // Append marker (0xFF) 150 | binary_data.push_back(static_cast(0xFF)); 151 | 152 | // Write temp binary 153 | for (int retry = 0; retry < 3; ++retry) { 154 | std::ofstream out_file(output_path, std::ios::binary); 155 | if (out_file) { 156 | out_file.write(binary_data.data(), binary_data.size()); 157 | out_file.close(); 158 | return true; 159 | } 160 | Logger::logWarning(std::format("Retry {}: Failed to write temp binary: {}", retry + 1, GetLastError())); 161 | std::this_thread::sleep_for(std::chrono::milliseconds(100)); 162 | } 163 | Logger::logWarning("Failed to write temp binary after retries"); 164 | return false; 165 | } 166 | 167 | bool Rebuild::replaceWithNewBinary(const std::string& newFilePath, const std::string& bat_path) { 168 | if (!isValidExecutable(newFilePath)) { 169 | Logger::logWarning("New binary is not a valid executable"); 170 | return false; 171 | } 172 | 173 | std::string current_exe = getCurrentExePath(); 174 | 175 | // Write batch file 176 | for (int retry = 0; retry < 3; ++retry) { 177 | std::ofstream bat_file(bat_path); 178 | if (bat_file) { 179 | bat_file << "@echo off\n"; 180 | bat_file << "timeout /t 1 >nul\n"; 181 | bat_file << "move /Y \"" << newFilePath << "\" \"" << current_exe << "\"\n"; 182 | bat_file << "if exist \"" << current_exe << "\" (\n"; 183 | bat_file << " start \"\" \"" << current_exe << "\" --run /requestuia\n"; 184 | bat_file << " del \"%~f0\"\n"; 185 | bat_file << ")\n"; 186 | bat_file.close(); 187 | break; 188 | } 189 | Logger::logWarning(std::format("Retry {}: Failed to create batch file: {}", retry + 1, GetLastError())); 190 | std::this_thread::sleep_for(std::chrono::milliseconds(100)); 191 | } 192 | 193 | if (!std::filesystem::exists(bat_path)) { 194 | Logger::logWarning("Failed to create batch file after retries"); 195 | return false; 196 | } 197 | 198 | // Run batch file 199 | SHELLEXECUTEINFOA sei = { sizeof(sei) }; 200 | sei.fMask = SEE_MASK_NOCLOSEPROCESS | SEE_MASK_NO_CONSOLE; 201 | sei.lpFile = bat_path.c_str(); 202 | sei.lpVerb = "open"; 203 | sei.nShow = SW_HIDE; 204 | if (!ShellExecuteExA(&sei)) { 205 | Logger::logWarning(std::format("Failed to execute batch file: {}", GetLastError())); 206 | std::filesystem::remove(bat_path); 207 | return false; 208 | } 209 | 210 | return true; 211 | } 212 | 213 | void Rebuild::cleanupTempFiles() { 214 | char temp_dir[MAX_PATH]; 215 | if (GetTempPathA(MAX_PATH, temp_dir) == 0) { 216 | Logger::logWarning(std::format("Failed to get TEMP path for cleanup: {}", GetLastError())); 217 | return; 218 | } 219 | 220 | std::string temp_exe = std::string(temp_dir) + "ErScripts_temp_" + random_suffix_ + ".exe"; 221 | std::string temp_bat = std::string(temp_dir) + "rebuild_" + random_suffix_ + ".bat"; 222 | 223 | if (std::filesystem::exists(temp_exe)) { 224 | std::filesystem::remove(temp_exe); 225 | Logger::logInfo(std::format("Cleaned up temp file: {}", temp_exe)); 226 | } 227 | if (std::filesystem::exists(temp_bat)) { 228 | std::filesystem::remove(temp_bat); 229 | Logger::logInfo(std::format("Cleaned up batch file: {}", temp_bat)); 230 | } 231 | } 232 | 233 | bool Rebuild::rebuildAndRelaunch(bool should_rebuild) { 234 | if (!should_rebuild) { 235 | // Unpack for --run 236 | return unpackIfNeeded(); 237 | } 238 | 239 | // Unpack before rebuild 240 | if (!unpackIfNeeded()) { 241 | Logger::logWarning("Failed to unpack binary before rebuild"); 242 | return false; 243 | } 244 | 245 | // Create new binary 246 | std::string new_binary; 247 | if (!createPolymorphicBinary(new_binary)) { 248 | Logger::logWarning("Failed to create polymorphic binary"); 249 | return false; 250 | } 251 | 252 | // Create batch file in TEMP 253 | char temp_dir[MAX_PATH]; 254 | if (GetTempPathA(MAX_PATH, temp_dir) == 0) { 255 | Logger::logWarning(std::format("Failed to get TEMP path: {}", GetLastError())); 256 | std::filesystem::remove(new_binary); 257 | return false; 258 | } 259 | std::string bat_path = std::string(temp_dir) + "rebuild_" + random_suffix_ + ".bat"; 260 | 261 | // Replace and relaunch 262 | if (replaceWithNewBinary(new_binary, bat_path)) { 263 | Logger::logInfo("Rebuild initiated. Relaunching..."); 264 | return true; 265 | } 266 | else { 267 | Logger::logWarning("Failed to replace binary for rebuild"); 268 | std::filesystem::remove(new_binary); 269 | if (std::filesystem::exists(bat_path)) { 270 | std::filesystem::remove(bat_path); 271 | } 272 | return false; 273 | } 274 | } -------------------------------------------------------------------------------- /ErScripts/Rebuild.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | #include 3 | #include 4 | 5 | class Rebuild { 6 | public: 7 | Rebuild(); 8 | bool rebuildAndRelaunch(bool should_rebuild); 9 | static void cleanupTempFiles(); 10 | 11 | private: 12 | std::string generateRandomString(size_t length); 13 | bool createPolymorphicBinary(std::string& output_path); 14 | bool replaceWithNewBinary(const std::string& newFilePath, const std::string& bat_path); 15 | bool isValidExecutable(const std::string& filePath); 16 | std::string getCurrentExeName(); 17 | std::string getCurrentExePath(); 18 | bool unpackIfNeeded(); 19 | static std::string random_suffix_; 20 | }; -------------------------------------------------------------------------------- /ErScripts/Render.cpp: -------------------------------------------------------------------------------- 1 | #include "Overlay.h" 2 | 3 | std::string FormatElapsedTime(double timeInMillis) { 4 | int totalMillis = std::lround(timeInMillis); 5 | int seconds = totalMillis / 1000; 6 | int millis = (totalMillis % 1000) / 100; 7 | 8 | std::stringstream formattedTime; 9 | formattedTime << std::setw(2) << std::setfill('0') << seconds << "." 10 | << std::setw(1) << std::setfill('0') << millis; 11 | 12 | return formattedTime.str(); 13 | } 14 | 15 | void Overlay::Render() noexcept { 16 | RGBColor gradient_color; 17 | 18 | if (cfg->watermarkState) { 19 | bool initilizeWatermark = false; 20 | char window_title[32]; 21 | 22 | if (!initilizeWatermark) { 23 | // Get the current window title 24 | if (!GetConsoleTitleA(window_title, sizeof(window_title))) 25 | window_title[0] = '\0'; 26 | initilizeWatermark = true; 27 | } 28 | 29 | // Get FPS directly from ImGui 30 | //float fps = ImGui::GetIO().Framerate; 31 | 32 | // Get current time 33 | auto now = std::chrono::system_clock::to_time_t(std::chrono::system_clock::now()); 34 | 35 | char timeBuffer[26]; // Buffer size required by ctime_s 36 | if (ctime_s(timeBuffer, sizeof(timeBuffer), &now) != 0) { 37 | Logger::logWarning("Failed to get current time string."); 38 | } 39 | 40 | std::string timeStr(timeBuffer); 41 | timeStr = timeStr.substr(11, 8); // Extract "HH:MM:SS" 42 | 43 | // Get GMT offset (Windows-specific) 44 | TIME_ZONE_INFORMATION tzi; 45 | DWORD tzResult = GetTimeZoneInformation(&tzi); 46 | int offsetMinutes = 0; 47 | if (tzResult != TIME_ZONE_ID_INVALID) { 48 | offsetMinutes = -tzi.Bias; 49 | if (tzResult == TIME_ZONE_ID_DAYLIGHT) { 50 | offsetMinutes -= tzi.DaylightBias; 51 | } 52 | } 53 | 54 | // Convert minutes to hours and determine GMT+ or GMT- 55 | int offsetHours = offsetMinutes / 60; 56 | std::string gmtOffset = " GMT"; 57 | if (offsetHours > 0) { 58 | gmtOffset += "+" + std::to_string(offsetHours); 59 | } 60 | else if (offsetHours < 0) { 61 | gmtOffset += std::to_string(offsetHours); // Includes the negative sign 62 | } 63 | else { 64 | gmtOffset += "0"; 65 | } 66 | 67 | // Append GMT offset to time string 68 | timeStr += gmtOffset; 69 | 70 | // Format watermark text 71 | std::string watermarkText = std::format(" {} | {} | Ping {}ms | {}", window_title, globals::nickname, globals::cs2_ping, timeStr); 72 | 73 | ImGui::SetNextWindowBgAlpha(cfg->watermarkTransparency); 74 | 75 | if (!cfg->watermarkTransparency) ImGui::PushStyleVar(ImGuiStyleVar_WindowBorderSize, 0.0f); 76 | else ImGui::PushStyleVar(ImGuiStyleVar_WindowBorderSize, 1.0f); 77 | ImGui::PushStyleVar(ImGuiStyleVar_WindowRounding, 10.0f); 78 | ImGui::Begin(" Watermark", nullptr, ImGuiWindowFlags_NoCollapse | ImGuiWindowFlags_NoScrollbar | ImGuiWindowFlags_NoResize | ImGuiWindowFlags_NoTitleBar | ImGuiWindowFlags_NoMove); 79 | ImGui::PushFont(arial_font); 80 | ImGui::SetWindowFontScale(16.0f / ImGui::GetFontSize()); 81 | 82 | float watermarkSize = ImGui::CalcTextSize(watermarkText.c_str()).x + 20.0f; 83 | 84 | ImGui::SetWindowPos({ globals::width - watermarkSize - 6.0f, 5.0f }); 85 | ImGui::SetWindowSize({ watermarkSize, 25 }); 86 | 87 | // Use ImGui::Text() instead of AddText() for better UTF-8 handling 88 | gradient_color = gradient.getCurrentColor(); 89 | ImColor color = cfg->watermarkGradientState ? ImColor(gradient_color.r, gradient_color.g, gradient_color.b) : ImColor(1.f, 1.f, 1.f); 90 | ImGui::TextColored(color, watermarkText.c_str()); 91 | 92 | ImGui::SetWindowFontScale(1.0f); 93 | ImGui::PopFont(); 94 | ImGui::PopStyleVar(2); 95 | ImGui::End(); 96 | } 97 | 98 | if (ErScripts::GetCursorState()) { 99 | if (!(GetAsyncKeyState(VK_TAB) & 0x8000)) { 100 | if (cfg->sniperCrosshairState && globals::sniperCrosshairState && !globals::isScope) { 101 | RenderCrosshair(*globals::config); 102 | } 103 | else if (cfg->recoilCrosshairState && (GetAsyncKeyState(VK_LBUTTON) & 0x8000)) { 104 | RenderCrosshair(*globals::config); 105 | } 106 | } 107 | } 108 | 109 | if (cfg->bombTimerState) { 110 | if (globals::bombTime || globals::menuState) { 111 | static bool isBombTimerWindowInitilized = false; 112 | 113 | ImGui::SetNextWindowBgAlpha(cfg->bombTimerTransparency); 114 | if (!isBombTimerWindowInitilized) { 115 | ImGui::SetNextWindowPos({ cfg->bombTimerPos[0], cfg->bombTimerPos[1] }); 116 | isBombTimerWindowInitilized = true; 117 | } 118 | 119 | if (!cfg->bombTimerTransparency) ImGui::PushStyleVar(ImGuiStyleVar_WindowBorderSize, 0.0f); 120 | else ImGui::PushStyleVar(ImGuiStyleVar_WindowBorderSize, 1.0f * cfg->bombTimerScale); // Scaled border size 121 | ImGui::PushStyleVar(ImGuiStyleVar_WindowRounding, 10.0f * cfg->bombTimerScale); // Scaled rounding 122 | ImGui::Begin(" Bomb Timer", nullptr, ImGuiWindowFlags_NoCollapse | ImGuiWindowFlags_NoScrollbar | ImGuiWindowFlags_NoResize | ImGuiWindowFlags_NoTitleBar); 123 | 124 | ImGui::SetWindowSize({ 146.0f * cfg->bombTimerScale, 70.0f * cfg->bombTimerScale }); // Scaled window size 125 | 126 | ImVec2 bombTimer_pos = ImGui::GetWindowPos(); 127 | ImVec2 bombTimer_size = ImGui::GetWindowSize(); 128 | 129 | if (bombTimer_pos.x < 0) { 130 | bombTimer_pos.x = 0; 131 | } 132 | if (bombTimer_pos.y < 0) { 133 | bombTimer_pos.y = 0; 134 | } 135 | if (bombTimer_pos.x + bombTimer_size.x > globals::width) { 136 | bombTimer_pos.x = globals::width - bombTimer_size.x; 137 | } 138 | if (bombTimer_pos.y + bombTimer_size.y > globals::height) { 139 | bombTimer_pos.y = globals::height - bombTimer_size.y; 140 | } 141 | 142 | if ((bombTimer_pos.x != ImGui::GetWindowPos().x || bombTimer_pos.y != ImGui::GetWindowPos().y) && (GetAsyncKeyState(VK_LBUTTON) & 0x8000) == 0) { 143 | ImGui::SetWindowPos(" Bomb Timer", bombTimer_pos); 144 | } 145 | 146 | ImVec2 windowPos = ImGui::GetWindowPos(); 147 | ImVec2 windowSize = ImGui::GetWindowSize(); 148 | ImVec2 iconPos = ImVec2(windowPos.x + 5.0f * cfg->bombTimerScale, windowPos.y + (windowSize.y - 64.0f * cfg->bombTimerScale) / 2); // Scaled position and icon size 149 | ImVec2 c4IconSize = ImVec2(64.0f * cfg->bombTimerScale, 64.0f * cfg->bombTimerScale); // Scaled icon size 150 | 151 | gradient_color = gradient.getCurrentColor(); 152 | ImColor color = cfg->bombTimerGradientState ? ImColor(gradient_color.r, gradient_color.g, gradient_color.b) : ImColor(1.f, 1.f, 1.f); 153 | ImGui::GetWindowDrawList()->AddImage( 154 | (ImTextureID)bombTexture, 155 | iconPos, 156 | ImVec2(iconPos.x + c4IconSize.x, iconPos.y + c4IconSize.y), 157 | ImVec2(0, 0), ImVec2(1, 1), 158 | ImColor(color) 159 | ); 160 | 161 | // Position for circular timer (right side, centered vertically) 162 | ImVec2 timerCenter = ImVec2( 163 | windowPos.x + windowSize.x - 39.0f * cfg->bombTimerScale, // Scaled from right edge 164 | windowPos.y + windowSize.y / 2 // Center vertically 165 | ); 166 | 167 | // Set text color based on time remaining 168 | ImVec4 timeColor = ImVec4(1.0f, 0.3f, 0.0f, 1.0f); // Red default 169 | 170 | if (globals::bombTime) { 171 | if ((globals::defusekitState && globals::bombTime >= 5000) || 172 | (!globals::defusekitState && globals::bombTime >= 10000)) { 173 | timeColor = ImVec4(0.3f, 1.0f, 0.0f, 1.0f); // Green when safe 174 | } 175 | 176 | // Draw circular timer with scaled parameters 177 | CircularTimer(timerCenter, 0.0f, 40000.0f, globals::bombTime, 32 * cfg->bombTimerScale, 22.0f * cfg->bombTimerScale, 4.0f * cfg->bombTimerScale, true, timeColor); 178 | 179 | // Center the time text below the timer with scaled font size 180 | std::string timeText = FormatElapsedTime(globals::bombTime); 181 | RenderText(bold_font, timeText, timerCenter, 14.0f * cfg->bombTimerScale, ImColor(timeColor), true, true, false, false); 182 | } 183 | else if (globals::menuState) { 184 | CircularTimer(timerCenter, 0.0f, 40000.0f, 10000.0f, 32 * cfg->bombTimerScale, 22.0f * cfg->bombTimerScale, 4.0f * cfg->bombTimerScale, true, timeColor); 185 | 186 | RenderText(bold_font, "10.0", timerCenter, 14.0f * cfg->bombTimerScale, ImColor(timeColor), true, true, false, false); 187 | } 188 | 189 | // Store window position 190 | cfg->bombTimerPos[0] = windowPos.x; 191 | cfg->bombTimerPos[1] = windowPos.y; 192 | 193 | ImGui::PopStyleVar(2); 194 | ImGui::End(); 195 | } 196 | } 197 | 198 | if (cfg->keystrokesState) { 199 | static bool isKeystrokesWindowInitilized = false; 200 | 201 | ImGui::SetNextWindowBgAlpha(0.5f); 202 | 203 | if (!isKeystrokesWindowInitilized) { 204 | ImGui::SetNextWindowPos({ cfg->keystrokesPos[0], cfg->keystrokesPos[1] }); 205 | isKeystrokesWindowInitilized = true; 206 | } 207 | 208 | 209 | ImGui::Begin(" Keystrokes", nullptr, ImGuiWindowFlags_NoCollapse | ImGuiWindowFlags_NoScrollbar | ImGuiWindowFlags_AlwaysAutoResize | ImGuiWindowFlags_NoTitleBar | (globals::menuState ? ImGuiWindowFlags_None : ImGuiWindowFlags_NoBackground)); 210 | 211 | ImGui::PushFont(arial_font); 212 | ImGui::SetWindowFontScale(cfg->keystrokesScale * 0.5f); 213 | 214 | // Style settings for a beautiful look, scaled 215 | ImGui::PushStyleVar(ImGuiStyleVar_FrameRounding, 8.0f * cfg->keystrokesScale); // Rounded corners 216 | ImGui::PushStyleVar(ImGuiStyleVar_FramePadding, ImVec2(4.0f * cfg->keystrokesScale, 4.0f * cfg->keystrokesScale)); // Padding inside buttons 217 | ImGui::PushStyleVar(ImGuiStyleVar_ItemSpacing, ImVec2(6.0f * cfg->keystrokesScale, 6.0f * cfg->keystrokesScale)); // Spacing between elements 218 | 219 | gradient_color = gradient.getCurrentColor(); 220 | ImGui::PushStyleColor(ImGuiCol_Text, cfg->keystrokesGradientState ? ImVec4(gradient_color.r / 255.0f, gradient_color.g / 255.0f, gradient_color.b / 255.0f, 1.0f) : ImVec4(1.f, 1.f, 1.f, 1.f)); 221 | 222 | ImVec2 keystrokes_pos = ImGui::GetWindowPos(); 223 | ImVec2 keystrokes_size = ImGui::GetWindowSize(); 224 | 225 | if (keystrokes_pos.x < 0) { 226 | keystrokes_pos.x = 0; 227 | } 228 | if (keystrokes_pos.y < 0) { 229 | keystrokes_pos.y = 0; 230 | } 231 | if (keystrokes_pos.x + keystrokes_size.x > globals::width) { 232 | keystrokes_pos.x = globals::width - keystrokes_size.x; 233 | } 234 | if (keystrokes_pos.y + keystrokes_size.y > globals::height) { 235 | keystrokes_pos.y = globals::height - keystrokes_size.y; 236 | } 237 | 238 | if ((keystrokes_pos.x != ImGui::GetWindowPos().x || keystrokes_pos.y != ImGui::GetWindowPos().y) && (GetAsyncKeyState(VK_LBUTTON) & 0x8000) == 0) { 239 | ImGui::SetWindowPos(" Keystrokes", keystrokes_pos); 240 | } 241 | 242 | // Key states with smooth transitions 243 | static float wAlpha = 0.0f, aAlpha = 0.0f, sAlpha = 0.0f, dAlpha = 0.0f, lmbAlpha = 0.0f, rmbAlpha = 0.0f; 244 | bool wPressed = GetAsyncKeyState('W') & 0x8000; 245 | bool aPressed = GetAsyncKeyState('A') & 0x8000; 246 | bool sPressed = GetAsyncKeyState('S') & 0x8000; 247 | bool dPressed = GetAsyncKeyState('D') & 0x8000; 248 | bool lmbPressed = GetAsyncKeyState(VK_LBUTTON) & 0x8000; 249 | bool rmbPressed = GetAsyncKeyState(VK_RBUTTON) & 0x8000; 250 | 251 | ImVec4 releasedColor = ImVec4(cfg->keystrokesReleasedColor.r / 255.0f, cfg->keystrokesReleasedColor.g / 255.0f, cfg->keystrokesReleasedColor.b / 255.0f, cfg->keystrokesTransparency); 252 | ImVec4 pressedColor = ImVec4(cfg->keystrokesPressedColor.r / 255.0f, cfg->keystrokesPressedColor.g / 255.0f, cfg->keystrokesPressedColor.b / 255.0f, 1.0f); 253 | 254 | // Smoothly interpolate alpha values for animation 255 | wAlpha += (wPressed ? 1.0f - wAlpha : -wAlpha) * ImGui::GetIO().DeltaTime * cfg->keystrokesAnimationSpeed; 256 | aAlpha += (aPressed ? 1.0f - aAlpha : -aAlpha) * ImGui::GetIO().DeltaTime * cfg->keystrokesAnimationSpeed; 257 | sAlpha += (sPressed ? 1.0f - sAlpha : -sAlpha) * ImGui::GetIO().DeltaTime * cfg->keystrokesAnimationSpeed; 258 | dAlpha += (dPressed ? 1.0f - dAlpha : -dAlpha) * ImGui::GetIO().DeltaTime * cfg->keystrokesAnimationSpeed; 259 | lmbAlpha += (lmbPressed ? 1.0f - lmbAlpha : -lmbAlpha) * ImGui::GetIO().DeltaTime * cfg->keystrokesAnimationSpeed; 260 | rmbAlpha += (rmbPressed ? 1.0f - rmbAlpha : -rmbAlpha) * ImGui::GetIO().DeltaTime * cfg->keystrokesAnimationSpeed; 261 | 262 | // WASD Layout 263 | ImGui::BeginGroup(); 264 | ImGui::Dummy(ImVec2(0, 0)); ImGui::SameLine(36.0f * cfg->keystrokesScale); // Offset for centering 265 | ImGui::PushStyleColor(ImGuiCol_Button, ImLerp(releasedColor, pressedColor, wAlpha)); 266 | ImGui::Button("W", ImVec2(30.0f * cfg->keystrokesScale, 30.0f * cfg->keystrokesScale)); 267 | ImGui::PopStyleColor(); 268 | 269 | ImGui::PushStyleColor(ImGuiCol_Button, ImLerp(releasedColor, pressedColor, aAlpha)); 270 | ImGui::Button("A", ImVec2(30.0f * cfg->keystrokesScale, 30.0f * cfg->keystrokesScale)); ImGui::SameLine(); 271 | ImGui::PopStyleColor(); 272 | 273 | ImGui::PushStyleColor(ImGuiCol_Button, ImLerp(releasedColor, pressedColor, sAlpha)); 274 | ImGui::Button("S", ImVec2(30.0f * cfg->keystrokesScale, 30.0f * cfg->keystrokesScale)); ImGui::SameLine(); 275 | ImGui::PopStyleColor(); 276 | 277 | ImGui::PushStyleColor(ImGuiCol_Button, ImLerp(releasedColor, pressedColor, dAlpha)); 278 | ImGui::Button("D", ImVec2(30.0f * cfg->keystrokesScale, 30.0f * cfg->keystrokesScale)); 279 | ImGui::PopStyleColor(); 280 | ImGui::EndGroup(); 281 | 282 | // Mouse Buttons 283 | ImGui::BeginGroup(); 284 | ImGui::PushStyleColor(ImGuiCol_Button, ImLerp(releasedColor, pressedColor, lmbAlpha)); 285 | ImGui::Button("LMB", ImVec2(48.0f * cfg->keystrokesScale, 30.0f * cfg->keystrokesScale)); ImGui::SameLine(); 286 | ImGui::PopStyleColor(); 287 | 288 | ImGui::PushStyleColor(ImGuiCol_Button, ImLerp(releasedColor, pressedColor, rmbAlpha)); 289 | ImGui::Button("RMB", ImVec2(48.0f * cfg->keystrokesScale, 30.0f * cfg->keystrokesScale)); 290 | ImGui::PopStyleColor(); 291 | ImGui::EndGroup(); 292 | 293 | ImVec2 pos = ImGui::GetWindowPos(); 294 | cfg->keystrokesPos[0] = pos.x; 295 | cfg->keystrokesPos[1] = pos.y; 296 | 297 | ImGui::SetWindowFontScale(1.f); // Restore the original font size 298 | ImGui::PopFont(); // Restore the original font 299 | ImGui::PopStyleVar(3); // Restore style vars 300 | ImGui::PopStyleColor(1); // Restore style colors 301 | ImGui::End(); 302 | }} 303 | -------------------------------------------------------------------------------- /ErScripts/RoundStartAlert.cpp: -------------------------------------------------------------------------------- 1 | #include "ErScripts.h" 2 | #include "SimpleSound.h" 3 | #include "SoundsShellcodes.h" 4 | 5 | void ErScripts::RoundStartAlert() { 6 | std::thread([this]() { 7 | SimpleSound sound(alertSound, alertSoundLen); 8 | sound.secondBuffer(cfg->roundStartAlertFileName); 9 | 10 | std::string lastCustomFile = cfg->roundStartAlertFileName; 11 | 12 | bool oldRoundStartState = globals::roundStartState; 13 | 14 | while (!globals::finish) { 15 | if (cfg->roundStartAlertState) { 16 | if (cfg->roundStartAlertFileName != lastCustomFile) { 17 | sound.secondBuffer(cfg->roundStartAlertFileName); 18 | 19 | lastCustomFile = cfg->roundStartAlertFileName; 20 | } 21 | 22 | if (globals::roundStartState != oldRoundStartState) { 23 | oldRoundStartState = globals::roundStartState; 24 | 25 | if (globals::roundStartState) { 26 | if (!ErScripts::GetWindowState()) { 27 | sound.setVolume(cfg->roundStartAlertVolume); 28 | sound.play(); 29 | } 30 | } 31 | } 32 | } 33 | 34 | std::this_thread::sleep_for(std::chrono::microseconds(15625)); 35 | } 36 | }).detach(); 37 | } -------------------------------------------------------------------------------- /ErScripts/SimpleSound.cpp: -------------------------------------------------------------------------------- 1 | #include "SimpleSound.h" 2 | #include "Logger.h" 3 | #include 4 | #include 5 | #include 6 | #include 7 | 8 | #pragma comment(lib, "winmm.lib") 9 | 10 | namespace fs = std::filesystem; 11 | 12 | SimpleSound::SimpleSound(const unsigned char* data, unsigned int size) { 13 | if (!loadFromBuffer(tempDefaultFile_, data, size)) { 14 | Logger::logWarning("Failed to load sound from buffer (size: " + std::to_string(size) + ")"); 15 | throw std::runtime_error("Failed to load sound from buffer"); 16 | } 17 | } 18 | 19 | SimpleSound::~SimpleSound() { 20 | if (!tempDefaultFile_.empty()) { 21 | removeTempFile(tempDefaultFile_); 22 | } 23 | } 24 | 25 | void SimpleSound::setVolume(int volume) noexcept { 26 | if (volume < 0) volume = 0; 27 | if (volume > 100) volume = 100; 28 | volume_ = volume / 100.0f; 29 | DWORD vol = static_cast(volume_ * 0xFFFF); 30 | waveOutSetVolume(NULL, (vol << 16) | vol); 31 | } 32 | 33 | void SimpleSound::play() { 34 | std::wstring filePath = tempSecondFile_.empty() ? tempDefaultFile_ : tempSecondFile_; 35 | 36 | playRequests_.emplace_front(std::async(std::launch::async, [filePath, volume = volume_, tempDefaultFile = tempDefaultFile_]() { 37 | std::wstring alias = generateAlias(); 38 | 39 | if (mciSendStringW((L"open \"" + filePath + L"\" type waveaudio alias " + alias).c_str(), NULL, 0, NULL) != 0) { 40 | if (mciSendStringW((L"open \"" + tempDefaultFile + L"\" type waveaudio alias " + alias).c_str(), NULL, 0, NULL) != 0) { 41 | Logger::logWarning("Failed to open WAV file"); 42 | return; 43 | } 44 | } 45 | 46 | if (mciSendStringW((L"play " + alias + L" wait").c_str(), NULL, 0, NULL) != 0) { 47 | Logger::logWarning("Failed to play WAV"); 48 | mciSendStringW((L"close " + alias).c_str(), NULL, 0, NULL); 49 | return; 50 | } 51 | 52 | mciSendStringW((L"close " + alias).c_str(), NULL, 0, NULL); 53 | })); 54 | } 55 | 56 | bool SimpleSound::secondBuffer(const std::string& filePath) { 57 | if (!tempSecondFile_.empty()) { 58 | tempSecondFile_.clear(); 59 | } 60 | 61 | bool fileExists = fs::exists(filePath); 62 | 63 | if (fileExists) { 64 | tempSecondFile_ = std::wstring(filePath.begin(), filePath.end()); 65 | } 66 | else { 67 | fileExists = fs::exists(filePath + ".WAV"); 68 | 69 | if (fileExists) { 70 | tempSecondFile_ = std::format(L"{}.WAV", std::wstring(filePath.begin(), filePath.end())).c_str(); 71 | } 72 | } 73 | 74 | return fileExists; 75 | } 76 | 77 | bool SimpleSound::loadFromBuffer(std::wstring &tempFile, const unsigned char* data, unsigned int size) { 78 | if (!data || size == 0) { 79 | Logger::logWarning("Invalid buffer: null or zero size"); 80 | return false; 81 | } 82 | 83 | std::ostringstream oss; 84 | oss << std::hex << std::setfill('0'); 85 | for (unsigned int i = 0; i < (std::min)(size, 16u); ++i) { 86 | oss << std::setw(2) << static_cast(data[i]) << " "; 87 | } 88 | 89 | tempFile = createTempFile(data, size); 90 | return !tempFile.empty(); 91 | } 92 | 93 | std::wstring SimpleSound::createTempFile(const unsigned char* data, unsigned int size) { 94 | wchar_t tempPath[MAX_PATH]; 95 | wchar_t tempFile[MAX_PATH]; 96 | if (GetTempPathW(MAX_PATH, tempPath) == 0 || GetTempFileNameW(tempPath, L"WAV", 0, tempFile) == 0) { 97 | Logger::logWarning("Failed to create temporary file path"); 98 | return L""; 99 | } 100 | 101 | std::ofstream file(tempFile, std::ios::binary); 102 | if (!file.is_open()) { 103 | Logger::logWarning("Failed to open temporary file for writing"); 104 | return L""; 105 | } 106 | 107 | file.write(reinterpret_cast(data), size); 108 | if (!file.good()) { 109 | Logger::logWarning("Failed to write to temporary file"); 110 | file.close(); 111 | removeTempFile(tempDefaultFile_); 112 | return L""; 113 | } 114 | 115 | file.close(); 116 | 117 | return std::wstring(tempFile); 118 | } 119 | bool SimpleSound::removeTempFile(std::wstring& tempFile) { 120 | auto startTime = std::chrono::steady_clock::now(); 121 | const auto timeout = std::chrono::seconds(3); 122 | 123 | while (fs::exists(tempFile)) { 124 | try { 125 | fs::permissions(tempFile, fs::perms::all, fs::perm_options::remove); 126 | 127 | // Attempt to remove the file 128 | if (fs::remove(tempFile)) { 129 | return true; 130 | } 131 | } 132 | catch (...) {} 133 | 134 | // Check if 3 seconds have passed 135 | if (std::chrono::steady_clock::now() - startTime >= timeout) { 136 | Logger::logWarning("Failed to remove file within 3 seconds."); 137 | break; 138 | } 139 | 140 | // Wait a bit before retrying 141 | std::this_thread::sleep_for(std::chrono::milliseconds(100)); 142 | } 143 | 144 | return false; 145 | } 146 | 147 | std::wstring SimpleSound::generateAlias() { 148 | const std::wstring chars = L"abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789"; 149 | std::random_device rd; 150 | std::mt19937 gen(rd()); 151 | std::uniform_int_distribution<> dis(0, chars.size() - 1); 152 | 153 | std::wstring alias(8, L'\0'); 154 | for (int i = 0; i < 8; ++i) { 155 | alias[i] = chars[dis(gen)]; 156 | } 157 | return alias; 158 | } -------------------------------------------------------------------------------- /ErScripts/SimpleSound.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include 4 | #include 5 | #include 6 | #include 7 | #include 8 | 9 | class SimpleSound { 10 | public: 11 | SimpleSound(const unsigned char* data, unsigned int size); // Initalizate default sound 12 | ~SimpleSound(); // Delete default sound 13 | 14 | void setVolume(int volume) noexcept; 15 | void play(); 16 | 17 | bool secondBuffer(const std::string& filePath); // Initalizate second sound 18 | private: 19 | float volume_ = 1.0f; 20 | std::wstring tempDefaultFile_, tempSecondFile_; 21 | std::forward_list> playRequests_; 22 | 23 | bool loadFromBuffer(std::wstring& tempFile, const unsigned char* data, unsigned int size); 24 | std::wstring createTempFile(const unsigned char* data, unsigned int size); 25 | bool removeTempFile(std::wstring& tempFile); 26 | static std::wstring generateAlias(); 27 | }; -------------------------------------------------------------------------------- /ErScripts/SteamTools.cpp: -------------------------------------------------------------------------------- 1 | #include "SteamTools.h" 2 | #include "Logger.h" 3 | 4 | namespace fs = std::filesystem; 5 | 6 | // VdfParseError implementation 7 | VdfParseError::VdfParseError(const std::string& message) 8 | : std::runtime_error(message) {} 9 | 10 | std::wstring SteamTools::getSteamInstallPath() { 11 | HKEY hKey{}; 12 | wchar_t steamPath[256]{}; 13 | DWORD pathSize = sizeof(steamPath); 14 | 15 | if (RegOpenKeyEx(HKEY_LOCAL_MACHINE, L"SOFTWARE\\WOW6432Node\\Valve\\Steam", 0, KEY_READ, &hKey) != ERROR_SUCCESS) { 16 | throw std::runtime_error("Failed to open Steam registry key"); 17 | } 18 | 19 | // Use a custom deleter function 20 | auto keyDeleter = [](HKEY* key) { 21 | if (key && *key) { 22 | RegCloseKey(*key); 23 | } 24 | }; 25 | std::unique_ptr keyGuard(&hKey, keyDeleter); 26 | 27 | if (RegQueryValueEx(hKey, L"InstallPath", nullptr, nullptr, reinterpret_cast(steamPath), &pathSize) != ERROR_SUCCESS) { 28 | throw std::runtime_error("Failed to read Steam InstallPath from registry"); 29 | } 30 | 31 | return std::wstring(steamPath); 32 | } 33 | 34 | std::wstring SteamTools::getSteamUserID() { 35 | HKEY hKey{}; 36 | DWORD userID; 37 | DWORD size = sizeof(userID); 38 | 39 | if (RegOpenKeyEx(HKEY_CURRENT_USER, L"Software\\Valve\\Steam\\ActiveProcess", 0, KEY_READ, &hKey) == ERROR_SUCCESS) { 40 | if (RegQueryValueEx(hKey, L"ActiveUser", nullptr, nullptr, (LPBYTE)&userID, &size) == ERROR_SUCCESS) { 41 | RegCloseKey(hKey); 42 | return std::to_wstring(userID); 43 | } 44 | RegCloseKey(hKey); 45 | } 46 | 47 | return L""; 48 | } 49 | 50 | std::string SteamTools::getSteamNickname() { 51 | // Get the Steam user ID as a wide string 52 | std::wstring steamUserID = getSteamUserID(); 53 | if (steamUserID.empty()) { 54 | throw std::runtime_error("Failed to retrieve Steam UserID."); 55 | } 56 | 57 | // Convert the Steam install path (wstring) and user ID (wstring) to std::string 58 | std::wstring installPathW = getSteamInstallPath(); 59 | std::string installPath(installPathW.begin(), installPathW.end()); 60 | std::string steamUserIDStr(steamUserID.begin(), steamUserID.end()); 61 | 62 | // Construct the path to the localconfig.vdf file 63 | std::string vdfFilePath = installPath + "\\userdata\\" + steamUserIDStr + "\\config\\localconfig.vdf"; 64 | 65 | if (!fs::exists(vdfFilePath)) { 66 | throw std::runtime_error("localconfig.vdf not found."); 67 | } 68 | 69 | try { 70 | // Open the VDF file and read it line by line 71 | std::ifstream vdfFile(vdfFilePath); 72 | std::string line; 73 | std::string personaName; 74 | bool foundPersonaName = false; 75 | 76 | while (std::getline(vdfFile, line)) { 77 | // Look for the line with "PersonaName" 78 | std::size_t keyPos = line.find("\"PersonaName\""); 79 | if (keyPos != std::string::npos) { 80 | // Find the first quote after the key 81 | std::size_t valueStartQuote = line.find("\"", keyPos + std::string("\"PersonaName\"").length()); 82 | if (valueStartQuote != std::string::npos) { 83 | // Find the ending quote after the starting quote 84 | std::size_t valueEndQuote = line.find("\"", valueStartQuote + 1); 85 | if (valueEndQuote != std::string::npos) { 86 | personaName = line.substr(valueStartQuote + 1, valueEndQuote - valueStartQuote - 1); 87 | foundPersonaName = true; 88 | break; 89 | } 90 | } 91 | } 92 | } 93 | 94 | if (!foundPersonaName) { 95 | throw std::runtime_error("PersonaName not found."); 96 | } 97 | 98 | return personaName; 99 | } 100 | catch (const std::exception& e) { 101 | throw std::runtime_error("Error reading localconfig.vdf: " + std::string(e.what())); 102 | } 103 | } 104 | 105 | std::wstring SteamTools::getSteamConfigPath() { 106 | std::wstring steamPath = getSteamInstallPath(); 107 | if (steamPath.empty()) { 108 | return L""; 109 | } 110 | 111 | std::wstring steamUserID = getSteamUserID(); 112 | if (steamUserID.empty()) { 113 | return L""; 114 | } 115 | 116 | return steamPath + L"\\userdata\\" + steamUserID; 117 | } 118 | 119 | std::wstring SteamTools::trim(std::wstring_view str) { 120 | size_t first = str.find_first_not_of(L" \t\r\n"); 121 | if (first == std::wstring::npos) return L""; 122 | size_t last = str.find_last_not_of(L" \t\r\n"); 123 | return std::wstring(str.substr(first, last - first + 1)); 124 | } 125 | 126 | std::map> SteamTools::parseVdfContent(std::wstring_view content) { 127 | std::map> appPaths; 128 | std::wistringstream iss(content.data()); 129 | std::wstring line, currentPath; 130 | bool inLibraryFolder = false; 131 | bool inAppsSection = false; 132 | 133 | while (std::getline(iss, line)) { 134 | line.erase(0, line.find_first_not_of(L" \t")); // Trim left 135 | line.erase(line.find_last_not_of(L" \t") + 1); // Trim right 136 | if (line.empty()) continue; 137 | 138 | if (line.size() >= 3 && line[0] == L'"' && iswdigit(line[1]) && line[2] == L'"') { 139 | inLibraryFolder = true; 140 | currentPath.clear(); 141 | continue; 142 | } 143 | 144 | if (inLibraryFolder && line == L"{") continue; 145 | 146 | if (inLibraryFolder && line.starts_with(L"\"path\"")) { 147 | size_t start = line.find(L'"', 7); 148 | size_t end = line.find(L'"', start + 1); 149 | if (start == std::wstring::npos || end == std::wstring::npos) { 150 | throw VdfParseError("Malformed path line: " + std::string(line.begin(), line.end())); 151 | } 152 | currentPath = line.substr(start + 1, end - start - 1); 153 | std::replace(currentPath.begin(), currentPath.end(), L'/', L'\\'); 154 | std::wstring normalizedPath; 155 | for (size_t i = 0; i < currentPath.length(); ++i) { 156 | if (i > 0 && currentPath[i] == L'\\' && currentPath[i - 1] == L'\\') continue; 157 | normalizedPath += currentPath[i]; 158 | } 159 | currentPath = normalizedPath; 160 | Logger::logSuccess(std::format(L"Found library path: {}", currentPath)); 161 | continue; 162 | } 163 | 164 | if (inLibraryFolder && line == L"\"apps\"") { 165 | inAppsSection = true; 166 | continue; 167 | } 168 | 169 | if (inAppsSection && line == L"}") { 170 | inAppsSection = false; 171 | continue; 172 | } 173 | 174 | if (inLibraryFolder && !inAppsSection && line == L"}") { 175 | inLibraryFolder = false; 176 | continue; 177 | } 178 | 179 | if (inAppsSection && line.starts_with(L"\"")) { 180 | size_t appIdStart = line.find(L'"'); 181 | size_t appIdEnd = line.find(L'"', appIdStart + 1); 182 | size_t sizeStart = line.find(L'"', appIdEnd + 1); 183 | size_t sizeEnd = line.find(L'"', sizeStart + 1); 184 | if (appIdEnd == std::wstring::npos || sizeEnd == std::wstring::npos) { 185 | throw VdfParseError("Malformed app entry: " + std::string(line.begin(), line.end())); 186 | } 187 | 188 | std::wstring wAppId = line.substr(appIdStart + 1, appIdEnd - appIdStart - 1); 189 | std::string appId(wAppId.begin(), wAppId.end()); 190 | if (!appId.empty() && !currentPath.empty()) { 191 | fs::path basePath = fs::path(currentPath) / L"steamapps" / L"common"; 192 | std::wstring normalizedPath = basePath.wstring(); 193 | std::replace(normalizedPath.begin(), normalizedPath.end(), L'/', L'\\'); 194 | std::wstring singleSlashPath; 195 | for (size_t i = 0; i < normalizedPath.length(); ++i) { 196 | if (i > 0 && normalizedPath[i] == L'\\' && normalizedPath[i - 1] == L'\\') continue; 197 | singleSlashPath += normalizedPath[i]; 198 | } 199 | appPaths[appId].push_back(singleSlashPath); 200 | // Logger::logInfo(std::format(L"App {} potentially at: {}", wAppId, singleSlashPath)); 201 | } 202 | } 203 | } 204 | 205 | return appPaths; 206 | } 207 | 208 | std::optional SteamTools::getAppInstallPath(std::string_view appId, const GameConfig& config) { 209 | try { 210 | std::wstring steamPath = getSteamInstallPath(); 211 | if (steamPath.empty()) { 212 | Logger::logWarning(L"Could not determine Steam installation path"); 213 | return std::nullopt; 214 | } 215 | Logger::logSuccess(std::format(L"Steam install path from registry: {}", steamPath)); 216 | 217 | fs::path vdfPath = fs::path(steamPath) / L"steamapps" / L"libraryfolders.vdf"; 218 | std::wstring vdfPathStr = vdfPath.wstring(); 219 | std::replace(vdfPathStr.begin(), vdfPathStr.end(), L'/', L'\\'); 220 | Logger::logInfo(std::format(L"Looking for VDF at: {}", vdfPathStr)); 221 | 222 | std::wifstream file(vdfPath); 223 | if (!file.is_open()) { 224 | Logger::logWarning(L"Failed to open VDF file: " + vdfPathStr); 225 | return std::nullopt; 226 | } 227 | 228 | std::wstringstream wss; 229 | wss << file.rdbuf(); 230 | std::wstring content = wss.str(); 231 | file.close(); 232 | Logger::logSuccess(L"Successfully read VDF file"); 233 | 234 | auto appPaths = parseVdfContent(content); 235 | auto it = appPaths.find(std::string(appId)); 236 | if (it == appPaths.end()) { 237 | Logger::logWarning(std::format(L"AppID {} not found in VDF", std::wstring(appId.begin(), appId.end()))); 238 | return std::nullopt; 239 | } 240 | 241 | const auto& potentialPaths = it->second; 242 | for (const auto& basePath : potentialPaths) { 243 | Logger::logInfo(std::format(L"Exploring library path: {}", basePath)); 244 | for (const auto& folder : config.possibleFolders) { 245 | fs::path fullPath = fs::path(basePath) / folder; 246 | std::wstring fullPathStr = fullPath.wstring(); 247 | std::replace(fullPathStr.begin(), fullPathStr.end(), L'/', L'\\'); 248 | Logger::logInfo(std::format(L"Checking: {}", fullPathStr)); 249 | if (fs::exists(fullPath)) { 250 | if (fs::is_directory(fullPath)) { 251 | Logger::logSuccess(std::format(L"Found {} at: {}", 252 | std::wstring(appId.begin(), appId.end()), fullPathStr)); 253 | return fullPathStr; 254 | } 255 | else { 256 | Logger::logInfo(std::format(L"Path {} exists but is not a directory", fullPathStr)); 257 | } 258 | } 259 | } 260 | } 261 | 262 | Logger::logWarning(std::format(L"AppID {} found in VDF but no valid installation folder located", 263 | std::wstring(appId.begin(), appId.end()))); 264 | return std::nullopt; 265 | } 266 | catch (const std::exception& e) { 267 | Logger::logWarning(std::format("Error getting app install path: {}", e.what())); 268 | return std::nullopt; 269 | } 270 | } 271 | 272 | std::wstring SteamTools::getLaunchOptions(std::string_view appId) { 273 | std::wstring steamConfigPath = getSteamConfigPath(); 274 | if (steamConfigPath.empty()) { 275 | Logger::logWarning(L"Failed to get Steam config path."); 276 | return L""; 277 | } 278 | 279 | std::wstring localConfigPath = steamConfigPath + L"\\config\\localconfig.vdf"; 280 | return parseLaunchOptions(localConfigPath, appId); 281 | } 282 | 283 | std::wstring SteamTools::parseLaunchOptions(const std::wstring& filePath, std::string_view appId) { 284 | std::wifstream file(filePath); 285 | if (!file.is_open()) { 286 | Logger::logWarning(L"Failed to open: " + filePath); 287 | return L""; 288 | } 289 | 290 | std::wstring line; 291 | bool inAppSection = false; 292 | int braceDepth = 0; 293 | std::wstring appIdStr(appId.begin(), appId.end()); 294 | std::wregex launchOptionsRegex(L"\"LaunchOptions\"\\s+\"([^\"]+)\""); 295 | 296 | while (std::getline(file, line)) { 297 | line = trim(line); 298 | 299 | if (!inAppSection && line == L"\"" + appIdStr + L"\"") { 300 | inAppSection = true; 301 | braceDepth = 0; 302 | continue; 303 | } 304 | 305 | if (inAppSection) { 306 | if (line == L"{") { 307 | braceDepth++; 308 | } 309 | else if (line == L"}") { 310 | braceDepth--; 311 | if (braceDepth == 0) { 312 | inAppSection = false; 313 | } 314 | } 315 | 316 | // Only match LaunchOptions when at depth 1 (main app section) 317 | if (braceDepth == 1) { 318 | std::wsmatch match; 319 | if (std::regex_search(line, match, launchOptionsRegex)) { 320 | file.close(); 321 | return match[1].str(); 322 | } 323 | } 324 | } 325 | } 326 | 327 | file.close(); 328 | return L""; 329 | } 330 | 331 | std::optional SteamTools::getCrosshairSettings(std::string_view appId) { 332 | try { 333 | std::wstring steamConfigPath = getSteamConfigPath(); 334 | if (steamConfigPath.empty()) { 335 | Logger::logWarning(L"Failed to get Steam config path"); 336 | return std::nullopt; 337 | } 338 | 339 | // Construct the path to CS2 user convars file 340 | std::wstring configPath = steamConfigPath + L"\\" + 341 | std::wstring(appId.begin(), appId.end()) + 342 | L"\\local\\cfg\\cs2_user_convars_0_slot0.vcfg"; 343 | 344 | Logger::logInfo(std::format(L"Looking for crosshair settings at: {}", configPath)); 345 | 346 | if (!fs::exists(configPath)) { 347 | Logger::logWarning(std::format(L"Crosshair config file not found: {}", configPath)); 348 | return std::nullopt; 349 | } 350 | 351 | Config crosshair = parseCrosshairSettings(configPath); 352 | if (crosshair.isEmpty()) { 353 | Logger::logWarning(L"No crosshair settings found in config file"); 354 | return std::nullopt; 355 | } 356 | 357 | Logger::logSuccess(L"Successfully parsed crosshair settings"); 358 | return crosshair; 359 | } 360 | catch (const std::exception& e) { 361 | Logger::logWarning(std::format("Error parsing crosshair settings: {}", e.what())); 362 | return std::nullopt; 363 | } 364 | } 365 | 366 | SteamTools::Config SteamTools::parseCrosshairSettings(const std::wstring& filePath) { 367 | Config crosshair; 368 | 369 | std::wifstream file(filePath); 370 | if (!file.is_open()) { 371 | Logger::logWarning(std::format(L"Failed to open crosshair config: {}", filePath)); 372 | return crosshair; 373 | } 374 | 375 | std::wstringstream wss; 376 | wss << file.rdbuf(); 377 | std::wstring content = wss.str(); 378 | 379 | std::map> convarMap { 380 | {L"cl_crosshairgap", [&](Config& ch, const std::wstring& val) { ch.gap = std::stof(val); }}, 381 | {L"cl_crosshair_outlinethickness", [&](Config& ch, const std::wstring& val) { ch.outlineThickness = std::stof(val); }}, 382 | {L"cl_crosshaircolor_r", [&](Config& ch, const std::wstring& val) { ch.red = static_cast(std::stoi(val)); }}, 383 | {L"cl_crosshaircolor_g", [&](Config& ch, const std::wstring& val) { ch.green = static_cast(std::stoi(val)); }}, 384 | {L"cl_crosshaircolor_b", [&](Config& ch, const std::wstring& val) { ch.blue = static_cast(std::stoi(val)); }}, 385 | {L"cl_crosshairalpha", [&](Config& ch, const std::wstring& val) { ch.alpha = static_cast(std::stoi(val)); }}, 386 | //{L"cl_crosshair_dynamic_splitdist", [&](Config& ch, const std::wstring& val) { ch.dynamicSplitDist = static_cast(std::stoi(val)); }}, 387 | //{L"cl_fixedcrosshairgap", [&](Config& ch, const std::wstring& val) { ch.fixedCrosshairGap = std::stof(val); }}, 388 | {L"cl_crosshaircolor", [&](Config& ch, const std::wstring& val) { ch.color = static_cast(std::stoi(val)); }}, 389 | {L"cl_crosshair_drawoutline", [&](Config& ch, const std::wstring& val) { ch.drawOutline = val == L"true" || val == L"1"; }}, 390 | //{L"cl_crosshair_dynamic_splitalpha_innermod", [&](Config& ch, const std::wstring& val) { ch.dynamicSplitAlphaInnerMod = std::stof(val); }}, 391 | //{L"cl_crosshair_dynamic_splitalpha_outermod", [&](Config& ch, const std::wstring& val) { ch.dynamicSplitAlphaOuterMod = std::stof(val); }}, 392 | //{L"cl_crosshair_dynamic_maxdist_splitratio", [&](Config& ch, const std::wstring& val) { ch.dynamicMaxDistSplitRatio = std::stof(val); }}, 393 | {L"cl_crosshairthickness", [&](Config& ch, const std::wstring& val) { ch.thickness = std::stof(val); }}, 394 | {L"cl_crosshairdot", [&](Config& ch, const std::wstring& val) { ch.dot = val == L"true" || val == L"1"; }}, 395 | //{L"cl_crosshairgap_useweaponvalue", [&](Config& ch, const std::wstring& val) { ch.gapUseWeaponValue = val == L"true" || val == L"1"; }}, 396 | //{L"cl_crosshairusealpha", [&](Config& ch, const std::wstring& val) { ch.useAlpha = val == L"true" || val == L"1"; }}, 397 | {L"cl_crosshair_t", [&](Config& ch, const std::wstring& val) { ch.tStyle = val == L"true" || val == L"1"; }}, 398 | {L"cl_crosshairstyle", [&](Config& ch, const std::wstring& val) { ch.style = static_cast(std::stoi(val)); }}, 399 | {L"cl_crosshairsize", [&](Config& ch, const std::wstring& val) { ch.size = std::stof(val); }} 400 | }; 401 | 402 | std::wistringstream iss(content); 403 | std::wstring line; 404 | bool inConvars = false; 405 | int braceDepth = 0; 406 | 407 | while (std::getline(iss, line)) { 408 | line = trim(line); 409 | if (line.empty()) continue; 410 | 411 | if (line == L"\"convars\"") { 412 | inConvars = true; 413 | continue; 414 | } 415 | 416 | if (inConvars) { 417 | if (line == L"{") { 418 | braceDepth++; 419 | continue; 420 | } 421 | if (line == L"}") { 422 | braceDepth--; 423 | if (braceDepth == 0) { 424 | inConvars = false; 425 | break; 426 | } 427 | continue; 428 | } 429 | 430 | if (line.starts_with(L"\"cl_crosshair") || line.starts_with(L"\"cl_fixedcrosshair")) { 431 | std::wistringstream lineStream(line); 432 | std::wstring key, value; 433 | if (lineStream >> std::quoted(key) >> std::quoted(value)) { 434 | auto it = convarMap.find(key); 435 | if (it != convarMap.end()) { 436 | try { 437 | it->second(crosshair, value); 438 | Logger::logInfo(std::format(L"Parsed: {} \"{}\"", key, value)); // Log during parsing 439 | } 440 | catch (const std::exception& e) { 441 | std::string narrowWhat = e.what(); 442 | std::wstring wideWhat(narrowWhat.begin(), narrowWhat.end()); 443 | Logger::logWarning(std::format(L"Error parsing {} with value {}: {}", key, value, wideWhat)); 444 | } 445 | } 446 | } 447 | } 448 | } 449 | } 450 | 451 | return crosshair; 452 | } 453 | 454 | void SteamTools::printCrosshairSettings(const std::optional& ch) { 455 | Logger::logSuccess("Crosshair settings:"); 456 | Logger::logInfo(std::format("cl_crosshairgap \"{:.1f}\"", ch->gap)); 457 | Logger::logInfo(std::format("cl_crosshair_outlinethickness \"{:.1f}\"", ch->outlineThickness)); 458 | Logger::logInfo(std::format("cl_crosshaircolor_r \"{}\"", ch->red)); 459 | Logger::logInfo(std::format("cl_crosshaircolor_g \"{}\"", ch->green)); 460 | Logger::logInfo(std::format("cl_crosshaircolor_b \"{}\"", ch->blue)); 461 | Logger::logInfo(std::format("cl_crosshairalpha \"{}\"", ch->alpha)); 462 | //Logger::logInfo(std::format("cl_crosshair_dynamic_splitdist \"{}\"", ch->dynamicSplitDist)); 463 | //Logger::logInfo(std::format("cl_fixedcrosshairgap \"{:.1f}\"", ch->fixedCrosshairGap)); 464 | Logger::logInfo(std::format("cl_crosshaircolor \"{}\"", ch->color)); 465 | Logger::logInfo(std::format("cl_crosshair_drawoutline \"{}\"", ch->drawOutline ? 1 : 0)); 466 | //Logger::logInfo(std::format("cl_crosshair_dynamic_splitalpha_innermod \"{:.1f}\"", ch->dynamicSplitAlphaInnerMod)); 467 | //Logger::logInfo(std::format("cl_crosshair_dynamic_splitalpha_outermod \"{:.1f}\"", ch->dynamicSplitAlphaOuterMod)); 468 | //Logger::logInfo(std::format("cl_crosshair_dynamic_maxdist_splitratio \"{:.1f}\"", ch->dynamicMaxDistSplitRatio)); 469 | Logger::logInfo(std::format("cl_crosshairthickness \"{:.1f}\"", ch->thickness)); 470 | Logger::logInfo(std::format("cl_crosshairdot \"{}\"", ch->dot ? 1 : 0)); 471 | //Logger::logInfo(std::format("cl_crosshairgap_useweaponvalue \"{}\"", ch->gapUseWeaponValue ? 1 : 0)); 472 | //Logger::logInfo(std::format("cl_crosshairusealpha \"{}\"", ch->useAlpha ? 1 : 0)); 473 | Logger::logInfo(std::format("cl_crosshair_t \"{}\"", ch->tStyle ? 1 : 0)); 474 | Logger::logInfo(std::format("cl_crosshairstyle \"{}\"", ch->style)); 475 | Logger::logInfo(std::format("cl_crosshairsize \"{:.1f}\"", ch->size)); 476 | } -------------------------------------------------------------------------------- /ErScripts/SteamTools.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include 4 | #include 5 | #include 6 | #include 7 | #include 8 | #include 9 | #include 10 | #include 11 | #include 12 | #include 13 | #include 14 | #include 15 | #include 16 | #include 17 | 18 | class VdfParseError : public std::runtime_error { 19 | public: 20 | explicit VdfParseError(const std::string& message); 21 | }; 22 | 23 | class SteamTools { 24 | public: 25 | // Configuration struct for game-specific folder names 26 | struct GameConfig { 27 | std::vector possibleFolders; 28 | }; 29 | 30 | // CS2 Crosshair struct 31 | struct Config { 32 | /* Crosshair */ 33 | float gap = 0.0f; // cl_crosshairgap 34 | float outlineThickness = 0.0f; // cl_crosshair_outlinethickness 35 | uint8_t red = 0; // cl_crosshaircolor_r 36 | uint8_t green = 0; // cl_crosshaircolor_g 37 | uint8_t blue = 0; // cl_crosshaircolor_b 38 | uint8_t alpha = 0; // cl_crosshairalpha 39 | //uint8_t dynamicSplitDist = 0; // cl_crosshair_dynamic_splitdist 40 | //float fixedCrosshairGap = 0.0f; // cl_fixedcrosshairgap 41 | uint8_t color = 0; // cl_crosshaircolor 42 | bool drawOutline = false; // cl_crosshair_drawoutline 43 | //float dynamicSplitAlphaInnerMod = 0.0f; // cl_crosshair_dynamic_splitalpha_innermod 44 | //float dynamicSplitAlphaOuterMod = 0.0f; // cl_crosshair_dynamic_splitalpha_outermod 45 | //float dynamicMaxDistSplitRatio = 0.0f; // cl_crosshair_dynamic_maxdist_splitratio 46 | float thickness = 0.0f; // cl_crosshairthickness 47 | bool dot = false; // cl_crosshairdot 48 | //bool gapUseWeaponValue = false; // cl_crosshairgap_useweaponvalue 49 | //bool useAlpha = false; // cl_crosshairusealpha 50 | bool tStyle = false; // cl_crosshair_t 51 | uint8_t style = 0; // cl_crosshairstyle 52 | float size = 0.0f; // cl_crosshairsize 53 | 54 | /* Other */ 55 | float sensitivity = 0.0f; // sensitivity 56 | float zoomSensitivity = 0.0f; // zoom_sensitivity_ratio 57 | float yaw = 0.0f; // m_yaw 58 | 59 | [[nodiscard]] bool isEmpty() const noexcept { 60 | return size == 0.0f && thickness == 0.0f && 61 | red == 0 && green == 0 && blue == 0 && alpha == 0 && 62 | !dot && !drawOutline; 63 | } 64 | }; 65 | 66 | // Get the installation path for a given Steam AppID 67 | static std::optional getAppInstallPath(std::string_view appId, const GameConfig& config); 68 | 69 | // Get launch options for a given Steam AppID 70 | static std::wstring getLaunchOptions(std::string_view appId); 71 | 72 | // Get user nickname 73 | static std::string getSteamNickname(); 74 | 75 | // Get crosshair settings for a given Steam AppID 76 | static std::optional getCrosshairSettings(std::string_view appId); 77 | 78 | // Print crosshair settings 79 | static void printCrosshairSettings(const std::optional& ch); 80 | 81 | private: 82 | // Get Steam installation path from registry 83 | static std::wstring getSteamInstallPath(); 84 | 85 | // Get Steam active user id from registry 86 | static std::wstring getSteamUserID(); 87 | 88 | // Get Steam config path 89 | static std::wstring getSteamConfigPath(); 90 | 91 | // Parse VDF content into a map of app IDs to their base paths 92 | static std::map> parseVdfContent(std::wstring_view content); 93 | 94 | // Parse launch options from a VDF file for a specific AppID 95 | static std::wstring parseLaunchOptions(const std::wstring& filePath, std::string_view appId); 96 | 97 | // Trim whitespace from a string 98 | static std::wstring trim(std::wstring_view str); 99 | 100 | // Parse a crosshair configuration from a file 101 | static Config parseCrosshairSettings(const std::wstring& filePath); 102 | }; -------------------------------------------------------------------------------- /ErScripts/UIAccess.c: -------------------------------------------------------------------------------- 1 | #include "UIAccess.h" 2 | #include 3 | #include 4 | 5 | static DWORD DuplicateWinloginToken(DWORD dwSessionId, DWORD dwDesiredAccess, PHANDLE phToken) { 6 | DWORD dwErr; 7 | PRIVILEGE_SET ps; 8 | 9 | ps.PrivilegeCount = 1; 10 | ps.Control = PRIVILEGE_SET_ALL_NECESSARY; 11 | 12 | if (LookupPrivilegeValue(NULL, SE_TCB_NAME, &ps.Privilege[0].Luid)) { 13 | HANDLE hSnapshot = CreateToolhelp32Snapshot(TH32CS_SNAPPROCESS, 0); 14 | if (INVALID_HANDLE_VALUE != hSnapshot) { 15 | BOOL bCont, bFound = FALSE; 16 | PROCESSENTRY32 pe; 17 | 18 | pe.dwSize = sizeof(pe); 19 | dwErr = ERROR_NOT_FOUND; 20 | 21 | for (bCont = Process32First(hSnapshot, &pe); bCont; bCont = Process32Next(hSnapshot, &pe)) { 22 | HANDLE hProcess; 23 | 24 | if (0 != _tcsicmp(pe.szExeFile, TEXT("winlogon.exe"))) { 25 | continue; 26 | } 27 | 28 | hProcess = OpenProcess(PROCESS_QUERY_LIMITED_INFORMATION, FALSE, pe.th32ProcessID); 29 | if (hProcess) { 30 | HANDLE hToken; 31 | DWORD dwRetLen, sid; 32 | 33 | if (OpenProcessToken(hProcess, TOKEN_QUERY | TOKEN_DUPLICATE, &hToken)) { 34 | BOOL fTcb; 35 | 36 | if (PrivilegeCheck(hToken, &ps, &fTcb) && fTcb) { 37 | if (GetTokenInformation(hToken, TokenSessionId, &sid, sizeof(sid), &dwRetLen) && sid == dwSessionId) { 38 | bFound = TRUE; 39 | if (DuplicateTokenEx(hToken, dwDesiredAccess, NULL, SecurityImpersonation, TokenImpersonation, phToken)) { 40 | dwErr = ERROR_SUCCESS; 41 | } 42 | else { 43 | dwErr = GetLastError(); 44 | } 45 | } 46 | } 47 | CloseHandle(hToken); 48 | } 49 | CloseHandle(hProcess); 50 | } 51 | 52 | if (bFound) break; 53 | } 54 | 55 | CloseHandle(hSnapshot); 56 | } 57 | else { 58 | dwErr = GetLastError(); 59 | } 60 | } 61 | else { 62 | dwErr = GetLastError(); 63 | } 64 | 65 | 66 | return dwErr; 67 | } 68 | 69 | static DWORD CreateUIAccessToken(PHANDLE phToken) { 70 | DWORD dwErr; 71 | HANDLE hTokenSelf; 72 | 73 | if (OpenProcessToken(GetCurrentProcess(), TOKEN_QUERY | TOKEN_DUPLICATE, &hTokenSelf)) { 74 | DWORD dwSessionId, dwRetLen; 75 | 76 | if (GetTokenInformation(hTokenSelf, TokenSessionId, &dwSessionId, sizeof(dwSessionId), &dwRetLen)) { 77 | HANDLE hTokenSystem; 78 | 79 | dwErr = DuplicateWinloginToken(dwSessionId, TOKEN_IMPERSONATE, &hTokenSystem); 80 | if (ERROR_SUCCESS == dwErr) { 81 | if (SetThreadToken(NULL, hTokenSystem)) { 82 | if (DuplicateTokenEx(hTokenSelf, TOKEN_QUERY | TOKEN_DUPLICATE | TOKEN_ASSIGN_PRIMARY | TOKEN_ADJUST_DEFAULT, NULL, SecurityAnonymous, TokenPrimary, phToken)) { 83 | BOOL bUIAccess = TRUE; 84 | 85 | if (!SetTokenInformation(*phToken, TokenUIAccess, &bUIAccess, sizeof(bUIAccess))) { 86 | dwErr = GetLastError(); 87 | CloseHandle(*phToken); 88 | } 89 | } 90 | else { 91 | dwErr = GetLastError(); 92 | } 93 | RevertToSelf(); 94 | } 95 | else { 96 | dwErr = GetLastError(); 97 | } 98 | CloseHandle(hTokenSystem); 99 | } 100 | } 101 | else { 102 | dwErr = GetLastError(); 103 | } 104 | 105 | CloseHandle(hTokenSelf); 106 | } 107 | else { 108 | dwErr = GetLastError(); 109 | } 110 | 111 | return dwErr; 112 | } 113 | 114 | static BOOL CheckForUIAccess(DWORD* pdwErr, DWORD* pfUIAccess) { 115 | BOOL result = FALSE; 116 | HANDLE hToken; 117 | 118 | if (OpenProcessToken(GetCurrentProcess(), TOKEN_QUERY, &hToken)) { 119 | DWORD dwRetLen; 120 | 121 | if (GetTokenInformation(hToken, TokenUIAccess, pfUIAccess, sizeof(*pfUIAccess), &dwRetLen)) { 122 | result = TRUE; 123 | } 124 | else { 125 | *pdwErr = GetLastError(); 126 | } 127 | CloseHandle(hToken); 128 | } 129 | else { 130 | *pdwErr = GetLastError(); 131 | } 132 | 133 | return result; 134 | } 135 | 136 | DWORD PrepareForUIAccess() { 137 | DWORD dwErr; 138 | HANDLE hTokenUIAccess; 139 | BOOL fUIAccess; 140 | 141 | if (CheckForUIAccess(&dwErr, &fUIAccess)) { 142 | if (fUIAccess) { 143 | dwErr = ERROR_SUCCESS; 144 | } 145 | else { 146 | dwErr = CreateUIAccessToken(&hTokenUIAccess); 147 | if (ERROR_SUCCESS == dwErr) { 148 | STARTUPINFO si; 149 | PROCESS_INFORMATION pi; 150 | 151 | GetStartupInfo(&si); 152 | if (CreateProcessAsUserW(hTokenUIAccess, NULL, GetCommandLine(), NULL, NULL, FALSE, 0, NULL, NULL, &si, &pi)) { 153 | CloseHandle(pi.hProcess), CloseHandle(pi.hThread); 154 | ExitProcess(0); 155 | } 156 | else { 157 | dwErr = GetLastError(); 158 | } 159 | 160 | CloseHandle(hTokenUIAccess); 161 | } 162 | } 163 | } 164 | 165 | return dwErr; 166 | } -------------------------------------------------------------------------------- /ErScripts/UIAccess.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | #include 3 | 4 | EXTERN_C DWORD PrepareForUIAccess(); -------------------------------------------------------------------------------- /ErScripts/Updater.cpp: -------------------------------------------------------------------------------- 1 | // Updater.cpp 2 | #include "Updater.h" 3 | #include "Logger.h" 4 | #include 5 | #include 6 | #include 7 | #include 8 | #include 9 | #include 10 | #include 11 | #include 12 | #include // Added for random_device, mt19937, uniform_int_distribution 13 | 14 | #pragma comment(lib, "wininet.lib") 15 | 16 | std::string Updater::random_suffix_; 17 | 18 | Updater::Updater(const std::string& currentVersion, const std::string& githubAuthor, const std::string& repoName, const std::string& downloadExeName) : 19 | currentVersion(currentVersion), 20 | currentExeName(getCurrentExeName()), 21 | downloadExeName(downloadExeName + ".exe"), 22 | releasesUrl("https://api.github.com/repos/" + githubAuthor + "/" + repoName + "/releases/latest"), 23 | githubAuthor(githubAuthor), 24 | repoName(repoName) { 25 | Logger::EnableANSIColors(); 26 | if (currentExeName.empty()) { 27 | Logger::logWarning(std::format("Failed to get current executable name, defaulting to {}", downloadExeName)); 28 | currentExeName = downloadExeName; 29 | } 30 | // Generate suffix once 31 | if (random_suffix_.empty()) { 32 | random_suffix_ = generateRandomString(8); 33 | } 34 | } 35 | 36 | std::string Updater::generateRandomString(size_t length) { 37 | static const char charset[] = "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ"; 38 | std::random_device rd; 39 | std::mt19937 gen(rd()); 40 | std::uniform_int_distribution<> dist(0, sizeof(charset) - 2); 41 | std::string result; 42 | result.reserve(length); 43 | for (size_t i = 0; i < length; ++i) { 44 | result += charset[dist(gen)]; 45 | } 46 | return result; 47 | } 48 | 49 | std::string Updater::getCurrentExeName() { 50 | char exePath[MAX_PATH]; 51 | DWORD pathLength = GetModuleFileNameA(NULL, exePath, MAX_PATH); 52 | if (pathLength == 0) { 53 | Logger::logWarning(std::format("Failed to get module filename: {}", GetLastError())); 54 | return ""; 55 | } 56 | 57 | std::string fullPath(exePath, pathLength); 58 | size_t lastSlash = fullPath.find_last_of("\\/"); 59 | if (lastSlash != std::string::npos) { 60 | return fullPath.substr(lastSlash + 1); 61 | } 62 | return fullPath; 63 | } 64 | 65 | std::wstring Updater::stringToWstring(const std::string& str) { 66 | int size = MultiByteToWideChar(CP_UTF8, 0, str.c_str(), -1, nullptr, 0); 67 | std::wstring wstr(size, 0); 68 | MultiByteToWideChar(CP_UTF8, 0, str.c_str(), -1, &wstr[0], size); 69 | return wstr; 70 | } 71 | 72 | std::string Updater::downloadJsonData(const std::string& url) { 73 | std::string jsonData; 74 | std::wstring wideUrl = stringToWstring(url); 75 | 76 | HINTERNET hInternet = InternetOpenW(L"ProgramUpdater", INTERNET_OPEN_TYPE_DIRECT, NULL, NULL, 0); 77 | if (hInternet) { 78 | HINTERNET hConnect = InternetOpenUrlW(hInternet, wideUrl.c_str(), NULL, 0, INTERNET_FLAG_RELOAD, 0); 79 | if (hConnect) { 80 | std::ostringstream ss; 81 | char buffer[4096]; 82 | DWORD bytesRead; 83 | while (InternetReadFile(hConnect, buffer, sizeof(buffer), &bytesRead) && bytesRead > 0) { 84 | ss.write(buffer, bytesRead); 85 | } 86 | jsonData = ss.str(); 87 | InternetCloseHandle(hConnect); 88 | } 89 | else { 90 | Logger::logWarning(std::format("Failed to open URL: {}", GetLastError())); 91 | } 92 | InternetCloseHandle(hInternet); 93 | } 94 | else { 95 | Logger::logWarning(std::format("Failed to initialize WinINet: {}", GetLastError())); 96 | } 97 | return jsonData; 98 | } 99 | 100 | bool Updater::downloadFile(const std::string& url, const std::string& outputPath) { 101 | std::wstring wideUrl = stringToWstring(url); 102 | std::cout << "Downloading from: " << url << std::endl; 103 | 104 | HINTERNET hInternet = InternetOpenW(L"ProgramUpdater", INTERNET_OPEN_TYPE_DIRECT, NULL, NULL, 0); 105 | if (!hInternet) { 106 | Logger::logWarning(std::format("Failed to initialize WinINet: {}", GetLastError())); 107 | return false; 108 | } 109 | 110 | HINTERNET hConnect = InternetOpenUrlW(hInternet, wideUrl.c_str(), NULL, 0, INTERNET_FLAG_RELOAD, 0); 111 | if (!hConnect) { 112 | Logger::logWarning(std::format("Failed to open URL: {}", GetLastError())); 113 | InternetCloseHandle(hInternet); 114 | return false; 115 | } 116 | 117 | DWORD contentLength = 0; 118 | DWORD bufferLength = sizeof(contentLength); 119 | DWORD index = 0; 120 | if (!HttpQueryInfoW(hConnect, HTTP_QUERY_CONTENT_LENGTH | HTTP_QUERY_FLAG_NUMBER, &contentLength, &bufferLength, &index)) { 121 | Logger::logWarning(std::format("Warning: Could not get Content-Length: {}", GetLastError())); 122 | } 123 | else { 124 | Logger::logInfo(std::format("Expected file size: {} bytes", contentLength)); 125 | } 126 | 127 | std::ofstream outFile(outputPath, std::ios::binary); 128 | if (!outFile) { 129 | Logger::logWarning(std::format("Failed to create output file: {}", outputPath)); 130 | InternetCloseHandle(hConnect); 131 | InternetCloseHandle(hInternet); 132 | return false; 133 | } 134 | 135 | char buffer[4096]; 136 | DWORD bytesRead; 137 | size_t totalBytes = 0; 138 | while (InternetReadFile(hConnect, buffer, sizeof(buffer), &bytesRead)) { 139 | if (bytesRead == 0) break; 140 | outFile.write(buffer, bytesRead); 141 | totalBytes += bytesRead; 142 | } 143 | 144 | outFile.close(); 145 | InternetCloseHandle(hConnect); 146 | InternetCloseHandle(hInternet); 147 | 148 | Logger::logSuccess(std::format("Downloaded {} bytes to {}", totalBytes, outputPath)); 149 | 150 | if (contentLength > 0 && totalBytes != contentLength) { 151 | Logger::logWarning(std::format("Download incomplete: Expected {} bytes, got {}", contentLength, totalBytes)); 152 | std::filesystem::remove(outputPath); 153 | return false; 154 | } 155 | 156 | return true; 157 | } 158 | 159 | bool Updater::isValidExecutable(const std::string& filePath) { 160 | std::ifstream file(filePath, std::ios::binary); 161 | if (!file) { 162 | Logger::logWarning(std::format("Cannot open file for validation: {}", filePath)); 163 | return false; 164 | } 165 | 166 | char header[2]; 167 | file.read(header, 2); 168 | file.close(); 169 | bool isValid = (header[0] == 'M' && header[1] == 'Z'); 170 | if (!isValid) { 171 | Logger::logWarning(std::format("File is not a valid PE executable: {}", filePath)); 172 | } 173 | return isValid; 174 | } 175 | 176 | bool Updater::replaceExecutable(const std::string& newFilePath) { 177 | if (!isValidExecutable(newFilePath)) { 178 | Logger::logWarning("Downloaded file is not a valid executable"); 179 | return false; 180 | } 181 | 182 | std::string backupPath = currentExeName + "_old.exe"; 183 | std::string currentPath = currentExeName; 184 | 185 | // Get TEMP directory 186 | char temp_dir[MAX_PATH]; 187 | if (GetTempPathA(MAX_PATH, temp_dir) == 0) { 188 | Logger::logWarning(std::format("Failed to get TEMP path: {}", GetLastError())); 189 | return false; 190 | } 191 | std::string batPath = std::string(temp_dir) + "update_" + random_suffix_ + ".bat"; 192 | 193 | // Write batch file 194 | for (int retry = 0; retry < 3; ++retry) { 195 | std::ofstream batFile(batPath); 196 | if (batFile) { 197 | batFile << "@echo off\n"; 198 | batFile << "timeout /t 1 >nul\n"; 199 | batFile << "move /Y \"" << currentPath << "\" \"" << backupPath << "\"\n"; 200 | batFile << "move /Y \"" << newFilePath << "\" \"" << currentPath << "\"\n"; 201 | batFile << "if exist \"" << currentPath << "\" (\n"; 202 | batFile << " start \"\" \"" << currentPath << "\"\n"; 203 | batFile << " del \"" << backupPath << "\"\n"; 204 | batFile << " del \"%~f0\"\n"; 205 | batFile << ")\n"; 206 | batFile.close(); 207 | break; 208 | } 209 | Logger::logWarning(std::format("Retry {}: Failed to create batch file: {}", retry + 1, GetLastError())); 210 | std::this_thread::sleep_for(std::chrono::milliseconds(100)); 211 | } 212 | 213 | if (!std::filesystem::exists(batPath)) { 214 | Logger::logWarning("Failed to create batch file after retries"); 215 | return false; 216 | } 217 | 218 | // Run batch file 219 | SHELLEXECUTEINFOA sei = { sizeof(sei) }; 220 | sei.fMask = SEE_MASK_NOCLOSEPROCESS | SEE_MASK_NO_CONSOLE; 221 | sei.lpFile = batPath.c_str(); 222 | sei.lpVerb = "open"; 223 | sei.nShow = SW_HIDE; 224 | if (!ShellExecuteExA(&sei)) { 225 | Logger::logWarning(std::format("Failed to execute batch file: {}", GetLastError())); 226 | std::filesystem::remove(batPath); 227 | return false; 228 | } 229 | 230 | return true; 231 | } 232 | 233 | void Updater::cleanupTempFiles() { 234 | char temp_dir[MAX_PATH]; 235 | if (GetTempPathA(MAX_PATH, temp_dir) == 0) { 236 | Logger::logWarning(std::format("Failed to get TEMP path for cleanup: {}", GetLastError())); 237 | return; 238 | } 239 | 240 | std::string temp_exe = std::string(temp_dir) + "ErScripts_new_" + random_suffix_ + ".exe"; 241 | std::string temp_bat = std::string(temp_dir) + "update_" + random_suffix_ + ".bat"; 242 | 243 | if (std::filesystem::exists(temp_exe)) { 244 | std::filesystem::remove(temp_exe); 245 | Logger::logInfo(std::format("Cleaned up temp file: {}", temp_exe)); 246 | } 247 | if (std::filesystem::exists(temp_bat)) { 248 | std::filesystem::remove(temp_bat); 249 | Logger::logInfo(std::format("Cleaned up batch file: {}", temp_bat)); 250 | } 251 | } 252 | 253 | bool Updater::checkAndUpdate() { 254 | std::string jsonData = downloadJsonData(releasesUrl); 255 | if (jsonData.empty()) { 256 | Logger::logWarning("Could not check for updates."); 257 | return false; 258 | } 259 | 260 | try { 261 | auto json = nlohmann::json::parse(jsonData); 262 | std::string latestVersion = json["tag_name"].get().substr(1); 263 | 264 | if (latestVersion > currentVersion) { 265 | Logger::logInfo(std::format("New version available: {} (current: {})", latestVersion, currentVersion)); 266 | 267 | std::string downloadUrl = "https://github.com/" + githubAuthor + "/" + repoName + "/releases/download/v" + latestVersion + "/" + downloadExeName; 268 | 269 | // Use TEMP for temp file 270 | char temp_dir[MAX_PATH]; 271 | if (GetTempPathA(MAX_PATH, temp_dir) == 0) { 272 | Logger::logWarning(std::format("Failed to get TEMP path: {}", GetLastError())); 273 | return false; 274 | } 275 | std::string tempPath = std::string(temp_dir) + "ErScripts_new_" + random_suffix_ + ".exe"; 276 | 277 | if (downloadFile(downloadUrl, tempPath)) { 278 | if (replaceExecutable(tempPath)) { 279 | Logger::logSuccess("Update initiated. Restarting..."); 280 | return true; 281 | } 282 | } 283 | std::filesystem::remove(tempPath); 284 | return false; 285 | } 286 | else { 287 | return false; 288 | } 289 | } 290 | catch (const nlohmann::json::exception& e) { 291 | Logger::logWarning(std::format("JSON parsing error: {}", e.what())); 292 | return false; 293 | } 294 | } -------------------------------------------------------------------------------- /ErScripts/Updater.h: -------------------------------------------------------------------------------- 1 | // Updater.h 2 | #pragma once 3 | #include 4 | 5 | class Updater { 6 | public: 7 | Updater(const std::string& currentVersion, const std::string& githubAuthor, const std::string& repoName, const std::string& downloadExeName); 8 | bool checkAndUpdate(); 9 | static void cleanupTempFiles(); 10 | 11 | private: 12 | std::string currentVersion; 13 | std::string currentExeName; 14 | std::string downloadExeName; 15 | std::string releasesUrl; 16 | std::string githubAuthor; 17 | std::string repoName; 18 | static std::string random_suffix_; 19 | 20 | std::string generateRandomString(size_t length); // Declared 21 | std::string getCurrentExeName(); 22 | std::wstring stringToWstring(const std::string& str); 23 | std::string downloadJsonData(const std::string& url); 24 | bool downloadFile(const std::string& url, const std::string& outputPath); 25 | bool isValidExecutable(const std::string& filePath); 26 | bool replaceExecutable(const std::string& newFilePath); 27 | }; -------------------------------------------------------------------------------- /ErScripts/erscripts.ico: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/emp0ry/cs2-ErScripts/78ed5dcc81561cc25fc577c8348559754272bcd6/ErScripts/erscripts.ico -------------------------------------------------------------------------------- /ErScripts/imgui/imconfig.h: -------------------------------------------------------------------------------- 1 | //----------------------------------------------------------------------------- 2 | // DEAR IMGUI COMPILE-TIME OPTIONS 3 | // Runtime options (clipboard callbacks, enabling various features, etc.) can generally be set via the ImGuiIO structure. 4 | // You can use ImGui::SetAllocatorFunctions() before calling ImGui::CreateContext() to rewire memory allocation functions. 5 | //----------------------------------------------------------------------------- 6 | // A) You may edit imconfig.h (and not overwrite it when updating Dear ImGui, or maintain a patch/rebased branch with your modifications to it) 7 | // B) or '#define IMGUI_USER_CONFIG "my_imgui_config.h"' in your project and then add directives in your own file without touching this template. 8 | //----------------------------------------------------------------------------- 9 | // You need to make sure that configuration settings are defined consistently _everywhere_ Dear ImGui is used, which include the imgui*.cpp 10 | // files but also _any_ of your code that uses Dear ImGui. This is because some compile-time options have an affect on data structures. 11 | // Defining those options in imconfig.h will ensure every compilation unit gets to see the same data structure layouts. 12 | // Call IMGUI_CHECKVERSION() from your .cpp file to verify that the data structures your files are using are matching the ones imgui.cpp is using. 13 | //----------------------------------------------------------------------------- 14 | 15 | #pragma once 16 | 17 | //---- Define assertion handler. Defaults to calling assert(). 18 | // If your macro uses multiple statements, make sure is enclosed in a 'do { .. } while (0)' block so it can be used as a single statement. 19 | //#define IM_ASSERT(_EXPR) MyAssert(_EXPR) 20 | //#define IM_ASSERT(_EXPR) ((void)(_EXPR)) // Disable asserts 21 | 22 | //---- Define attributes of all API symbols declarations, e.g. for DLL under Windows 23 | // Using Dear ImGui via a shared library is not recommended, because of function call overhead and because we don't guarantee backward nor forward ABI compatibility. 24 | // - Windows DLL users: heaps and globals are not shared across DLL boundaries! You will need to call SetCurrentContext() + SetAllocatorFunctions() 25 | // for each static/DLL boundary you are calling from. Read "Context and Memory Allocators" section of imgui.cpp for more details. 26 | //#define IMGUI_API __declspec(dllexport) // MSVC Windows: DLL export 27 | //#define IMGUI_API __declspec(dllimport) // MSVC Windows: DLL import 28 | //#define IMGUI_API __attribute__((visibility("default"))) // GCC/Clang: override visibility when set is hidden 29 | 30 | //---- Don't define obsolete functions/enums/behaviors. Consider enabling from time to time after updating to clean your code of obsolete function/names. 31 | //#define IMGUI_DISABLE_OBSOLETE_FUNCTIONS 32 | 33 | //---- Disable all of Dear ImGui or don't implement standard windows/tools. 34 | // It is very strongly recommended to NOT disable the demo windows and debug tool during development. They are extremely useful in day to day work. Please read comments in imgui_demo.cpp. 35 | //#define IMGUI_DISABLE // Disable everything: all headers and source files will be empty. 36 | //#define IMGUI_DISABLE_DEMO_WINDOWS // Disable demo windows: ShowDemoWindow()/ShowStyleEditor() will be empty. 37 | //#define IMGUI_DISABLE_DEBUG_TOOLS // Disable metrics/debugger and other debug tools: ShowMetricsWindow(), ShowDebugLogWindow() and ShowIDStackToolWindow() will be empty. 38 | 39 | //---- Don't implement some functions to reduce linkage requirements. 40 | //#define IMGUI_DISABLE_WIN32_DEFAULT_CLIPBOARD_FUNCTIONS // [Win32] Don't implement default clipboard handler. Won't use and link with OpenClipboard/GetClipboardData/CloseClipboard etc. (user32.lib/.a, kernel32.lib/.a) 41 | //#define IMGUI_ENABLE_WIN32_DEFAULT_IME_FUNCTIONS // [Win32] [Default with Visual Studio] Implement default IME handler (require imm32.lib/.a, auto-link for Visual Studio, -limm32 on command-line for MinGW) 42 | //#define IMGUI_DISABLE_WIN32_DEFAULT_IME_FUNCTIONS // [Win32] [Default with non-Visual Studio compilers] Don't implement default IME handler (won't require imm32.lib/.a) 43 | //#define IMGUI_DISABLE_WIN32_FUNCTIONS // [Win32] Won't use and link with any Win32 function (clipboard, IME). 44 | //#define IMGUI_ENABLE_OSX_DEFAULT_CLIPBOARD_FUNCTIONS // [OSX] Implement default OSX clipboard handler (need to link with '-framework ApplicationServices', this is why this is not the default). 45 | //#define IMGUI_DISABLE_DEFAULT_SHELL_FUNCTIONS // Don't implement default platform_io.Platform_OpenInShellFn() handler (Win32: ShellExecute(), require shell32.lib/.a, Mac/Linux: use system("")). 46 | //#define IMGUI_DISABLE_DEFAULT_FORMAT_FUNCTIONS // Don't implement ImFormatString/ImFormatStringV so you can implement them yourself (e.g. if you don't want to link with vsnprintf) 47 | //#define IMGUI_DISABLE_DEFAULT_MATH_FUNCTIONS // Don't implement ImFabs/ImSqrt/ImPow/ImFmod/ImCos/ImSin/ImAcos/ImAtan2 so you can implement them yourself. 48 | //#define IMGUI_DISABLE_FILE_FUNCTIONS // Don't implement ImFileOpen/ImFileClose/ImFileRead/ImFileWrite and ImFileHandle at all (replace them with dummies) 49 | //#define IMGUI_DISABLE_DEFAULT_FILE_FUNCTIONS // Don't implement ImFileOpen/ImFileClose/ImFileRead/ImFileWrite and ImFileHandle so you can implement them yourself if you don't want to link with fopen/fclose/fread/fwrite. This will also disable the LogToTTY() function. 50 | //#define IMGUI_DISABLE_DEFAULT_ALLOCATORS // Don't implement default allocators calling malloc()/free() to avoid linking with them. You will need to call ImGui::SetAllocatorFunctions(). 51 | //#define IMGUI_DISABLE_SSE // Disable use of SSE intrinsics even if available 52 | 53 | //---- Enable Test Engine / Automation features. 54 | //#define IMGUI_ENABLE_TEST_ENGINE // Enable imgui_test_engine hooks. Generally set automatically by include "imgui_te_config.h", see Test Engine for details. 55 | 56 | //---- Include imgui_user.h at the end of imgui.h as a convenience 57 | // May be convenient for some users to only explicitly include vanilla imgui.h and have extra stuff included. 58 | //#define IMGUI_INCLUDE_IMGUI_USER_H 59 | //#define IMGUI_USER_H_FILENAME "my_folder/my_imgui_user.h" 60 | 61 | //---- Pack colors to BGRA8 instead of RGBA8 (to avoid converting from one to another) 62 | //#define IMGUI_USE_BGRA_PACKED_COLOR 63 | 64 | //---- Use 32-bit for ImWchar (default is 16-bit) to support Unicode planes 1-16. (e.g. point beyond 0xFFFF like emoticons, dingbats, symbols, shapes, ancient languages, etc...) 65 | //#define IMGUI_USE_WCHAR32 66 | 67 | //---- Avoid multiple STB libraries implementations, or redefine path/filenames to prioritize another version 68 | // By default the embedded implementations are declared static and not available outside of Dear ImGui sources files. 69 | //#define IMGUI_STB_TRUETYPE_FILENAME "my_folder/stb_truetype.h" 70 | //#define IMGUI_STB_RECT_PACK_FILENAME "my_folder/stb_rect_pack.h" 71 | //#define IMGUI_STB_SPRINTF_FILENAME "my_folder/stb_sprintf.h" // only used if IMGUI_USE_STB_SPRINTF is defined. 72 | //#define IMGUI_DISABLE_STB_TRUETYPE_IMPLEMENTATION 73 | //#define IMGUI_DISABLE_STB_RECT_PACK_IMPLEMENTATION 74 | //#define IMGUI_DISABLE_STB_SPRINTF_IMPLEMENTATION // only disabled if IMGUI_USE_STB_SPRINTF is defined. 75 | 76 | //---- Use stb_sprintf.h for a faster implementation of vsnprintf instead of the one from libc (unless IMGUI_DISABLE_DEFAULT_FORMAT_FUNCTIONS is defined) 77 | // Compatibility checks of arguments and formats done by clang and GCC will be disabled in order to support the extra formats provided by stb_sprintf.h. 78 | //#define IMGUI_USE_STB_SPRINTF 79 | 80 | //---- Use FreeType to build and rasterize the font atlas (instead of stb_truetype which is embedded by default in Dear ImGui) 81 | // Requires FreeType headers to be available in the include path. Requires program to be compiled with 'misc/freetype/imgui_freetype.cpp' (in this repository) + the FreeType library (not provided). 82 | // On Windows you may use vcpkg with 'vcpkg install freetype --triplet=x64-windows' + 'vcpkg integrate install'. 83 | //#define IMGUI_ENABLE_FREETYPE 84 | 85 | //---- Use FreeType + plutosvg or lunasvg to render OpenType SVG fonts (SVGinOT) 86 | // Only works in combination with IMGUI_ENABLE_FREETYPE. 87 | // - lunasvg is currently easier to acquire/install, as e.g. it is part of vcpkg. 88 | // - plutosvg will support more fonts and may load them faster. It currently requires to be built manually but it is fairly easy. See misc/freetype/README for instructions. 89 | // - Both require headers to be available in the include path + program to be linked with the library code (not provided). 90 | // - (note: lunasvg implementation is based on Freetype's rsvg-port.c which is licensed under CeCILL-C Free Software License Agreement) 91 | //#define IMGUI_ENABLE_FREETYPE_PLUTOSVG 92 | //#define IMGUI_ENABLE_FREETYPE_LUNASVG 93 | 94 | //---- Use stb_truetype to build and rasterize the font atlas (default) 95 | // The only purpose of this define is if you want force compilation of the stb_truetype backend ALONG with the FreeType backend. 96 | //#define IMGUI_ENABLE_STB_TRUETYPE 97 | 98 | //---- Define constructor and implicit cast operators to convert back<>forth between your math types and ImVec2/ImVec4. 99 | // This will be inlined as part of ImVec2 and ImVec4 class declarations. 100 | /* 101 | #define IM_VEC2_CLASS_EXTRA \ 102 | constexpr ImVec2(const MyVec2& f) : x(f.x), y(f.y) {} \ 103 | operator MyVec2() const { return MyVec2(x,y); } 104 | 105 | #define IM_VEC4_CLASS_EXTRA \ 106 | constexpr ImVec4(const MyVec4& f) : x(f.x), y(f.y), z(f.z), w(f.w) {} \ 107 | operator MyVec4() const { return MyVec4(x,y,z,w); } 108 | */ 109 | //---- ...Or use Dear ImGui's own very basic math operators. 110 | //#define IMGUI_DEFINE_MATH_OPERATORS 111 | 112 | //---- Use 32-bit vertex indices (default is 16-bit) is one way to allow large meshes with more than 64K vertices. 113 | // Your renderer backend will need to support it (most example renderer backends support both 16/32-bit indices). 114 | // Another way to allow large meshes while keeping 16-bit indices is to handle ImDrawCmd::VtxOffset in your renderer. 115 | // Read about ImGuiBackendFlags_RendererHasVtxOffset for details. 116 | //#define ImDrawIdx unsigned int 117 | 118 | //---- Override ImDrawCallback signature (will need to modify renderer backends accordingly) 119 | //struct ImDrawList; 120 | //struct ImDrawCmd; 121 | //typedef void (*MyImDrawCallback)(const ImDrawList* draw_list, const ImDrawCmd* cmd, void* my_renderer_user_data); 122 | //#define ImDrawCallback MyImDrawCallback 123 | 124 | //---- Debug Tools: Macro to break in Debugger (we provide a default implementation of this in the codebase) 125 | // (use 'Metrics->Tools->Item Picker' to pick widgets with the mouse and break into them for easy debugging.) 126 | //#define IM_DEBUG_BREAK IM_ASSERT(0) 127 | //#define IM_DEBUG_BREAK __debugbreak() 128 | 129 | //---- Debug Tools: Enable slower asserts 130 | //#define IMGUI_DEBUG_PARANOID 131 | 132 | //---- Tip: You can add extra functions within the ImGui:: namespace from anywhere (e.g. your own sources/header files) 133 | /* 134 | namespace ImGui 135 | { 136 | void MyFunction(const char* name, MyMatrix44* mtx); 137 | } 138 | */ 139 | -------------------------------------------------------------------------------- /ErScripts/imgui/imgui_impl_dx11.h: -------------------------------------------------------------------------------- 1 | // dear imgui: Renderer Backend for DirectX11 2 | // This needs to be used along with a Platform Backend (e.g. Win32) 3 | 4 | // Implemented features: 5 | // [X] Renderer: User texture binding. Use 'ID3D11ShaderResourceView*' as ImTextureID. Read the FAQ about ImTextureID! 6 | // [X] Renderer: Large meshes support (64k+ vertices) with 16-bit indices. 7 | // [X] Renderer: Expose selected render state for draw callbacks to use. Access in '(ImGui_ImplXXXX_RenderState*)GetPlatformIO().Renderer_RenderState'. 8 | 9 | // You can use unmodified imgui_impl_* files in your project. See examples/ folder for examples of using this. 10 | // Prefer including the entire imgui/ repository into your project (either as a copy or as a submodule), and only build the backends you need. 11 | // Learn about Dear ImGui: 12 | // - FAQ https://dearimgui.com/faq 13 | // - Getting Started https://dearimgui.com/getting-started 14 | // - Documentation https://dearimgui.com/docs (same as your local docs/ folder). 15 | // - Introduction, links and more at the top of imgui.cpp 16 | 17 | #pragma once 18 | #include "imgui.h" // IMGUI_IMPL_API 19 | #ifndef IMGUI_DISABLE 20 | 21 | struct ID3D11Device; 22 | struct ID3D11DeviceContext; 23 | struct ID3D11SamplerState; 24 | 25 | // Follow "Getting Started" link and check examples/ folder to learn about using backends! 26 | IMGUI_IMPL_API bool ImGui_ImplDX11_Init(ID3D11Device* device, ID3D11DeviceContext* device_context); 27 | IMGUI_IMPL_API void ImGui_ImplDX11_Shutdown(); 28 | IMGUI_IMPL_API void ImGui_ImplDX11_NewFrame(); 29 | IMGUI_IMPL_API void ImGui_ImplDX11_RenderDrawData(ImDrawData* draw_data); 30 | 31 | // Use if you want to reset your rendering device without losing Dear ImGui state. 32 | IMGUI_IMPL_API bool ImGui_ImplDX11_CreateDeviceObjects(); 33 | IMGUI_IMPL_API void ImGui_ImplDX11_InvalidateDeviceObjects(); 34 | 35 | // [BETA] Selected render state data shared with callbacks. 36 | // This is temporarily stored in GetPlatformIO().Renderer_RenderState during the ImGui_ImplDX11_RenderDrawData() call. 37 | // (Please open an issue if you feel you need access to more data) 38 | struct ImGui_ImplDX11_RenderState 39 | { 40 | ID3D11Device* Device; 41 | ID3D11DeviceContext* DeviceContext; 42 | ID3D11SamplerState* SamplerDefault; 43 | }; 44 | 45 | #endif // #ifndef IMGUI_DISABLE 46 | -------------------------------------------------------------------------------- /ErScripts/imgui/imgui_impl_win32.h: -------------------------------------------------------------------------------- 1 | // dear imgui: Platform Backend for Windows (standard windows API for 32-bits AND 64-bits applications) 2 | // This needs to be used along with a Renderer (e.g. DirectX11, OpenGL3, Vulkan..) 3 | 4 | // Implemented features: 5 | // [X] Platform: Clipboard support (for Win32 this is actually part of core dear imgui) 6 | // [X] Platform: Mouse support. Can discriminate Mouse/TouchScreen/Pen. 7 | // [X] Platform: Keyboard support. Since 1.87 we are using the io.AddKeyEvent() function. Pass ImGuiKey values to all key functions e.g. ImGui::IsKeyPressed(ImGuiKey_Space). [Legacy VK_* values are obsolete since 1.87 and not supported since 1.91.5] 8 | // [X] Platform: Gamepad support. Enabled with 'io.ConfigFlags |= ImGuiConfigFlags_NavEnableGamepad'. 9 | // [X] Platform: Mouse cursor shape and visibility. Disable with 'io.ConfigFlags |= ImGuiConfigFlags_NoMouseCursorChange'. 10 | 11 | // You can use unmodified imgui_impl_* files in your project. See examples/ folder for examples of using this. 12 | // Prefer including the entire imgui/ repository into your project (either as a copy or as a submodule), and only build the backends you need. 13 | // Learn about Dear ImGui: 14 | // - FAQ https://dearimgui.com/faq 15 | // - Getting Started https://dearimgui.com/getting-started 16 | // - Documentation https://dearimgui.com/docs (same as your local docs/ folder). 17 | // - Introduction, links and more at the top of imgui.cpp 18 | 19 | #pragma once 20 | #include "imgui.h" // IMGUI_IMPL_API 21 | #ifndef IMGUI_DISABLE 22 | 23 | // Follow "Getting Started" link and check examples/ folder to learn about using backends! 24 | IMGUI_IMPL_API bool ImGui_ImplWin32_Init(void* hwnd); 25 | IMGUI_IMPL_API bool ImGui_ImplWin32_InitForOpenGL(void* hwnd); 26 | IMGUI_IMPL_API void ImGui_ImplWin32_Shutdown(); 27 | IMGUI_IMPL_API void ImGui_ImplWin32_NewFrame(); 28 | 29 | // Win32 message handler your application need to call. 30 | // - Intentionally commented out in a '#if 0' block to avoid dragging dependencies on from this helper. 31 | // - You should COPY the line below into your .cpp code to forward declare the function and then you can call it. 32 | // - Call from your application's message handler. Keep calling your message handler unless this function returns TRUE. 33 | 34 | #if 0 35 | extern IMGUI_IMPL_API LRESULT ImGui_ImplWin32_WndProcHandler(HWND hWnd, UINT msg, WPARAM wParam, LPARAM lParam); 36 | #endif 37 | 38 | // DPI-related helpers (optional) 39 | // - Use to enable DPI awareness without having to create an application manifest. 40 | // - Your own app may already do this via a manifest or explicit calls. This is mostly useful for our examples/ apps. 41 | // - In theory we could call simple functions from Windows SDK such as SetProcessDPIAware(), SetProcessDpiAwareness(), etc. 42 | // but most of the functions provided by Microsoft require Windows 8.1/10+ SDK at compile time and Windows 8/10+ at runtime, 43 | // neither we want to require the user to have. So we dynamically select and load those functions to avoid dependencies. 44 | IMGUI_IMPL_API void ImGui_ImplWin32_EnableDpiAwareness(); 45 | IMGUI_IMPL_API float ImGui_ImplWin32_GetDpiScaleForHwnd(void* hwnd); // HWND hwnd 46 | IMGUI_IMPL_API float ImGui_ImplWin32_GetDpiScaleForMonitor(void* monitor); // HMONITOR monitor 47 | 48 | // Transparency related helpers (optional) [experimental] 49 | // - Use to enable alpha compositing transparency with the desktop. 50 | // - Use together with e.g. clearing your framebuffer with zero-alpha. 51 | IMGUI_IMPL_API void ImGui_ImplWin32_EnableAlphaCompositing(void* hwnd); // HWND hwnd 52 | 53 | #endif // #ifndef IMGUI_DISABLE 54 | -------------------------------------------------------------------------------- /ErScripts/main.cpp: -------------------------------------------------------------------------------- 1 | #include "GSIServer.h" 2 | #include "Updater.h" 3 | #include "UIAccess.h" 4 | #include "Rebuild.h" 5 | #include "ErScripts.h" 6 | #include "Overlay.h" 7 | 8 | #define APP_VERSION "1.2.3" 9 | 10 | int main(int argc, char* argv[]) { 11 | Logger::EnableANSIColors(); 12 | 13 | if (!IsDebuggerPresent()) { 14 | /* Rebuild */ 15 | //bool should_rebuild = true; 16 | //for (int i = 1; i < argc; ++i) { 17 | // if (std::string(argv[i]) == "--run") { 18 | // should_rebuild = false; 19 | // break; 20 | // } 21 | //} 22 | 23 | // Check if program already running 24 | CreateMutexA(0, FALSE, "Local\\erscripts"); 25 | if (GetLastError() == ERROR_ALREADY_EXISTS) { 26 | //Rebuild::cleanupTempFiles(); 27 | Updater::cleanupTempFiles(); 28 | MessageBoxA(NULL, "ErScripts is already running!", 0, MB_OK); 29 | return -1; 30 | } 31 | 32 | // Rebuild or unpack 33 | //Rebuild rebuilder; 34 | //if (!rebuilder.rebuildAndRelaunch(should_rebuild)) { 35 | // std::cout << "[*] Rebuild or unpack failed. Continuing with current binary.\n"; 36 | //} 37 | //if (should_rebuild) { 38 | // return 0; // Exit after relaunch 39 | //} 40 | 41 | /* Auto updater */ 42 | Updater updater(APP_VERSION, "emp0ry", "cs2-ErScripts", "ErScripts"); 43 | if (updater.checkAndUpdate()) 44 | return 0; 45 | 46 | /* Ui Access */ 47 | DWORD dwErr = PrepareForUIAccess(); 48 | if (dwErr != ERROR_SUCCESS) { 49 | Logger::logWarning(std::format("Failed to prepare for UI Access: {}", dwErr)); 50 | std::cout << "[*] Press Enter to exit..."; 51 | std::cin.get(); 52 | return -1; 53 | } 54 | } 55 | 56 | SetConsoleTitleA(std::format("ErScripts {}", APP_VERSION).c_str()); 57 | 58 | std::cout << "[-] *---------------------------------------*" << std::endl; 59 | std::cout << "[>] | ErScripts by emp0ry |" << std::endl; 60 | std::cout << "[>] | GitHub - https://github.com/emp0ry/ |" << std::endl; 61 | std::cout << "[-] *---------------------------------------*" << std::endl; 62 | 63 | cfg->load("default"); 64 | gradient.setConfig(cfg->gradient); 65 | 66 | ErScripts es; 67 | es.Initalization(); 68 | 69 | Overlay overlay; 70 | overlay.run(); 71 | 72 | GSIServer gsi; 73 | gsi.run(); 74 | 75 | es.ConsoleLogStream(); 76 | es.AutoAccept(); 77 | es.BombTimer(); 78 | es.KillSound(); 79 | es.RoundStartAlert(); 80 | es.InitBinds(); 81 | es.PixelTrigger(); 82 | es.Crosshair(); 83 | es.RGBCrosshair(); 84 | es.KnifeSwitch(); 85 | es.AutoPistol(); 86 | es.AntiAfk(); 87 | es.CS2Binds(); 88 | es.KillSay(); 89 | es.AutoStop(); 90 | es.ChatSpammer(); 91 | 92 | while (!globals::finish) { 93 | static int prevExitBind = cfg->erScriptsExitBind; 94 | static std::chrono::steady_clock::time_point changeTime; 95 | static bool isDelayActive = false; 96 | 97 | if (prevExitBind != cfg->erScriptsExitBind) { 98 | prevExitBind = cfg->erScriptsExitBind; 99 | changeTime = std::chrono::steady_clock::now(); 100 | isDelayActive = true; 101 | } 102 | else if (isDelayActive) { 103 | auto now = std::chrono::steady_clock::now(); 104 | auto elapsed = std::chrono::duration_cast(now - changeTime).count(); 105 | if (elapsed >= 2000) { 106 | isDelayActive = false; 107 | } 108 | } 109 | else { 110 | if (GetAsyncKeyState(cfg->erScriptsExitBind) & 0x8000) globals::finish = true; 111 | } 112 | 113 | /* Update Ping */ 114 | if (ErScripts::GetWindowState && ErScripts::GetCursorState()) { 115 | static auto lastUpdate = std::chrono::steady_clock::now(); 116 | 117 | auto now = std::chrono::steady_clock::now(); 118 | std::chrono::duration elapsed = now - lastUpdate; 119 | 120 | if (cfg->watermarkState) { 121 | if (elapsed.count() >= cfg->watermarkPingUpdateRate) { 122 | ErScripts::CommandsSender(5, "sys_info;clear"); 123 | lastUpdate = now; 124 | } 125 | } 126 | 127 | if (cfg->sniperCrosshairState || cfg->recoilCrosshairState || cfg->angleBindState) { 128 | if (elapsed.count() >= 1.5f) { 129 | ErScripts::CommandsSender(5, "print_changed_convars; clear; echo *---------------------------------------*; echo | ErScripts by emp0ry |; echo | GitHub - github.com/emp0ry/ |; echo *---------------------------------------*"); 130 | } 131 | } 132 | } 133 | 134 | std::this_thread::sleep_for(std::chrono::milliseconds(25)); 135 | } 136 | 137 | gsi.stop(); 138 | 139 | exit(0); 140 | } -------------------------------------------------------------------------------- /ErScripts/resource.h: -------------------------------------------------------------------------------- 1 | //{{NO_DEPENDENCIES}} 2 | // Microsoft Visual C++ generated include file. 3 | // Used by ErScripts.rc 4 | // 5 | #define IDI_ICON1 103 6 | 7 | // Next default values for new objects 8 | // 9 | #ifdef APSTUDIO_INVOKED 10 | #ifndef APSTUDIO_READONLY_SYMBOLS 11 | #define _APS_NEXT_RESOURCE_VALUE 104 12 | #define _APS_NEXT_COMMAND_VALUE 40001 13 | #define _APS_NEXT_CONTROL_VALUE 1001 14 | #define _APS_NEXT_SYMED_VALUE 101 15 | #endif 16 | #endif 17 | -------------------------------------------------------------------------------- /Image/bombtimer.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/emp0ry/cs2-ErScripts/78ed5dcc81561cc25fc577c8348559754272bcd6/Image/bombtimer.gif -------------------------------------------------------------------------------- /Image/crosshair.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/emp0ry/cs2-ErScripts/78ed5dcc81561cc25fc577c8348559754272bcd6/Image/crosshair.gif -------------------------------------------------------------------------------- /Image/keystrokes.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/emp0ry/cs2-ErScripts/78ed5dcc81561cc25fc577c8348559754272bcd6/Image/keystrokes.gif -------------------------------------------------------------------------------- /Image/menu1.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/emp0ry/cs2-ErScripts/78ed5dcc81561cc25fc577c8348559754272bcd6/Image/menu1.png -------------------------------------------------------------------------------- /Image/menu2.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/emp0ry/cs2-ErScripts/78ed5dcc81561cc25fc577c8348559754272bcd6/Image/menu2.png -------------------------------------------------------------------------------- /LICENSE.txt: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2025 emp0ry 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # ErScripts 🎮 2 | [![GitHub release](https://img.shields.io/github/v/release/emp0ry/cs2-ErScripts?label=Latest%20Version)](https://github.com/emp0ry/cs2-ErScripts/releases/latest) 3 | [![GitHub release](https://img.shields.io/github/downloads/emp0ry/cs2-ErScripts/total.svg)](https://github.com/emp0ry/cs2-ErScripts/releases/latest) 4 | [![License](https://img.shields.io/github/license/emp0ry/cs2-ErScripts)](LICENSE.txt) 5 | 6 | ### Enhance Your *Counter-Strike 2* Experience 7 | 8 | **ErScripts** is a lightweight, open-source tool crafted for *Counter-Strike 2 (CS2)* fans to personalize and streamline their gameplay. It operates externally—never modifying game memory or files—using only what CS2 provides through console commands, Game State Integration (GSI), and log files. 9 | 10 | --- 11 | 12 | ## ⚠️ Disclaimer 13 | 14 | **ErScripts** is a personal project by an independent developer, not affiliated with or endorsed by Valve Corporation, the creators of CS2. It offers features like *Auto Accept*, *Jump Throw*, and *Bomb Timer* to enhance convenience and fun. However: 15 | 16 | - **Valve’s Rules**: Some features automate actions via external inputs or scripts. Valve discourages automation in competitive play, and while *ErScripts* avoids game memory manipulation (no VAC violations), **use in official matchmaking may still risk account bans** at Valve’s discretion. 17 | - **User Responsibility**: You are solely responsible for complying with [Valve’s Terms of Service](https://store.steampowered.com/subscriber_agreement/) and [CS2 Rules](https://www.counter-strike.net/). The developer is not liable for bans, data issues, or loss of functionality. 18 | - **Intended Use**: This tool is for personal enjoyment, experimentation, and learning—not for gaining unfair advantages. Use it responsibly in appropriate settings (e.g., private servers or casual play) to respect the CS2 community. 19 | 20 | --- 21 | 22 | ## ✨ What is ErScripts? 23 | 24 | **ErScripts** is a utility that adds convenience and flair to CS2 through an external overlay and automation features. It leverages: 25 | 26 | - **External Overlay**: Tracks CS2’s window for visual enhancements. 27 | - **Config & Binds**: Executes commands via CS2’s built-in `exec` system (e.g., `bind "KEY" "exec erscripts"`). 28 | - **Game State Integration (GSI)**: Reads live game data like weapon status or bomb timers. 29 | - **Console Logs**: Parses `console.log` for events like match acceptance. 30 | 31 | No game files are altered beyond user-created configs, and no hacks are involved—just clean, creative tools for CS2 enthusiasts. 32 | 33 | --- 34 | 35 | ## 🚀 Features 36 | 37 | Here’s what *ErScripts* offers: 38 | 39 | | Feature | Description | 40 | |--------------------------|-----------------------------------------------------------------------------| 41 | | **Auto Accept** | Clicks "Accept" when a match is found via `console.log` monitoring. | 42 | | ~~**Pixel Trigger**~~ | ~~Disabled feature (previously fired on color changes).~~ | 43 | | **Bomb Timer** | Shows a real-time bomb countdown with defuse kit alerts via GSI. | 44 | | **Sniper Crosshair** | Overlays a custom crosshair for snipers, synced with in-game settings. | 45 | | **Recoil Crosshair** | Enable recoil crosshair with a static center crosshair. | 46 | | **RGB Crosshair** | Adds a gradient effect to your crosshair using console commands. | 47 | | **Keystrokes** | Displays WASD and mouse inputs on-screen for streams or fun. | 48 | | **Knife Switch** | Switches knife hand based on your weapon via `switchhands`. | 49 | | **Auto Pistol** | Rapid-fires pistols by repeating `attack` commands. | 50 | | **Anti AFK** | Prevents AFK kicks with subtle inputs. | 51 | | **Long Jump** | Combines duck + jump for longer leaps with one keypress. | 52 | | **Jump Throw** | Simplifies jump-throw combos for consistent utility tosses. | 53 | | **Drop Bomb** | Drops the bomb and switches back instantly. | 54 | | **Kill Say** | Sends a custom chat message after kills. | 55 | | **Kill Sound** | Plays a sound on kills for extra flair. | 56 | | **Round Start Alert** | Sounds an alert if a round begins while you’re tabbed out. | 57 | | **Auto Stop** | Press the opposite key for an auto stop. | 58 | | **Chat Spammer** | Automatically send repeated messages in chat. | 59 | | **Angle Bind** | Bind to offset yaw (horizontal view) angle. | 60 | | **Watermark** | Shows ping, time, and game info as an overlay. | 61 | | **FPS Limiter** | Caps overlay FPS for smoother performance. | 62 | | **Capture Bypass** | Hides the overlay from recordings/streams. | 63 | 64 | --- 65 | 66 | ## 🛠️ Installation 67 | 68 | Get started easily: 69 | 70 | 1. **Download**: Grab the latest release from [GitHub Releases](https://github.com/emp0ry/cs2-ErScripts/releases). 71 | 2. **Set Launch Options**: In Steam, add `-conclearlog -condebug +bind scancode104 exec erscripts1` to CS2’s launch options. 72 | 3. **First Run**: Launch `ErScripts.exe` *before* CS2 (afterward, it’s fine to start with CS2 running). 73 | 4. **Play**: Open CS2, config settings in the ErScripts menu (*press Insert to show/hide the menu*), and enjoy! 74 | 5. **Exit**: Close via the "X" button in the menu or press the "End" key. 75 | 6. **Fullscreen Mode**: To play in fullscreen mode, right-click `cs2.exe`, go to **Properties → Compatibility**, and **uncheck** the *Disable fullscreen optimizations*. 76 | 77 | You can change the key bindings for opening the menu and exiting the program in **Menu → Custom Binds**. 78 | 79 | --- 80 | 81 | ## ⚙️ Configuration 82 | 83 | Tailor *ErScripts* to your liking: 84 | 85 | ### Auto Accept 86 | - **Waiting Time**: Delay (in seconds) before searching for the "Accept" button. 87 | 88 | ### Bomb Timer 89 | - **Scale**: Adjust timer size. 90 | - **Gradient**: Enable/disable gradient icon. 91 | - **Transparency**: Set background opacity. 92 | 93 | ### Sniper Crosshair 94 | - **Reload Icon**: Syncs with your in-game crosshair settings. 95 | 96 | ### Keystrokes 97 | - **Scale**: Size of the display. 98 | - **Gradient**: Toggle gradient text. 99 | - **Animation Speed**: Speed of keypress animations. 100 | - **Colors**: Customize pressed/released colors. 101 | - **Transparency**: Opacity when keys are released. 102 | 103 | ### Kill Sound, Round Start Alert 104 | - **Volume**: Adjust sound loudness. 105 | - **File Name**: Specify the WAV file for the custom sound. 106 | - Leave empty to use the default sound. 107 | - Enter a custom WAV file (e.g., `sound.wav`) located in the executable’s folder. 108 | - You can write without specifying `.wav` (e.g., if it’s `sound.wav`, just write `sound`). 109 | - You can use a subfolder like `sounds` (e.g., `sounds/audio.wav` or `sounds/audio`). 110 | - If the file is missing or invalid, it defaults to the built-in sound. 111 | 112 | ### Auto Stop 113 | - **Toggle**: Enables hotkey to toggle auto-stop on/off (true) or activates it only while held (false). 114 | 115 | ### Chat Spammer 116 | - **Chat Message**: Message to be repeatedly sent in chat. 117 | - **Hotkey**: Bind to toggle chat spammer on/off. If set to `None`, the chat spammer will run without a hotkey. 118 | 119 | ### Angle Bind 120 | - **Hotkey** Bind to offset the player yaw (horizontal view) angle. 121 | - **Degree** The amount of yaw offset, from -180° to 180°. 122 | 123 | ### Watermark 124 | - **Gradient**: Toggle gradient text. 125 | - **Transparency**: Background opacity. 126 | - **Ping Update Rate**: Refresh frequency for ping display. 127 | 128 | ### Gradient Manager (RGB Effects) 129 | - **Steps**: Smoothness of color transitions. 130 | - **Delay**: Speed of color shifts. 131 | - **Start/End Hue**: Choose color range. 132 | - **Saturation**: Color intensity (0 = gray, 1 = vibrant). 133 | - **Value**: Brightness (0 = dark, 1 = bright). 134 | 135 | *Other features (e.g., Knife Switch, Jump Throw, FPS Limiter) are easy to configure—no details needed here!* 136 | 137 | --- 138 | 139 | ## 🧠 How It Works 140 | 141 | *ErScripts* enhances CS2 safely and externally by: 142 | 143 | - **Command Sending**: Features like *Jump Throw*, *Drop Bomb*, and *Auto Pistol* work by writing CS2 commands (e.g., `+jump; -attack`) to numbered config files (e.g., `erscripts1.cfg`). These are triggered via pre-bound keys (e.g., `F13` to `F24`) simulated by *ErScripts*. In CS2, you bind a key to `exec erscripts1` (set via launch options), and *ErScripts* presses that key to run the command. 144 | - **Auto Accept**: Monitors `console.log` for match detection, then simulates a mouse click on the "Accept" button. 145 | - **Sniper Crosshair**: Reads active weapon data from GSI and overlays a custom crosshair synced with your settings. 146 | - **Bomb Timer**: Tracks bomb state via GSI, updating the display with defuse urgency cues. 147 | - **RGB Crosshair**: Cycles colors by sending console commands to adjust crosshair settings. 148 | - **Knife Switch**: Uses `switchhands` via config files triggered by keybinds. 149 | 150 | This method relies entirely on CS2’s native systems—no memory reading, writing, or injection—keeping it aligned with standard scripting practices while avoiding game file tampering. 151 | 152 | --- 153 | 154 | ## 🖼️ Images 155 | 156 | ### Menu 157 | | ![Menu1](./Image/menu1.png) | ![Menu2](./Image/menu2.png) | 158 | |-----------------------------|-----------------------------| 159 | 160 | ### Keystrokes, Crosshair, Bomb Timer 161 | | ![Keystrokes](./Image/keystrokes.gif) | ![Crosshair](./Image/crosshair.gif)
![Bomb Timer](./Image/bombtimer.gif) | 162 | |---------------------------------------|-------------------------------------------------------------------------------| 163 | 164 | --- 165 | 166 | ## 🛡️ Built With 167 | 168 | Powered by these amazing tools: 169 | - **[Dear ImGui](https://github.com/ocornut/imgui)**: Overlay and UI framework. 170 | - **[nlohmann/json](https://github.com/nlohmann/json)**: GSI and config parsing. 171 | - **[cpp-httplib](https://github.com/yhirose/cpp-httplib)**: GSI data handling. 172 | - **Windows API**: Window tracking and input simulation. 173 | - **Standard C++**: Core functionality and file management. 174 | 175 | --- 176 | 177 | ## 💖 Support the Project 178 | 179 | Love ErScripts? Fuel its development with a coffee! 180 | 181 | [![Buy Me a Coffee](https://www.buymeacoffee.com/assets/img/custom_images/orange_img.png)](https://www.buymeacoffee.com/emp0ry) 182 | 183 | --- 184 | 185 | ## 📜 License 186 | 187 | Released under the [MIT License](LICENSE.txt). Feel free to use, modify, and share—just keep the original license and credit [emp0ry](https://github.com/emp0ry). 188 | 189 | --- 190 | 191 | ## 🌟 Get Involved 192 | 193 | Spotted a bug? Have a feature idea? 194 | - File an [Issue](https://github.com/emp0ry/cs2-ErScripts/issues) or submit a [Pull Request](https://github.com/emp0ry/cs2-ErScripts/pulls). 195 | - Join the community and let’s elevate CS2 together! 196 | 197 | --- 198 | 199 | Created with ❤️ by [emp0ry](https://github.com/emp0ry) -------------------------------------------------------------------------------- /docs/css/styles.css: -------------------------------------------------------------------------------- 1 | html, body { 2 | height: 100%; 3 | margin: 0; 4 | padding: 0; 5 | } 6 | 7 | body { 8 | font-family: 'Poppins', sans-serif; 9 | background: linear-gradient(135deg, #2f0057 0%, #4b0082 50%, #8a2be2 100%); 10 | background: #4b0082; /* Fallback */ 11 | color: #ffffff; 12 | min-height: 100vh; 13 | overflow-x: hidden; 14 | display: flex; 15 | flex-direction: column; 16 | } 17 | 18 | .container { 19 | max-width: 900px; 20 | width: 90%; 21 | margin: 0 auto; 22 | /* padding: 40px 20px; */ 23 | flex: 1; 24 | } 25 | 26 | h1 { 27 | font-size: 3.5em; 28 | font-weight: 600; 29 | margin-bottom: 20px; 30 | color: #ffffff; 31 | text-shadow: 0 2px 4px rgba(0, 0, 0, 0.3); 32 | } 33 | 34 | h2 { 35 | font-size: 2em; 36 | font-weight: 600; 37 | margin-top: 40px; 38 | } 39 | 40 | p { 41 | font-size: 1.3em; 42 | font-weight: 400; 43 | line-height: 1.5; 44 | } 45 | 46 | .btn { 47 | display: inline-block; 48 | padding: 15px 35px; 49 | font-size: 1.2em; 50 | font-weight: 600; 51 | color: #ffffff; 52 | background: linear-gradient(90deg, #6a0dad, #9b30ff); 53 | text-decoration: none; 54 | border-radius: 50px; 55 | transition: transform 0.3s ease, box-shadow 0.3s ease; 56 | box-shadow: 0 5px 15px rgba(0, 0, 0, 0.3); 57 | -webkit-tap-highlight-color: rgba(0, 0, 0, 0); 58 | white-space: nowrap; 59 | text-align: center; 60 | } 61 | 62 | .btn:hover { 63 | transform: translateY(-3px); 64 | box-shadow: 0 8px 20px rgba(0, 0, 0, 0.4); 65 | } 66 | 67 | .btn:active { 68 | transform: scale(0.95); 69 | box-shadow: 0 3px 10px rgba(0, 0, 0, 0.2); 70 | } 71 | 72 | .about { 73 | background: rgba(25, 0, 46, 0.6); 74 | padding: 20px; 75 | border-radius: 15px; 76 | margin-bottom: 40px; 77 | -webkit-backdrop-filter: blur(12px); 78 | backdrop-filter: blur(12px); 79 | box-shadow: 0 4px 8px rgba(0, 0, 0, 0.2); 80 | display: flex; 81 | flex-wrap: wrap; 82 | justify-content: space-between; 83 | align-items: center; 84 | gap: 15px; 85 | } 86 | 87 | .about .btn.back-to-hub { 88 | float: none; 89 | } 90 | 91 | .features { 92 | display: grid; 93 | grid-template-columns: repeat(auto-fit, minmax(250px, 1fr)); 94 | gap: 20px; 95 | margin-bottom: 40px; 96 | } 97 | 98 | .feature-card { 99 | background: rgba(25, 0, 46, 0.6); 100 | padding: 20px; 101 | border-radius: 15px; 102 | box-shadow: 0 4px 8px rgba(0, 0, 0, 0.2); 103 | transition: transform 0.3s ease; 104 | -webkit-backdrop-filter: blur(12px); 105 | backdrop-filter: blur(12px); 106 | } 107 | 108 | .feature-card:hover { 109 | transform: translateY(-5px); 110 | } 111 | 112 | .feature-card h3 { 113 | font-size: 1.5em; 114 | margin-bottom: 10px; 115 | } 116 | 117 | footer { 118 | text-align: center; 119 | padding: 15px; 120 | font-size: 0.9em; 121 | background: rgba(47, 0, 87, 0.5); 122 | width: 100vw; 123 | margin-left: calc(-50vw + 50%); 124 | position: relative; 125 | box-sizing: border-box; 126 | color: #fff; 127 | margin-top: auto; 128 | } 129 | 130 | footer a { 131 | color: #9b30ff; 132 | text-decoration: none; 133 | } 134 | 135 | footer a:hover { 136 | text-decoration: underline; 137 | } 138 | 139 | /* Fallback for browsers without backdrop-filter support */ 140 | @supports not (backdrop-filter: blur(12px)) { 141 | .about, .feature-card { 142 | background: rgba(25, 0, 46, 0.8); 143 | } 144 | } 145 | 146 | /* Mobile responsiveness */ 147 | @media (max-width: 600px) { 148 | h1 { 149 | font-size: 2.5em; 150 | } 151 | h2 { 152 | font-size: 1.5em; 153 | } 154 | p { 155 | font-size: 1em; 156 | } 157 | .about { 158 | flex-direction: column; 159 | gap: 10px; 160 | align-items: stretch; 161 | } 162 | .btn { 163 | width: 100%; 164 | box-sizing: border-box; 165 | text-align: center; 166 | } 167 | .container { 168 | padding: 20px 10px; 169 | } 170 | .features { 171 | grid-template-columns: 1fr; 172 | } 173 | } 174 | 175 | /* Narrow screens (e.g., iPhone SE) */ 176 | @media (max-width: 400px) { 177 | h1 { 178 | font-size: 2.2em; 179 | } 180 | h2 { 181 | font-size: 1.3em; 182 | } 183 | p { 184 | font-size: 0.9em; 185 | } 186 | .about { 187 | padding: 15px; 188 | } 189 | .btn { 190 | width: 100%; 191 | text-align: center; 192 | } 193 | .container { 194 | padding: 15px 5px; 195 | } 196 | .feature-card { 197 | padding: 15px; 198 | } 199 | } -------------------------------------------------------------------------------- /docs/img/icon.ico: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/emp0ry/cs2-ErScripts/78ed5dcc81561cc25fc577c8348559754272bcd6/docs/img/icon.ico -------------------------------------------------------------------------------- /docs/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 33 | 34 | 35 | 36 | 37 | 38 | 39 | 40 | 41 | 42 | 43 | 44 | 45 | 46 | 47 | 48 | 49 | 50 | 51 | 52 | 53 | 54 | 55 | 56 | 57 | 58 | 59 | 60 | 61 | 62 | 63 | 64 | 65 | 66 | 67 | 68 | 69 | 70 | 71 | ErScripts - Safe CS2 Scripting Tools 72 | 73 | 74 | 89 | 90 | 91 |
92 |
93 |

ErScripts

94 |
95 |
96 | 97 |
98 |
99 |

ErScripts is a collection of scripts for Counter-Strike 2. No memory interaction or injections.

100 | Visit the Repository 101 | Back to Hub 102 |
103 | 104 |
105 |

Featured Scripts

106 |
107 |
108 |

Automation Tools

109 |

Streamline repetitive tasks to save time and effort.

110 |
111 |
112 |

Utility Helpers

113 |

Small, powerful tools to boost your gaming experience.

114 |
115 |
116 |

Custom Solutions

117 |

Tailored scripts for unique CS2 challenges.

118 |
119 |
120 |
121 |
122 | 123 | 126 | 127 | --------------------------------------------------------------------------------