// cstrike — универсальный нативный лаунчер CS (GoldSrc/hl.exe). // Определяет герцовку и разрешение монитора через WinAPI и запускает hl.exe // с параметрами выбранного мода, подставляя -freq и -w/-h. // // cstrike.exe infinity 1024 мод INFINITY (-steam ... -gl), 1024x768 // cstrike.exe gigabyte 1024 мод GIGABYTE, 1024x768 // cstrike.exe infinity мод INFINITY, НАТИВНОЕ разрешение монитора // cstrike.exe gigabyte мод GIGABYTE, НАТИВНОЕ разрешение монитора // cstrike.exe 800 мод по умолчанию (gigabyte), 800x600 // cstrike.exe info показать герцовку/разрешение мониторов // // hl.exe ищется в ПАПКЕ РЯДОМ С ЛАУНЧЕРОМ — положи cstrike.exe в каталог игры. // // Сборка (в "x64 Native Tools Command Prompt" или после vcvars64.bat): // cl /O2 /EHsc /std:c++17 cstrike.cpp user32.lib #include #include #include #include #include #include // === Наборы ключей по модам (взяты из .bat) === // -freq и -w/-h добавляются отдельно, поэтому в шаблоны не входят. static const wchar_t* kArgsInfinity = L"-game cstrike -steam " L"-noforcemacell -noforcemparms -noforcemspd " L"-nomsaa -nomaster -32bpp " L"+ex_interp 0.01 -gl"; static const wchar_t* kArgsGigabyte = L"-game cstrike " L"-noforcemacell -noforcemparms -noforcemspd " L"-nomsaa -nomaster -32bpp " L"+ex_interp 0.01 -gl"; // Пресеты разрешения (ширина -> высота, 4:3). struct Preset { int w; int h; }; static const Preset kPresets[] = { {640, 480}, {800, 600}, {1024, 768} }; static const int kDefaultWidth = 1024; static bool PresetHeight(int width, int& outHeight) { for (const Preset& p : kPresets) if (p.w == width) { outHeight = p.h; return true; } return false; } // Полный путь к hl.exe в каталоге, где лежит сам лаунчер. static std::wstring GameExePath() { wchar_t self[MAX_PATH]; const DWORD n = GetModuleFileNameW(nullptr, self, MAX_PATH); std::wstring dir(self, n); const size_t slash = dir.find_last_of(L"\\/"); if (slash != std::wstring::npos) dir.resize(slash + 1); return dir + L"hl.exe"; } // Текущий режим основного монитора. Возвращает false, если не определён. static bool GetPrimaryMode(DWORD& width, DWORD& height, DWORD& freq) { DISPLAY_DEVICEW dev = {}; dev.cb = sizeof(dev); for (DWORD i = 0; EnumDisplayDevicesW(nullptr, i, &dev, 0); ++i, dev.cb = sizeof(dev)) { const bool active = (dev.StateFlags & DISPLAY_DEVICE_ATTACHED_TO_DESKTOP) != 0; const bool primary = (dev.StateFlags & DISPLAY_DEVICE_PRIMARY_DEVICE) != 0; if (!active || !primary) continue; DEVMODEW dm = {}; dm.dmSize = sizeof(dm); if (EnumDisplaySettingsW(dev.DeviceName, ENUM_CURRENT_SETTINGS, &dm)) { width = dm.dmPelsWidth; height = dm.dmPelsHeight; freq = dm.dmDisplayFrequency; return true; } } return false; } static void PrintDisplays() { wprintf(L"=== Активные дисплеи ===\n\n"); int found = 0; DISPLAY_DEVICEW dev = {}; dev.cb = sizeof(dev); for (DWORD i = 0; EnumDisplayDevicesW(nullptr, i, &dev, 0); ++i, dev.cb = sizeof(dev)) { if ((dev.StateFlags & DISPLAY_DEVICE_ATTACHED_TO_DESKTOP) == 0) continue; const bool primary = (dev.StateFlags & DISPLAY_DEVICE_PRIMARY_DEVICE) != 0; DEVMODEW dm = {}; dm.dmSize = sizeof(dm); if (!EnumDisplaySettingsW(dev.DeviceName, ENUM_CURRENT_SETTINGS, &dm)) continue; ++found; std::wstring monitorName = dev.DeviceString; DISPLAY_DEVICEW mon = {}; mon.cb = sizeof(mon); if (EnumDisplayDevicesW(dev.DeviceName, 0, &mon, 0)) monitorName = mon.DeviceString; wprintf(L"Дисплей #%d%s\n", found, primary ? L" [основной]" : L""); wprintf(L" Устройство : %s\n", dev.DeviceName); wprintf(L" Монитор : %s\n", monitorName.c_str()); wprintf(L" Разрешение : %lu x %lu\n", dm.dmPelsWidth, dm.dmPelsHeight); wprintf(L" Частота (Гц) : %lu\n", dm.dmDisplayFrequency); wprintf(L" Глубина цвета: %lu бит\n", dm.dmBitsPerPel); wprintf(L" Позиция : (%ld, %ld)\n\n", dm.dmPosition.x, dm.dmPosition.y); } if (found == 0) wprintf(L"Активные дисплеи не найдены.\n"); } // modArgs — набор ключей мода; width < 0 — нативное разрешение, иначе пресет 4:3. static int LaunchGame(const wchar_t* modName, const wchar_t* modArgs, int width) { DWORD nativeW = 0, nativeH = 0, freq = 0; const bool haveMode = GetPrimaryMode(nativeW, nativeH, freq); int useW, useH; if (width > 0) { useW = width; PresetHeight(width, useH); } else if (haveMode && nativeW > 0 && nativeH > 0) { useW = static_cast(nativeW); useH = static_cast(nativeH); } else { PresetHeight(kDefaultWidth, useH); useW = kDefaultWidth; wprintf(L"Не удалось определить нативное разрешение — использую %dx%d.\n", useW, useH); } wprintf(L"Мод: %s\n", modName); if (haveMode && freq > 0) wprintf(L"Detected Refresh Rate: %lu Hz\n", freq); else wprintf(L"Не удалось определить герцовку — запуск без -freq.\n"); wprintf(L"Разрешение: %d x %d%s\n", useW, useH, width > 0 ? L" (пресет)" : L" (нативное)"); const std::wstring exe = GameExePath(); // Собираем командную строку: exe + ключи мода + -w/-h + -freq. std::wstring cmd = L"\""; cmd += exe; cmd += L"\" "; cmd += modArgs; wchar_t tail[128]; if (haveMode && freq > 0) swprintf(tail, 128, L" -w %d -h %d -freq %lu", useW, useH, freq); else swprintf(tail, 128, L" -w %d -h %d", useW, useH); cmd += tail; wprintf(L"Запуск: %s\n", cmd.c_str()); if (GetFileAttributesW(exe.c_str()) == INVALID_FILE_ATTRIBUTES) { wprintf(L"Не найден hl.exe рядом с лаунчером:\n %s\n", exe.c_str()); wprintf(L"Положи cstrike.exe в каталог игры (где лежит hl.exe).\n"); return 1; } std::wstring workDir = exe; const size_t slash = workDir.find_last_of(L"\\/"); if (slash != std::wstring::npos) workDir.resize(slash); STARTUPINFOW si = {}; si.cb = sizeof(si); PROCESS_INFORMATION pi = {}; std::vector buf(cmd.begin(), cmd.end()); buf.push_back(L'\0'); const BOOL ok = CreateProcessW( exe.c_str(), buf.data(), nullptr, nullptr, FALSE, 0, nullptr, workDir.c_str(), &si, &pi); if (!ok) { wprintf(L"Не удалось запустить процесс (код ошибки %lu).\n", GetLastError()); return 1; } CloseHandle(pi.hThread); CloseHandle(pi.hProcess); return 0; } int wmain(int argc, wchar_t** argv) { SetConsoleOutputCP(CP_UTF8); _setmode(_fileno(stdout), _O_U8TEXT); // корректный UTF-8 вывод (консоль и пайп) // Разбор аргументов (в любом порядке): // info -> показать дисплеи // infinity | gigabyte -> выбор мода // 640 | 800 | 1024 -> пресет; без числа -> нативное разрешение bool info = false; int width = -1; // -1 = нативное const wchar_t* modName = L"GIGABYTE"; const wchar_t* modArgs = kArgsGigabyte; for (int i = 1; i < argc; ++i) { const wchar_t* a = argv[i]; if (_wcsicmp(a, L"info") == 0 || _wcsicmp(a, L"--info") == 0 || _wcsicmp(a, L"-i") == 0) { info = true; } else if (_wcsicmp(a, L"infinity") == 0) { modName = L"INFINITY"; modArgs = kArgsInfinity; } else if (_wcsicmp(a, L"gigabyte") == 0) { modName = L"GIGABYTE"; modArgs = kArgsGigabyte; } else { wchar_t* end = nullptr; const long val = wcstol(a, &end, 10); if (end != a && *end == L'\0') { int h; if (PresetHeight(static_cast(val), h)) width = static_cast(val); else wprintf(L"Неизвестное разрешение %ld. Доступно: 640, 800, 1024. " L"Использую нативное.\n", val); } else { wprintf(L"Неизвестный аргумент: %s (ожидается infinity|gigabyte|640|800|1024|info)\n", a); } } } if (info) { PrintDisplays(); return 0; } return LaunchGame(modName, modArgs, width); }