mirror of
https://github.com/diamante0018/MW3ServerFreezer.git
synced 2025-04-19 11:42:53 +00:00
Refactor console ending this saga
This commit is contained in:
parent
7f5643dcae
commit
59b5a16d6f
@ -74,13 +74,13 @@ private:
|
|||||||
key_catcher::on_key_press(
|
key_catcher::on_key_press(
|
||||||
"Z", []([[maybe_unused]] const game::LocalClientNum_t& local_client) {
|
"Z", []([[maybe_unused]] const game::LocalClientNum_t& local_client) {
|
||||||
game::Dvar_SetBool(cl_EnableCheats, true);
|
game::Dvar_SetBool(cl_EnableCheats, true);
|
||||||
console::print("Enabled cl_EnableCheats");
|
console::info("Enabled cl_EnableCheats");
|
||||||
});
|
});
|
||||||
|
|
||||||
key_catcher::on_key_press(
|
key_catcher::on_key_press(
|
||||||
"X", []([[maybe_unused]] const game::LocalClientNum_t& local_client) {
|
"X", []([[maybe_unused]] const game::LocalClientNum_t& local_client) {
|
||||||
game::Dvar_SetBool(cl_EnableCheats, false);
|
game::Dvar_SetBool(cl_EnableCheats, false);
|
||||||
console::print("Disabled cl_EnableCheats");
|
console::info("Disabled cl_EnableCheats");
|
||||||
});
|
});
|
||||||
|
|
||||||
key_catcher::on_key_press(
|
key_catcher::on_key_press(
|
||||||
@ -88,7 +88,7 @@ private:
|
|||||||
const auto* cmd =
|
const auto* cmd =
|
||||||
utils::string::va("cmd mr %i 2 allies", *game::serverId);
|
utils::string::va("cmd mr %i 2 allies", *game::serverId);
|
||||||
command::execute(cmd, true);
|
command::execute(cmd, true);
|
||||||
console::print("Executed: {}", cmd);
|
console::info("Executed: {}", cmd);
|
||||||
});
|
});
|
||||||
|
|
||||||
key_catcher::on_key_press(
|
key_catcher::on_key_press(
|
||||||
@ -96,7 +96,7 @@ private:
|
|||||||
const auto* cmd =
|
const auto* cmd =
|
||||||
utils::string::va("cmd mr %i -1 endround", *game::serverId);
|
utils::string::va("cmd mr %i -1 endround", *game::serverId);
|
||||||
command::execute(cmd, true);
|
command::execute(cmd, true);
|
||||||
console::print("Executed: {}", cmd);
|
console::info("Executed: {}", cmd);
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
@ -89,7 +89,7 @@ private:
|
|||||||
add("quit", game::Com_Quit_f);
|
add("quit", game::Com_Quit_f);
|
||||||
add("vstr", [](const params& params) {
|
add("vstr", [](const params& params) {
|
||||||
if (params.size() < 2) {
|
if (params.size() < 2) {
|
||||||
console::print("vstr <variablename> : execute a variable command");
|
console::info("vstr <variablename> : execute a variable command");
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -97,12 +97,12 @@ private:
|
|||||||
const auto* dvar = game::Dvar_FindVar(dvar_name);
|
const auto* dvar = game::Dvar_FindVar(dvar_name);
|
||||||
|
|
||||||
if (dvar == nullptr) {
|
if (dvar == nullptr) {
|
||||||
console::print("{} doesn't exist", dvar_name);
|
console::info("{} doesn't exist", dvar_name);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
if (dvar->type != game::dvar_type::DVAR_TYPE_STRING &&
|
if (dvar->type != game::dvar_type::DVAR_TYPE_STRING &&
|
||||||
dvar->type != game::dvar_type::DVAR_TYPE_ENUM) {
|
dvar->type != game::dvar_type::DVAR_TYPE_ENUM) {
|
||||||
console::print("{} is not a string-based dvar\n", dvar->name);
|
console::info("{} is not a string-based dvar\n", dvar->name);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1,39 +1,165 @@
|
|||||||
#include <std_include.hpp>
|
#include <std_include.hpp>
|
||||||
#include "../loader/component_loader.hpp"
|
#include "../loader/component_loader.hpp"
|
||||||
|
|
||||||
|
#include <utils/hook.hpp>
|
||||||
#include <utils/thread.hpp>
|
#include <utils/thread.hpp>
|
||||||
|
#include <utils/flags.hpp>
|
||||||
|
#include <utils/concurrency.hpp>
|
||||||
|
|
||||||
#include "console.hpp"
|
#include "console.hpp"
|
||||||
|
#include "command.hpp"
|
||||||
|
|
||||||
namespace console {
|
namespace console {
|
||||||
namespace {
|
namespace {
|
||||||
std::thread thread;
|
using message_queue = std::queue<std::string>;
|
||||||
|
utils::concurrency::container<message_queue> messages;
|
||||||
|
|
||||||
LRESULT __stdcall sys_start_console(HWND, UINT, WPARAM, LPARAM) {
|
std::string format(va_list* ap, const char* message) {
|
||||||
game::Sys_ShowConsole();
|
static thread_local char buffer[0x1000];
|
||||||
return 0;
|
|
||||||
|
const auto count = _vsnprintf_s(buffer, _TRUNCATE, message, *ap);
|
||||||
|
|
||||||
|
if (count < 0)
|
||||||
|
return {};
|
||||||
|
return {buffer};
|
||||||
}
|
}
|
||||||
|
|
||||||
void console_unlock() {
|
void dispatch_message(const std::string& message) {
|
||||||
const auto callback = SetWindowLongA(
|
messages.access([&message](message_queue& msgs) { msgs.emplace(message); });
|
||||||
*game::g_wv_hWnd, GWL_WNDPROC, reinterpret_cast<LONG>(sys_start_console));
|
|
||||||
|
|
||||||
SendMessageA(*game::g_wv_hWnd, WM_QUIT, 0, 0);
|
|
||||||
SetWindowLongA(*game::g_wv_hWnd, GWL_WNDPROC, callback);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void show_console() {
|
void append_text(const char* text) { dispatch_message(text); }
|
||||||
if (*game::s_wcd_hWnd) {
|
|
||||||
ShowWindow(*game::s_wcd_hWnd, SW_SHOW);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
} // namespace
|
} // namespace
|
||||||
|
|
||||||
|
class component final : public component_interface {
|
||||||
|
public:
|
||||||
|
component() {
|
||||||
|
ShowWindow(GetConsoleWindow(), SW_HIDE);
|
||||||
|
|
||||||
|
(void)_pipe(this->handles_, 1024, _O_TEXT);
|
||||||
|
(void)_dup2(this->handles_[1], 1);
|
||||||
|
(void)_dup2(this->handles_[1], 2);
|
||||||
|
}
|
||||||
|
|
||||||
|
void post_start() override {
|
||||||
|
this->terminate_runner_ = false;
|
||||||
|
|
||||||
|
this->console_runner_ = utils::thread::create_named_thread(
|
||||||
|
"Console IO", [this] { this->runner(); });
|
||||||
|
}
|
||||||
|
|
||||||
|
void post_unpack() override {
|
||||||
|
utils::hook(0x446930, append_text, HOOK_JUMP).install()->quick();
|
||||||
|
|
||||||
|
this->initialize();
|
||||||
|
}
|
||||||
|
|
||||||
|
void pre_destroy() override {
|
||||||
|
this->terminate_runner_ = true;
|
||||||
|
|
||||||
|
printf("\r\n");
|
||||||
|
_flushall();
|
||||||
|
|
||||||
|
if (this->console_runner_.joinable()) {
|
||||||
|
this->console_runner_.join();
|
||||||
|
}
|
||||||
|
|
||||||
|
if (this->console_thread_.joinable()) {
|
||||||
|
this->console_thread_.join();
|
||||||
|
}
|
||||||
|
|
||||||
|
_close(this->handles_[0]);
|
||||||
|
_close(this->handles_[1]);
|
||||||
|
|
||||||
|
messages.access([&](message_queue& msgs) { msgs = {}; });
|
||||||
|
}
|
||||||
|
|
||||||
|
private:
|
||||||
|
volatile bool console_initialized_ = false;
|
||||||
|
volatile bool terminate_runner_ = false;
|
||||||
|
|
||||||
|
std::thread console_runner_;
|
||||||
|
std::thread console_thread_;
|
||||||
|
|
||||||
|
int handles_[2]{};
|
||||||
|
|
||||||
|
void initialize() {
|
||||||
|
this->console_thread_ =
|
||||||
|
utils::thread::create_named_thread("Console", [this]() {
|
||||||
|
if (!utils::flags::has_flag("noconsole")) {
|
||||||
|
game::Sys_ShowConsole();
|
||||||
|
}
|
||||||
|
|
||||||
|
messages.access(
|
||||||
|
[&](message_queue&) { this->console_initialized_ = true; });
|
||||||
|
|
||||||
|
MSG msg;
|
||||||
|
while (!this->terminate_runner_) {
|
||||||
|
if (PeekMessageA(&msg, nullptr, NULL, NULL, PM_REMOVE)) {
|
||||||
|
if (msg.message == WM_QUIT) {
|
||||||
|
command::execute("quit", false);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
TranslateMessage(&msg);
|
||||||
|
DispatchMessage(&msg);
|
||||||
|
} else {
|
||||||
|
this->log_messages();
|
||||||
|
std::this_thread::sleep_for(1ms);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
void log_messages() const {
|
||||||
|
if (this->console_initialized_ && !messages.get_raw().empty()) {
|
||||||
|
std::queue<std::string> message_queue_copy;
|
||||||
|
|
||||||
|
{
|
||||||
|
messages.access([&](message_queue& msgs) {
|
||||||
|
message_queue_copy = std::move(msgs);
|
||||||
|
msgs = {};
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
while (!message_queue_copy.empty()) {
|
||||||
|
log_message(message_queue_copy.front());
|
||||||
|
message_queue_copy.pop();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fflush(stdout);
|
||||||
|
fflush(stderr);
|
||||||
|
}
|
||||||
|
|
||||||
|
static void log_message(const std::string& message) {
|
||||||
|
OutputDebugStringA(message.data());
|
||||||
|
game::Conbuf_AppendText(message.data());
|
||||||
|
}
|
||||||
|
|
||||||
|
void runner() const {
|
||||||
|
char buffer[1024];
|
||||||
|
|
||||||
|
while (!this->terminate_runner_ && this->handles_[0]) {
|
||||||
|
const auto len = _read(this->handles_[0], buffer, sizeof(buffer));
|
||||||
|
if (len > 0) {
|
||||||
|
dispatch_message(std::string(buffer, len));
|
||||||
|
} else {
|
||||||
|
std::this_thread::sleep_for(1ms);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
std::this_thread::yield();
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
HWND get_window() { return *reinterpret_cast<HWND*>(0x5A86330); }
|
||||||
|
|
||||||
#ifdef _DEBUG
|
#ifdef _DEBUG
|
||||||
void console_print(const std::source_location& location, std::string_view fmt,
|
void print(const std::source_location& location, std::string_view fmt,
|
||||||
std::format_args&& args) {
|
std::format_args&& args) {
|
||||||
#else
|
#else
|
||||||
void console_print(std::string_view fmt, std::format_args&& args) {
|
void print(std::string_view fmt, std::format_args&& args) {
|
||||||
#endif
|
#endif
|
||||||
#ifdef _DEBUG
|
#ifdef _DEBUG
|
||||||
const auto msg = std::vformat(fmt, args);
|
const auto msg = std::vformat(fmt, args);
|
||||||
@ -44,28 +170,8 @@ void console_print(std::string_view fmt, std::format_args&& args) {
|
|||||||
const auto line = std::vformat(fmt, args) + "\n";
|
const auto line = std::vformat(fmt, args) + "\n";
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
if (IsDebuggerPresent()) {
|
dispatch_message(line);
|
||||||
OutputDebugStringA(line.data());
|
|
||||||
}
|
|
||||||
|
|
||||||
game::Conbuf_AppendText(line.data());
|
|
||||||
}
|
}
|
||||||
|
|
||||||
class component final : public component_interface {
|
|
||||||
public:
|
|
||||||
void post_unpack() override {
|
|
||||||
thread = utils::thread::create_named_thread("Console Thread", []() {
|
|
||||||
console_unlock();
|
|
||||||
show_console();
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
void pre_destroy() override {
|
|
||||||
if (thread.joinable()) {
|
|
||||||
thread.join();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
};
|
|
||||||
} // namespace console
|
} // namespace console
|
||||||
|
|
||||||
REGISTER_COMPONENT(console::component)
|
REGISTER_COMPONENT(console::component)
|
||||||
|
@ -1,28 +1,30 @@
|
|||||||
#pragma once
|
#pragma once
|
||||||
|
|
||||||
namespace console {
|
namespace console {
|
||||||
|
HWND get_window();
|
||||||
|
|
||||||
#ifdef _DEBUG
|
#ifdef _DEBUG
|
||||||
void console_print(const std::source_location& location, std::string_view fmt,
|
void print(const std::source_location& location,
|
||||||
std::format_args&& args);
|
|
||||||
|
std::string_view fmt, std::format_args&& args);
|
||||||
#else
|
#else
|
||||||
void console_print(std::string_view fmt, std::format_args&& args);
|
void print(std::string_view fmt, std::format_args&& args);
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
static inline void log(std::string_view fmt, std::format_args&& args) {
|
static inline void console_log(std::string_view fmt, std::format_args&& args) {
|
||||||
#ifdef _DEBUG
|
#ifdef _DEBUG
|
||||||
console_print(std::source_location::current(), fmt, std::move(args));
|
print(std::source_location::current(), fmt, std::move(args));
|
||||||
#else
|
#else
|
||||||
console_print(fmt, std::move(args));
|
print(fmt, std::move(args));
|
||||||
#endif
|
#endif
|
||||||
}
|
}
|
||||||
|
|
||||||
static inline void print(std::string_view fmt) {
|
|
||||||
log(fmt, std::make_format_args(0));
|
|
||||||
}
|
|
||||||
|
|
||||||
template <typename... Args>
|
template <typename... Args>
|
||||||
static inline void print(std::string_view fmt, Args&&... args) {
|
static inline void info(std::string_view fmt, Args&&... args) {
|
||||||
log(fmt, std::make_format_args(args...));
|
console_log(fmt, std::make_format_args(args...));
|
||||||
|
}
|
||||||
|
|
||||||
|
static inline void info(std::string_view fmt) {
|
||||||
|
console_log(fmt, std::make_format_args(0));
|
||||||
}
|
}
|
||||||
} // namespace console
|
} // namespace console
|
||||||
|
@ -3,20 +3,35 @@
|
|||||||
|
|
||||||
#include <utils/hook.hpp>
|
#include <utils/hook.hpp>
|
||||||
|
|
||||||
|
#include "scheduler.hpp"
|
||||||
#include "console.hpp"
|
#include "console.hpp"
|
||||||
|
|
||||||
namespace dvar_patches {
|
namespace dvar_patches {
|
||||||
|
namespace {
|
||||||
void dvar_set_from_string_by_name_stub(const char* dvar_name,
|
void dvar_set_from_string_by_name_stub(const char* dvar_name,
|
||||||
const char* string) {
|
const char* string) {
|
||||||
console::print("Server tried setting {} with value {}", dvar_name, string);
|
console::info("Server tried setting {} with value {}", dvar_name, string);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void dvar_override_cheat_protection_stub(bool /*a1*/) {
|
||||||
|
*game::isCheatOverride = true;
|
||||||
|
}
|
||||||
|
} // namespace
|
||||||
|
|
||||||
class component final : public component_interface {
|
class component final : public component_interface {
|
||||||
public:
|
public:
|
||||||
void post_unpack() override {
|
void post_unpack() override {
|
||||||
utils::hook(0x59C0EF, dvar_set_from_string_by_name_stub, HOOK_CALL)
|
utils::hook(0x59C0EF, dvar_set_from_string_by_name_stub, HOOK_CALL)
|
||||||
.install()
|
.install()
|
||||||
->quick();
|
->quick();
|
||||||
|
|
||||||
|
*game::isCheatOverride = true;
|
||||||
|
utils::hook(0x482CC0, dvar_override_cheat_protection_stub, HOOK_JUMP)
|
||||||
|
.install()
|
||||||
|
->quick();
|
||||||
|
|
||||||
|
// Remove read/write protection
|
||||||
|
utils::hook::nop(0x649227, 6);
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
} // namespace dvar_patches
|
} // namespace dvar_patches
|
||||||
|
@ -82,17 +82,17 @@ private:
|
|||||||
static void add_exploit_commands() {
|
static void add_exploit_commands() {
|
||||||
command::add("exploit", []([[maybe_unused]] const command::params& params) {
|
command::add("exploit", []([[maybe_unused]] const command::params& params) {
|
||||||
game::Dvar_SetBool(cl_exploit, true);
|
game::Dvar_SetBool(cl_exploit, true);
|
||||||
console::print("Enabled cl_exploit");
|
console::info("Enabled cl_exploit");
|
||||||
});
|
});
|
||||||
|
|
||||||
command::add("undo_exploit",
|
command::add("undoExploit",
|
||||||
[]([[maybe_unused]] const command::params& params) {
|
[]([[maybe_unused]] const command::params& params) {
|
||||||
game::Dvar_SetBool(cl_exploit, false);
|
game::Dvar_SetBool(cl_exploit, false);
|
||||||
console::print("Disabled cl_exploit");
|
console::info("Disabled cl_exploit");
|
||||||
});
|
});
|
||||||
|
|
||||||
command::add(
|
command::add(
|
||||||
"send_command", []([[maybe_unused]] const command::params& params) {
|
"sendCommand", []([[maybe_unused]] const command::params& params) {
|
||||||
if (params.size() < 2)
|
if (params.size() < 2)
|
||||||
return;
|
return;
|
||||||
|
|
||||||
@ -100,7 +100,7 @@ private:
|
|||||||
return;
|
return;
|
||||||
|
|
||||||
const auto cmd = std::format("queryserverinfo ;{}", params.join(1));
|
const auto cmd = std::format("queryserverinfo ;{}", params.join(1));
|
||||||
console::print("Sending OOB packet {}", cmd);
|
console::info("Sending OOB packet {}", cmd);
|
||||||
game::NET_OutOfBandPrint(game::NS_SERVER,
|
game::NET_OutOfBandPrint(game::NS_SERVER,
|
||||||
game::localClientConnection->serverAddress,
|
game::localClientConnection->serverAddress,
|
||||||
cmd.data());
|
cmd.data());
|
||||||
|
@ -4,4 +4,26 @@ namespace game {
|
|||||||
ScreenPlacement* ScrPlace_GetUnsafeFullPlacement() {
|
ScreenPlacement* ScrPlace_GetUnsafeFullPlacement() {
|
||||||
return scrPlaceFullUnsafe;
|
return scrPlaceFullUnsafe;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void __declspec(naked) Dvar_SetVariant(dvar_t* /*dvar*/, DvarValue /*value*/,
|
||||||
|
DvarSetSource /*source*/) {
|
||||||
|
static DWORD func = 0x649170;
|
||||||
|
|
||||||
|
__asm {
|
||||||
|
pushad
|
||||||
|
|
||||||
|
mov eax, [esp + 0x20 + 0x4] // dvar
|
||||||
|
push [esp + 0x20 + 0x18] // source
|
||||||
|
push [esp + 0x20 + 0x18] // value
|
||||||
|
push [esp + 0x20 + 0x18] // value
|
||||||
|
push [esp + 0x20 + 0x18] // value
|
||||||
|
push [esp + 0x20 + 0x18] // value
|
||||||
|
call func
|
||||||
|
add esp, 0x14
|
||||||
|
|
||||||
|
popad
|
||||||
|
|
||||||
|
ret
|
||||||
|
}
|
||||||
|
}
|
||||||
} // namespace game
|
} // namespace game
|
||||||
|
@ -16,6 +16,7 @@ private:
|
|||||||
};
|
};
|
||||||
|
|
||||||
ScreenPlacement* ScrPlace_GetUnsafeFullPlacement();
|
ScreenPlacement* ScrPlace_GetUnsafeFullPlacement();
|
||||||
|
void Dvar_SetVariant(dvar_t* dvar, DvarValue value, DvarSetSource source);
|
||||||
} // namespace game
|
} // namespace game
|
||||||
|
|
||||||
#include "symbols.hpp"
|
#include "symbols.hpp"
|
||||||
|
@ -184,6 +184,13 @@ enum dvar_type : std::int8_t {
|
|||||||
DVAR_TYPE_FLOAT_3_COLOR = 0x9,
|
DVAR_TYPE_FLOAT_3_COLOR = 0x9,
|
||||||
};
|
};
|
||||||
|
|
||||||
|
enum class DvarSetSource {
|
||||||
|
DVAR_SOURCE_INTERNAL,
|
||||||
|
DVAR_SOURCE_EXTERNAL,
|
||||||
|
DVAR_SOURCE_SCRIPT,
|
||||||
|
DVAR_SOURCE_DEVGUI,
|
||||||
|
};
|
||||||
|
|
||||||
union DvarValue {
|
union DvarValue {
|
||||||
bool enabled;
|
bool enabled;
|
||||||
int integer;
|
int integer;
|
||||||
@ -194,6 +201,8 @@ union DvarValue {
|
|||||||
char color[4];
|
char color[4];
|
||||||
};
|
};
|
||||||
|
|
||||||
|
static_assert(sizeof(DvarValue) == 0x10);
|
||||||
|
|
||||||
struct enum_limit {
|
struct enum_limit {
|
||||||
int stringCount;
|
int stringCount;
|
||||||
const char** strings;
|
const char** strings;
|
||||||
|
@ -88,4 +88,5 @@ WEAK symbol<HWND> s_wcd_hWnd{0x5A86330};
|
|||||||
WEAK symbol<int> serverId{0xFF5058};
|
WEAK symbol<int> serverId{0xFF5058};
|
||||||
WEAK symbol<connstate_t> connectionState{0x1060214};
|
WEAK symbol<connstate_t> connectionState{0x1060214};
|
||||||
WEAK symbol<ScreenPlacement> scrPlaceFullUnsafe{0x1337FC0};
|
WEAK symbol<ScreenPlacement> scrPlaceFullUnsafe{0x1337FC0};
|
||||||
|
WEAK symbol<bool> isCheatOverride{0x8B4444};
|
||||||
} // namespace game
|
} // namespace game
|
||||||
|
@ -6,6 +6,8 @@
|
|||||||
|
|
||||||
#include <WinSock2.h>
|
#include <WinSock2.h>
|
||||||
#include <Windows.h>
|
#include <Windows.h>
|
||||||
|
#include <corecrt_io.h>
|
||||||
|
#include <fcntl.h>
|
||||||
|
|
||||||
#include <algorithm>
|
#include <algorithm>
|
||||||
#include <cassert>
|
#include <cassert>
|
||||||
@ -14,6 +16,7 @@
|
|||||||
#include <mutex>
|
#include <mutex>
|
||||||
#include <string>
|
#include <string>
|
||||||
#include <source_location>
|
#include <source_location>
|
||||||
|
#include <queue>
|
||||||
|
|
||||||
#pragma comment(lib, "ntdll.lib")
|
#pragma comment(lib, "ntdll.lib")
|
||||||
|
|
||||||
|
46
src/common/utils/flags.cpp
Normal file
46
src/common/utils/flags.cpp
Normal file
@ -0,0 +1,46 @@
|
|||||||
|
#include <string>
|
||||||
|
|
||||||
|
#include "../utils/nt.hpp"
|
||||||
|
#include "../utils/string.hpp"
|
||||||
|
#include "flags.hpp"
|
||||||
|
|
||||||
|
#include <cassert>
|
||||||
|
#include <shellapi.h>
|
||||||
|
|
||||||
|
namespace utils::flags {
|
||||||
|
void parse_flags(std::vector<std::string>& flags) {
|
||||||
|
int num_args;
|
||||||
|
auto* const argv = CommandLineToArgvW(GetCommandLineW(), &num_args);
|
||||||
|
|
||||||
|
assert(flags.empty());
|
||||||
|
|
||||||
|
if (argv == nullptr) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
for (auto i = 0; i < num_args; ++i) {
|
||||||
|
std::wstring wide_flag(argv[i]);
|
||||||
|
if (wide_flag[0] == L'-') {
|
||||||
|
wide_flag.erase(wide_flag.begin());
|
||||||
|
const auto converted_string = string::convert(wide_flag);
|
||||||
|
flags.emplace_back(string::to_lower(converted_string));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
LocalFree(argv);
|
||||||
|
}
|
||||||
|
|
||||||
|
bool has_flag(const std::string& flag) {
|
||||||
|
static auto parsed = false;
|
||||||
|
static std::vector<std::string> enabled_flags;
|
||||||
|
|
||||||
|
if (!parsed) {
|
||||||
|
parse_flags(enabled_flags);
|
||||||
|
parsed = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
return std::ranges::any_of(
|
||||||
|
enabled_flags.cbegin(), enabled_flags.cend(),
|
||||||
|
[flag](const auto& elem) { return elem == string::to_lower(flag); });
|
||||||
|
}
|
||||||
|
} // namespace utils::flags
|
5
src/common/utils/flags.hpp
Normal file
5
src/common/utils/flags.hpp
Normal file
@ -0,0 +1,5 @@
|
|||||||
|
#pragma once
|
||||||
|
|
||||||
|
namespace utils::flags {
|
||||||
|
bool has_flag(const std::string& flag);
|
||||||
|
}
|
Loading…
x
Reference in New Issue
Block a user