savepoint, changelog later..

This commit is contained in:
Dmitry 2019-07-27 17:36:24 +03:00 committed by jeefo
commit 1bc1fd1913
45 changed files with 12866 additions and 10981 deletions

File diff suppressed because it is too large Load diff

78
include/crlib/cr-alloc.h Normal file
View file

@ -0,0 +1,78 @@
//
// Yet Another POD-Bot, based on PODBot by Markus Klinge ("CountFloyd").
// Copyright (c) YaPB Development Team.
//
// This software is licensed under the BSD-style license.
// Additional exceptions apply. For full license details, see LICENSE.txt or visit:
// https://yapb.ru/license
//
#pragma once
#include <new>
#include <crlib/cr-basic.h>
#include <crlib/cr-movable.h>
#include <crlib/cr-platform.h>
CR_NAMESPACE_BEGIN
// default allocator for cr-objects
class Allocator : public Singleton <Allocator> {
public:
Allocator () = default;
~Allocator () = default;
public:
template <typename T> T *allocate (const size_t length = 1) {
auto ptr = reinterpret_cast <T *> (calloc (length, sizeof (T)));
if (!ptr) {
plat.abort ();
}
// calloc on linux with debug enabled doesn't always zero out memory
#if defined (CR_DEBUG) && !defined (CR_WINDOWS)
auto *zeroing = reinterpret_cast <uint8 *> (ptr);
for (size_t i = 0; i < length; ++i) {
zeroing[i] = 0;
}
#endif
return ptr;
}
template <typename T> void deallocate (T *ptr) {
if (ptr) {
free (reinterpret_cast <T *> (ptr));
}
}
public:
template <typename T, typename ...Args> void construct (T *d, Args &&...args) {
new (d) T (cr::forward <Args> (args)...);
}
template <typename T> void destruct (T *d) {
d->~T ();
}
public:
template <typename T, typename ...Args> T *create (Args &&...args) {
auto d = allocate <T> ();
new (d) T (cr::forward <Args> (args)...);
return d;
}
template <typename T> void destroy (T *ptr) {
if (ptr) {
destruct (ptr);
deallocate (ptr);
}
}
};
static auto &alloc = Allocator::get ();
CR_NAMESPACE_END

390
include/crlib/cr-array.h Normal file
View file

@ -0,0 +1,390 @@
//
// Yet Another POD-Bot, based on PODBot by Markus Klinge ("CountFloyd").
// Copyright (c) YaPB Development Team.
//
// This software is licensed under the BSD-style license.
// Additional exceptions apply. For full license details, see LICENSE.txt or visit:
// https://yapb.ru/license
//
#pragma once
#include <crlib/cr-basic.h>
#include <crlib/cr-alloc.h>
#include <crlib/cr-movable.h>
#include <crlib/cr-random.h>
// policy to reserve memory
CR_DECLARE_SCOPED_ENUM (ReservePolicy,
MultipleByTwo,
PlusOne
);
CR_NAMESPACE_BEGIN
// simple array class like std::vector
template <typename T, ReservePolicy R = ReservePolicy::MultipleByTwo> class Array : public DenyCopying {
public:
T *m_data = nullptr;
size_t m_capacity = 0;
size_t m_length = 0;
public:
explicit Array () = default;
Array (const size_t amount) {
reserve (amount);
}
Array (Array &&rhs) noexcept {
m_data = rhs.m_data;
m_length = rhs.m_length;
m_capacity = rhs.m_capacity;
rhs.reset ();
}
~Array () {
destroy ();
}
private:
void destructElements () noexcept {
for (size_t i = 0; i < m_length; ++i) {
alloc.destruct (&m_data[i]);
}
}
void transferElements (T *dest, T *src, size_t length) noexcept {
for (size_t i = 0; i < length; ++i) {
alloc.construct (&dest[i], cr::move (src[i]));
alloc.destruct (&src[i]);
}
}
void destroy () {
destructElements ();
alloc.deallocate (m_data);
}
void reset () {
m_data = nullptr;
m_capacity = 0;
m_length = 0;
}
public:
bool reserve (const size_t amount) {
if (m_length + amount < m_capacity) {
return true;
}
auto capacity = m_capacity ? m_capacity : 8;
if (cr::fix (R == ReservePolicy::MultipleByTwo)) {
while (m_length + amount > capacity) {
capacity *= 2;
}
}
else {
capacity = amount + m_capacity + 1;
}
auto data = alloc.allocate <T> (capacity);
transferElements (data, m_data, m_length);
alloc.deallocate (m_data);
m_data = data;
m_capacity = capacity;
return true;
}
bool resize (const size_t amount) {
if (amount < m_length) {
while (amount < m_length) {
discard ();
}
}
else if (amount > m_length) {
if (!ensure (amount)) {
return false;
}
size_t pushLength = amount - m_length;
for (size_t i = 0; i < pushLength; ++i) {
push (T ());
}
}
return true;
}
bool ensure (const size_t amount) {
if (amount <= m_length) {
return true;
}
return reserve (amount - m_length);
}
size_t length () const {
return m_length;
}
size_t capacity () const {
return m_capacity;
}
template <typename U> bool set (size_t index, U &&object) {
if (index >= m_capacity) {
if (!reserve (index + 1)) {
return false;
}
}
alloc.construct (&m_data[index], cr::forward <U> (object));
if (index >= m_length) {
m_length = index + 1;
}
return true;
}
template <typename U> bool insert (size_t index, U &&object) {
return insert (index, &object, 1);
}
template <typename U> bool insert (size_t index, U *objects, size_t count = 1) {
if (!objects || !count) {
return false;
}
const size_t capacity = (m_length > index ? m_length : index) + count;
if (capacity >= m_capacity && !reserve (capacity)) {
return false;
}
if (index >= m_length) {
for (size_t i = 0; i < count; ++i) {
alloc.construct (&m_data[i + index], cr::forward <U> (objects[i]));
}
m_length = capacity;
}
else {
size_t i = 0;
for (i = m_length; i > index; --i) {
m_data[i + count - 1] = cr::move (m_data[i - 1]);
}
for (i = 0; i < count; ++i) {
alloc.construct (&m_data[i + index], cr::forward <U> (objects[i]));
}
m_length += count;
}
return true;
}
bool insert (size_t at, const Array &rhs) {
if (&rhs == this) {
return false;
}
return insert (at, &rhs.m_data[0], rhs.m_length);
}
bool erase (const size_t index, const size_t count) {
if (index + count > m_capacity) {
return false;
}
m_length -= count;
for (size_t i = index; i < m_length; ++i) {
m_data[i] = cr::move (m_data[i + count]);
}
return true;
}
bool shift () {
return erase (0, 1);
}
template <typename U> bool unshift (U &&object) {
return insert (0, &object);
}
bool remove (const T &object) {
return erase (index (object), 1);
}
template <typename U> bool push (U &&object) {
if (!reserve (1)) {
return false;
}
alloc.construct (&m_data[m_length], cr::forward <U> (object));
m_length++;
return true;
}
template <typename ...Args> bool emplace (Args &&...args) {
if (!reserve (1)) {
return false;
}
alloc.construct (&m_data[m_length], cr::forward <Args> (args)...);
m_length++;
return true;
}
T pop () {
auto object = cr::move (m_data[m_length - 1]);
discard ();
return object;
}
void discard () {
erase (m_length - 1, 1);
}
size_t index (const T &object) const {
return &object - &m_data[0];
}
void shuffle () {
for (size_t i = m_length; i >= 1; --i) {
cr::swap (m_data[i - 1], m_data[rg.int_ (i, m_length - 2)]);
}
}
void reverse () {
for (size_t i = 0; i < m_length / 2; ++i) {
cr::swap (m_data[i], m_data[m_length - 1 - i]);
}
}
template <typename U> bool extend (U &&rhs) {
if (m_length == 0) {
*this = cr::move (rhs);
}
else {
for (size_t i = 0; i < rhs.length (); ++i) {
if (!push (cr::move (rhs[i]))) {
return false;
}
}
}
return true;
}
template <typename U> bool assign (U &&rhs) {
clear ();
return extend (cr::move (rhs));
}
void clear () {
destructElements ();
m_length = 0;
}
bool empty () const {
return m_length == 0;
}
bool shrink () {
if (m_length == m_capacity || !m_length) {
return false;
}
auto data = alloc.allocate <T> (m_length);
transferElements (data, m_data, m_length);
alloc.deallocate (m_data);
m_data = data;
m_capacity = m_length;
return true;
}
const T &at (size_t index) const {
return m_data[index];
}
T &at (size_t index) {
return m_data[index];
}
const T &first () const {
return m_data[0];
}
T &first () {
return m_data[0];
}
T &last () {
return m_data[m_length - 1];
}
const T &last () const {
return m_data[m_length - 1];
}
const T &random () const {
return m_data[rg.int_ <size_t> (0, m_length - 1)];
}
T &random () {
return m_data[rg.int_ <size_t> (0u, m_length - 1u)];
}
T *data () {
return m_data;
}
T *data () const {
return m_data;
}
public:
Array &operator = (Array &&rhs) noexcept {
if (this != &rhs) {
destroy ();
m_data = rhs.m_data;
m_length = rhs.m_length;
m_capacity = rhs.m_capacity;
rhs.reset ();
}
return *this;
}
public:
const T &operator [] (size_t index) const {
return at (index);
}
T &operator [] (size_t index) {
return at (index);
}
// for range-based loops
public:
T *begin () {
return m_data;
}
T *begin () const {
return m_data;
}
T *end () {
return m_data + m_length;
}
T *end () const {
return m_data + m_length;
}
};
CR_NAMESPACE_END

128
include/crlib/cr-basic.h Normal file
View file

@ -0,0 +1,128 @@
//
// Yet Another POD-Bot, based on PODBot by Markus Klinge ("CountFloyd").
// Copyright (c) YaPB Development Team.
//
// This software is licensed under the BSD-style license.
// Additional exceptions apply. For full license details, see LICENSE.txt or visit:
// https://yapb.ru/license
//
#pragma once
// our global namaespace
#define CR_NAMESPACE_BEGIN namespace cr {
#define CR_NAMESPACE_END }
// undef base max
#if defined (max)
# undef max
#endif
// undef base min
#if defined (min)
# undef min
#endif
#include <stdio.h>
#include <stdlib.h>
#include <stddef.h>
#include <limits.h>
CR_NAMESPACE_BEGIN
//
// some useful type definitions
//
namespace types {
using int8 = signed char;
using int16 = signed short;
using int32 = signed int;
using uint8 = unsigned char;
using uint16 = unsigned short;
using uint32 = unsigned int;
using uint64 = unsigned long long;
};
// make types available for our own use
using namespace cr::types;
//
// global helper stuff
//
template <typename T, size_t N> constexpr size_t bufsize (const T (&)[N]) {
return N - 1;
}
template <typename T> constexpr T abs (const T &a) {
return a > 0 ? a : -a;
}
template <typename T> constexpr T bit (const T &a) {
return static_cast <T> (1ULL << a);
}
template <typename T> constexpr T min (const T &a, const T &b) {
return a < b ? a : b;
}
template <typename T> constexpr T max (const T &a, const T &b) {
return a > b ? a : b;
}
template <typename T> constexpr T square (const T &value) {
return value * value;
}
template <typename T> constexpr T clamp (const T &x, const T &a, const T &b) {
return min (max (x, a), b);
}
template <typename T> const T &fix (const T &type) {
return type;
}
//
// base classes
//
// simple non-copying base class
class DenyCopying {
protected:
explicit DenyCopying () = default;
~DenyCopying () = default;
public:
DenyCopying (const DenyCopying &) = delete;
DenyCopying &operator = (const DenyCopying &) = delete;
};
// singleton for objects
template <typename T> struct Singleton : private DenyCopying {
public:
static inline T &get () {
static T ref_;
return ref_;
};
};
// simple scoped enum, instaed of enum class
#define CR_DECLARE_SCOPED_ENUM_TYPE(enumName, enumType, ...) \
CR_NAMESPACE_BEGIN \
namespace enums { \
struct _##enumName : protected DenyCopying { \
enum Type : enumType { \
__VA_ARGS__ \
}; \
}; \
}; \
CR_NAMESPACE_END \
using enumName = ::cr::enums::_##enumName::Type; \
// same as above, but with int32 type
#define CR_DECLARE_SCOPED_ENUM(enumName, ...) \
CR_DECLARE_SCOPED_ENUM_TYPE(enumName, int32, __VA_ARGS__); \
CR_NAMESPACE_END
// platform-dependant-stuff
#include <crlib/cr-platform.h>

141
include/crlib/cr-binheap.h Normal file
View file

@ -0,0 +1,141 @@
//
// Yet Another POD-Bot, based on PODBot by Markus Klinge ("CountFloyd").
// Copyright (c) YaPB Development Team.
//
// This software is licensed under the BSD-style license.
// Additional exceptions apply. For full license details, see LICENSE.txt or visit:
// https://yapb.ru/license
//
#pragma once
#include <crlib/cr-array.h>
CR_NAMESPACE_BEGIN
// simple priority queue
template <typename T> class BinaryHeap final : public DenyCopying {
private:
Array <T> m_data;
public:
explicit BinaryHeap () = default;
BinaryHeap (BinaryHeap &&rhs) noexcept : m_data (cr::move (rhs.m_data))
{ }
~BinaryHeap () = default;
public:
template <typename U> bool push (U &&item) {
if (!m_data.push (cr::move (item))) {
return false;
}
size_t length = m_data.length ();
if (length > 1) {
percolateUp (length - 1);
}
return true;
}
template <typename ...Args> bool emplace (Args &&...args) {
if (!m_data.emplace (cr::forward <Args> (args)...)) {
return false;
}
size_t length = m_data.length ();
if (length > 1) {
percolateUp (length - 1);
}
return true;
}
const T &top () const {
return m_data[0];
}
T pop () {
if (m_data.length () == 1) {
return m_data.pop ();
}
auto key (cr::move (m_data[0]));
m_data[0] = cr::move (m_data.last ());
m_data.discard ();
percolateDown (0);
return key;
}
public:
size_t length () const {
return m_data.length ();
}
bool empty () const {
return !m_data.length ();
}
void clear () {
m_data.clear ();
}
private:
void percolateUp (size_t index) {
while (index != 0) {
size_t parent = this->parent (index);
if (m_data[parent] <= m_data[index]) {
break;
}
cr::swap (m_data[index], m_data[parent]);
index = parent;
}
}
void percolateDown (size_t index) {
while (this->left (index) < m_data.length ()) {
size_t best = this->left (index);
if (this->right (index) < m_data.length ()) {
size_t right_index = this->right (index);
if (m_data[right_index] < m_data[best]) {
best = right_index;
}
}
if (m_data[index] <= m_data[best]) {
break;
}
cr::swap (m_data[index], m_data[best]);
index = best;
best = this->left (index);
}
}
private:
BinaryHeap &operator = (BinaryHeap &&rhs) noexcept {
if (this != &rhs) {
m_data = cr::move (rhs.m_data);
}
return *this;
}
private:
static constexpr size_t left (size_t index) {
return index << 1 | 1;
}
static constexpr size_t right (size_t index) {
return ++index << 1;
}
static constexpr size_t parent (size_t index) {
return --index >> 1;
}
};
CR_NAMESPACE_END

41
include/crlib/cr-color.h Normal file
View file

@ -0,0 +1,41 @@
//
// Yet Another POD-Bot, based on PODBot by Markus Klinge ("CountFloyd").
// Copyright (c) YaPB Development Team.
//
// This software is licensed under the BSD-style license.
// Additional exceptions apply. For full license details, see LICENSE.txt or visit:
// https://yapb.ru/license
//
#pragma once
#include <crlib/cr-basic.h>
CR_NAMESPACE_BEGIN
// simple color holder
class Color final {
public:
int32 red = 0, green = 0, blue = 0;
public:
explicit Color (int32 r, int32 g, int32 b) : red (r), green (g), blue (b) { }
Color () = default;
~Color () = default;
public:
void reset () {
red = green = blue = 0;
}
int32 avg () const {
return sum () / (sizeof (Color) / sizeof (int32));
}
int32 sum () const {
return red + green + blue;
}
};
CR_NAMESPACE_END

View file

@ -0,0 +1,40 @@
//
// Yet Another POD-Bot, based on PODBot by Markus Klinge ("CountFloyd").
// Copyright (c) YaPB Development Team.
//
// This software is licensed under the BSD-style license.
// Additional exceptions apply. For full license details, see LICENSE.txt or visit:
// https://yapb.ru/license
//
#pragma once
#include <stdio.h>
#include <crlib/cr-platform.h>
#include <crlib/cr-basic.h>
#include <crlib/cr-alloc.h>
#include <crlib/cr-array.h>
#include <crlib/cr-binheap.h>
#include <crlib/cr-files.h>
#include <crlib/cr-lambda.h>
#include <crlib/cr-http.h>
#include <crlib/cr-library.h>
#include <crlib/cr-dict.h>
#include <crlib/cr-logger.h>
#include <crlib/cr-math.h>
#include <crlib/cr-vector.h>
#include <crlib/cr-random.h>
#include <crlib/cr-ulz.h>
#include <crlib/cr-color.h>
CR_NAMESPACE_BEGIN
namespace types {
using StringArray = Array <String>;
using IntArray = Array <int>;
};
using namespace cr::types;
CR_NAMESPACE_END

237
include/crlib/cr-dict.h Normal file
View file

@ -0,0 +1,237 @@
//
// Yet Another POD-Bot, based on PODBot by Markus Klinge ("CountFloyd").
// Copyright (c) YaPB Development Team.
//
// This software is licensed under the BSD-style license.
// Additional exceptions apply. For full license details, see LICENSE.txt or visit:
// https://yapb.ru/license
//
#pragma once
#include <crlib/cr-basic.h>
#include <crlib/cr-array.h>
#include <crlib/cr-string.h>
#include <crlib/cr-twin.h>
CR_NAMESPACE_BEGIN
// template for hashing our string
template <typename K> struct StringHash {
uint32 operator () (const K &key) const {
char *str = const_cast <char *> (key.chars ());
uint32 hash = 0;
while (*str++) {
hash = ((hash << 5) + hash) + *str;
}
return hash;
}
};
// template for hashing integers
template <typename K> struct IntHash {
uint32 operator () (K key) const {
key = ((key >> 16) ^ key) * 0x119de1f3;
key = ((key >> 16) ^ key) * 0x119de1f3;
key = (key >> 16) ^ key;
return key;
}
};
namespace detail {
struct DictionaryList {
uint32 index;
DictionaryList *next;
};
template <typename K, typename V> struct DictionaryBucket {
uint32 hash = static_cast <size_t> (-1);
K key = K ();
V value = V ();
public:
DictionaryBucket () = default;
~DictionaryBucket () = default;
};
}
// basic dictionary
template <class K, class V, class H = StringHash <K>, size_t HashSize = 36> class Dictionary final : public DenyCopying {
public:
static constexpr size_t kInvalidIndex = static_cast <size_t> (-1);
private:
Array <detail::DictionaryList *> m_table;
Array <detail::DictionaryBucket <K, V>> m_buckets;
H m_hasher;
private:
uint32 hash (const K &key) const {
return m_hasher (key);
}
size_t findIndexOrAllocate (const K &key, bool allocate) {
auto hashed = hash (key);
auto pos = hashed % m_table.length ();
for (auto bucket = m_table[pos]; bucket != nullptr; bucket = bucket->next) {
if (m_buckets[bucket->index].hash == hashed) {
return bucket->index;
}
}
if (allocate) {
size_t created = m_buckets.length ();
m_buckets.resize (created + 1);
auto allocated = alloc.allocate <detail::DictionaryList> ();
allocated->index = created;
allocated->next = m_table[pos];
m_table[pos] = allocated;
m_buckets[created].key = key;
m_buckets[created].hash = hashed;
return created;
}
return kInvalidIndex;
}
size_t findIndex (const K &key) const {
return const_cast <Dictionary *> (this)->findIndexOrAllocate (key, false);
}
public:
explicit Dictionary () {
reset ();
}
Dictionary (Dictionary &&rhs) noexcept : m_table (cr::move (rhs.m_table)), m_buckets (cr::move (rhs.m_buckets)), m_hasher (cr::move (rhs.m_hasher))
{ }
~Dictionary () {
clear ();
}
public:
bool exists (const K &key) const {
return findIndex (key) != kInvalidIndex;
}
bool empty () const {
return m_buckets.empty ();
}
size_t length () const {
return m_buckets.length ();
}
bool find (const K &key, V &value) const {
size_t index = findIndex (key);
if (index == kInvalidIndex) {
return false;
}
value = m_buckets[index].value;
return true;
}
template <typename U> bool push (const K &key, U &&value) {
operator [] (key) = cr::forward <U> (value);
return true;
}
bool remove (const K &key) {
auto hashed = hash (key);
auto pos = hashed % m_table.length ();
auto *bucket = m_table[pos];
detail::DictionaryList *next = nullptr;
while (bucket != nullptr) {
if (m_buckets[bucket->index].hash == hashed) {
if (!next) {
m_table[pos] = bucket->next;
}
else {
next->next = bucket->next;
}
m_buckets.erase (bucket->index, 1);
alloc.deallocate (bucket);
bucket = nullptr;
return true;
}
next = bucket;
bucket = bucket->next;
}
return false;
}
void clear () {
for (auto object : m_table) {
while (object != nullptr) {
auto next = object->next;
alloc.deallocate (object);
object = next;
}
}
m_table.clear ();
m_buckets.clear ();
reset ();
}
void reset () {
m_table.resize (HashSize);
for (size_t i = 0; i < HashSize; ++i) {
m_table[i] = nullptr;
}
}
public:
V &operator [] (const K &key) {
return m_buckets[findIndexOrAllocate (key, true)].value;
}
const V &operator [] (const K &key) const {
return m_buckets[findIndex (key)].value;
}
Dictionary &operator = (Dictionary &&rhs) noexcept {
if (this != &rhs) {
m_table = cr::move (rhs.m_table);
m_buckets = cr::move (rhs.m_buckets);
m_hasher = cr::move (rhs.m_hasher);
}
return *this;
}
// for range-based loops
public:
detail::DictionaryBucket <K, V> *begin () {
return m_buckets.begin ();
}
detail::DictionaryBucket <K, V> *begin () const {
return m_buckets.begin ();
}
detail::DictionaryBucket <K, V> *end () {
return m_buckets.end ();
}
detail::DictionaryBucket <K, V> *end () const {
return m_buckets.end ();
}
};
CR_NAMESPACE_END

334
include/crlib/cr-files.h Normal file
View file

@ -0,0 +1,334 @@
//
// Yet Another POD-Bot, based on PODBot by Markus Klinge ("CountFloyd").
// Copyright (c) YaPB Development Team.
//
// This software is licensed under the BSD-style license.
// Additional exceptions apply. For full license details, see LICENSE.txt or visit:
// https://yapb.ru/license
//
#pragma once
#include <stdio.h>
#include <crlib/cr-string.h>
#include <crlib/cr-lambda.h>
CR_NAMESPACE_BEGIN
// simple stdio file wrapper
class File final : private DenyCopying {
private:
FILE *m_handle = nullptr;
size_t m_length = 0;
public:
explicit File () = default;
File (const String &file, const String &mode = "rt") {
open (file, mode);
}
~File () {
close ();
}
public:
bool open (const String &file, const String &mode) {
if ((m_handle = fopen (file.chars (), mode.chars ())) == nullptr) {
return false;
}
fseek (m_handle, 0L, SEEK_END);
m_length = ftell (m_handle);
fseek (m_handle, 0L, SEEK_SET);
return true;
}
void close () {
if (m_handle != nullptr) {
fclose (m_handle);
m_handle = nullptr;
}
m_length = 0;
}
bool eof () const {
return !!feof (m_handle);
}
bool flush () const {
return !!fflush (m_handle);
}
int getChar () const {
return fgetc (m_handle);
}
char *getString (char *buffer, int count) const {
return fgets (buffer, count, m_handle);
}
bool getLine (String &line) {
line.clear ();
int ch;
while ((ch = getChar ()) != EOF && !eof ()) {
line += static_cast <char> (ch);
if (ch == '\n') {
break;
}
}
return !eof ();
}
template <typename ...Args> size_t puts (const char *fmt, Args ...args) {
if (!*this) {
return false;
}
return fprintf (m_handle, fmt, cr::forward <Args> (args)...);
}
bool puts (const String &buffer) {
if (!*this) {
return false;
}
if (fputs (buffer.chars (), m_handle) < 0) {
return false;
}
return true;
}
int putChar (int ch) {
return fputc (ch, m_handle);
}
size_t read (void *buffer, size_t size, size_t count = 1) {
return fread (buffer, size, count, m_handle);
}
size_t write (void *buffer, size_t size, size_t count = 1) {
return fwrite (buffer, size, count, m_handle);
}
bool seek (long offset, int origin) {
if (fseek (m_handle, offset, origin) != 0) {
return false;
}
return true;
}
void rewind () {
::rewind (m_handle);
}
size_t length () const {
return m_length;
}
public:
explicit operator bool () const {
return m_handle != nullptr;
}
public:
static inline bool exists (const String &file) {
File fp;
if (fp.open (file, "rb")) {
fp.close ();
return true;
}
return false;
}
static inline void createPath (const char *path) {
for (auto str = const_cast <char *> (path) + 1; *str; ++str) {
if (*str == '/') {
*str = 0;
plat.createDirectory (path);
*str = '/';
}
}
plat.createDirectory (path);
}
};
// wrapper for memory file for loading data into the memory
class MemFileStorage : public Singleton <MemFileStorage> {
private:
using LoadFunction = Lambda <uint8 * (const char *, int *)>;
using FreeFunction = Lambda <void (void *)>;
private:
LoadFunction m_load = nullptr;
FreeFunction m_free = nullptr;
public:
inline MemFileStorage () = default;
inline ~MemFileStorage () = default;
public:
void initizalize (LoadFunction loader, FreeFunction unloader) {
m_load = cr::move (loader);
m_free = cr::move (unloader);
}
public:
uint8 *load (const String &file, int *size) {
if (m_load) {
return m_load (file.chars (), size);
}
return nullptr;
}
void unload (void *buffer) {
if (m_free) {
m_free (buffer);
}
}
};
class MemFile final : public DenyCopying {
private:
static constexpr char kEOF = static_cast <char> (-1);
private:
uint8 *m_data = nullptr;
size_t m_length = 0, m_pos = 0;
public:
explicit MemFile () = default;
MemFile (const String &file) {
open (file);
}
~MemFile () {
close ();
}
public:
bool open (const String &file) {
m_length = 0;
m_pos = 0;
m_data = MemFileStorage::get ().load (file.chars (), reinterpret_cast <int *> (&m_length));
if (!m_data) {
return false;
}
return true;
}
void close () {
MemFileStorage::get ().unload (m_data);
m_length = 0;
m_pos = 0;
m_data = nullptr;
}
char getChar () {
if (!m_data || m_pos >= m_length) {
return kEOF;
}
auto ch = m_data[m_pos];
m_pos++;
return static_cast <char> (ch);
}
char *getString (char *buffer, size_t count) {
if (!m_data || m_pos >= m_length) {
return nullptr;
}
size_t index = 0;
buffer[0] = 0;
for (; index < count - 1;) {
if (m_pos < m_length) {
buffer[index] = m_data[m_pos++];
if (buffer[index++] == '\n') {
break;
}
}
else {
break;
}
}
buffer[index] = 0;
return index ? buffer : nullptr;
}
bool getLine (String &line) {
line.clear ();
char ch;
while ((ch = getChar ()) != kEOF) {
line += ch;
if (ch == '\n') {
break;
}
}
return !eof ();
}
size_t read (void *buffer, size_t size, size_t count = 1) {
if (!m_data || m_length <= m_pos || !buffer || !size || !count) {
return 0;
}
size_t blocks_read = size * count <= m_length - m_pos ? size * count : m_length - m_pos;
memcpy (buffer, &m_data[m_pos], blocks_read);
m_pos += blocks_read;
return blocks_read / size;
}
bool seek (size_t offset, int origin) {
if (!m_data || m_pos >= m_length) {
return false;
}
if (origin == SEEK_SET) {
if (offset >= m_length) {
return false;
}
m_pos = offset;
}
else if (origin == SEEK_END) {
if (offset >= m_length) {
return false;
}
m_pos = m_length - offset;
}
else {
if (m_pos + offset >= m_length) {
return false;
}
m_pos += offset;
}
return true;
}
size_t length () const {
return m_length;
}
bool eof () const {
return m_pos >= m_length;
}
void rewind () {
m_pos = 0;
}
public:
explicit operator bool () const {
return !!m_data && m_length > 0;
}
};
CR_NAMESPACE_END

425
include/crlib/cr-http.h Normal file
View file

@ -0,0 +1,425 @@
//
// Yet Another POD-Bot, based on PODBot by Markus Klinge ("CountFloyd").
// Copyright (c) YaPB Development Team.
//
// This software is licensed under the BSD-style license.
// Additional exceptions apply. For full license details, see LICENSE.txt or visit:
// https://yapb.ru/license
//
#pragma once
#include <stdio.h>
#include <crlib/cr-string.h>
#include <crlib/cr-files.h>
#include <crlib/cr-logger.h>
#include <crlib/cr-platform.h>
#if defined (CR_LINUX) || defined (CR_OSX)
# include <netinet/in.h>
# include <sys/socket.h>
# include <sys/types.h>
# include <arpa/inet.h>
# include <unistd.h>
# include <errno.h>
# include <netdb.h>
# include <fcntl.h>
#elif defined (CR_WINDOWS)
# define WIN32_LEAN_AND_MEAN
# include <windows.h>
# include <winsock2.h>
#endif
// status codes for http client
CR_DECLARE_SCOPED_ENUM (HttpClientResult,
OK = 0,
NotFound,
Forbidden,
SocketError,
ConnectError,
HttpOnly,
Undefined,
NoLocalFile = -1,
LocalFileExists = -2
);
CR_NAMESPACE_BEGIN
class Socket final : public DenyCopying {
private:
int32 m_socket;
uint32 m_timeout;
public:
Socket () : m_socket (-1), m_timeout (2) {
#if defined(CR_WINDOWS)
WSADATA wsa;
if (WSAStartup (MAKEWORD (1, 1), &wsa) != 0) {
logger.error ("Unable to inialize sockets.");
}
#endif
}
~Socket () {
disconnect ();
#if defined (CR_WINDOWS)
WSACleanup ();
#endif
}
public:
bool connect (const String &hostname) {
auto host = gethostbyname (hostname.chars ());
if (!host) {
return false;
}
m_socket = static_cast <int> (socket (AF_INET, SOCK_STREAM, 0));
if (m_socket < 0) {
return false;
}
auto getTimeouts = [&] () -> Twin <char *, size_t> {
#if defined (CR_WINDOWS)
DWORD tv = m_timeout * 1000;
#else
timeval tv { static_cast <time_t> (m_timeout), 0 };
#endif
return { reinterpret_cast <char *> (&tv), sizeof (tv) };
};
auto timeouts = getTimeouts ();
setsockopt (m_socket, SOL_SOCKET, SO_RCVTIMEO, timeouts.first, timeouts.second);
setsockopt (m_socket, SOL_SOCKET, SO_SNDTIMEO, timeouts.first, timeouts.second);
sockaddr_in dest;
memset (&dest, 0, sizeof (dest));
dest.sin_family = AF_INET;
dest.sin_port = htons (80);
dest.sin_addr.s_addr = inet_addr (inet_ntoa (*(reinterpret_cast <in_addr *> (host->h_addr))));
if (::connect (m_socket, reinterpret_cast <sockaddr *> (&dest), static_cast <int> (sizeof (dest))) == -1) {
disconnect ();
return false;
}
return true;
}
void setTimeout (uint32 timeout) {
m_timeout = timeout;
}
void disconnect () {
#if defined(CR_WINDOWS)
if (m_socket != -1) {
closesocket (m_socket);
}
#else
if (m_socket != -1)
close (m_socket);
#endif
}
public:
template <typename U> int32 send (const U *buffer, int32 length) const {
return ::send (m_socket, reinterpret_cast <const char *> (buffer), length, 0);
}
template <typename U> int32 recv (U *buffer, int32 length) {
return ::recv (m_socket, reinterpret_cast <char *> (buffer), length, 0);
}
};
namespace detail {
// simple http uri omitting query-string and port
struct HttpUri {
String path, protocol, host;
public:
static HttpUri parse (const String &uri) {
HttpUri result;
if (uri.empty ()) {
return result;
}
size_t protocol = uri.find ("://");
if (protocol != String::kInvalidIndex) {
result.protocol = uri.substr (0, protocol);
size_t host = uri.find ("/", protocol + 3);
if (host != String::kInvalidIndex) {
result.path = uri.substr (host + 1);
result.host = uri.substr (protocol + 3, host - protocol - 3);
return result;
}
}
return result;
}
};
};
// simple http client for downloading/uploading files only
class HttpClient final : public Singleton <HttpClient> {
private:
static constexpr int32 kMaxRecvErrors = 12;
private:
Socket m_socket;
String m_userAgent = "crlib";
HttpClientResult m_code = HttpClientResult::Undefined;
int32 m_chunkSize = 4096;
public:
HttpClient () = default;
~HttpClient () = default;
private:
HttpClientResult parseResponseHeader (uint8 *buffer) {
bool isFinished = false;
int32 pos = 0, symbols = 0, errors = 0;
// prase response header
while (!isFinished && pos < m_chunkSize) {
if (m_socket.recv (&buffer[pos], 1) < 1) {
if (++errors > kMaxRecvErrors) {
isFinished = true;
}
else {
continue;
}
}
switch (buffer[pos]) {
case '\r':
break;
case '\n':
isFinished = (symbols == 0);
symbols = 0;
break;
default:
symbols++;
break;
}
pos++;
}
String response (reinterpret_cast <const char *> (buffer));
size_t responseCodeStart = response.find ("HTTP/1.1");
if (responseCodeStart != String::kInvalidIndex) {
String respCode = response.substr (responseCodeStart + 9, 3).trim ();
if (respCode == "200") {
return HttpClientResult::OK;
}
else if (respCode == "403") {
return HttpClientResult::Forbidden;
}
else if (respCode == "404") {
return HttpClientResult::NotFound;
}
}
return HttpClientResult::NotFound;
}
public:
// simple blocked download
bool downloadFile (const String &url, const String &localPath) {
if (File::exists (localPath.chars ())) {
m_code = HttpClientResult::LocalFileExists;
return false;
}
auto uri = detail::HttpUri::parse (url);
// no https...
if (uri.protocol == "https") {
m_code = HttpClientResult::HttpOnly;
return false;
}
// unable to connect...
if (!m_socket.connect (uri.host)) {
m_code = HttpClientResult::ConnectError;
m_socket.disconnect ();
return false;
}
String request;
request.appendf ("GET /%s HTTP/1.1\r\n", uri.path.chars ());
request.append ("Accept: */*\r\n");
request.append ("Connection: close\r\n");
request.append ("Keep-Alive: 115\r\n");
request.appendf ("User-Agent: %s\r\n", m_userAgent.chars ());
request.appendf ("Host: %s\r\n\r\n", uri.host.chars ());
if (m_socket.send (request.chars (), static_cast <int32> (request.length ())) < 1) {
m_code = HttpClientResult::SocketError;
m_socket.disconnect ();
return false;
}
Array <uint8, ReservePolicy::PlusOne> buffer (m_chunkSize);
m_code = parseResponseHeader (buffer.data ());
if (m_code != HttpClientResult::OK) {
m_socket.disconnect ();
return false;
}
// receive the file
File file (localPath, "wb");
if (!file) {
m_code = HttpClientResult::Undefined;
m_socket.disconnect ();
return false;
}
int32 length = 0;
int32 errors = 0;
for (;;) {
length = m_socket.recv (buffer.data (), m_chunkSize);
if (length > 0) {
file.write (buffer.data (), length);
}
else if (++errors > 12) {
break;
}
}
file.close ();
m_socket.disconnect ();
m_code = HttpClientResult::OK;
return true;
}
bool uploadFile (const String &url, const String &localPath) {
if (!File::exists (localPath.chars ())) {
m_code = HttpClientResult::NoLocalFile;
return false;
}
auto uri = detail::HttpUri::parse (url);
// no https...
if (uri.protocol == "https") {
m_code = HttpClientResult::HttpOnly;
return false;
}
// unable to connect...
if (!m_socket.connect (uri.host)) {
m_code = HttpClientResult::ConnectError;
m_socket.disconnect ();
return false;
}
// receive the file
File file (localPath, "rb");
if (!file) {
m_code = HttpClientResult::Undefined;
m_socket.disconnect ();
return false;
}
String boundaryName = localPath;
size_t boundarySlash = localPath.findLastOf ("\\/");
if (boundarySlash != String::kInvalidIndex) {
boundaryName = localPath.substr (boundarySlash + 1);
}
const String &kBoundary = "---crlib_upload_boundary_1337";
String request, start, end;
start.appendf ("--%s\r\n", kBoundary.chars ());
start.appendf ("Content-Disposition: form-data; name='file'; filename='%s'\r\n", boundaryName.chars ());
start.append ("Content-Type: application/octet-stream\r\n\r\n");
end.appendf ("\r\n--%s--\r\n\r\n", kBoundary.chars ());
request.appendf ("POST /%s HTTP/1.1\r\n", uri.path.chars ());
request.appendf ("Host: %s\r\n", uri.host.chars ());
request.appendf ("User-Agent: %s\r\n", m_userAgent.chars ());
request.appendf ("Content-Type: multipart/form-data; boundary=%s\r\n", kBoundary.chars ());
request.appendf ("Content-Length: %d\r\n\r\n", file.length () + start.length () + end.length ());
// send the main request
if (m_socket.send (request.chars (), static_cast <int32> (request.length ())) < 1) {
m_code = HttpClientResult::SocketError;
m_socket.disconnect ();
return false;
}
// send boundary start
if (m_socket.send (start.chars (), static_cast <int32> (start.length ())) < 1) {
m_code = HttpClientResult::SocketError;
m_socket.disconnect ();
return false;
}
Array <uint8, ReservePolicy::PlusOne> buffer (m_chunkSize);
int32 length = 0;
for (;;) {
length = static_cast <int32> (file.read (buffer.data (), 1, m_chunkSize));
if (length > 0) {
m_socket.send (buffer.data (), length);
}
else {
break;
}
}
// send boundary end
if (m_socket.send (end.chars (), static_cast <int32> (end.length ())) < 1) {
m_code = HttpClientResult::SocketError;
m_socket.disconnect ();
return false;
}
m_code = parseResponseHeader (buffer.data ());
m_socket.disconnect ();
return m_code == HttpClientResult::OK;
}
public:
void setUserAgent (const String &ua) {
m_userAgent = ua;
}
HttpClientResult getLastStatusCode () {
return m_code;
}
void setChunkSize (int32 chunkSize) {
m_chunkSize = chunkSize;
}
void setTimeout (uint32 timeout) {
m_socket.setTimeout (timeout);
}
};
// expose global http client
static auto &http = HttpClient::get ();
CR_NAMESPACE_END

173
include/crlib/cr-lambda.h Normal file
View file

@ -0,0 +1,173 @@
//
// Yet Another POD-Bot, based on PODBot by Markus Klinge ("CountFloyd").
// Copyright (c) YaPB Development Team.
//
// This software is licensed under the BSD-style license.
// Additional exceptions apply. For full license details, see LICENSE.txt or visit:
// https://yapb.ru/license
//
#pragma once
#include <crlib/cr-alloc.h>
#include <crlib/cr-uniqueptr.h>
CR_NAMESPACE_BEGIN
static constexpr uint32 kLambdaSmallBufferSize = sizeof (void *) * 16;
template <typename> class Lambda;
template <typename R, typename ...Args> class Lambda <R (Args...)> {
class LambdaFunctorWrapper {
public:
LambdaFunctorWrapper () = default;
virtual ~LambdaFunctorWrapper () = default;
public:
virtual void move (uint8 *to) = 0;
virtual void small (uint8 *to) const = 0;
virtual R invoke (Args &&...) = 0;
virtual UniquePtr <LambdaFunctorWrapper> clone () const = 0;
public:
void operator delete (void *ptr) {
alloc.deallocate (ptr);
}
};
template <typename T> class LambdaFunctor : public LambdaFunctorWrapper {
private:
T m_callable;
public:
LambdaFunctor (const T &callable) : LambdaFunctorWrapper (), m_callable (callable)
{ }
LambdaFunctor (T &&callable) : LambdaFunctorWrapper (), m_callable (cr::move (callable))
{ }
~LambdaFunctor () override = default;
public:
void move (uint8 *to) override {
new (to) LambdaFunctor<T> (cr::move (m_callable));
}
void small (uint8 *to) const override {
new (to) LambdaFunctor<T> (m_callable);
}
R invoke (Args &&... args) override {
return m_callable (cr::forward <Args> (args)...);
}
UniquePtr <LambdaFunctorWrapper> clone () const override {
return createUniqueBase <LambdaFunctor <T>, LambdaFunctorWrapper> (m_callable);
}
};
union {
UniquePtr <LambdaFunctorWrapper> m_functor;
uint8 m_small[kLambdaSmallBufferSize];
};
bool m_smallObject;
private:
void destroy () {
if (m_smallObject) {
reinterpret_cast <LambdaFunctorWrapper *> (m_small)->~LambdaFunctorWrapper ();
}
else {
m_functor.reset ();
}
}
void swap (Lambda &rhs) noexcept {
cr::swap (rhs, *this);
}
public:
Lambda () noexcept : Lambda (nullptr)
{ }
Lambda (decltype (nullptr)) noexcept : m_functor (nullptr), m_smallObject (false)
{ }
Lambda (const Lambda &rhs) {
if (rhs.m_smallObject) {
reinterpret_cast <const LambdaFunctorWrapper *> (rhs.m_small)->small (m_small);
}
else {
new (m_small) UniquePtr <LambdaFunctorWrapper> (rhs.m_functor->clone ());
}
m_smallObject = rhs.m_smallObject;
}
Lambda (Lambda &&rhs) noexcept {
if (rhs.m_smallObject) {
reinterpret_cast <LambdaFunctorWrapper *> (rhs.m_small)->move (m_small);
new (rhs.m_small) UniquePtr <LambdaFunctorWrapper> (nullptr);
}
else {
new (m_small) UniquePtr <LambdaFunctorWrapper> (cr::move (rhs.m_functor));
}
m_smallObject = rhs.m_smallObject;
rhs.m_smallObject = false;
}
template <typename F> Lambda (F function) {
if (cr::fix (sizeof (function) > kLambdaSmallBufferSize)) {
m_smallObject = false;
new (m_small) UniquePtr <LambdaFunctorWrapper> (createUniqueBase <LambdaFunctor <F>, LambdaFunctorWrapper> (cr::move (function)));
}
else {
m_smallObject = true;
new (m_small) LambdaFunctor<F> (cr::move (function));
}
}
~Lambda () {
destroy ();
}
public:
Lambda &operator = (const Lambda &rhs) {
destroy ();
Lambda tmp (rhs);
swap (tmp);
return *this;
}
Lambda &operator = (Lambda &&rhs) noexcept {
destroy ();
if (rhs.m_smallObject) {
reinterpret_cast <LambdaFunctorWrapper *> (rhs.m_small)->move (m_small);
new (rhs.m_small) UniquePtr <LambdaFunctorWrapper> (nullptr);
}
else {
new (m_small) UniquePtr <LambdaFunctorWrapper> (cr::move (rhs.m_functor));
}
m_smallObject = rhs.m_smallObject;
rhs.m_smallObject = false;
return *this;
}
explicit operator bool () const noexcept {
return m_smallObject || !!m_functor;
}
public:
R operator () (Args ...args) {
return m_smallObject ? reinterpret_cast <LambdaFunctorWrapper *> (m_small)->invoke (cr::forward <Args> (args)...) : m_functor->invoke (cr::forward <Args> (args)...);
}
};
CR_NAMESPACE_END

View file

@ -0,0 +1,89 @@
//
// Yet Another POD-Bot, based on PODBot by Markus Klinge ("CountFloyd").
// Copyright (c) YaPB Development Team.
//
// This software is licensed under the BSD-style license.
// Additional exceptions apply. For full license details, see LICENSE.txt or visit:
// https://yapb.ru/license
//
#pragma once
#include <crlib/cr-basic.h>
#include <crlib/cr-string.h>
#if defined (CR_LINUX) || defined (CR_OSX)
# include <dlfcn.h>
# include <errno.h>
# include <fcntl.h>
# include <sys/stat.h>
# include <unistd.h>
#elif defined (CR_WINDOWS)
# define WIN32_LEAN_AND_MEAN
# include <windows.h>
#endif
CR_NAMESPACE_BEGIN
// handling dynamic library loading
class SharedLibrary final : public DenyCopying {
private:
void *m_handle = nullptr;
public:
explicit SharedLibrary () = default;
SharedLibrary (const String &file) {
if (file.empty ()) {
return;
}
load (file);
}
~SharedLibrary () {
unload ();
}
public:
inline bool load (const String &file) noexcept {
#ifdef CR_WINDOWS
m_handle = LoadLibraryA (file.chars ());
#else
m_handle = dlopen (file.chars (), RTLD_NOW);
#endif
return m_handle != nullptr;
}
void unload () noexcept {
if (!*this) {
return;
}
#ifdef CR_WINDOWS
FreeLibrary (static_cast <HMODULE> (m_handle));
#else
dlclose (m_handle);
#endif
}
template <typename R> R resolve (const char *function) const {
if (!*this) {
return nullptr;
}
return reinterpret_cast <R> (
#ifdef CR_WINDOWS
GetProcAddress (static_cast <HMODULE> (m_handle), function)
#else
dlsym (m_handle, function)
#endif
);
}
public:
explicit operator bool () const {
return m_handle != nullptr;
}
};
CR_NAMESPACE_END

98
include/crlib/cr-logger.h Normal file
View file

@ -0,0 +1,98 @@
//
// Yet Another POD-Bot, based on PODBot by Markus Klinge ("CountFloyd").
// Copyright (c) YaPB Development Team.
//
// This software is licensed under the BSD-style license.
// Additional exceptions apply. For full license details, see LICENSE.txt or visit:
// https://yapb.ru/license
//
#pragma once
#include <time.h>
#include <crlib/cr-files.h>
#include <crlib/cr-lambda.h>
#if defined (CR_WINDOWS)
# define WIN32_LEAN_AND_MEAN
# include <windows.h>
#endif
CR_NAMESPACE_BEGIN
class SimpleLogger final : public Singleton <SimpleLogger> {
public:
using PrintFunction = Lambda <void (const char *)>;
private:
File m_handle;
PrintFunction m_printer;
public:
SimpleLogger () = default;
~SimpleLogger () {
m_handle.close ();
}
private:
void logToFile (const char *level, const char *msg) {
if (!m_handle) {
return;
}
time_t ticks = time (&ticks);
auto tm = localtime (&ticks);
auto timebuf = strings.chars ();
strftime (timebuf, StringBuffer::StaticBufferSize, "%Y-%m-%d %H:%M:%S", tm);
m_handle.puts ("%s (%s): %s\n", timebuf, level, msg);
}
public:
template <typename ...Args> void fatal (const char *fmt, Args ...args) {
auto msg = strings.format (fmt, cr::forward <Args> (args)...);
logToFile ("FATAL", msg);
if (m_printer) {
m_printer (msg);
}
plat.abort (msg);
}
template <typename ...Args> void error (const char *fmt, Args ...args) {
auto msg = strings.format (fmt, cr::forward <Args> (args)...);
logToFile ("ERROR", msg);
if (m_printer) {
m_printer (msg);
}
}
template <typename ...Args> void message (const char *fmt, Args ...args) {
auto msg = strings.format (fmt, cr::forward <Args> (args)...);
logToFile ("INFO", msg);
if (m_printer) {
m_printer (msg);
}
}
public:
void initialize (const String &filename, PrintFunction printFunction) {
if (m_handle) {
m_handle.close ();
}
m_printer = cr::move (printFunction);
m_handle.open (filename, "at");
}
};
// expose global instance
static auto &logger = SimpleLogger::get ();
CR_NAMESPACE_END

179
include/crlib/cr-math.h Normal file
View file

@ -0,0 +1,179 @@
//
// Yet Another POD-Bot, based on PODBot by Markus Klinge ("CountFloyd").
// Copyright (c) YaPB Development Team.
//
// This software is licensed under the BSD-style license.
// Additional exceptions apply. For full license details, see LICENSE.txt or visit:
// https://yapb.ru/license
//
#pragma once
#include <crlib/cr-basic.h>
#if defined (CR_HAS_SSE2)
# include <immintrin.h>
#endif
CR_NAMESPACE_BEGIN
constexpr float kFloatEpsilon = 0.01f;
constexpr float kFloatEqualEpsilon = 0.001f;
constexpr float kFloatCmpEpsilon = 1.192092896e-07f;
constexpr float kMathPi = 3.141592653589793115997963468544185161590576171875f;
constexpr float kMathPiReciprocal = 1.0f / kMathPi;
constexpr float kMathPiHalf = kMathPi / 2;
constexpr float kDegreeToRadians = kMathPi / 180.0f;
constexpr float kRadiansToDegree = 180.0f / kMathPi;
constexpr bool fzero (const float e) {
return cr::abs (e) < kFloatEpsilon;
}
constexpr bool fequal (const float a, const float b) {
return cr:: abs (a - b) < kFloatEqualEpsilon;
}
constexpr float radiansToDegrees (const float r) {
return r * kRadiansToDegree;
}
constexpr float degreesToRadians (const float d) {
return d * kDegreeToRadians;
}
constexpr float modAngles (const float a) {
return 360.0f / 65536.0f * (static_cast <int> (a * (65536.0f / 360.0f)) & 65535);
}
constexpr float normalizeAngles (const float a) {
return 360.0f / 65536.0f * (static_cast <int> ((a + 180.0f) * (65536.0f / 360.0f)) & 65535) - 180.0f;
}
constexpr float anglesDifference (const float a, const float b) {
return normalizeAngles (a - b);
}
inline float sinf (const float value) {
const auto sign = static_cast <int32> (value * kMathPiReciprocal);
const float calc = (value - static_cast <float> (sign) * kMathPi);
const float sqr = cr::square (calc);
const float res = 1.00000000000000000000e+00f + sqr * (-1.66666671633720397949e-01f + sqr * (8.33333376795053482056e-03f + sqr * (-1.98412497411482036114e-04f +
sqr * (2.75565571428160183132e-06f + sqr * (-2.50368472620721149724e-08f + sqr * (1.58849267073435385100e-10f + sqr * -6.58925550841432672300e-13f))))));
return (sign & 1) ? -calc * res : value * res;
}
inline float cosf (const float value) {
const auto sign = static_cast <int32> (value * kMathPiReciprocal);
const float calc = (value - static_cast <float> (sign) * kMathPi);
const float sqr = cr::square (calc);
const float res = sqr * (-5.00000000000000000000e-01f + sqr * (4.16666641831398010254e-02f + sqr * (-1.38888671062886714935e-03f + sqr * (2.48006890615215525031e-05f +
sqr * (-2.75369927749125054106e-07f + sqr * (2.06207229069832465029e-09f + sqr * -9.77507137733812925262e-12f))))));
const float f = -1.00000000000000000000e+00f;
return (sign & 1) ? f - res : -f + res;
}
inline float atanf (const float x) {
const float sqr = cr::square (x);
return x * (48.70107004404898384f + sqr * (49.5326263772254345f + sqr * 9.40604244231624f)) / (48.70107004404996166f + sqr * (65.7663163908956299f + sqr * (21.587934067020262f + sqr)));
};
inline float atan2f (const float y, const float x) {
const float ax = cr::abs (x);
const float ay = cr::abs (y);
if (ax < 1e-7f && ay < 1e-7f) {
return 0.0f;
}
if (ax > ay) {
if (x < 0.0f) {
if (y >= 0.0f) {
return atanf (y / x) + kMathPi;
}
return atanf (y / x) - kMathPi;
}
return atanf (y / x);
}
if (y < 0.0f) {
return atanf (-x / y) - kMathPiHalf;
}
return atanf (-x / y) + kMathPiHalf;
}
inline float powf (const float x, const float y) {
union {
float d;
int x;
} res { x };
res.x = static_cast <int> (y * (res.x - 1064866805) + 1064866805);
return res.d;
}
inline float sqrtf (const float value) {
return powf (value, 0.5f);
}
inline float tanf (const float value) {
return sinf (value) / cosf (value);
}
constexpr float ceilf (const float x) {
return static_cast <float> (65536 - static_cast <int> (65536.0f - x));
}
inline void sincosf (const float x, const float y, const float z, float *sines, float *cosines) {
#if defined (CR_HAS_SSE2)
auto set = _mm_set_ps (x, y, z, 0.0f);
auto _mm_sin = [] (__m128 rad) -> __m128 {
static auto pi2 = _mm_set_ps1 (kMathPi * 2);
static auto rp1 = _mm_set_ps1 (4.0f / kMathPi);
static auto rp2 = _mm_set_ps1 (-4.0f / (kMathPi * kMathPi));
static auto val = _mm_cmpnlt_ps (rad, _mm_set_ps1 (kMathPi));
static auto csi = _mm_castsi128_ps (_mm_set1_epi32 (0x80000000));
val = _mm_and_ps (val, pi2);
rad = _mm_sub_ps (rad, val);
val = _mm_cmpngt_ps (rad, _mm_set_ps1 (-kMathPi));
val = _mm_and_ps (val, pi2);
rad = _mm_add_ps (rad, val);
val = _mm_mul_ps (_mm_andnot_ps (csi, rad), rp2);
val = _mm_add_ps (val, rp1);
auto si = _mm_mul_ps (val, rad);
val = _mm_mul_ps (_mm_andnot_ps (csi, si), si);
val = _mm_sub_ps (val, si);
val = _mm_mul_ps (val, _mm_set_ps1 (0.225f));
return _mm_add_ps (val, si);
};
static auto hpi = _mm_set_ps1 (kMathPiHalf);
auto s = _mm_sin (set);
auto c = _mm_sin (_mm_add_ps (set, hpi));
_mm_store_ps (sines, _mm_shuffle_ps (s, s, _MM_SHUFFLE (0, 1, 2, 3)));
_mm_store_ps (cosines, _mm_shuffle_ps (c, c, _MM_SHUFFLE (0, 1, 2, 3)));
#else
sines[0] = cr::sinf (x);
sines[1] = cr::sinf (y);
sines[2] = cr::sinf (z);
cosines[0] = cr::cosf (x);
cosines[1] = cr::cosf (y);
cosines[2] = cr::cosf (z);
#endif
}
CR_NAMESPACE_END

View file

@ -0,0 +1,58 @@
//
// Yet Another POD-Bot, based on PODBot by Markus Klinge ("CountFloyd").
// Copyright (c) YaPB Development Team.
//
// This software is licensed under the BSD-style license.
// Additional exceptions apply. For full license details, see LICENSE.txt or visit:
// https://yapb.ru/license
//
#pragma once
#include <crlib/cr-basic.h>
CR_NAMESPACE_BEGIN
namespace detail {
template <typename T> struct ClearRef {
using Type = T;
};
template <typename T> struct ClearRef <T &> {
using Type = T;
};
template <typename T> struct ClearRef <T &&> {
using Type = T;
};
};
template <typename T> typename detail::ClearRef <T>::Type constexpr &&move (T &&type) noexcept {
return static_cast <typename detail::ClearRef <T>::Type &&> (type);
}
template <typename T> constexpr T &&forward (typename detail::ClearRef <T>::Type &type) noexcept {
return static_cast <T &&> (type);
}
template <typename T> constexpr T &&forward (typename detail::ClearRef <T>::Type &&type) noexcept {
return static_cast <T &&> (type);
}
template <typename T> inline void swap (T &left, T &right) noexcept {
auto temp = cr::move (left);
left = cr::move (right);
right = cr::move (temp);
}
template <typename T, size_t S> inline void swap (T (&left)[S], T (&right)[S]) noexcept {
if (&left == &right) {
return;
}
auto begin = left;
auto end = begin + S;
for (auto temp = right; begin != end; ++begin, ++temp) {
cr::swap (*begin, *temp);
}
}
CR_NAMESPACE_END

172
include/crlib/cr-platform.h Normal file
View file

@ -0,0 +1,172 @@
//
// Yet Another POD-Bot, based on PODBot by Markus Klinge ("CountFloyd").
// Copyright (c) YaPB Development Team.
//
// This software is licensed under the BSD-style license.
// Additional exceptions apply. For full license details, see LICENSE.txt or visit:
// https://yapb.ru/license
//
#pragma once
CR_NAMESPACE_BEGIN
// detects the build platform
#if defined(__linux__)
# define CR_LINUX
#elif defined(__APPLE__)
# define CR_OSX
#elif defined(_WIN32)
# define CR_WINDOWS
#endif
#if defined(__ANDROID__)
# define CR_ANDROID
# if defined(LOAD_HARDFP)
# define CR_ANDROID_HARD_FP
# endif
#endif
// detects the compiler
#if defined(_MSC_VER)
# define CR_CXX_MSVC
#elif defined(__clang__)
# define CR_CXX_CLANG
#endif
// configure export macros
#if defined(CR_WINDOWS)
# define CR_EXPORT extern "C" __declspec (dllexport)
#elif defined(CR_LINUX) || defined(CR_OSX)
# define CR_EXPORT extern "C" __attribute__((visibility("default")))
#else
# error "Can't configure export macros. Compiler unrecognized."
#endif
#if defined(__x86_64) || defined(__x86_64__) || defined(__amd64__) || defined(__amd64) || (defined(_MSC_VER) && defined(_M_X64))
# define CR_ARCH_X64
#elif defined(__i686) || defined(__i686__) || defined(__i386) || defined(__i386__) || defined(i386) || (defined(_MSC_VER) && defined(_M_IX86))
# define CR_ARCH_X86
#endif
#if (defined(CR_ARCH_X86) || defined(CR_ARCH_X64)) && !defined(CR_DEBUG)
# define CR_HAS_SSE2
#endif
CR_NAMESPACE_END
#if defined(CR_WINDOWS)
# include <direct.h>
# define stricmp _stricmp
#else
# include <unistd.h>
# include <sys/stat.h>
# define stricmp strcasecmp
#endif
#include <assert.h>
#if defined (CR_ANDROID)
# include <android/log.h>
#endif
CR_NAMESPACE_BEGIN
// helper struct for platform detection
struct Platform : public Singleton <Platform> {
bool isWindows = false;
bool isLinux = false;
bool isOSX = false;
bool isAndroid = false;
bool isAndroidHardFP = false;
bool isX64 = false;
Platform () {
#if defined(CR_WINDOWS)
isWindows = true;
#endif
#if defined(CR_ANDROID)
isAndroid = true;
# if defined (CR_ANDROID_HARD_FP)
isAndroidHardFP = true;
# endif
#endif
#if defined(CR_LINUX)
isLinux = true;
#endif
#if defined (CR_OSX)
isOSX = true;
#endif
#if defined (CR_ARCH_X64)
isX64 = true;
#endif
}
// helper platform-dependant functions
template <typename U> bool checkPointer (U *ptr) {
#if defined(CR_WINDOWS)
if (IsBadCodePtr (reinterpret_cast <FARPROC> (ptr))) {
return false;
}
#else
(void) (ptr);
#endif
return true;
}
bool createDirectory (const char *dir) {
int result = 0;
#if defined(CR_WINDOWS)
result = _mkdir (dir);
#else
result = mkdir (dir, 0777);
#endif
return !!result;
}
bool removeDirectory (const char *dir) {
#if defined(CR_WINDOWS)
_unlink (dir);
#else
unlink (dir);
#endif
return true;
}
bool hasModule (const char *mod) {
#if defined(CR_WINDOWS)
return GetModuleHandleA (mod) != nullptr;
#else
(void) (mod);
return true;
#endif
}
void abort (const char *msg = "OUT OF MEMORY!") noexcept {
fprintf (stderr, "%s\n", msg);
#if defined (CR_ANDROID)
__android_log_write (ANDROID_LOG_ERROR, "crlib.fatal", msg);
#endif
#if defined(CR_WINDOWS)
if (msg) {
DestroyWindow (GetForegroundWindow ());
MessageBoxA (GetActiveWindow (), msg, "crlib.fatal", MB_ICONSTOP);
}
#endif
::abort ();
}
};
// expose platform singleton
static auto &plat = Platform::get ();
CR_NAMESPACE_END

66
include/crlib/cr-random.h Normal file
View file

@ -0,0 +1,66 @@
//
// Yet Another POD-Bot, based on PODBot by Markus Klinge ("CountFloyd").
// Copyright (c) YaPB Development Team.
//
// This software is licensed under the BSD-style license.
// Additional exceptions apply. For full license details, see LICENSE.txt or visit:
// https://yapb.ru/license
//
#pragma once
#include <time.h>
#include <crlib/cr-basic.h>
CR_NAMESPACE_BEGIN
// random number generator see: https://github.com/preshing/RandomSequence/
class Random final : public Singleton <Random> {
private:
uint32 m_index, m_offset;
uint64 m_divider;
public:
explicit Random () {
const auto base = static_cast <uint32> (time (nullptr));
const auto offset = base + 1;
m_index = premute (premute (base) + 0x682f0161);
m_offset = premute (premute (offset) + 0x46790905);
m_divider = (static_cast <uint64> (1)) << 32;
}
~Random () = default;
private:
uint32 premute (uint32 index) {
static constexpr auto prime = 4294967291u;
if (index >= prime) {
return index;
}
const uint32 residue = (static_cast <uint64> (index) * index) % prime;
return (index <= prime / 2) ? residue : prime - residue;
}
uint32 generate () {
return premute ((premute (m_index++) + m_offset) ^ 0x5bf03635);
}
public:
template <typename U> U int_ (U low, U high) {
return static_cast <U> (generate () * (static_cast <double> (high) - static_cast <double> (low) + 1.0) / m_divider + static_cast <double> (low));
}
float float_ (float low, float high) {
return static_cast <float> (generate () * (static_cast <double> (high) - static_cast <double> (low)) / (m_divider - 1) + static_cast <double> (low));
}
template <typename U> bool chance (const U max, const U maxChance = 100) {
return int_ <U> (0, maxChance) < max;
}
};
// expose global random generator
static auto &rg = Random::get ();
CR_NAMESPACE_END

870
include/crlib/cr-string.h Normal file
View file

@ -0,0 +1,870 @@
//
// Yet Another POD-Bot, based on PODBot by Markus Klinge ("CountFloyd").
// Copyright (c) YaPB Development Team.
//
// This software is licensed under the BSD-style license.
// Additional exceptions apply. For full license details, see LICENSE.txt or visit:
// https://yapb.ru/license
//
#pragma once
#include <string.h>
#include <ctype.h>
#include <crlib/cr-basic.h>
#include <crlib/cr-alloc.h>
#include <crlib/cr-movable.h>
#include <crlib/cr-twin.h>
#include <crlib/cr-uniqueptr.h>
CR_NAMESPACE_BEGIN
// small-string optimized string class, sso stuff based on: https://github.com/elliotgoodrich/SSO-23/
class String final {
public:
static constexpr size_t kInvalidIndex = static_cast <size_t> (-1);
private:
static constexpr size_t kExcessSpace = 32;
private:
using Length = Twin <size_t, size_t>;
private:
union Data {
struct Big {
char excess[kExcessSpace - sizeof (char *) - 2 * sizeof (size_t)];
char *ptr;
size_t length;
size_t capacity;
} big;
struct Small {
char str[sizeof (Big) / sizeof (char) - 1];
uint8 length;
} small;
} m_data;
private:
static size_t const kSmallCapacity = sizeof (typename Data::Big) / sizeof (char) - 1;
public:
explicit String () {
reset ();
assign ("", 0);
}
String (const char *str, size_t length = 0) {
reset ();
assign (str, length);
}
String (const String &str) {
reset ();
assign (str.data (), str.length ());
}
String (const char ch) {
reset ();
assign (ch);
}
String (String &&rhs) noexcept {
m_data = rhs.m_data;
rhs.setMoved ();
}
~String () {
destroy ();
}
private:
template <typename T> static uint8 &getMostSignificantByte (T &object) {
return *(reinterpret_cast <uint8 *> (&object) + sizeof (object) - 1);
}
template <size_t N> static bool getLeastSignificantBit (uint8 byte) {
return byte & cr::bit (N);
}
template <size_t N> static bool getMostSignificantBit (uint8 byte) {
return byte & cr::bit (CHAR_BIT - N - 1);
}
template <size_t N> static void setLeastSignificantBit (uint8 &byte, bool bit) {
if (bit) {
byte |= cr::bit (N);
}
else {
byte &= ~cr::bit (N);
}
}
template <size_t N> static void setMostSignificantBit (uint8 &byte, bool bit) {
if (bit) {
byte |= cr::bit (CHAR_BIT - N - 1);
}
else {
byte &= ~cr::bit (CHAR_BIT - N - 1);
}
}
void destroy () {
if (!isSmall ()) {
alloc.deallocate (m_data.big.ptr);
}
}
void reset () {
m_data.small.length = 0;
m_data.small.str[0] = '\0';
m_data.big.ptr = nullptr;
m_data.big.length = 0;
}
void endString (const char *str, size_t at) {
const_cast <char *> (str)[at] = '\0';
}
void moveString (const char *dst, const char *src, size_t length) {
if (!dst) {
return;
}
memmove (const_cast <char *> (dst), src, length);
}
const char *data () const {
return isSmall () ? m_data.small.str : m_data.big.ptr;
}
void setMoved () {
setSmallLength (0);
}
void setLength (size_t amount, size_t capacity) {
if (amount <= kSmallCapacity) {
endString (m_data.small.str, amount);
setSmallLength (static_cast <uint8> (amount));
}
else {
endString (m_data.big.ptr, amount);
setDataNonSmall (amount, capacity);
}
}
bool isSmall () const {
return !getLeastSignificantBit <0> (m_data.small.length) && !getLeastSignificantBit <1> (m_data.small.length);
}
void setSmallLength (uint8 length) {
m_data.small.length = static_cast <char> (kSmallCapacity - length) << 2;
}
size_t getSmallLength () const {
return kSmallCapacity - ((m_data.small.length >> 2) & 63u);
}
void setDataNonSmall (size_t length, size_t capacity) {
uint8 &lengthHighByte = getMostSignificantByte (length);
uint8 &capacityHighByte = getMostSignificantByte (capacity);
const bool lengthHasHighBit = getMostSignificantBit <0> (lengthHighByte);
const bool capacityHasHighBit = getMostSignificantBit <0> (capacityHighByte);
const bool capacityHasSecHighBit = getMostSignificantBit <1> (capacityHighByte);
setMostSignificantBit <0> (lengthHighByte, capacityHasSecHighBit);
capacityHighByte <<= 2;
setLeastSignificantBit <0> (capacityHighByte, capacityHasHighBit);
setLeastSignificantBit <1> (capacityHighByte, !lengthHasHighBit);
m_data.big.length = length;
m_data.big.capacity = capacity;
}
Length getDataNonSmall () const {
size_t length = m_data.big.length;
size_t capacity = m_data.big.capacity;
uint8 &lengthHighByte = getMostSignificantByte (length);
uint8 &capacityHighByte = getMostSignificantByte (capacity);
const bool capacityHasHighBit = getLeastSignificantBit <0> (capacityHighByte);
const bool lengthHasHighBit = !getLeastSignificantBit <1> (capacityHighByte);
const bool capacityHasSecHighBit = getMostSignificantBit <0> (lengthHighByte);
setMostSignificantBit <0> (lengthHighByte, lengthHasHighBit);
capacityHighByte >>= 2;
setMostSignificantBit <0> (capacityHighByte, capacityHasHighBit);
setMostSignificantBit <1> (capacityHighByte, capacityHasSecHighBit);
return { length, capacity };
}
public:
String &assign (const char *str, size_t length = 0) {
length = length > 0 ? length : strlen (str);
if (length <= kSmallCapacity) {
moveString (m_data.small.str, str, length);
endString (m_data.small.str, length);
setSmallLength (static_cast <uint8> (length));
}
else {
auto capacity = cr::max (kSmallCapacity * 2, length);
m_data.big.ptr = alloc.allocate <char> (capacity + 1);
if (m_data.big.ptr) {
moveString (m_data.big.ptr, str, length);
endString (m_data.big.ptr, length);
setDataNonSmall (length, capacity);
}
}
return *this;
}
String &assign (const String &str, size_t length = 0) {
return assign (str.chars (), length);
}
String &assign (const char ch) {
const char str[] { ch, '\0' };
return assign (str, strlen (str));
}
String &append (const char *str, size_t length = 0) {
if (empty ()) {
return assign (str, length);
}
length = length > 0 ? length : strlen (str);
size_t oldLength = this->length ();
size_t newLength = oldLength + length;
resize (newLength);
moveString (&data ()[oldLength], str, length);
endString (data (), newLength);
return *this;
}
String &append (const String &str, size_t length = 0) {
return append (str.chars (), length);
}
String &append (const char ch) {
const char str[] { ch, '\0' };
return append (str, strlen (str));
}
template <typename ...Args> String &assignf (const char *fmt, Args ...args) {
const size_t size = snprintf (nullptr, 0, fmt, args...);
Array <char, ReservePolicy::PlusOne> buffer (size + 1);
snprintf (buffer.data (), size + 1, fmt, cr::forward <Args> (args)...);
return assign (buffer.data ());
}
template <typename ...Args> String &appendf (const char *fmt, Args ...args) {
if (empty ()) {
return assignf (fmt, cr::forward <Args> (args)...);
}
const size_t size = snprintf (nullptr, 0, fmt, args...) + length ();
Array <char, ReservePolicy::PlusOne> buffer (size + 1);
snprintf (buffer.data (), size + 1, fmt, cr::forward <Args> (args)...);
return append (buffer.data ());
}
void resize (size_t amount) {
size_t oldLength = length ();
if (amount <= kSmallCapacity) {
if (!isSmall ()) {
auto ptr = m_data.big.ptr;
moveString (m_data.small.str, ptr, cr::min (oldLength, amount));
alloc.deallocate (ptr);
}
setLength (amount, 0);
}
else {
size_t newCapacity = 0;
if (isSmall ()) {
newCapacity = cr::max (amount, kSmallCapacity * 2);
auto ptr = alloc.allocate <char> (newCapacity + 1);
moveString (ptr, m_data.small.str, cr::min (oldLength, amount));
m_data.big.ptr = ptr;
}
else if (amount < capacity ()) {
newCapacity = capacity ();
}
else {
newCapacity = cr::max (amount, capacity () * 3 / 2);
auto ptr = alloc.allocate <char> (newCapacity + 1);
moveString (ptr, m_data.big.ptr, cr::min (oldLength, amount));
alloc.deallocate (m_data.big.ptr);
m_data.big.ptr = ptr;
}
setLength (amount, newCapacity);
}
}
bool insert (size_t index, const String &str) {
if (str.empty ()) {
return false;
}
const size_t strLength = str.length ();
const size_t dataLength = length ();
if (index >= dataLength) {
append (str.chars (), strLength);
}
else {
resize (dataLength + strLength);
for (size_t i = dataLength; i > index; --i) {
at (i + strLength - 1) = at (i - 1);
}
for (size_t i = 0; i < strLength; ++i) {
at (i + index) = str.at (i);
}
}
return true;
}
bool erase (size_t index, size_t count = 1) {
const size_t dataLength = length ();
if (index + count > dataLength) {
return false;
}
const size_t newLength = dataLength - count;
for (size_t i = index; i < newLength; ++i) {
at (i) = at (i + count);
}
resize (newLength);
return true;
}
size_t find (char pattern, size_t start = 0) const {
for (size_t i = start; i < length (); ++i) {
if (at (i) == pattern) {
return i;
}
}
return kInvalidIndex;
}
size_t find (const String &pattern, size_t start = 0) const {
const size_t patternLength = pattern.length ();
const size_t dataLength = length ();
if (patternLength > dataLength || start > dataLength) {
return kInvalidIndex;
}
for (size_t i = start; i <= dataLength - patternLength; ++i) {
size_t index = 0;
for (; at (index) && index < patternLength; ++index) {
if (at (i + index) != pattern.at (index)) {
break;
}
}
if (!pattern.at (index)) {
return i;
}
}
return kInvalidIndex;
}
size_t rfind (char pattern) const {
for (size_t i = length (); i != 0; i--) {
if (at (i) == pattern) {
return i;
}
}
return kInvalidIndex;
}
size_t rfind (const String &pattern) const {
const size_t patternLength = pattern.length ();
const size_t dataLength = length ();
if (patternLength > dataLength) {
return kInvalidIndex;
}
bool match = true;
for (size_t i = dataLength - 1; i >= patternLength; i--) {
match = true;
for (size_t j = patternLength - 1; j > 0; j--) {
if (at (i + j) != pattern.at (j)) {
match = false;
break;
}
}
if (match) {
return i;
}
}
return kInvalidIndex;
}
size_t findFirstOf (const String &pattern, size_t start = 0) const {
const size_t patternLength = pattern.length ();
const size_t dataLength = length ();
for (size_t i = start; i < dataLength; ++i) {
for (size_t j = 0; j < patternLength; ++j) {
if (at (i) == pattern.at (j)) {
return i;
}
}
}
return kInvalidIndex;
}
size_t findLastOf (const String &pattern) const {
const size_t patternLength = pattern.length ();
const size_t dataLength = length ();
for (size_t i = dataLength - 1; i > 0; i--) {
for (size_t j = 0; j < patternLength; ++j) {
if (at (i) == pattern.at (j)) {
return i;
}
}
}
return kInvalidIndex;
}
size_t findFirstNotOf (const String &pattern, size_t start = 0) const {
const size_t patternLength = pattern.length ();
const size_t dataLength = length ();
bool different = true;
for (size_t i = start; i < dataLength; ++i) {
different = true;
for (size_t j = 0; j < patternLength; ++j) {
if (at (i) == pattern.at (j)) {
different = false;
break;
}
}
if (different) {
return i;
}
}
return kInvalidIndex;
}
size_t findLastNotOf (const String &pattern) const {
const size_t patternLength = pattern.length ();
const size_t dataLength = length ();
bool different = true;
for (size_t i = dataLength - 1; i > 0; i--) {
different = true;
for (size_t j = 0; j < patternLength; ++j) {
if (at (i) == pattern.at (j)) {
different = false;
break;
}
}
if (different) {
return i;
}
}
return kInvalidIndex;
}
size_t countChar (char ch) const {
size_t count = 0;
for (size_t i = 0, e = length (); i != e; ++i) {
if (at (i) == ch) {
++count;
}
}
return count;
}
size_t countStr (const String &pattern) const {
const size_t patternLen = pattern.length ();
const size_t dataLength = length ();
if (patternLen > dataLength) {
return 0;
}
size_t count = 0;
for (size_t i = 0, e = dataLength - patternLen + 1; i != e; ++i) {
if (substr (i, patternLen).compare (pattern)) {
++count;
}
}
return count;
}
String substr (size_t start, size_t count = kInvalidIndex) const {
start = cr::min (start, length ());
if (count == kInvalidIndex) {
count = length ();
}
return String (data () + start, cr::min (count, length () - start));
}
size_t replace (const String &needle, const String &to) {
if (needle.empty () || to.empty ()) {
return 0;
}
size_t replaced = 0, pos = 0;
while (pos < length ()) {
pos = find (needle, pos);
if (pos == kInvalidIndex) {
break;
}
erase (pos, needle.length ());
insert (pos, to);
pos += to.length ();
replaced++;
}
return replaced;
}
bool startsWith (const String &prefix) const {
const size_t prefixLength = prefix.length ();
const size_t dataLength = length ();
return prefixLength <= dataLength && strncmp (data (), prefix.data (), prefixLength) == 0;
}
bool endsWith (const String &suffix) const {
const size_t suffixLength = suffix.length ();
const size_t dataLength = length ();
return suffixLength <= dataLength && strncmp (data () + dataLength - suffixLength, suffix.data (), suffixLength) == 0;
}
Array <String> split (const String &delim) const {
Array <String> tokens;
size_t prev = 0, pos = 0;
while ((pos = find (delim, pos)) != kInvalidIndex) {
tokens.push (substr (prev, pos - prev));
prev = ++pos;
}
tokens.push (substr (prev, pos - prev));
return tokens;
}
size_t length () const {
if (isSmall ()) {
return getSmallLength ();
}
else {
return getDataNonSmall ().first;
}
}
size_t capacity () const {
if (isSmall ()) {
return sizeof (m_data) - 1;
}
else {
return getDataNonSmall ().second;
}
}
bool small () const {
return isSmall ();
}
bool empty () const {
return length () == 0;
}
void clear () {
assign ("");
}
const char *chars () const {
return data ();
}
const char &at (size_t index) const {
return begin ()[index];
}
char &at (size_t index) {
return begin ()[index];
}
int32 compare (const String &rhs) const {
return strcmp (rhs.data (), data ());
}
int32 compare (const char *rhs) const {
return strcmp (rhs, data ());
}
bool contains (const String &rhs) const {
return find (rhs) != kInvalidIndex;
}
String &lowercase () {
for (auto &ch : *this) {
ch = static_cast <char> (::tolower (ch));
}
return *this;
}
String &uppercase () {
for (auto &ch : *this) {
ch = static_cast <char> (::toupper (ch));
}
return *this;
}
int32 int_ () const {
return atoi (data ());
}
float float_ () const {
return static_cast <float> (atof (data ()));
}
String &ltrim (const String &characters = "\r\n\t ") {
size_t begin = length ();
for (size_t i = 0; i < begin; ++i) {
if (characters.find (at (i)) == kInvalidIndex) {
begin = i;
break;
}
}
return *this = substr (begin, length () - begin);
}
String &rtrim (const String &characters = "\r\n\t ") {
size_t end = 0;
for (size_t i = length (); i > 0; --i) {
if (characters.find (at (i - 1)) == kInvalidIndex) {
end = i;
break;
}
}
return *this = substr (0, end);
}
String &trim (const String &characters = "\r\n\t ") {
return ltrim (characters).rtrim (characters);
}
public:
String &operator = (String &&rhs) noexcept {
destroy ();
m_data = rhs.m_data;
rhs.setMoved ();
return *this;
}
String &operator = (const String &rhs) {
return assign (rhs);
}
String &operator = (const char *rhs) {
return assign (rhs);
}
String &operator = (char rhs) {
return assign (rhs);
}
String &operator += (const String &rhs) {
return append (rhs);
}
String &operator += (const char *rhs) {
return append (rhs);
}
const char &operator [] (size_t index) const {
return at (index);
}
char &operator [] (size_t index) {
return at (index);
}
friend String operator + (const String &lhs, char rhs) {
return String (lhs).append (rhs);
}
friend String operator + (char lhs, const String &rhs) {
return String (lhs).append (rhs);
}
friend String operator + (const String &lhs, const char *rhs) {
return String (lhs).append (rhs);
}
friend String operator + (const char *lhs, const String &rhs) {
return String (lhs).append (rhs);
}
friend String operator + (const String &lhs, const String &rhs) {
return String (lhs).append (rhs);
}
friend bool operator == (const String &lhs, const String &rhs) {
return lhs.compare (rhs) == 0;
}
friend bool operator < (const String &lhs, const String &rhs) {
return lhs.compare (rhs) < 0;
}
friend bool operator > (const String &lhs, const String &rhs) {
return lhs.compare (rhs) > 0;
}
friend bool operator == (const char *lhs, const String &rhs) {
return rhs.compare (lhs) == 0;
}
friend bool operator == (const String &lhs, const char *rhs) {
return lhs.compare (rhs) == 0;
}
friend bool operator != (const String &lhs, const String &rhs) {
return lhs.compare (rhs) != 0;
}
friend bool operator != (const char *lhs, const String &rhs) {
return rhs.compare (lhs) != 0;
}
friend bool operator != (const String &lhs, const char *rhs) {
return lhs.compare (rhs) != 0;
}
public:
static String join (const Array <String> &sequence, const String &delim, size_t start = 0) {
if (sequence.empty ()) {
return "";
}
if (sequence.length () == 1) {
return sequence.at (0);
}
String result;
for (size_t index = start; index < sequence.length (); ++index) {
if (index != start) {
result += delim + sequence[index];
}
else {
result += sequence[index];
}
}
return result;
}
// for range-based loops
public:
char *begin () {
return const_cast <char *> (data ());
}
char *begin () const {
return const_cast <char *> (data ());
}
char *end () {
return begin () + length ();
}
char *end () const {
return begin () + length ();
}
};
// simple rotation-string pool for holding temporary data passed to different modules and for formatting
class StringBuffer final : public Singleton <StringBuffer> {
public:
enum : size_t {
StaticBufferSize = static_cast <size_t> (768),
RotationCount = static_cast <size_t> (32)
};
private:
char m_data[RotationCount + 1][StaticBufferSize] {};
size_t m_rotate = 0;
public:
StringBuffer () = default;
~StringBuffer () = default;
public:
char *chars () {
if (++m_rotate >= RotationCount) {
m_rotate = 0;
}
return m_data[cr::clamp <size_t> (m_rotate, 0, RotationCount)];
}
template <typename U, typename ...Args> U *format (const U *fmt, Args ...args) {
auto buffer = Singleton <StringBuffer>::get ().chars ();
snprintf (buffer, StaticBufferSize, fmt, args...);
return buffer;
}
template <typename U> U *format (const U *fmt) {
auto buffer = Singleton <StringBuffer>::get ().chars ();
strncpy (buffer, fmt, StaticBufferSize);
return buffer;
}
};
// expose global string pool
static auto &strings = StringBuffer::get ();
CR_NAMESPACE_END

75
include/crlib/cr-twin.h Normal file
View file

@ -0,0 +1,75 @@
//
// Yet Another POD-Bot, based on PODBot by Markus Klinge ("CountFloyd").
// Copyright (c) YaPB Development Team.
//
// This software is licensed under the BSD-style license.
// Additional exceptions apply. For full license details, see LICENSE.txt or visit:
// https://yapb.ru/license
//
#pragma once
#include <crlib/cr-basic.h>
#include <crlib/cr-movable.h>
CR_NAMESPACE_BEGIN
// simple pair (twin)
template <typename A, typename B> class Twin {
public:
A first;
B second;
public:
template <typename T, typename U> Twin (T &&a, U &&b) : first (cr::forward <T> (a)), second (cr::forward <U> (b)) { }
template <typename T, typename U> Twin (const Twin <T, U> &rhs) : first (rhs.first), second (rhs.second) { }
template <typename T, typename U> Twin (Twin <T, U> &&rhs) noexcept : first (cr::move (rhs.first)), second (cr::move (rhs.second)) { }
public:
explicit Twin () = default;
~Twin () = default;
public:
template <typename T, typename U> Twin &operator = (const Twin <T, U> &rhs) {
first = rhs.first;
second = rhs.second;
return *this;
}
template <typename T, typename U> Twin &operator = (Twin <T, U> &&rhs) {
first = cr::move (rhs.first);
second = cr::move (rhs.second);
return *this;
}
// specialized operators for binary heap, do not use as it's test only second element
public:
friend bool operator < (const Twin &a, const Twin &b) {
return a.second < b.second;
}
friend bool operator <= (const Twin &a, const Twin &b) {
return a.second <= b.second;
}
friend bool operator > (const Twin &a, const Twin &b) {
return b.second < a.second;
}
friend bool operator >= (const Twin &a, const Twin &b) {
return b.second <= a.second;
}
};
// creating pairs
template <typename A, typename B> constexpr Twin <A, B> makeTwin (A &&a, B &&b) {
return Twin <A, B> (cr::forward <A> (a), cr::forward <B> (b));
}
template <typename A, typename B> constexpr Twin <A, B> makeTwin (const A &a, const B &b) {
return Twin <A, B> (a, b);
}
CR_NAMESPACE_END

View file

@ -9,62 +9,69 @@
#pragma once
#include <crlib/cr-array.h>
CR_NAMESPACE_BEGIN
// see https://github.com/encode84/ulz/
class FastLZ final : NonCopyable {
class ULZ final : DenyCopying {
public:
static constexpr int EXCESS = 16;
static constexpr int WINDOW_BITS = 17;
static constexpr int WINDOW_SIZE = cr::bit (WINDOW_BITS);
static constexpr int WINDOW_MASK = WINDOW_SIZE - 1;
static constexpr int MIN_MATCH = 4;
static constexpr int MAX_CHAIN = cr::bit (5);
static constexpr int HASH_BITS = 19;
static constexpr int HASH_SIZE = cr::bit (HASH_BITS);
static constexpr int NIL = -1;
static constexpr int UNCOMPRESS_RESULT_FAILED = -1;
enum : int32 {
Excess = 16,
UncompressFailure = -1
};
private:
int *m_hashTable;
int *m_prevTable;
enum : int32 {
WindowBits = 17,
WindowSize = cr::bit (WindowBits),
WindowMask = WindowSize - 1,
MinMatch = 4,
MaxChain = cr::bit (5),
HashBits = 19,
HashLength = cr::bit (HashBits),
EmptyHash = -1,
};
private:
Array <int32, ReservePolicy::PlusOne> m_hashTable;
Array <int32, ReservePolicy::PlusOne> m_prevTable;
public:
FastLZ (void) {
m_hashTable = new int[HASH_SIZE];
m_prevTable = new int[WINDOW_SIZE];
}
~FastLZ (void) {
delete [] m_hashTable;
delete [] m_prevTable;
ULZ () {
m_hashTable.resize (HashLength);
m_prevTable.resize (WindowSize);
}
~ULZ () = default;
public:
int compress (uint8 *in, int inLength, uint8 *out) {
for (int i = 0; i < HASH_SIZE; i++) {
m_hashTable[i] = NIL;
int32 compress (uint8 *in, int32 inputLength, uint8 *out) {
for (auto &htb : m_hashTable) {
htb = EmptyHash;
}
uint8 *op = out;
int anchor = 0;
int cur = 0;
int32 anchor = 0;
int32 cur = 0;
while (cur < inLength) {
const int maxMatch = inLength - cur;
while (cur < inputLength) {
const int32 maxMatch = inputLength - cur;
int bestLength = 0;
int dist = 0;
int32 bestLength = 0;
int32 dist = 0;
if (maxMatch >= MIN_MATCH) {
const int limit = cr::max (cur - WINDOW_SIZE, NIL);
if (maxMatch >= MinMatch) {
const int32 limit = cr::max <int32> (cur - WindowSize, EmptyHash);
int chainLength = MAX_CHAIN;
int lookup = m_hashTable[hash32 (&in[cur])];
int32 chainLength = MaxChain;
int32 lookup = m_hashTable[hash32 (&in[cur])];
while (lookup > limit) {
if (in[lookup + bestLength] == in[cur + bestLength] && load32 (&in[lookup]) == load32 (&in[cur])) {
int length = MIN_MATCH;
int32 length = MinMatch;
while (length < maxMatch && in[lookup + length] == in[cur + length]) {
length++;
@ -83,31 +90,31 @@ public:
if (--chainLength == 0) {
break;
}
lookup = m_prevTable[lookup & WINDOW_MASK];
lookup = m_prevTable[lookup & WindowMask];
}
}
if (bestLength == MIN_MATCH && (cur - anchor) >= (7 + 128)) {
if (bestLength == MinMatch && (cur - anchor) >= (7 + 128)) {
bestLength = 0;
}
if (bestLength >= MIN_MATCH && bestLength < maxMatch && (cur - anchor) != 6) {
const int next = cur + 1;
const int targetLength = bestLength + 1;
const int limit = cr::max (next - WINDOW_SIZE, NIL);
if (bestLength >= MinMatch && bestLength < maxMatch && (cur - anchor) != 6) {
const int32 next = cur + 1;
const int32 target = bestLength + 1;
const int32 limit = cr::max <int32> (next - WindowSize, EmptyHash);
int chainLength = MAX_CHAIN;
int lookup = m_hashTable[hash32 (&in[next])];
int32 chainLength = MaxChain;
int32 lookup = m_hashTable[hash32 (&in[next])];
while (lookup > limit) {
if (in[lookup + bestLength] == in[next + bestLength] && load32 (&in[lookup]) == load32 (&in[next])) {
int length = MIN_MATCH;
int32 length = MinMatch;
while (length < targetLength && in[lookup + length] == in[next + length]) {
while (length < target && in[lookup + length] == in[next + length]) {
length++;
}
if (length == targetLength) {
if (length == target) {
bestLength = 0;
break;
}
@ -116,16 +123,16 @@ public:
if (--chainLength == 0) {
break;
}
lookup = m_prevTable[lookup & WINDOW_MASK];
lookup = m_prevTable[lookup & WindowMask];
}
}
if (bestLength >= MIN_MATCH) {
const int length = bestLength - MIN_MATCH;
const int token = ((dist >> 12) & 16) + cr::min (length, 15);
if (bestLength >= MinMatch) {
const int32 length = bestLength - MinMatch;
const int32 token = ((dist >> 12) & 16) + cr::min <int32> (length, 15);
if (anchor != cur) {
const int run = cur - anchor;
const int32 run = cur - anchor;
if (run >= 7) {
add (op, (7 << 5) + token);
@ -150,7 +157,7 @@ public:
while (bestLength-- != 0) {
const uint32 hash = hash32 (&in[cur]);
m_prevTable[cur & WINDOW_MASK] = m_hashTable[hash];
m_prevTable[cur & WindowMask] = m_hashTable[hash];
m_hashTable[hash] = cur++;
}
anchor = cur;
@ -158,13 +165,13 @@ public:
else {
const uint32 hash = hash32 (&in[cur]);
m_prevTable[cur & WINDOW_MASK] = m_hashTable[hash];
m_prevTable[cur & WindowMask] = m_hashTable[hash];
m_hashTable[hash] = cur++;
}
}
if (anchor != cur) {
const int run = cur - anchor;
const int32 run = cur - anchor;
if (run >= 7) {
add (op, 7 << 5);
@ -179,25 +186,25 @@ public:
return op - out;
}
int uncompress (uint8 *in, int inLength, uint8 *out, int outLength) {
int32 uncompress (uint8 *in, int32 inputLength, uint8 *out, int32 outLength) {
uint8 *op = out;
uint8 *ip = in;
const uint8 *opEnd = op + outLength;
const uint8 *ipEnd = ip + inLength;
const uint8 *ipEnd = ip + inputLength;
while (ip < ipEnd) {
const int token = *ip++;
const int32 token = *ip++;
if (token >= 32) {
int run = token >> 5;
int32 run = token >> 5;
if (run == 7) {
run += decode (ip);
}
if ((opEnd - op) < run || (ipEnd - ip) < run) {
return UNCOMPRESS_RESULT_FAILED;
return UncompressFailure;
}
copy (op, ip, run);
@ -208,22 +215,22 @@ public:
break;
}
}
int length = (token & 15) + MIN_MATCH;
int32 length = (token & 15) + MinMatch;
if (length == (15 + MIN_MATCH)) {
if (length == (15 + MinMatch)) {
length += decode (ip);
}
if ((opEnd - op) < length) {
return UNCOMPRESS_RESULT_FAILED;
return UncompressFailure;
}
const int dist = ((token & 16) << 12) + load16 (ip);
const int32 dist = ((token & 16) << 12) + load16 (ip);
ip += 2;
uint8 *cp = op - dist;
if ((op - out) < dist) {
return UNCOMPRESS_RESULT_FAILED;
return UncompressFailure;
}
if (dist >= 8) {
@ -232,7 +239,7 @@ public:
}
else
{
for (int i = 0; i < 4; i++) {
for (int32 i = 0; i < 4; ++i) {
*op++ = *cp++;
}
@ -241,7 +248,7 @@ public:
}
}
}
return (ip == ipEnd) ? op - out : UNCOMPRESS_RESULT_FAILED;
return static_cast <int32> (ip == ipEnd) ? static_cast <int32> (op - out) : UncompressFailure;
}
private:
@ -253,7 +260,7 @@ private:
return *reinterpret_cast <const uint32 *> (ptr);
}
inline void store16 (void *ptr, int val) {
inline void store16 (void *ptr, int32 val) {
*reinterpret_cast <uint16 *> (ptr) = static_cast <uint16> (val);
}
@ -262,18 +269,18 @@ private:
}
inline uint32 hash32 (void *ptr) {
return (load32 (ptr) * 0x9E3779B9) >> (32 - HASH_BITS);
return (load32 (ptr) * 0x9E3779B9) >> (32 - HashBits);
}
inline void copy (uint8 *dst, uint8 *src, int count) {
inline void copy (uint8 *dst, uint8 *src, int32 count) {
copy64 (dst, src);
for (int i = 8; i < count; i += 8) {
for (int32 i = 8; i < count; i += 8) {
copy64 (dst + i, src + i);
}
}
inline void add (uint8 *&dst, int val) {
inline void add (uint8 *&dst, int32 val) {
*dst++ = static_cast <uint8> (val);
}
@ -290,7 +297,7 @@ private:
inline uint32 decode (uint8 *&ptr) {
uint32 val = 0;
for (int i = 0; i <= 21; i += 7) {
for (int32 i = 0; i <= 21; i += 7) {
const uint32 cur = *ptr++;
val += cur << i;
@ -300,4 +307,8 @@ private:
}
return val;
}
};
};
CR_NAMESPACE_END

View file

@ -0,0 +1,99 @@
//
// Yet Another POD-Bot, based on PODBot by Markus Klinge ("CountFloyd").
// Copyright (c) YaPB Development Team.
//
// This software is licensed under the BSD-style license.
// Additional exceptions apply. For full license details, see LICENSE.txt or visit:
// https://yapb.ru/license
//
#pragma once
#include <crlib/cr-basic.h>
#include <crlib/cr-movable.h>
CR_NAMESPACE_BEGIN
// simple unique ptr
template <typename T> class UniquePtr final : public DenyCopying {
private:
T *m_ptr = nullptr;
public:
UniquePtr () = default;
explicit UniquePtr (T *ptr) : m_ptr (ptr)
{ }
UniquePtr (UniquePtr &&rhs) noexcept : m_ptr (rhs.m_ptr) {
rhs.m_ptr = nullptr;
}
~UniquePtr () {
destroy ();
}
public:
T *get () const {
return m_ptr;
}
T *release () {
auto ret = m_ptr;
m_ptr = nullptr;
return ret;
}
void reset (T *ptr = nullptr) {
destroy ();
m_ptr = ptr;
}
private:
void destroy () {
if (m_ptr) {
alloc.destroy (m_ptr);
m_ptr = nullptr;
}
}
public:
UniquePtr &operator = (UniquePtr &&rhs) noexcept {
if (this != &rhs) {
destroy ();
m_ptr = rhs.m_ptr;
rhs.m_ptr = nullptr;
}
return *this;
}
UniquePtr &operator = (decltype (nullptr)) {
destroy ();
return *this;
}
T &operator * () const {
return *m_ptr;
}
T *operator -> () const {
return m_ptr;
}
explicit operator bool () const {
return m_ptr != nullptr;
}
};
// create unique
template <typename T, typename... Args> UniquePtr <T> createUnique (Args &&... args) {
return UniquePtr <T> (alloc.create <T> (cr::forward <Args> (args)...));
}
// create unique (base class)
template <typename D, typename B, typename... Args> UniquePtr <B> createUniqueBase (Args &&... args) {
return UniquePtr <B> (alloc.create <D> (cr::forward <Args> (args)...));
}
CR_NAMESPACE_END

232
include/crlib/cr-vector.h Normal file
View file

@ -0,0 +1,232 @@
//
// Yet Another POD-Bot, based on PODBot by Markus Klinge ("CountFloyd").
// Copyright (c) YaPB Development Team.
//
// This software is licensed under the BSD-style license.
// Additional exceptions apply. For full license details, see LICENSE.txt or visit:
// https://yapb.ru/license
//
#pragma once
#include <crlib/cr-math.h>
CR_NAMESPACE_BEGIN
// 3dmath vector
class Vector final {
public:
float x = 0.0f, y = 0.0f, z = 0.0f;
public:
Vector (const float scaler = 0.0f) : x (scaler), y (scaler), z (scaler)
{ }
explicit Vector (const float _x, const float _y, const float _z) : x (_x), y (_y), z (_z)
{ }
Vector (float *rhs) : x (rhs[0]), y (rhs[1]), z (rhs[2])
{ }
Vector (const Vector &) = default;
public:
operator float *() {
return &x;
}
operator const float * () const {
return &x;
}
Vector operator + (const Vector &rhs) const {
return Vector (x + rhs.x, y + rhs.y, z + rhs.z);
}
Vector operator - (const Vector &rhs) const {
return Vector (x - rhs.x, y - rhs.y, z - rhs.z);
}
Vector operator - () const {
return Vector (-x, -y, -z);
}
friend Vector operator * (const float scale, const Vector &rhs) {
return Vector (rhs.x * scale, rhs.y * scale, rhs.z * scale);
}
Vector operator * (const float scale) const {
return Vector (scale * x, scale * y, scale * z);
}
Vector operator / (const float div) const {
const float inv = 1 / div;
return Vector (inv * x, inv * y, inv * z);
}
// cross product
Vector operator ^ (const Vector &rhs) const {
return Vector (y * rhs.z - z * rhs.y, z * rhs.x - x * rhs.z, x * rhs.y - y * rhs.x);
}
// dot product
float operator | (const Vector &rhs) const {
return x * rhs.x + y * rhs.y + z * rhs.z;
}
const Vector &operator += (const Vector &rhs) {
x += rhs.x;
y += rhs.y;
z += rhs.z;
return *this;
}
const Vector &operator -= (const Vector &right) {
x -= right.x;
y -= right.y;
z -= right.z;
return *this;
}
const Vector &operator *= (float scale) {
x *= scale;
y *= scale;
z *= scale;
return *this;
}
const Vector &operator /= (float div) {
const float inv = 1 / div;
x *= inv;
y *= inv;
z *= inv;
return *this;
}
bool operator == (const Vector &rhs) const {
return cr::fequal (x, rhs.x) && cr::fequal (y, rhs.y) && cr::fequal (z, rhs.z);
}
bool operator != (const Vector &rhs) const {
return !cr::fequal (x, rhs.x) && !cr::fequal (y, rhs.y) && !cr::fequal (z, rhs.z);
}
Vector &operator = (const Vector &) = default;
public:
float length () const {
return cr::sqrtf (lengthSq ());
}
float length2d () const {
return cr::sqrtf (x * x + y * y);
}
float lengthSq () const {
return x * x + y * y + z * z;
}
Vector get2d () const {
return Vector (x, y, 0.0f);
}
Vector normalize () const {
float len = length () + cr::kFloatCmpEpsilon;
if (cr::fzero (len)) {
return Vector (0.0f, 0.0f, 1.0f);
}
len = 1.0f / len;
return Vector (x * len, y * len, z * len);
}
Vector normalize2d () const {
float len = length2d () + cr::kFloatCmpEpsilon;
if (cr::fzero (len)) {
return Vector (0.0f, 1.0f, 0.0f);
}
len = 1.0f / len;
return Vector (x * len, y * len, 0.0f);
}
bool empty () const {
return cr::fzero (x) && cr::fzero (y) && cr::fzero (z);
}
static const Vector &null () {
static const auto s_zero = Vector (0.0f, 0.0f, 0.0f);
return s_zero;
}
void clear () {
x = y = z = 0.0f;
}
Vector clampAngles () {
x = cr::normalizeAngles (x);
y = cr::normalizeAngles (y);
z = 0.0f;
return *this;
}
float pitch () const {
if (cr::fzero (x) && cr::fzero (y)) {
return 0.0f;
}
return cr::degreesToRadians (cr::atan2f (z, length2d ()));
}
float yaw () const {
if (cr::fzero (x) && cr::fzero (y)) {
return 0.0f;
}
return cr::radiansToDegrees (cr:: atan2f (y, x));
}
Vector angles () const {
if (cr::fzero (x) && cr::fzero (y)) {
return Vector (z > 0.0f ? 90.0f : 270.0f, 0.0, 0.0f);
}
return Vector (cr::radiansToDegrees (cr::atan2f (z, length2d ())), cr::radiansToDegrees (cr::atan2f (y, x)), 0.0f);
}
void buildVectors (Vector *forward, Vector *right, Vector *upward) const {
enum { pitch, yaw, roll, unused, max };
float sines[max] = { 0.0f, 0.0f, 0.0f, 0.0f };
float cosines[max] = { 0.0f, 0.0f, 0.0f, 0.0f };
// compute the sine and cosine compontents
cr::sincosf (cr::degreesToRadians (x), cr::degreesToRadians (y), cr::degreesToRadians (z), sines, cosines);
if (forward) {
forward->x = cosines[pitch] * cosines[yaw];
forward->y = cosines[pitch] * sines[yaw];
forward->z = -sines[pitch];
}
if (right) {
right->x = -sines[roll] * sines[pitch] * cosines[yaw] + cosines[roll] * sines[yaw];
right->y = -sines[roll] * sines[pitch] * sines[yaw] - cosines[roll] * cosines[yaw];
right->z = -sines[roll] * cosines[pitch];
}
if (upward) {
upward->x = cosines[roll] * sines[pitch] * cosines[yaw] + sines[roll] * sines[yaw];
upward->y = cosines[roll] * sines[pitch] * sines[yaw] - sines[roll] * cosines[yaw];
upward->z = cosines[roll] * cosines[pitch];
}
}
};
// expose global null vector
static auto &nullvec = Vector::null ();
CR_NAMESPACE_END

View file

@ -10,95 +10,91 @@
#pragma once
// line draw
enum DrawLineType : int {
DRAW_SIMPLE,
DRAW_ARROW,
DRAW_NUM
};
CR_DECLARE_SCOPED_ENUM (DrawLine,
Simple,
Arrow,
Count
);
// trace ignore
enum TraceIgnore : int {
TRACE_IGNORE_NONE = 0,
TRACE_IGNORE_GLASS = cr::bit (0),
TRACE_IGNORE_MONSTERS = cr::bit (1),
TRACE_IGNORE_EVERYTHING = TRACE_IGNORE_GLASS | TRACE_IGNORE_MONSTERS
};
CR_DECLARE_SCOPED_ENUM (TraceIgnore,
None = 0,
Glass = cr::bit (0),
Monsters = cr::bit (1),
Everything = Glass | Monsters
);
// variable type
enum VarType : int {
VT_NORMAL = 0,
VT_READONLY,
VT_PASSWORD,
VT_NOSERVER,
VT_NOREGISTER
};
CR_DECLARE_SCOPED_ENUM (Var,
Normal = 0,
ReadOnly,
Password,
NoServer,
NoRegister
);
// netmessage functions
enum NetMsgId : int {
NETMSG_UNDEFINED = -1,
NETMSG_VGUI = 1,
NETMSG_SHOWMENU = 2,
NETMSG_WEAPONLIST = 3,
NETMSG_CURWEAPON = 4,
NETMSG_AMMOX = 5,
NETMSG_AMMOPICKUP = 6,
NETMSG_DAMAGE = 7,
NETMSG_MONEY = 8,
NETMSG_STATUSICON = 9,
NETMSG_DEATH = 10,
NETMSG_SCREENFADE = 11,
NETMSG_HLTV = 12,
NETMSG_TEXTMSG = 13,
NETMSG_TEAMINFO = 14,
NETMSG_BARTIME = 15,
NETMSG_SENDAUDIO = 17,
NETMSG_SAYTEXT = 18,
NETMSG_BOTVOICE = 19,
NETMSG_NVGTOGGLE = 20,
NETMSG_FLASHBAT = 21,
NETMSG_FLASHLIGHT = 22,
NETMSG_ITEMSTATUS = 23,
NETMSG_NUM = 25
};
CR_DECLARE_SCOPED_ENUM (NetMsg,
None = -1,
VGUI = 1,
ShowMenu = 2,
WeaponList = 3,
CurWeapon = 4,
AmmoX = 5,
AmmoPickup = 6,
Damage = 7,
Money = 8,
StatusIcon = 9,
DeathMsg = 10,
ScreenFade = 11,
HLTV = 12,
TextMsg = 13,
TeamInfo = 14,
BarTime = 15,
SendAudio = 17,
SayText = 18,
BotVoice = 19,
NVGToggle = 20,
FlashBat = 21,
Fashlight = 22,
ItemStatus = 23,
Count = 25
);
// supported cs's
enum GameFlags : int {
GAME_CSTRIKE16 = cr::bit (0), // counter-strike 1.6 and above
GAME_XASH_ENGINE = cr::bit (1), // counter-strike 1.6 under the xash engine (additional flag)
GAME_CZERO = cr::bit (2), // counter-strike: condition zero
GAME_LEGACY = cr::bit (3), // counter-strike 1.3-1.5 with/without steam
GAME_MOBILITY = cr::bit (4), // additional flag that bot is running on android (additional flag)
GAME_OFFICIAL_CSBOT = cr::bit (5), // additional flag that indicates official cs bots are in game
GAME_METAMOD = cr::bit (6), // game running under meta\mod
GAME_CSDM = cr::bit (7), // csdm mod currently in use
GAME_CSDM_FFA = cr::bit (8), // csdm mod with ffa mode
GAME_REGAMEDLL = cr::bit (9), // server dll is a regamedll
GAME_SUPPORT_SVC_PINGS = cr::bit (10), // on that game version we can fake bots pings
GAME_SUPPORT_BOT_VOICE = cr::bit (11) // on that game version we can use chatter
};
CR_DECLARE_SCOPED_ENUM (GameFlags,
Modern = cr::bit (0), // counter-strike 1.6 and above
Xash3D = cr::bit (1), // counter-strike 1.6 under the xash engine (additional flag)
ConditionZero = cr::bit (2), // counter-strike: condition zero
Legacy = cr::bit (3), // counter-strike 1.3-1.5 with/without steam
Mobility = cr::bit (4), // additional flag that bot is running on android (additional flag)
CSBot = cr::bit (5), // additional flag that indicates official cs bots are in game
Metamod = cr::bit (6), // game running under meta\mod
CSDM = cr::bit (7), // csdm mod currently in use
FreeForAll = cr::bit (8), // csdm mod with ffa mode
ReGameDLL = cr::bit (9), // server dll is a regamedll
HasFakePings = cr::bit (10), // on that game version we can fake bots pings
HasBotVoice = cr::bit (11) // on that game version we can use chatter
);
// defines map type
enum MapFlags : int {
MAP_AS = cr::bit (0),
MAP_CS = cr::bit (1),
MAP_DE = cr::bit (2),
MAP_ES = cr::bit (3),
MAP_KA = cr::bit (4),
MAP_FY = cr::bit (5),
// additional flags
MAP_HAS_DOORS = cr::bit (6)
};
CR_DECLARE_SCOPED_ENUM (MapFlags,
Assassination = cr::bit (0),
HostageRescue = cr::bit (1),
Demolition = cr::bit (2),
Escape = cr::bit (3),
KnifeArena = cr::bit (4),
Fun = cr::bit (5),
HasDoors = cr::bit (10) // additional flags
);
// variable reg pair
struct VarPair {
VarType type;
Var type;
cvar_t reg;
bool missing;
const char *regval;
class ConVar *self;
bool regMissing;
const char *regVal;
};
// network message block
@ -106,7 +102,7 @@ struct MessageBlock {
int bot;
int state;
int msg;
int regMsgs[NETMSG_NUM];
int regMsgs[NetMsg::Count];
};
// referentia vector info
@ -117,14 +113,14 @@ struct RefVector {
// entity prototype
using EntityFunction = void (*) (entvars_t *);
// compare language
struct LangComprarer {
size_t operator () (const String &key) const {
char *str = const_cast <char *> (key.chars ());
size_t hash = key.length ();
// language hasher
struct HashLangString {
uint32 operator () (const String &key) const {
auto str = reinterpret_cast <uint8 *> (const_cast <char *> (key.chars ()));
uint32 hash = 0;
while (*str++) {
if (!isalpha (*str)) {
if (!isalnum (*str)) {
continue;
}
hash = ((*str << 5) + hash) + *str;
@ -136,8 +132,8 @@ struct LangComprarer {
// provides utility functions to not call original engine (less call-cost)
class Game final : public Singleton <Game> {
private:
int m_drawModels[DRAW_NUM];
int m_spawnCount[TEAM_UNASSIGNED];
int m_drawModels[DrawLine::Count];
int m_spawnCount[Team::Unassigned];
// bot client command
bool m_isBotCommand;
@ -147,9 +143,9 @@ private:
edict_t *m_localEntity;
Array <VarPair> m_cvars;
HashMap <String, String, LangComprarer> m_language;
Dictionary <String, String, HashLangString> m_language;
Library m_gameLib;
SharedLibrary m_gameLib;
MessageBlock m_msgBlock;
bool m_precached;
@ -161,33 +157,18 @@ public:
RefVector vec;
public:
Game (void);
~Game (void);
Game ();
~Game ();
public:
// precaches internal stuff
void precache (void);
void precache ();
// initialize levels
void levelInitialize (edict_t *ents, int max);
// prints data to servers console
void print (const char *fmt, ...);
// prints chat message to all players
void chatPrint (const char *fmt, ...);
// prints center message to all players
void centerPrint (const char *fmt, ...);
// prints center message to specified player
void centerPrint (edict_t *ent, const char *fmt, ...);
// prints message to client console
void clientPrint (edict_t *ent, const char *fmt, ...);
// display world line
void drawLine (edict_t *ent, const Vector &start, const Vector &end, int width, int noise, int red, int green, int blue, int brightness, int speed, int life, DrawLineType type = DRAW_SIMPLE);
void drawLine (edict_t *ent, const Vector &start, const Vector &end, int width, int noise, const Color &color, int brightness, int speed, int life, DrawLine type = DrawLine::Simple);
// test line
void testLine (const Vector &start, const Vector &end, int ignoreFlags, edict_t *ignoreEntity, TraceResult *ptr);
@ -199,34 +180,31 @@ public:
float getWaveLen (const char *fileName);
// we are on dedicated server ?
bool isDedicated (void);
bool isDedicated ();
// get stripped down mod name
const char *getModName (void);
const char *getModName ();
// get the valid mapname
const char *getMapName (void);
const char *getMapName ();
// get the "any" entity origin
Vector getAbsPos (edict_t *ent);
// send server command
void execCmd (const char *fmt, ...);
// registers a server command
void registerCmd (const char *command, void func (void));
void registerCmd (const char *command, void func_ ());
// play's sound to client
void playSound (edict_t *ent, const char *sound);
// sends bot command
void execBotCmd (edict_t *ent, const char *fmt, ...);
void prepareBotArgs (edict_t *ent, String str);
// adds cvar to registration stack
void pushVarToRegStack (const char *variable, const char *value, VarType varType, bool regMissing, const char *regVal, ConVar *self);
void addNewCvar (const char *variable, const char *value, Var varType, bool regMissing, const char *regVal, class ConVar *self);
// sends local registration stack for engine registration
void pushRegStackToEngine (bool gameVars = false);
void registerCvars (bool gameVars = false);
// translates bot message into needed language
const char *translate (const char *input);
@ -235,19 +213,19 @@ public:
void processMessages (void *ptr);
// checks whether softwared rendering is enabled
bool isSoftwareRenderer (void);
bool isSoftwareRenderer ();
// load the cs binary in non metamod mode
bool loadCSBinary (void);
bool loadCSBinary ();
// do post-load stuff
bool postload (void);
bool postload ();
// detects if csdm mod is in use
void detectDeathmatch (void);
void detectDeathmatch ();
// executes stuff every 1 second
void slowFrame (void);
void slowFrame ();
// begin message handler
void beginMessage (edict_t *ent, int dest, int type);
@ -255,26 +233,23 @@ public:
// public inlines
public:
// get the current time on server
float timebase (void) const {
float timebase () const {
return globals->time;
}
// get "maxplayers" limit on server
int maxClients (void) const {
int maxClients () const {
return globals->maxClients;
}
// get the fakeclient command interface
bool isBotCmd (void) const {
bool isBotCmd () const {
return m_isBotCommand;
}
// gets custom engine args for client command
const char *botArgs (void) const {
static String args;
args = String::join (m_botArgs, " ", m_botArgs[0] == "say" || m_botArgs[0] == "say_team" ? 1 : 0);
return args.chars ();
const char *botArgs () const {
return strings.format (String::join (m_botArgs, " ", m_botArgs[0] == "say" || m_botArgs[0] == "say_team" ? 1 : 0).chars ());
}
// gets custom engine argv for client command
@ -286,7 +261,7 @@ public:
}
// gets custom engine argc for client command
int botArgc (void) const {
int botArgc () const {
return m_botArgs.length ();
}
@ -295,18 +270,28 @@ public:
return static_cast <edict_t *> (m_startEntity + index);
};
// gets edict pointer out of entity index (player)
edict_t *playerOfIndex (const int index) {
return entityOfIndex (index) + 1;
};
// gets edict index out of it's pointer
int indexOfEntity (const edict_t *ent) {
return static_cast <int> (ent - m_startEntity);
};
// gets edict index of it's pointer (player)
int indexOfPlayer (const edict_t *ent) {
return indexOfEntity (ent) - 1;
}
// verify entity isn't null
bool isNullEntity (const edict_t *ent) {
return !ent || !indexOfEntity (ent) || ent->free;
}
// get the wroldspawn entity
edict_t *getStartEntity (void) {
edict_t *getStartEntity () {
return m_startEntity;
}
@ -320,12 +305,17 @@ public:
// adds translation pair from config
void addTranslation (const String &original, const String &translated) {
m_language.put (original, translated);
m_language.push (original, translated);
}
// clear the translation table
void clearTranslation () {
m_language.clear ();
}
// resets the message capture mechanism
void resetMessages (void) {
m_msgBlock.msg = NETMSG_UNDEFINED;
void resetMessages () {
m_msgBlock.msg = NetMsg::None;
m_msgBlock.state = 0;
m_msgBlock.bot = 0;
};
@ -358,12 +348,12 @@ public:
}
// sets the precache to uninitialize
void setUnprecached (void) {
void setUnprecached () {
m_precached = false;
}
// gets the local entity (host edict)
edict_t *getLocalEntity (void) {
edict_t *getLocalEntity () {
return m_localEntity;
}
@ -374,17 +364,12 @@ public:
// builds referential vector
void makeVectors (const Vector &in) {
in.makeVectors (&vec.forward, &vec.right, &vec.up);
}
// what kind of map we're running ?
bool isMap (const int map) const {
return (m_mapFlags & map) == map;
in.buildVectors (&vec.forward, &vec.right, &vec.up);
}
// what kind of game engine / game dll / mod / tool we're running ?
bool is (const int type) const {
return (m_gameFlags & type) == type;
return !!(m_gameFlags & type);
}
// adds game flag
@ -394,43 +379,79 @@ public:
// gets the map type
bool mapIs (const int type) const {
return (m_mapFlags & type) == type;
return !!(m_mapFlags & type);
}
// get loaded gamelib
Library &getLib (void) {
const SharedLibrary &lib () {
return m_gameLib;
}
// helper to sending the client message
void sendClientMessage (bool console, edict_t *ent, const char *message);
// send server command
template <typename ...Args> void serverCommand (const char *fmt, Args ...args) {
engfuncs.pfnServerCommand (strncat (strings.format (fmt, cr::forward <Args> (args)...), "\n", StringBuffer::StaticBufferSize));
}
// send a bot command
template <typename ...Args> void botCommand (edict_t *ent, const char *fmt, Args ...args) {
prepareBotArgs (ent, strings.format (fmt, cr::forward <Args> (args)...));
}
// prints data to servers console
template <typename ...Args> void print (const char *fmt, Args ...args) {
engfuncs.pfnServerPrint (strncat (strings.format (translate (fmt), cr::forward <Args> (args)...), "\n", StringBuffer::StaticBufferSize));
}
// prints center message to specified player
template <typename ...Args> void clientPrint (edict_t *ent, const char *fmt, Args ...args) {
if (isNullEntity (ent)) {
print (fmt, cr::forward <Args> (args)...);
return;
}
sendClientMessage (true, ent, strncat (strings.format (translate (fmt), cr::forward <Args> (args)...), "\n", StringBuffer::StaticBufferSize));
}
// prints message to client console
template <typename ...Args> void centerPrint (edict_t *ent, const char *fmt, Args ...args) {
if (isNullEntity (ent)) {
print (fmt, cr::forward <Args> (args)...);
return;
}
sendClientMessage (false, ent, strncat (strings.format (translate (fmt), cr::forward <Args> (args)...), "\n", StringBuffer::StaticBufferSize));
}
};
// simplify access for console variables
class ConVar {
public:
cvar_t *m_eptr;
cvar_t *eptr;
public:
ConVar (const char *name, const char *initval, VarType type = VT_NOSERVER, bool regMissing = false, const char *regVal = nullptr) : m_eptr (nullptr) {
Game::ref ().pushVarToRegStack (name, initval, type, regMissing, regVal, this);
ConVar (const char *name, const char *initval, Var type = Var::NoServer, bool regMissing = false, const char *regVal = nullptr) : eptr (nullptr) {
Game::get ().addNewCvar (name, initval, type, regMissing, regVal, this);
}
bool boolean (void) const {
return m_eptr->value > 0.0f;
bool bool_ () const {
return eptr->value > 0.0f;
}
int integer (void) const {
return static_cast <int> (m_eptr->value);
int int_ () const {
return static_cast <int> (eptr->value);
}
float flt (void) const {
return m_eptr->value;
float float_ () const {
return eptr->value;
}
const char *str (void) const {
return m_eptr->string;
const char *str () const {
return eptr->string;
}
void set (float val) {
engfuncs.pfnCVarSetFloat (m_eptr->name, val);
engfuncs.pfnCVarSetFloat (eptr->name, val);
}
void set (int val) {
@ -438,7 +459,7 @@ public:
}
void set (const char *val) {
engfuncs.pfnCvar_DirectSet (m_eptr, const_cast <char *> (val));
engfuncs.pfnCvar_DirectSet (eptr, const_cast <char *> (val));
}
};
@ -447,26 +468,26 @@ private:
bool m_autoDestruct { false };
public:
MessageWriter (void) = default;
MessageWriter () = default;
MessageWriter (int dest, int type, const Vector &pos = Vector::null (), edict_t *to = nullptr) {
MessageWriter (int dest, int type, const Vector &pos = nullvec, edict_t *to = nullptr) {
start (dest, type, pos, to);
m_autoDestruct = true;
}
virtual ~MessageWriter (void) {
~MessageWriter () {
if (m_autoDestruct) {
end ();
}
}
public:
MessageWriter &start (int dest, int type, const Vector &pos = Vector::null (), edict_t *to = nullptr) {
MessageWriter &start (int dest, int type, const Vector &pos = nullvec, edict_t *to = nullptr) {
engfuncs.pfnMessageBegin (dest, type, pos, to);
return *this;
}
void end (void) {
void end () {
engfuncs.pfnMessageEnd ();
}
@ -475,6 +496,11 @@ public:
return *this;
}
MessageWriter &writeLong (int val) {
engfuncs.pfnWriteLong (val);
return *this;
}
MessageWriter &writeChar (int val) {
engfuncs.pfnWriteChar (val);
return *this;
@ -497,11 +523,11 @@ public:
public:
static inline uint16 fu16 (float value, float scale) {
return cr::clamp <uint16> (static_cast <uint16> (value * scale), 0, 0xffff);
return cr::clamp <uint16> (static_cast <uint16> (value * cr::bit (static_cast <short> (scale))), 0, 0xffff);
}
static inline short fs16 (float value, float scale) {
return cr::clamp <short> (static_cast <short> (value * scale), -32767, 32767);
return cr::clamp <short> (static_cast <short> (value * cr::bit (static_cast <short> (scale))), -32767, 32767);
}
};
@ -511,28 +537,28 @@ private:
int m_lightstyleValue[MAX_LIGHTSTYLEVALUE];
bool m_doAnimation = false;
SimpleColor m_point;
Color m_point;
model_t *m_worldModel = nullptr;
public:
LightMeasure (void) {
LightMeasure () {
initializeLightstyles ();
m_point.reset ();
}
public:
void initializeLightstyles (void);
void animateLight (void);
void initializeLightstyles ();
void animateLight ();
void updateLight (int style, char *value);
float getLightLevel (const Vector &point);
float getSkyColor (void);
float getSkyColor ();
private:
template <typename S, typename M> bool recursiveLightPoint (const M *node, const Vector &start, const Vector &end);
public:
void resetWorldModel (void) {
void resetWorldModel () {
m_worldModel = nullptr;
}

View file

@ -85,6 +85,25 @@ typedef struct {
int iHitgroup; // 0 == generic, non zero is specific body part
} TraceResult;
typedef struct usercmd_s {
short lerp_msec; // Interpolation time on client
byte msec; // Duration in ms of command
vec3_t viewangles; // Command view angles.
// intended velocities
float forwardmove; // Forward velocity.
float sidemove; // Sideways velocity.
float upmove; // Upward velocity.
byte lightlevel; // Light level at spot where we are standing.
unsigned short buttons; // Attack buttons
byte impulse; // Impulse command issued.
byte weaponselect; // Current weapon id
// Experimental player impact stuff.
int impact_index;
vec3_t impact_position;
} usercmd_t;
typedef uint32 CRC32_t;
// Engine hands this to DLLs for functionality callbacks
@ -111,7 +130,7 @@ typedef struct enginefuncs_s {
edict_t *(*pfnEntitiesInPVS) (edict_t *pplayer);
void (*pfnMakeVectors) (const float *rgflVector);
void (*pfnAngleVectors) (const float *rgflVector, float *forward, float *right, float *up);
edict_t *(*pfnCreateEntity) (void);
edict_t *(*pfnCreateEntity) ();
void (*pfnRemoveEntity) (edict_t *e);
edict_t *(*pfnCreateNamedEntity) (int className);
void (*pfnMakeStatic) (edict_t *ent);
@ -130,14 +149,14 @@ typedef struct enginefuncs_s {
void (*pfnTraceSphere) (const float *v1, const float *v2, int fNoMonsters, float radius, edict_t *pentToSkip, TraceResult *ptr);
void (*pfnGetAimVector) (edict_t *ent, float speed, float *rgflReturn);
void (*pfnServerCommand) (char *str);
void (*pfnServerExecute) (void);
void (*pfnServerExecute) ();
void (*pfnClientCommand) (edict_t *ent, char const *szFmt, ...);
void (*pfnParticleEffect) (const float *org, const float *dir, float color, float count);
void (*pfnLightStyle) (int style, char *val);
int (*pfnDecalIndex) (const char *name);
int (*pfnPointContents) (const float *rgflVector);
void (*pfnMessageBegin) (int msg_dest, int msg_type, const float *pOrigin, edict_t *ed);
void (*pfnMessageEnd) (void);
void (*pfnMessageEnd) ();
void (*pfnWriteByte) (int value);
void (*pfnWriteChar) (int value);
void (*pfnWriteShort) (int value);
@ -151,7 +170,7 @@ typedef struct enginefuncs_s {
const char *(*pfnCVarGetString) (const char *szVarName);
void (*pfnCVarSetFloat) (const char *szVarName, float flValue);
void (*pfnCVarSetString) (const char *szVarName, const char *szValue);
void (*pfnAlertMessage) (ALERT_TYPE atype, char *szFmt, ...);
void (*pfnAlertMessage) (ALERT_TYPE atype, const char *szFmt, ...);
void (*pfnEngineFprintf) (void *pfile, char *szFmt, ...);
void *(*pfnPvAllocEntPrivateData) (edict_t *ent, int32 cb);
void *(*pfnPvEntPrivateData) (edict_t *ent);
@ -172,9 +191,9 @@ typedef struct enginefuncs_s {
const char *(*pfnNameForFunction) (uint32 function);
void (*pfnClientPrintf) (edict_t *ent, PRINT_TYPE ptype, const char *szMsg); // JOHN: engine callbacks so game DLL can print messages to individual clients
void (*pfnServerPrint) (const char *szMsg);
const char *(*pfnCmd_Args) (void); // these 3 added
const char *(*pfnCmd_Args) (); // these 3 added
const char *(*pfnCmd_Argv) (int argc); // so game DLL can easily
int (*pfnCmd_Argc) (void); // access client 'cmd' strings
int (*pfnCmd_Argc) (); // access client 'cmd' strings
void (*pfnGetAttachment) (const edict_t *ent, int iAttachment, float *rgflOrigin, float *rgflAngles);
void (*pfnCRC32_Init) (CRC32_t *pulCRC);
void (*pfnCRC32_ProcessBuffer) (CRC32_t *pulCRC, void *p, int len);
@ -183,7 +202,7 @@ typedef struct enginefuncs_s {
int32 (*pfnRandomLong) (int32 lLow, int32 lHigh);
float (*pfnRandomFloat) (float flLow, float flHigh);
void (*pfnSetView) (const edict_t *client, const edict_t *pViewent);
float (*pfnTime) (void);
float (*pfnTime) ();
void (*pfnCrosshairAngle) (const edict_t *client, float pitch, float yaw);
uint8 *(*pfnLoadFileForMe) (char const *szFilename, int *pLength);
void (*pfnFreeFile) (void *buffer);
@ -195,7 +214,7 @@ typedef struct enginefuncs_s {
void (*pfnSetClientMaxspeed) (const edict_t *ent, float fNewMaxspeed);
edict_t *(*pfnCreateFakeClient) (const char *netname); // returns nullptr if fake client can't be created
void (*pfnRunPlayerMove) (edict_t *fakeclient, const float *viewangles, float forwardmove, float sidemove, float upmove, uint16 buttons, uint8 impulse, uint8 msec);
int (*pfnNumberOfEntities) (void);
int (*pfnNumberOfEntities) ();
char *(*pfnGetInfoKeyBuffer) (edict_t *e); // passing in nullptr gets the serverinfo
char *(*pfnInfoKeyValue) (char *infobuffer, char const *key);
void (*pfnSetKeyValue) (char *infobuffer, char *key, char *value);
@ -205,7 +224,7 @@ typedef struct enginefuncs_s {
int (*pfnPrecacheGeneric) (char *s);
int (*pfnGetPlayerUserId) (edict_t *e); // returns the server assigned userid for this player. useful for logging frags, etc. returns -1 if the edict couldn't be found in the list of clients
void (*pfnBuildSoundMsg) (edict_t *entity, int channel, const char *sample, float volume, float attenuation, int fFlags, int pitch, int msg_dest, int msg_type, const float *pOrigin, edict_t *ed);
int (*pfnIsDedicatedServer) (void); // is this a dedicated server?
int (*pfnIsDedicatedServer) (); // is this a dedicated server?
cvar_t *(*pfnCVarGetPointer) (const char *szVarName);
unsigned int (*pfnGetPlayerWONId) (edict_t *e); // returns the server assigned WONid for this player. useful for logging frags, etc. returns -1 if the edict couldn't be found in the list of clients
@ -221,7 +240,7 @@ typedef struct enginefuncs_s {
void (*pfnDeltaSetField) (struct delta_s *pFields, const char *fieldname);
void (*pfnDeltaUnsetField) (struct delta_s *pFields, const char *fieldname);
void (*pfnDeltaAddEncoder) (char *name, void (*conditionalencode) (struct delta_s *pFields, const uint8 *from, const uint8 *to));
int (*pfnGetCurrentPlayer) (void);
int (*pfnGetCurrentPlayer) ();
int (*pfnCanSkipPlayer) (const edict_t *player);
int (*pfnDeltaFindField) (struct delta_s *pFields, const char *fieldname);
void (*pfnDeltaSetFieldByIndex) (struct delta_s *pFields, int fieldNumber);
@ -231,7 +250,7 @@ typedef struct enginefuncs_s {
void (*pfnCvar_DirectSet) (struct cvar_t *var, char *value);
void (*pfnForceUnmodified) (FORCE_TYPE type, float *mins, float *maxs, const char *szFilename);
void (*pfnGetPlayerStats) (const edict_t *client, int *ping, int *packet_loss);
void (*pfnAddServerCommand) (char *cmd_name, void (*function) (void));
void (*pfnAddServerCommand) (char *cmd_name, void (*function) ());
int (*pfnVoice_GetClientListening) (int iReceiver, int iSender);
int (*pfnVoice_SetClientListening) (int iReceiver, int iSender, int bListen);
@ -244,18 +263,20 @@ typedef struct enginefuncs_s {
int (*pfnGetFileSize) (char *szFilename);
unsigned int (*pfnGetApproxWavePlayLen) (const char *filepath);
int (*pfnIsCareerMatch) (void);
int (*pfnIsCareerMatch) ();
int (*pfnGetLocalizedStringLength) (const char *label);
void (*pfnRegisterTutorMessageShown) (int mid);
int (*pfnGetTimesTutorMessageShown) (int mid);
void (*pfnProcessTutorMessageDecayBuffer) (int *buffer, int bufferLength);
void (*pfnConstructTutorMessageDecayBuffer) (int *buffer, int bufferLength);
void (*pfnResetTutorMessageDecayData) (void);
void (*pfnResetTutorMessageDecayData) ();
void (*pfnQueryClientCVarValue) (const edict_t *player, const char *cvarName);
void (*pfnQueryClientCVarValue2) (const edict_t *player, const char *cvarName, int requestID);
int (*pfnCheckParm) (const char *pchCmdLineToken, char **ppnext);
#ifdef EIFACE_2019
edict_t *(*pfnPEntityOfEntIndexAllEntities) (int iEntIndex);
#endif
} enginefuncs_t;
// Passed to pfnKeyValue
@ -270,7 +291,7 @@ typedef struct customization_s customization_t;
typedef struct {
// Initialize/shutdown the game (one-time call after loading of game .dll )
void (*pfnGameInit) (void);
void (*pfnGameInit) ();
int (*pfnSpawn) (edict_t *pent);
void (*pfnThink) (edict_t *pent);
void (*pfnUse) (edict_t *pentUsed, edict_t *pentOther);
@ -286,7 +307,7 @@ typedef struct {
void (*pfnSaveGlobalState) (SAVERESTOREDATA *);
void (*pfnRestoreGlobalState) (SAVERESTOREDATA *);
void (*pfnResetGlobalState) (void);
void (*pfnResetGlobalState) ();
int (*pfnClientConnect) (edict_t *ent, const char *pszName, const char *pszAddress, char szRejectReason[128]);
@ -297,17 +318,17 @@ typedef struct {
void (*pfnClientUserInfoChanged) (edict_t *ent, char *infobuffer);
void (*pfnServerActivate) (edict_t *edictList, int edictCount, int clientMax);
void (*pfnServerDeactivate) (void);
void (*pfnServerDeactivate) ();
void (*pfnPlayerPreThink) (edict_t *ent);
void (*pfnPlayerPostThink) (edict_t *ent);
void (*pfnStartFrame) (void);
void (*pfnParmsNewLevel) (void);
void (*pfnParmsChangeLevel) (void);
void (*pfnStartFrame) ();
void (*pfnParmsNewLevel) ();
void (*pfnParmsChangeLevel) ();
// Returns string describing current .dll. E.g., TeamFotrress 2, Half-Life
const char *(*pfnGetGameDescription) (void);
const char *(*pfnGetGameDescription) ();
// Notify dll about a player customization.
void (*pfnPlayerCustomization) (edict_t *ent, struct customization_s *pCustom);
@ -327,10 +348,10 @@ typedef struct {
void (*pfnUpdateClientData) (const struct edict_s *ent, int sendweapons, struct clientdata_s *cd);
int (*pfnAddToFullPack) (struct entity_state_s *state, int e, edict_t *ent, edict_t *host, int hostflags, int player, uint8 *pSet);
void (*pfnCreateBaseline) (int player, int eindex, struct entity_state_s *baseline, struct edict_s *entity, int playermodelindex, float *player_mins, float *player_maxs);
void (*pfnRegisterEncoders) (void);
void (*pfnRegisterEncoders) ();
int (*pfnGetWeaponData) (struct edict_s *player, struct weapon_data_s *info);
void (*pfnCmdStart) (const edict_t *player, const struct c *cmd, unsigned int random_seed);
void (*pfnCmdStart) (const edict_t *player, usercmd_t *cmd, unsigned int random_seed);
void (*pfnCmdEnd) (const edict_t *player);
// Return 1 if the packet is valid. Set response_buffer_size if you want to send a response packet. Incoming, it holds the max
@ -341,7 +362,7 @@ typedef struct {
int (*pfnGetHullBounds) (int hullnumber, float *mins, float *maxs);
// Create baselines for certain "unplaced" items.
void (*pfnCreateInstancedBaselines) (void);
void (*pfnCreateInstancedBaselines) ();
// One of the pfnForceUnmodified files failed the consistency check for the specified player
// Return 0 to allow the client to continue, 1 to force immediate disconnection ( with an optional disconnect message of up to 256 characters )
@ -351,7 +372,7 @@ typedef struct {
// the sv_unlag cvar.
// Most games right now should return 0, until client-side weapon prediction code is written
// and tested for them.
int (*pfnAllowLagCompensation) (void);
int (*pfnAllowLagCompensation) ();
} gamefuncs_t;
// Current version.
@ -361,7 +382,7 @@ typedef struct {
// Called right before the object's memory is freed.
// Calls its destructor.
void (*pfnOnFreeEntPrivateData) (edict_t *pEnt);
void (*pfnGameShutdown) (void);
void (*pfnGameShutdown) ();
int (*pfnShouldCollide) (edict_t *pentTouched, edict_t *pentOther);
void (*pfnCvarValue) (const edict_t *pEnt, const char *value);

View file

@ -54,9 +54,9 @@ typedef int func_t; //
typedef int string_t; // from engine's pr_comp.h;
typedef float vec_t; // needed before including progdefs.h
#include "corelib.h"
#include <crlib/cr-vector.h>
typedef cr::classes::Vector vec3_t;
typedef cr::Vector vec3_t;
using namespace cr::types;
#include "const.h"

View file

@ -24,6 +24,7 @@ extern gamefuncs_t dllapi;
#define STRING(offset) (const char *)(globals->pStringBase + (int)offset)
// form fwgs-hlsdk
#if defined (CR_ARCH_X64)
static inline int MAKE_STRING (const char *val) {
long long ptrdiff = val - STRING (0);
@ -32,6 +33,9 @@ static inline int MAKE_STRING (const char *val) {
}
return static_cast <int> (ptrdiff);
}
#else
#define MAKE_STRING(str) ((uint64)(str) - (uint64)(STRING(0)))
#endif
#define ENGINE_STR(str) (const_cast <char *> (STRING (engfuncs.pfnAllocString (str))))
@ -93,17 +97,17 @@ typedef struct hudtextparms_s {
#define PUSH_BLOCK_ONLY_X 1
#define PUSH_BLOCK_ONLY_Y 2
#define VEC_HULL_MIN Vector (-16, -16, -36)
#define VEC_HULL_MAX Vector (16, 16, 36)
#define VEC_HUMAN_HULL_MIN Vector (-16, -16, 0)
#define VEC_HUMAN_HULL_MAX Vector (16, 16, 72)
#define VEC_HUMAN_HULL_DUCK Vector (16, 16, 36)
#define VEC_HULL_MIN Vector(-16, -16, -36)
#define VEC_HULL_MAX Vector(16, 16, 36)
#define VEC_HUMAN_HULL_MIN Vector(-16, -16, 0)
#define VEC_HUMAN_HULL_MAX Vector(16, 16, 72)
#define VEC_HUMAN_HULL_DUCK Vector(16, 16, 36)
#define VEC_VIEW Vector (0, 0, 28)
#define VEC_VIEW Vector(0, 0, 28)
#define VEC_DUCK_HULL_MIN Vector (-16, -16, -18)
#define VEC_DUCK_HULL_MAX Vector (16, 16, 18)
#define VEC_DUCK_VIEW Vector (0, 0, 12)
#define VEC_DUCK_HULL_MIN Vector(-16, -16, -18)
#define VEC_DUCK_HULL_MAX Vector(16, 16, 18)
#define VEC_DUCK_VIEW Vector(0, 0, 12)
#define SVC_TEMPENTITY 23
#define SVC_CENTERPRINT 26

View file

@ -1,97 +0,0 @@
//
// Yet Another POD-Bot, based on PODBot by Markus Klinge ("CountFloyd").
// Copyright (c) YaPB Development Team.
//
// This software is licensed under the BSD-style license.
// Additional exceptions apply. For full license details, see LICENSE.txt or visit:
// https://yapb.ru/license
//
#pragma once
// detects the build platform
#if defined(__linux__)
#define PLATFORM_LINUX
#elif defined(__APPLE__)
#define PLATFORM_OSX
#elif defined(_WIN32)
#define PLATFORM_WIN32
#endif
// by default sse has everyone
#define PLATFORM_HAS_SSE2
// detects the compiler
#if defined(_MSC_VER)
#define CXX_MSVC
#elif defined(__clang__)
#define CXX_CLANG
#endif
// configure export macros
#if defined(PLATFORM_WIN32)
#define SHARED_LIBRARAY_EXPORT extern "C" __declspec (dllexport)
#elif defined(PLATFORM_LINUX) || defined(PLATFORM_OSX)
#define SHARED_LIBRARAY_EXPORT extern "C" __attribute__ ((visibility ("default")))
#else
#error "Can't configure export macros. Compiler unrecognized."
#endif
// operating system specific macros, functions and typedefs
#ifdef PLATFORM_WIN32
#include <direct.h>
#include <string.h>
#define STD_CALL __stdcall
#define DLL_ENTRYPOINT int STD_CALL DllMain (HINSTANCE, DWORD dwReason, LPVOID)
#define DLL_DETACHING (dwReason == DLL_PROCESS_DETACH)
#define DLL_RETENTRY return TRUE
#if defined(CXX_MSVC) && !defined (_M_X64)
#define DLL_GIVEFNPTRSTODLL extern "C" void STD_CALL
#elif defined(CXX_CLANG) || defined (_M_X64)
#define DLL_GIVEFNPTRSTODLL SHARED_LIBRARAY_EXPORT void STD_CALL
#endif
// specify export parameter
#if defined(CXX_MSVC) || defined (CXX_CLANG)
#if !defined (_M_X64)
#pragma comment(linker, "/EXPORT:GiveFnptrsToDll=_GiveFnptrsToDll@8,@1")
#endif
#pragma comment(linker, "/SECTION:.data,RW")
#endif
#elif defined(PLATFORM_LINUX) || defined(PLATFORM_OSX)
#include <dlfcn.h>
#include <errno.h>
#include <fcntl.h>
#include <sys/stat.h>
#include <unistd.h>
#include <arpa/inet.h>
#include <netdb.h>
#include <netinet/in.h>
#include <sys/socket.h>
#include <sys/types.h>
#define DLL_ENTRYPOINT __attribute__ ((destructor)) void _fini (void)
#define DLL_DETACHING TRUE
#define DLL_RETENTRY return
#define DLL_GIVEFNPTRSTODLL extern "C" void __attribute__ ((visibility ("default")))
#define STD_CALL /* */
// android is a linux with a special cases
// @todo: sse should be working ok on x86 android?
#if defined(__ANDROID__)
#define PLATFORM_ANDROID
#if defined (__arm__) || defined (__aarch64__ )
#undef PLATFORM_HAS_SSE2
#endif
#endif
#else
#error "Platform unrecognized."
#endif

View file

@ -12,7 +12,7 @@
// general product information
#define PRODUCT_NAME "Yet Another POD-Bot"
#define PRODUCT_SHORT_NAME "YaPB"
#define PRODUCT_VERSION "2.91"
#define PRODUCT_VERSION "2.92"
#define PRODUCT_AUTHOR "YaPB Dev Team"
#define PRODUCT_URL "https://yapb.ru/"
#define PRODUCT_EMAIL "d@entix.io"
@ -26,7 +26,7 @@
#define PRODUCT_GIT_HASH "unspecified_hash"
#define PRODUCT_GIT_COMMIT_AUTHOR "unspecified_author"
#define PRODUCT_GIT_COMMIT_ID 0000
#define PRODUCT_VERSION_DWORD_INTERNAL 2, 91
#define PRODUCT_VERSION_DWORD_INTERNAL 2, 92
#define PRODUCT_VERSION_DWORD PRODUCT_VERSION_DWORD_INTERNAL, PRODUCT_GIT_COMMIT_ID
#define PRODUCT_SUPPORT_VERSION "Beta 6.6 - Condition Zero"
#define PRODUCT_COMMENTS "http://github.com/jeefo/yapb/"

File diff suppressed because it is too large Load diff