maint: better parsing of main arguments

Co-authored-by: William Roy <wroy@proton.me>
This commit is contained in:
6arelyFuture 2023-06-06 22:16:21 +02:00
parent dc0759ee63
commit 0b374cd08f
6 changed files with 305 additions and 65 deletions

View File

@ -6,116 +6,86 @@
#include "game/cg_client_side_effects_mp.hpp"
#include <utils/io.hpp>
#include <utils/string.hpp>
namespace
{
bool load_client_effects(const std::string& filename)
void load_client_effects(const std::string& filename)
{
assert(!filename.empty());
if (filename.empty())
{
console::error("filename parameter is empty\n");
return false;
throw std::runtime_error("filename parameter is empty");
}
const auto data = utils::io::read_file(filename);
if (data.empty())
std::string data;
if (!utils::io::read_file(filename, &data) || data.empty())
{
console::error("'%s' is empty\n", filename.data());
return false;
throw std::runtime_error(utils::string::va("'%s' is empty", filename.data()));
}
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())
{
console::error("filename parameter is empty\n");
return false;
throw std::runtime_error("filename parameter is empty");
}
const auto data = utils::io::read_file(filename);
if (data.empty())
std::string data;
if (!utils::io::read_file(filename, &data) || data.empty())
{
console::error("'%s' is empty\n", filename.data());
return false;
throw std::runtime_error(utils::string::va("'%s' is empty", filename.data()));
}
try
{
map_rotation::rotation_data rotation_data;
rotation_data.parse(data);
console::info("Successfully parsed map rotation\n");
}
catch (const std::exception& ex)
{
console::error("%s: '%s' contains invalid data!\n", ex.what(), filename.data());
return false;
console::error(utils::string::va("%s: '%s' contains invalid data!", ex.what(), filename.data()));
}
}
console::info("Successfully parsed map rotation\n");
return true;
}
}
int unsafe_main(std::string&& prog, std::vector<std::string>&& args)
{
// Parse command-line flags (only increment i for matching flags)
for (auto i = args.begin(); i != args.end();)
void unsafe_main(const std::span<char*> s)
{
if (*i == "-createfx")
auto p = [&](const std::string& o, const std::function <void(const std::string&)>& c) -> void
{
++i;
const auto filename = i != args.end() ? *i++ : std::string();
console::info("Parsing createfx '%s'\n", filename.data());
if (!load_client_effects(filename))
auto r(s | std::views::transform([](char* v) -> std::string
{
return EXIT_FAILURE;
}
}
else if (*i == "-map-rotation")
return { v };
}));
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)
{
++i;
const auto filename = i != args.end() ? *i++ : std::string();
console::info("Parsing map rotation '%s'\n", filename.data());
if (!load_map_rotation(filename))
{
return EXIT_FAILURE;
c({ *n });
}
};
p("-createfx", load_client_effects);
p("-map-rotation", load_map_rotation);
}
else
{
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[])
{
console::set_title("X Labs IW4x-validator");
console::log("Starting X Labs IW4x-validator");
console::set_title("AlterWare IW4x-validator");
console::log("Starting AlterWare iw4-validator");
try
{
std::string prog(argv[0]);
std::vector<std::string> args;
args.reserve(argc - 1);
args.assign(argv + 1, argv + argc);
return unsafe_main(std::move(prog), std::move(args));
unsafe_main(std::span(argv, argc));
return EXIT_SUCCESS;
}
catch (const std::exception& ex)
{

View File

@ -22,7 +22,7 @@
#undef min
#endif
#include <algorithm>
#include <cassert>
#include <cctype>
#include <csignal>
@ -30,12 +30,15 @@
#include <cstdint>
#include <cstdio>
#include <cstring>
#include <algorithm>
#include <fstream>
#include <functional>
#include <iostream>
#include <mutex>
#include <random>
#include <ranges>
#include <span>
#include <sstream>
#include <utility>
#include <vector>

106
src/utils/memory.cpp Normal file
View 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
View 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_;
};
}

View File

@ -3,6 +3,19 @@
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::stringstream ss(s);

View File

@ -1,7 +1,87 @@
#pragma once
#include "memory.hpp"
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::string to_lower(const std::string& text);