mirror of
https://github.com/alterware/iw4-validator.git
synced 2025-04-19 08:32:53 +00:00
maint: better parsing of main arguments
Co-authored-by: William Roy <wroy@proton.me>
This commit is contained in:
parent
dc0759ee63
commit
0b374cd08f
98
src/main.cpp
98
src/main.cpp
@ -6,116 +6,86 @@
|
|||||||
#include "game/cg_client_side_effects_mp.hpp"
|
#include "game/cg_client_side_effects_mp.hpp"
|
||||||
|
|
||||||
#include <utils/io.hpp>
|
#include <utils/io.hpp>
|
||||||
|
#include <utils/string.hpp>
|
||||||
|
|
||||||
namespace
|
namespace
|
||||||
{
|
{
|
||||||
bool load_client_effects(const std::string& filename)
|
void load_client_effects(const std::string& filename)
|
||||||
{
|
{
|
||||||
|
assert(!filename.empty());
|
||||||
if (filename.empty())
|
if (filename.empty())
|
||||||
{
|
{
|
||||||
console::error("filename parameter is empty\n");
|
throw std::runtime_error("filename parameter is empty");
|
||||||
return false;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
const auto data = utils::io::read_file(filename);
|
std::string data;
|
||||||
if (data.empty())
|
if (!utils::io::read_file(filename, &data) || data.empty())
|
||||||
{
|
{
|
||||||
console::error("'%s' is empty\n", filename.data());
|
throw std::runtime_error(utils::string::va("'%s' is empty", filename.data()));
|
||||||
return false;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
return game::parse_client_effects(data.data());
|
game::parse_client_effects(data.data());
|
||||||
}
|
}
|
||||||
|
|
||||||
bool load_map_rotation(const std::string& filename)
|
void load_map_rotation(const std::string& filename)
|
||||||
{
|
{
|
||||||
|
assert(!filename.empty());
|
||||||
if (filename.empty())
|
if (filename.empty())
|
||||||
{
|
{
|
||||||
console::error("filename parameter is empty\n");
|
throw std::runtime_error("filename parameter is empty");
|
||||||
return false;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
const auto data = utils::io::read_file(filename);
|
std::string data;
|
||||||
if (data.empty())
|
if (!utils::io::read_file(filename, &data) || data.empty())
|
||||||
{
|
{
|
||||||
console::error("'%s' is empty\n", filename.data());
|
throw std::runtime_error(utils::string::va("'%s' is empty", filename.data()));
|
||||||
return false;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
try
|
try
|
||||||
{
|
{
|
||||||
map_rotation::rotation_data rotation_data;
|
map_rotation::rotation_data rotation_data;
|
||||||
rotation_data.parse(data);
|
rotation_data.parse(data);
|
||||||
|
|
||||||
|
console::info("Successfully parsed map rotation\n");
|
||||||
}
|
}
|
||||||
catch (const std::exception& ex)
|
catch (const std::exception& ex)
|
||||||
{
|
{
|
||||||
console::error("%s: '%s' contains invalid data!\n", ex.what(), filename.data());
|
console::error(utils::string::va("%s: '%s' contains invalid data!", ex.what(), filename.data()));
|
||||||
return false;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
console::info("Successfully parsed map rotation\n");
|
|
||||||
return true;
|
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
|
||||||
int unsafe_main(std::string&& prog, std::vector<std::string>&& args)
|
void unsafe_main(const std::span<char*> s)
|
||||||
{
|
|
||||||
// Parse command-line flags (only increment i for matching flags)
|
|
||||||
for (auto i = args.begin(); i != args.end();)
|
|
||||||
{
|
{
|
||||||
if (*i == "-createfx")
|
auto p = [&](const std::string& o, const std::function <void(const std::string&)>& c) -> void
|
||||||
{
|
{
|
||||||
++i;
|
auto r(s | std::views::transform([](char* v) -> std::string
|
||||||
const auto filename = i != args.end() ? *i++ : std::string();
|
|
||||||
console::info("Parsing createfx '%s'\n", filename.data());
|
|
||||||
|
|
||||||
if (!load_client_effects(filename))
|
|
||||||
{
|
{
|
||||||
return EXIT_FAILURE;
|
return { v };
|
||||||
}
|
}));
|
||||||
}
|
|
||||||
else if (*i == "-map-rotation")
|
|
||||||
{
|
|
||||||
++i;
|
|
||||||
const auto filename = i != args.end() ? *i++ : std::string();
|
|
||||||
console::info("Parsing map rotation '%s'\n", filename.data());
|
|
||||||
|
|
||||||
if (!load_map_rotation(filename))
|
const auto i(std::ranges::find(r, o));
|
||||||
|
const auto e(r.end());
|
||||||
|
|
||||||
|
if (auto n(i != e ? std::ranges::next(i, 1, e) : e); i != e && n != e)
|
||||||
{
|
{
|
||||||
return EXIT_FAILURE;
|
c({ *n });
|
||||||
}
|
}
|
||||||
|
};
|
||||||
|
|
||||||
}
|
p("-createfx", load_client_effects);
|
||||||
else
|
p("-map-rotation", load_map_rotation);
|
||||||
{
|
|
||||||
console::info("X Labs IW4x validator tool\n"
|
|
||||||
"Usage: %s OPTIONS\n"
|
|
||||||
" -createfx <filename>\n"
|
|
||||||
" -fx <filename>\n"
|
|
||||||
" -map-rotation <filename>\n",
|
|
||||||
prog.data()
|
|
||||||
);
|
|
||||||
|
|
||||||
return EXIT_FAILURE;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
return EXIT_SUCCESS;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
int main(int argc, char* argv[])
|
int main(int argc, char* argv[])
|
||||||
{
|
{
|
||||||
console::set_title("X Labs IW4x-validator");
|
console::set_title("AlterWare IW4x-validator");
|
||||||
console::log("Starting X Labs IW4x-validator");
|
console::log("Starting AlterWare iw4-validator");
|
||||||
|
|
||||||
try
|
try
|
||||||
{
|
{
|
||||||
std::string prog(argv[0]);
|
unsafe_main(std::span(argv, argc));
|
||||||
std::vector<std::string> args;
|
return EXIT_SUCCESS;
|
||||||
|
|
||||||
args.reserve(argc - 1);
|
|
||||||
args.assign(argv + 1, argv + argc);
|
|
||||||
return unsafe_main(std::move(prog), std::move(args));
|
|
||||||
}
|
}
|
||||||
catch (const std::exception& ex)
|
catch (const std::exception& ex)
|
||||||
{
|
{
|
||||||
|
@ -22,7 +22,7 @@
|
|||||||
#undef min
|
#undef min
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
#include <algorithm>
|
|
||||||
#include <cassert>
|
#include <cassert>
|
||||||
#include <cctype>
|
#include <cctype>
|
||||||
#include <csignal>
|
#include <csignal>
|
||||||
@ -30,12 +30,15 @@
|
|||||||
#include <cstdint>
|
#include <cstdint>
|
||||||
#include <cstdio>
|
#include <cstdio>
|
||||||
#include <cstring>
|
#include <cstring>
|
||||||
|
|
||||||
|
#include <algorithm>
|
||||||
#include <fstream>
|
#include <fstream>
|
||||||
#include <functional>
|
#include <functional>
|
||||||
#include <iostream>
|
#include <iostream>
|
||||||
#include <mutex>
|
#include <mutex>
|
||||||
#include <random>
|
#include <random>
|
||||||
#include <ranges>
|
#include <ranges>
|
||||||
|
#include <span>
|
||||||
#include <sstream>
|
#include <sstream>
|
||||||
#include <utility>
|
#include <utility>
|
||||||
#include <vector>
|
#include <vector>
|
||||||
|
106
src/utils/memory.cpp
Normal file
106
src/utils/memory.cpp
Normal file
@ -0,0 +1,106 @@
|
|||||||
|
#include <std_include.hpp>
|
||||||
|
#include "memory.hpp"
|
||||||
|
|
||||||
|
namespace utils
|
||||||
|
{
|
||||||
|
memory::allocator memory::mem_allocator_;
|
||||||
|
|
||||||
|
memory::allocator::~allocator()
|
||||||
|
{
|
||||||
|
this->clear();
|
||||||
|
}
|
||||||
|
|
||||||
|
void memory::allocator::clear()
|
||||||
|
{
|
||||||
|
std::lock_guard _(this->mutex_);
|
||||||
|
|
||||||
|
for (const auto& data : this->pool_)
|
||||||
|
{
|
||||||
|
memory::free(data);
|
||||||
|
}
|
||||||
|
|
||||||
|
this->pool_.clear();
|
||||||
|
}
|
||||||
|
|
||||||
|
void memory::allocator::free(void* data)
|
||||||
|
{
|
||||||
|
std::lock_guard _(this->mutex_);
|
||||||
|
|
||||||
|
const auto j = std::find(this->pool_.begin(), this->pool_.end(), data);
|
||||||
|
if (j != this->pool_.end())
|
||||||
|
{
|
||||||
|
memory::free(data);
|
||||||
|
this->pool_.erase(j);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void memory::allocator::free(const void* data)
|
||||||
|
{
|
||||||
|
this->free(const_cast<void*>(data));
|
||||||
|
}
|
||||||
|
|
||||||
|
void* memory::allocator::allocate(const std::size_t length)
|
||||||
|
{
|
||||||
|
std::lock_guard _(this->mutex_);
|
||||||
|
|
||||||
|
const auto data = memory::allocate(length);
|
||||||
|
this->pool_.push_back(data);
|
||||||
|
return data;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool memory::allocator::empty() const
|
||||||
|
{
|
||||||
|
return this->pool_.empty();
|
||||||
|
}
|
||||||
|
|
||||||
|
char* memory::allocator::duplicate_string(const std::string& string)
|
||||||
|
{
|
||||||
|
std::lock_guard _(this->mutex_);
|
||||||
|
|
||||||
|
const auto data = memory::duplicate_string(string);
|
||||||
|
this->pool_.push_back(data);
|
||||||
|
return data;
|
||||||
|
}
|
||||||
|
|
||||||
|
void* memory::allocate(const std::size_t length)
|
||||||
|
{
|
||||||
|
return ::calloc(1, length);
|
||||||
|
}
|
||||||
|
|
||||||
|
char* memory::duplicate_string(const std::string& string)
|
||||||
|
{
|
||||||
|
const auto new_string = allocate_array<char>(string.size() + 1);
|
||||||
|
std::memcpy(new_string, string.data(), string.size());
|
||||||
|
return new_string;
|
||||||
|
}
|
||||||
|
|
||||||
|
void memory::free(void* data)
|
||||||
|
{
|
||||||
|
::free(data);
|
||||||
|
}
|
||||||
|
|
||||||
|
void memory::free(const void* data)
|
||||||
|
{
|
||||||
|
free(const_cast<void*>(data));
|
||||||
|
}
|
||||||
|
|
||||||
|
bool memory::is_set(const void* mem, const char chr, const std::size_t length)
|
||||||
|
{
|
||||||
|
auto* const mem_arr = static_cast<const char*>(mem);
|
||||||
|
|
||||||
|
for (std::size_t i = 0; i < length; ++i)
|
||||||
|
{
|
||||||
|
if (mem_arr[i] != chr)
|
||||||
|
{
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
memory::allocator* memory::get_allocator()
|
||||||
|
{
|
||||||
|
return &memory::mem_allocator_;
|
||||||
|
}
|
||||||
|
}
|
68
src/utils/memory.hpp
Normal file
68
src/utils/memory.hpp
Normal file
@ -0,0 +1,68 @@
|
|||||||
|
#pragma once
|
||||||
|
|
||||||
|
namespace utils
|
||||||
|
{
|
||||||
|
class memory final
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
class allocator final
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
~allocator();
|
||||||
|
|
||||||
|
void clear();
|
||||||
|
|
||||||
|
void free(void* data);
|
||||||
|
|
||||||
|
void free(const void* data);
|
||||||
|
|
||||||
|
void* allocate(std::size_t length);
|
||||||
|
|
||||||
|
template <typename T>
|
||||||
|
T* allocate()
|
||||||
|
{
|
||||||
|
return this->allocate_array<T>(1);
|
||||||
|
}
|
||||||
|
|
||||||
|
template <typename T>
|
||||||
|
T* allocate_array(const std::size_t count = 1)
|
||||||
|
{
|
||||||
|
return static_cast<T*>(this->allocate(count * sizeof(T)));
|
||||||
|
}
|
||||||
|
|
||||||
|
[[nodiscard]] bool empty() const;
|
||||||
|
|
||||||
|
char* duplicate_string(const std::string& string);
|
||||||
|
|
||||||
|
private:
|
||||||
|
std::mutex mutex_;
|
||||||
|
std::vector<void*> pool_;
|
||||||
|
};
|
||||||
|
|
||||||
|
static void* allocate(std::size_t length);
|
||||||
|
|
||||||
|
template <typename T>
|
||||||
|
static T* allocate()
|
||||||
|
{
|
||||||
|
return allocate_array<T>(1);
|
||||||
|
}
|
||||||
|
|
||||||
|
template <typename T>
|
||||||
|
static T* allocate_array(const std::size_t count = 1)
|
||||||
|
{
|
||||||
|
return static_cast<T*>(allocate(count * sizeof(T)));
|
||||||
|
}
|
||||||
|
|
||||||
|
static char* duplicate_string(const std::string& string);
|
||||||
|
|
||||||
|
static void free(void* data);
|
||||||
|
static void free(const void* data);
|
||||||
|
|
||||||
|
static bool is_set(const void* mem, char chr, std::size_t length);
|
||||||
|
|
||||||
|
static allocator* get_allocator();
|
||||||
|
|
||||||
|
private:
|
||||||
|
static allocator mem_allocator_;
|
||||||
|
};
|
||||||
|
}
|
@ -3,6 +3,19 @@
|
|||||||
|
|
||||||
namespace utils::string
|
namespace utils::string
|
||||||
{
|
{
|
||||||
|
const char* va(const char* fmt, ...)
|
||||||
|
{
|
||||||
|
static thread_local va_provider<8, 256> provider;
|
||||||
|
|
||||||
|
va_list ap;
|
||||||
|
va_start(ap, fmt);
|
||||||
|
|
||||||
|
const auto* result = provider.get(fmt, ap);
|
||||||
|
|
||||||
|
va_end(ap);
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
std::vector<std::string> split(const std::string& s, const char delim)
|
std::vector<std::string> split(const std::string& s, const char delim)
|
||||||
{
|
{
|
||||||
std::stringstream ss(s);
|
std::stringstream ss(s);
|
||||||
|
@ -1,7 +1,87 @@
|
|||||||
#pragma once
|
#pragma once
|
||||||
|
#include "memory.hpp"
|
||||||
|
|
||||||
namespace utils::string
|
namespace utils::string
|
||||||
{
|
{
|
||||||
|
template <class Type, std::size_t n>
|
||||||
|
constexpr auto ARRAY_COUNT(Type(&)[n]) { return n; }
|
||||||
|
|
||||||
|
template <std::size_t buffers, std::size_t min_buffer_size>
|
||||||
|
class va_provider final
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
static_assert(buffers != 0 && min_buffer_size != 0, "buffers and min_buffer_size mustn't be 0");
|
||||||
|
|
||||||
|
va_provider() : current_buffer_(0)
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
char* get(const char* format, va_list ap)
|
||||||
|
{
|
||||||
|
++this->current_buffer_ %= ARRAY_COUNT(this->string_pool_);
|
||||||
|
auto entry = &this->string_pool_[this->current_buffer_];
|
||||||
|
|
||||||
|
if (!entry->size_ || !entry->buffer_)
|
||||||
|
{
|
||||||
|
throw std::runtime_error("The string pool is not initialized");
|
||||||
|
}
|
||||||
|
|
||||||
|
while (true)
|
||||||
|
{
|
||||||
|
#ifdef _WIN32
|
||||||
|
const auto res = vsnprintf_s(entry->buffer_, entry->size_, _TRUNCATE, format, ap);
|
||||||
|
#else
|
||||||
|
const auto res = vsnprintf(entry->buffer_, entry->size_, format, ap);
|
||||||
|
#endif
|
||||||
|
|
||||||
|
if (res > 0) break; // Success
|
||||||
|
if (res == 0) return nullptr; // Error
|
||||||
|
|
||||||
|
entry->double_size();
|
||||||
|
}
|
||||||
|
|
||||||
|
return entry->buffer_;
|
||||||
|
}
|
||||||
|
|
||||||
|
private:
|
||||||
|
class entry final
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
explicit entry(const std::size_t size = min_buffer_size) : size_(size), buffer_(nullptr)
|
||||||
|
{
|
||||||
|
if (this->size_ < min_buffer_size) this->size_ = min_buffer_size;
|
||||||
|
this->allocate();
|
||||||
|
}
|
||||||
|
|
||||||
|
~entry()
|
||||||
|
{
|
||||||
|
if (this->buffer_) memory::get_allocator()->free(this->buffer_);
|
||||||
|
this->size_ = 0;
|
||||||
|
this->buffer_ = nullptr;
|
||||||
|
}
|
||||||
|
|
||||||
|
void allocate()
|
||||||
|
{
|
||||||
|
if (this->buffer_) memory::get_allocator()->free(this->buffer_);
|
||||||
|
this->buffer_ = memory::get_allocator()->allocate_array<char>(this->size_ + 1);
|
||||||
|
}
|
||||||
|
|
||||||
|
void double_size()
|
||||||
|
{
|
||||||
|
this->size_ *= 2;
|
||||||
|
this->allocate();
|
||||||
|
}
|
||||||
|
|
||||||
|
std::size_t size_;
|
||||||
|
char* buffer_;
|
||||||
|
};
|
||||||
|
|
||||||
|
std::size_t current_buffer_;
|
||||||
|
entry string_pool_[buffers];
|
||||||
|
};
|
||||||
|
|
||||||
|
const char* va(const char* fmt, ...);
|
||||||
|
|
||||||
std::vector<std::string> split(const std::string& s, char delim);
|
std::vector<std::string> split(const std::string& s, char delim);
|
||||||
|
|
||||||
std::string to_lower(const std::string& text);
|
std::string to_lower(const std::string& text);
|
||||||
|
Loading…
x
Reference in New Issue
Block a user