* Fixed bots not camping in camp spots. Fixed chatter/radio message cycling. (need feedback). Fixed CTs unable to defuse bomb. Fixed backward jump path generation in waypoint editor. Fixed autoradius in waypoint editor. Fixed autoradius menu non closeable. Fixed bots version display on entering game. Fixed memory leak in DLL-loader. (non metamod). Fixed bots able to see through smoke. Fixed team-detection on non-standard modes. Fixed quota & autovacate management. Fixed bunch of warnings from static analyzers. Greatly imporoved grenade throwing. Grealty reduced bot CPU usage. * Fixed stack-corruption in memory-file reader. Fixed A* pathfinder not working correctly. Fixed 'Tried to write to uninitialized sizebuf_t error' on bot add/remove. Minor tweaks to camping and bot enemy aiming * Make clang happy. * Fixed VIP-dection on some maps. Fixed occupied waypoint checker. Small refactoring of code with clang-format. * Fixed clang compilation * Fixed compilation. * Debugging seek cover task. Some more code cleanup. * Fixed typos. * Fixes to attack movement. Revert Z component updates. * Fixes for aiming at enemy. Fixes for seek cover & enemy hunt tasks. More refactoring. * Making clang happy once again? Tweaked grenade timers. * Revised language comparer hasher * Fixed build. * Fixed build. * Optimized headshot offsets. Optimized aim errors and enemy searches. Get rid of preprocessor macroses. Added back yb_think_fps. Use with caution. * Minor refactoring of code. * Check if tracking entity is still alive. Do not duck in crouch-goal waypoints. Remove ancient hack with failed goals. * Get rid of c++14 stuff. Tweaked isOccupiedPoint. * Changed pickup check radius. * Fix compilation. * Fixed bots ignore breakables. Fixed A* pathfinder. Fixed searching for optimal waypoints. Fixed bot waypoint reachability functions. * Get rid of new/delete calls in pathfinder. Disallow access to yapb waypoint menu on hlds. Minor refactoring. * Updated linux/osx makefile * Spaces -> Tabs in makefile. Made G++ happy. * Updated makefile. * Fixed heap buffer overflow in config loader code. * Lowered CPU usage a bit, by using "waypoint buckets" for searching closest node. Do not traceline for doors on map, that have no doors. Get rid stack-based containers. * Remove win-only debug crap. * Refactored string class. * Fix OSX compiling. * Minor refactoring of corelib to use cpp move-semantic. * Use reference for active grenades searcher. * Use system's atan2f () as it's eror rate is a bit lower. Fixed bots continuously stays in throw smoke task. Fixed bots reaching camp-goal jumping or stays they for some time. Increased radius for searching targets for grenades. Tweaked bot difficulty levels. Improved sniper weapon handling. Trying to stand still while shooting. Increase retreat level only if sniper weapon is low on ammo. Fixed predict path enemy tracking timer is always true. Allow bots to process their tasks while on freezetime, so on small maps they already aiming enemies when freezetime ends. Fied bots endlessy trying to pickup weapons. Reduce surpise timers when holding sniper weapons. New aim-at-head position calculation. Shoot delay timers are now based on bot's difficulty. Prefer smoke grenades more than flashbangs. Fixed kill-all bot command not killing one random bot for first time use. Do not play with jump velocity, now using the same as in waypoints. Tweaked shift move, so zero move speed not overriden with shift speed. Radius waypoint searcher use waypoint bucket as well. Increase reachability radius for dest waypoint, if it's currenlty owned by other bot. Partially fixed bots choice to use unreachable waypoints. * Makes OSX clang happy? * Support for compiling on llvm-win32, makefile to be done. Increased default reachability time. * Fixed build. * Move level-initialization stuff from Spawn to ServerActivate, so bot will not check init-stuff every entity spawn. This should save few CPU cycles. * Fixed active grenades list not working after changelevel. Reworked items pickup code, so every bot is not firing sphere search every time, but instead we maintain our own list of intresting entities, so every bot is accessing this list. This should lower CPU usage more a little. * Precache should be done in spawn... * Do not use engfuncs in intresting entities. * Fixed GCC-8.2 warnings. Minor refactoring. * Added some safety checks to intresting entities. Get rid of stdc++ dependency for GCC & ICC under linux. * Remove -g from release make. Cosmetic changes. * Re-enabled debug overlay. * Remove test header... * Some static-analyzer warnings fixed. Support for X64 build for FWGS Xash3D Engine. * Reduced time between selecting grenade and throwing it away. Do not try to kill bots that already dead with kill command. Several fixes from static-analyzers. * Update CI. * Fixed bot's not added after the changelevel on Xash3D engine. * Revert commit that enables movement during freezetime. Everything goes bad, when there is no freezetime.... * Bots will try to not strafe while in combat if seeing enemy only partially. Do not use "shift" when considering stuck. * Weapon price for Elite is 800$ since CS 1.6... * Fixed bots at difficulty 0 can't shoot enemies. * Cosmetic change. * Fixed assert in ClientDisconnect when quitting game while meta unloaded yapb module. Consider freed entities as invalid. * Bigger distance for throwing he grenades. * Faster version of atan2f(). * Removed accidentally left SSE header. * Cosmetic changes to enums. * Tweaked difficulty levels. Bots on Android will have a difficulty level 2 by default. Fixed LTO builds under linux. * Do not consider Android CS as legacy. * Get rid of system's math functions. Just for fun) * Use SSE2 for sincos function. * Fixed failed during load wayponts still allows to add bots, thus causing bot to crash. Added ability to delete waypoint by number using "yb wp delete". Enabled Link Time Optimization for Linux and OSX. * Fixed CI Builds.
1816 lines
40 KiB
C++
1816 lines
40 KiB
C++
//
|
|
// 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 <stdarg.h>
|
|
#include <stdio.h>
|
|
#include <stdlib.h>
|
|
#include <string.h>
|
|
|
|
#include <assert.h>
|
|
#include <ctype.h>
|
|
#include <float.h>
|
|
#include <limits.h>
|
|
#include <time.h>
|
|
#include <platform.h>
|
|
|
|
#ifdef PLATFORM_WIN32
|
|
#include <direct.h>
|
|
#else
|
|
#include <sys/stat.h>
|
|
#endif
|
|
|
|
#ifndef PLATFORM_WIN32
|
|
#define _unlink unlink
|
|
#define _mkdir(p) mkdir (p, 0777)
|
|
#define stricmp strcasecmp
|
|
#else
|
|
#define stricmp _stricmp
|
|
#endif
|
|
|
|
#ifdef PLATFORM_HAS_SSE2
|
|
#include <immintrin.h>
|
|
#endif
|
|
|
|
#undef min
|
|
#undef max
|
|
|
|
// to remove ugly A_ prefixes
|
|
namespace cr {
|
|
|
|
namespace types {
|
|
|
|
using int8 = signed char;
|
|
using int16 = signed short;
|
|
using int32 = signed long;
|
|
using uint8 = unsigned char;
|
|
using uint16 = unsigned short;
|
|
using uint32 = unsigned long;
|
|
|
|
}
|
|
|
|
using namespace cr::types;
|
|
|
|
constexpr float ONEPSILON = 0.01f;
|
|
constexpr float EQEPSILON = 0.001f;
|
|
constexpr float FLEPSILON = 1.192092896e-07f;
|
|
|
|
constexpr float PI = 3.141592653589793115997963468544185161590576171875f;
|
|
constexpr float PI_RECIPROCAL = 1.0f / PI;
|
|
constexpr float PI_HALF = PI / 2;
|
|
|
|
constexpr float D2R = PI / 180.0f;
|
|
constexpr float R2D = 180.0f / PI;
|
|
|
|
// from metamod-p
|
|
static inline bool checkptr (const void *ptr) {
|
|
#ifdef PLATFORM_WIN32
|
|
if (IsBadCodePtr (reinterpret_cast <FARPROC> (ptr)))
|
|
return false;
|
|
#endif
|
|
|
|
(void)(ptr);
|
|
return true;
|
|
}
|
|
|
|
template <typename T, size_t N> constexpr size_t bufsize (const T (&)[N]) {
|
|
return N - 1;
|
|
}
|
|
|
|
template <typename T, size_t N> constexpr size_t arrsize (const T (&)[N]) {
|
|
return N;
|
|
}
|
|
|
|
constexpr float square (const float value) {
|
|
return value * value;
|
|
}
|
|
|
|
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 clamp (const T x, const T a, const T b) {
|
|
return min (max (x, a), b);
|
|
}
|
|
|
|
template <typename T> constexpr T abs (const T a) {
|
|
return a > 0 ? a : -a;
|
|
}
|
|
|
|
static 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;
|
|
}
|
|
|
|
static inline float sqrtf (const float value) {
|
|
return powf (value, 0.5f);
|
|
}
|
|
|
|
static inline float sinf (const float value) {
|
|
const signed long sign = static_cast <signed long> (value * PI_RECIPROCAL);
|
|
const float calc = (value - static_cast <float> (sign) * PI);
|
|
|
|
const float sqr = 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;
|
|
}
|
|
|
|
static inline float cosf (const float value) {
|
|
const signed long sign = static_cast <signed long> (value * PI_RECIPROCAL);
|
|
const float calc = (value - static_cast <float> (sign) * PI);
|
|
|
|
const float sqr = 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;
|
|
}
|
|
|
|
static inline float tanf (const float value) {
|
|
return sinf (value) / cosf (value);
|
|
}
|
|
|
|
static inline float atan2f (const float y, const float x) {
|
|
auto atanf = [](const float x) {
|
|
const float sqr = square (x);
|
|
return x * (48.70107004404898384f + sqr * (49.5326263772254345f + sqr * 9.40604244231624f)) / (48.70107004404996166f + sqr * (65.7663163908956299f + sqr * (21.587934067020262f + sqr)));
|
|
};
|
|
|
|
const float ax = abs (x);
|
|
const float ay = 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) + PI;
|
|
}
|
|
return atanf (y / x) - PI;
|
|
}
|
|
return atanf (y / x);
|
|
}
|
|
|
|
if (y < 0.0f) {
|
|
return atanf (-x / y) - PI_HALF;
|
|
}
|
|
return atanf (-x / y) + PI_HALF;
|
|
}
|
|
|
|
static inline float ceilf (const float x) {
|
|
return static_cast <float> (65536 - static_cast <int> (65536.0f - x));
|
|
}
|
|
|
|
static inline void sincosf (const float x, const float y, const float z, float *sines, float *cosines) {
|
|
// this is the only place where sse2 stuff actually faster than chebyshev pads as we're can calculate 3 sines + 3 cosines
|
|
// using only two sse calls instead of 6 calls to sin/cos with standard functions
|
|
|
|
#if defined (PLATFORM_HAS_SSE2)
|
|
auto rad = _mm_set_ps (x, y, z, 0.0f);
|
|
|
|
auto _mm_sin = [] (__m128 rad) -> __m128 {
|
|
static auto pi2 = _mm_set_ps1 (PI * 2);
|
|
static auto rp1 = _mm_set_ps1 (4.0f / PI);
|
|
static auto rp2 = _mm_set_ps1 (-4.0f / (PI * PI));
|
|
static auto val = _mm_cmpnlt_ps (rad, _mm_set_ps1 (PI));
|
|
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 (-PI));
|
|
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 (PI_HALF);
|
|
|
|
auto s = _mm_sin (rad);
|
|
auto c = _mm_sin (_mm_add_ps (rad, 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] = sinf (x);
|
|
sines[1] = sinf (y);
|
|
sines[2] = sinf (z);
|
|
|
|
cosines[0] = cosf (x);
|
|
cosines[1] = cosf (y);
|
|
cosines[2] = cosf (z);
|
|
#endif
|
|
}
|
|
|
|
constexpr bool fzero (const float entry) {
|
|
return abs (entry) < ONEPSILON;
|
|
}
|
|
|
|
constexpr bool fequal (const float entry1, const float entry2) {
|
|
return abs (entry1 - entry2) < EQEPSILON;
|
|
}
|
|
|
|
constexpr float rad2deg (const float radian) {
|
|
return radian * R2D;
|
|
}
|
|
|
|
constexpr float deg2rad (const float degree) {
|
|
return degree * D2R;
|
|
}
|
|
|
|
constexpr float angleMod (const float angle) {
|
|
return 360.0f / 65536.0f * (static_cast <int> (angle * (65536.0f / 360.0f)) & 65535);
|
|
}
|
|
|
|
constexpr float angleNorm (const float angle) {
|
|
return 360.0f / 65536.0f * (static_cast <int> ((angle + 180.0f) * (65536.0f / 360.0f)) & 65535) - 180.0f;
|
|
}
|
|
|
|
constexpr float angleDiff (const float dest, const float src) {
|
|
return angleNorm (dest - src);
|
|
}
|
|
|
|
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> static inline typename ClearRef <T>::Type &&move (T &&type) {
|
|
return static_cast <typename ClearRef <T>::Type &&> (type);
|
|
}
|
|
|
|
template <typename T> static inline void swap (T &left, T &right) {
|
|
auto temp = move (left);
|
|
left = move (right);
|
|
right = move (temp);
|
|
}
|
|
|
|
template <typename T> static constexpr inline T &&forward (typename ClearRef <T>::Type &type) noexcept {
|
|
return static_cast <T &&> (type);
|
|
}
|
|
|
|
template <typename T> static constexpr inline T &&forward (typename ClearRef <T>::Type &&type) noexcept {
|
|
return static_cast <T &&> (type);
|
|
}
|
|
|
|
template <typename T> static inline void transfer (T *dest, T *src, size_t length) {
|
|
for (size_t i = 0; i < length; i++) {
|
|
dest[i] = move (src[i]);
|
|
}
|
|
}
|
|
|
|
namespace classes {
|
|
|
|
class NonCopyable {
|
|
protected:
|
|
NonCopyable (void) = default;
|
|
~NonCopyable (void) = default;
|
|
|
|
private:
|
|
NonCopyable (const NonCopyable &) = delete;
|
|
NonCopyable &operator = (const NonCopyable &) = delete;
|
|
};
|
|
|
|
template <typename T> class Singleton : private NonCopyable {
|
|
public:
|
|
inline static T *ptr (void) {
|
|
return &ref ();
|
|
}
|
|
|
|
inline static T &ref (void) {
|
|
static T ref;
|
|
return ref;
|
|
};
|
|
};
|
|
|
|
// see: https://github.com/preshing/RandomSequence/
|
|
class RandomSequence : public Singleton <RandomSequence> {
|
|
private:
|
|
unsigned int m_index;
|
|
unsigned int m_intermediateOffset;
|
|
unsigned long long m_divider;
|
|
|
|
private:
|
|
unsigned int premute (unsigned int x) {
|
|
static constexpr unsigned int prime = 4294967291u;
|
|
|
|
if (x >= prime) {
|
|
return x;
|
|
}
|
|
const unsigned int residue = (static_cast <unsigned long long> (x) * x) % prime;
|
|
return (x <= prime / 2) ? residue : prime - residue;
|
|
}
|
|
|
|
unsigned int random (void) {
|
|
return premute ((premute (m_index++) + m_intermediateOffset) ^ 0x5bf03635);
|
|
}
|
|
|
|
public:
|
|
RandomSequence (void) {
|
|
const unsigned int seedBase = static_cast <unsigned int> (time (nullptr));
|
|
const unsigned int seedOffset = seedBase + 1;
|
|
|
|
m_index = premute (premute (seedBase) + 0x682f0161);
|
|
m_intermediateOffset = premute (premute (seedOffset) + 0x46790905);
|
|
m_divider = (static_cast <unsigned long long> (1)) << 32;
|
|
}
|
|
|
|
template <typename U> inline U getInt (U low, U high) {
|
|
return static_cast <U> (random () * (static_cast <double> (high) - static_cast <double> (low) + 1.0) / m_divider + static_cast <double> (low));
|
|
}
|
|
|
|
inline float getFloat (float low, float high) {
|
|
return static_cast <float> (random () * (static_cast <double> (high) - static_cast <double> (low)) / (m_divider - 1) + static_cast <double> (low));
|
|
}
|
|
};
|
|
|
|
class Vector final {
|
|
public:
|
|
float x, y, z;
|
|
|
|
public:
|
|
inline Vector (float scaler = 0.0f) : x (scaler), y (scaler), z (scaler) {}
|
|
inline Vector (float inputX, float inputY, float inputZ) : x (inputX), y (inputY), z (inputZ) {}
|
|
inline Vector (float *other) : x (other[0]), y (other[1]), z (other[2]) {}
|
|
inline Vector (const Vector &right) : x (right.x), y (right.y), z (right.z) {}
|
|
|
|
public:
|
|
inline operator float * (void) {
|
|
return &x;
|
|
}
|
|
|
|
inline operator const float * (void) const {
|
|
return &x;
|
|
}
|
|
|
|
inline const Vector operator+ (const Vector &right) const {
|
|
return Vector (x + right.x, y + right.y, z + right.z);
|
|
}
|
|
|
|
inline const Vector operator- (const Vector &right) const {
|
|
return Vector (x - right.x, y - right.y, z - right.z);
|
|
}
|
|
|
|
inline const Vector operator- (void) const {
|
|
return Vector (-x, -y, -z);
|
|
}
|
|
|
|
friend inline const Vector operator* (const float vec, const Vector &right) {
|
|
return Vector (right.x * vec, right.y * vec, right.z * vec);
|
|
}
|
|
|
|
inline const Vector operator* (float vec) const {
|
|
return Vector (vec * x, vec * y, vec * z);
|
|
}
|
|
|
|
inline const Vector operator/ (float vec) const {
|
|
const float inv = 1 / vec;
|
|
return Vector (inv * x, inv * y, inv * z);
|
|
}
|
|
|
|
// cross product
|
|
inline const Vector operator^ (const Vector &right) const {
|
|
return Vector (y * right.z - z * right.y, z * right.x - x * right.z, x * right.y - y * right.x);
|
|
}
|
|
|
|
// dot product
|
|
inline float operator| (const Vector &right) const {
|
|
return x * right.x + y * right.y + z * right.z;
|
|
}
|
|
|
|
inline const Vector &operator+= (const Vector &right) {
|
|
x += right.x;
|
|
y += right.y;
|
|
z += right.z;
|
|
|
|
return *this;
|
|
}
|
|
|
|
inline const Vector &operator-= (const Vector &right) {
|
|
x -= right.x;
|
|
y -= right.y;
|
|
z -= right.z;
|
|
|
|
return *this;
|
|
}
|
|
|
|
inline const Vector &operator*= (float vec) {
|
|
x *= vec;
|
|
y *= vec;
|
|
z *= vec;
|
|
|
|
return *this;
|
|
}
|
|
|
|
inline const Vector &operator/= (float vec) {
|
|
const float inv = 1 / vec;
|
|
|
|
x *= inv;
|
|
y *= inv;
|
|
z *= inv;
|
|
|
|
return *this;
|
|
}
|
|
|
|
inline bool operator== (const Vector &right) const {
|
|
return fequal (x, right.x) && fequal (y, right.y) && fequal (z, right.z);
|
|
}
|
|
|
|
inline bool operator!= (const Vector &right) const {
|
|
return !fequal (x, right.x) && !fequal (y, right.y) && !fequal (z, right.z);
|
|
}
|
|
|
|
inline const Vector &operator= (const Vector &right) {
|
|
x = right.x;
|
|
y = right.y;
|
|
z = right.z;
|
|
|
|
return *this;
|
|
}
|
|
|
|
public:
|
|
inline float length (void) const {
|
|
return sqrtf (x * x + y * y + z * z);
|
|
}
|
|
|
|
inline float length2D (void) const {
|
|
return sqrtf (x * x + y * y);
|
|
}
|
|
|
|
inline float lengthSq (void) const {
|
|
return x * x + y * y + z * z;
|
|
}
|
|
|
|
inline Vector make2D (void) const {
|
|
return Vector (x, y, 0.0f);
|
|
}
|
|
|
|
inline Vector normalize (void) const {
|
|
float len = length () + static_cast <float> (FLEPSILON);
|
|
|
|
if (fzero (len)) {
|
|
return Vector (0.0f, 0.0f, 1.0f);
|
|
}
|
|
len = 1.0f / len;
|
|
return Vector (x * len, y * len, z * len);
|
|
}
|
|
|
|
inline Vector normalize2D (void) const {
|
|
float len = length2D () + static_cast <float> (FLEPSILON);
|
|
|
|
if (fzero (len)) {
|
|
return Vector (0.0f, 1.0f, 0.0f);
|
|
}
|
|
len = 1.0f / len;
|
|
return Vector (x * len, y * len, 0.0f);
|
|
}
|
|
|
|
inline bool empty (void) const {
|
|
return fzero (x) && fzero (y) && fzero (z);
|
|
}
|
|
|
|
inline static const Vector &null (void) {
|
|
static const Vector s_zero = Vector (0.0f, 0.0f, 0.0f);
|
|
return s_zero;
|
|
}
|
|
|
|
inline void nullify (void) {
|
|
x = y = z = 0.0f;
|
|
}
|
|
|
|
inline Vector clampAngles (void) {
|
|
x = angleNorm (x);
|
|
y = angleNorm (y);
|
|
z = 0.0f;
|
|
|
|
return *this;
|
|
}
|
|
|
|
inline float toPitch (void) const {
|
|
if (fzero (x) && fzero (y)) {
|
|
return 0.0f;
|
|
}
|
|
return rad2deg (atan2f (z, length2D ()));
|
|
}
|
|
|
|
inline float toYaw (void) const {
|
|
if (fzero (x) && fzero (y)) {
|
|
return 0.0f;
|
|
}
|
|
return rad2deg (atan2f (y, x));
|
|
}
|
|
|
|
inline Vector toAngles (void) const {
|
|
if (fzero (x) && fzero (y)) {
|
|
return Vector (z > 0.0f ? 90.0f : 270.0f, 0.0, 0.0f);
|
|
}
|
|
return Vector (rad2deg (atan2f (z, length2D ())), rad2deg (atan2f (y, x)), 0.0f);
|
|
}
|
|
|
|
inline void makeVectors (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
|
|
sincosf (deg2rad (x), deg2rad (y), deg2rad (y), 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];
|
|
}
|
|
}
|
|
};
|
|
|
|
class Library {
|
|
private:
|
|
void *m_ptr;
|
|
|
|
public:
|
|
Library (const char *filename) : m_ptr (nullptr) {
|
|
if (!filename) {
|
|
return;
|
|
}
|
|
load (filename);
|
|
}
|
|
|
|
virtual ~Library (void) {
|
|
if (!isValid ()) {
|
|
return;
|
|
}
|
|
#ifdef PLATFORM_WIN32
|
|
FreeLibrary ((HMODULE)m_ptr);
|
|
#else
|
|
dlclose (m_ptr);
|
|
#endif
|
|
}
|
|
|
|
public:
|
|
inline void *load (const char *filename) {
|
|
#ifdef PLATFORM_WIN32
|
|
m_ptr = LoadLibrary (filename);
|
|
#else
|
|
m_ptr = dlopen (filename, RTLD_NOW);
|
|
#endif
|
|
return m_ptr;
|
|
}
|
|
|
|
template <typename R> R resolve (const char *function) {
|
|
if (!isValid ()) {
|
|
return nullptr;
|
|
}
|
|
return (R)
|
|
#ifdef PLATFORM_WIN32
|
|
GetProcAddress (static_cast <HMODULE> (m_ptr), function);
|
|
#else
|
|
dlsym (m_ptr, function);
|
|
#endif
|
|
}
|
|
|
|
template <typename R> R handle (void) {
|
|
return static_cast <R> (m_ptr);
|
|
}
|
|
|
|
inline bool isValid (void) const {
|
|
return m_ptr != nullptr;
|
|
}
|
|
};
|
|
|
|
template <typename A, typename B> class Pair final {
|
|
public:
|
|
A first;
|
|
B second;
|
|
|
|
public:
|
|
Pair (A a, B b) : first (a), second (b) {
|
|
}
|
|
|
|
public:
|
|
Pair (void) = default;
|
|
~Pair (void) = default;
|
|
};
|
|
|
|
template <typename T> class Array : private NonCopyable {
|
|
public:
|
|
static constexpr size_t INVALID_INDEX = static_cast <size_t> (-1);
|
|
|
|
protected:
|
|
T *m_data;
|
|
size_t m_capacity;
|
|
size_t m_length;
|
|
|
|
public:
|
|
Array (void) : m_data (nullptr), m_capacity (0), m_length (0) {
|
|
}
|
|
|
|
Array (Array &&other) noexcept {
|
|
m_data = other.m_data;
|
|
m_length = other.m_length;
|
|
m_capacity = other.m_capacity;
|
|
|
|
other.reset ();
|
|
}
|
|
|
|
virtual ~Array (void) {
|
|
destroy ();
|
|
}
|
|
|
|
public:
|
|
void destroy (void) {
|
|
delete[] m_data;
|
|
reset ();
|
|
}
|
|
|
|
void reset (void) {
|
|
m_data = nullptr;
|
|
m_capacity = 0;
|
|
m_length = 0;
|
|
}
|
|
|
|
bool reserve (size_t growSize) {
|
|
if (m_length + growSize < m_capacity) {
|
|
return true;
|
|
}
|
|
size_t maxSize = max <size_t> (m_capacity + sizeof (T), static_cast <size_t> (16));
|
|
|
|
while (m_length + growSize > maxSize) {
|
|
maxSize *= 2;
|
|
}
|
|
|
|
if (maxSize >= INT_MAX / sizeof (T)) {
|
|
assert (!"Allocation Overflow!");
|
|
return false;
|
|
}
|
|
auto buffer = new T[maxSize];
|
|
|
|
if (m_data != nullptr) {
|
|
if (maxSize < m_length) {
|
|
m_length = maxSize;
|
|
}
|
|
transfer (buffer, m_data, m_length);
|
|
delete[] m_data;
|
|
}
|
|
m_data = move (buffer);
|
|
m_capacity = move (maxSize);
|
|
|
|
return true;
|
|
}
|
|
|
|
bool resize (size_t newSize) {
|
|
bool res = reserve (newSize);
|
|
|
|
while (m_length < newSize) {
|
|
push (T ());
|
|
}
|
|
return res;
|
|
}
|
|
|
|
inline size_t length (void) const {
|
|
return m_length;
|
|
}
|
|
|
|
inline size_t capacity (void) const {
|
|
return m_capacity;
|
|
}
|
|
|
|
bool set (size_t index, T object) {
|
|
if (index >= m_capacity) {
|
|
if (!reserve (index + 2)) {
|
|
return false;
|
|
}
|
|
}
|
|
m_data[index] = forward <T> (object);
|
|
|
|
if (index >= m_length) {
|
|
m_length = index + 1;
|
|
}
|
|
return true;
|
|
}
|
|
|
|
inline T &at (size_t index) {
|
|
return m_data[index];
|
|
}
|
|
|
|
bool at (size_t index, T &object) {
|
|
if (index >= m_length) {
|
|
return false;
|
|
}
|
|
object = m_data[index];
|
|
return true;
|
|
}
|
|
|
|
bool insert (size_t index, T object) {
|
|
return insert (index, &object, 1);
|
|
}
|
|
|
|
bool insert (size_t index, T *objects, size_t count = 1) {
|
|
if (!objects || !count) {
|
|
return false;
|
|
}
|
|
size_t newSize = (m_length > index ? m_length : index) + count;
|
|
|
|
if (newSize >= m_capacity && !reserve (newSize)) {
|
|
return false;
|
|
}
|
|
|
|
if (index >= m_length) {
|
|
for (size_t i = 0; i < count; i++) {
|
|
m_data[i + index] = forward <T> (objects[i]);
|
|
}
|
|
m_length = newSize;
|
|
}
|
|
else {
|
|
size_t i = 0;
|
|
|
|
for (i = m_length; i > index; i--) {
|
|
m_data[i + count - 1] = move (m_data[i - 1]);
|
|
}
|
|
for (i = 0; i < count; i++) {
|
|
m_data[i + index] = forward <T> (objects[i]);
|
|
}
|
|
m_length += count;
|
|
}
|
|
return true;
|
|
}
|
|
|
|
bool insert (size_t at, Array &other) {
|
|
if (&other == this) {
|
|
return false;
|
|
}
|
|
return insert (at, other.m_data, other.m_length);
|
|
}
|
|
|
|
bool erase (size_t index, 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] = move (m_data[i + count]);
|
|
}
|
|
return true;
|
|
}
|
|
|
|
bool shift (void) {
|
|
return erase (0, 1);
|
|
}
|
|
|
|
bool unshift (T object) {
|
|
return insert (0, &object);
|
|
}
|
|
|
|
bool erase (T &item) {
|
|
return erase (index (item), 1);
|
|
}
|
|
|
|
bool push (T object) {
|
|
return insert (m_length, &object);
|
|
}
|
|
|
|
bool push (T *objects, size_t count) {
|
|
return insert (m_length, objects, count);
|
|
}
|
|
|
|
bool extend (Array &other) {
|
|
if (&other == this) {
|
|
return false;
|
|
}
|
|
return insert (m_length, other.m_data, other.m_length);
|
|
}
|
|
|
|
void clear (void) {
|
|
m_length = 0;
|
|
}
|
|
|
|
inline bool empty (void) const {
|
|
return m_length <= 0;
|
|
}
|
|
|
|
void shrink (bool destroyEmpty = true) {
|
|
if (!m_length) {
|
|
if (destroyEmpty) {
|
|
destroy ();
|
|
}
|
|
return;
|
|
}
|
|
T *buffer = new T[m_length];
|
|
|
|
if (m_data != nullptr) {
|
|
for (size_t i = 0; i < m_length; i++) {
|
|
transfer (buffer, m_data);
|
|
}
|
|
delete[] m_data;
|
|
}
|
|
m_data = move (buffer);
|
|
m_capacity = move (m_length);
|
|
}
|
|
|
|
size_t index (T &item) {
|
|
return &item - &m_data[0];
|
|
}
|
|
|
|
T pop (void) {
|
|
T item = move (m_data[m_length - 1]);
|
|
erase (m_length - 1, 1);
|
|
|
|
return item;
|
|
}
|
|
|
|
const T &back (void) const {
|
|
return m_data[m_length - 1];
|
|
}
|
|
|
|
T &back (void) {
|
|
return m_data[m_length - 1];
|
|
}
|
|
|
|
bool last (T &item) {
|
|
if (m_length <= 0) {
|
|
return false;
|
|
}
|
|
item = m_data[m_length - 1];
|
|
return true;
|
|
}
|
|
|
|
bool assign (const Array &other) {
|
|
if (&other == this) {
|
|
return true;
|
|
}
|
|
if (!reserve (other.m_length)) {
|
|
return false;
|
|
}
|
|
transfer (m_data, other.m_data, other.m_length);
|
|
m_length = other.m_length;
|
|
|
|
return true;
|
|
}
|
|
|
|
void reverse (void) {
|
|
for (size_t i = 0; i < m_length / 2; i++) {
|
|
swap (m_data[i], m_data[m_length - 1 - i]);
|
|
}
|
|
}
|
|
|
|
T &random (void) const {
|
|
return m_data[RandomSequence::ref ().getInt (0, static_cast <int> (m_length - 1))];
|
|
}
|
|
|
|
Array &operator = (Array &&other) noexcept {
|
|
destroy ();
|
|
|
|
m_data = other.m_data;
|
|
m_length = other.m_length;
|
|
m_capacity = other.m_capacity;
|
|
|
|
other.reset ();
|
|
return *this;
|
|
}
|
|
|
|
T &operator [] (size_t index) {
|
|
return at (index);
|
|
}
|
|
|
|
const T &operator [] (size_t index) const {
|
|
return at (index);
|
|
}
|
|
|
|
T *begin (void) {
|
|
return m_data;
|
|
}
|
|
|
|
T *begin (void) const {
|
|
return m_data;
|
|
}
|
|
|
|
T *end (void) {
|
|
return m_data + m_length;
|
|
}
|
|
|
|
T *end (void) const {
|
|
return m_data + m_length;
|
|
}
|
|
};
|
|
|
|
template <typename A, typename B, size_t Capacity = 0> class BinaryHeap final : private Array <Pair <A, B>> {
|
|
private:
|
|
using type = Pair <A, B>;
|
|
using base = Array <type>;
|
|
|
|
using base::m_data;
|
|
using base::m_length;
|
|
|
|
public:
|
|
BinaryHeap (void) {
|
|
base::reserve (Capacity);
|
|
}
|
|
BinaryHeap (BinaryHeap &&other) noexcept : base (move (other)) { }
|
|
|
|
public:
|
|
void push (const A &first, const B &second) {
|
|
push ({ first, second });
|
|
}
|
|
|
|
void push (type &&pair) {
|
|
base::push (forward <type> (pair));
|
|
|
|
if (length () > 1) {
|
|
siftUp ();
|
|
}
|
|
}
|
|
|
|
A pop (void) {
|
|
assert (!empty ());
|
|
|
|
if (length () == 1) {
|
|
return base::pop ().first;
|
|
}
|
|
type value = move (m_data[0]);
|
|
|
|
m_data[0] = move (base::back ());
|
|
base::pop ();
|
|
|
|
siftDown ();
|
|
return value.first;
|
|
}
|
|
|
|
inline bool empty (void) const {
|
|
return !length ();
|
|
}
|
|
|
|
inline size_t length (void) const {
|
|
return m_length;
|
|
}
|
|
|
|
inline void clear (void) {
|
|
base::clear ();
|
|
}
|
|
|
|
void siftUp (void) {
|
|
size_t child = m_length - 1;
|
|
|
|
while (child != 0) {
|
|
size_t parentIndex = getParent (child);
|
|
|
|
if (m_data[parentIndex].second <= m_data[child].second) {
|
|
break;
|
|
}
|
|
swap (m_data[child], m_data[parentIndex]);
|
|
child = parentIndex;
|
|
}
|
|
}
|
|
|
|
void siftDown (void) {
|
|
size_t parent = 0;
|
|
size_t leftIndex = getLeft (parent);
|
|
|
|
auto ref = move (m_data[parent]);
|
|
|
|
while (leftIndex < m_length) {
|
|
size_t rightIndex = getRight (parent);
|
|
|
|
if (rightIndex < m_length) {
|
|
if (m_data[rightIndex].second < m_data[leftIndex].second) {
|
|
leftIndex = rightIndex;
|
|
}
|
|
}
|
|
|
|
if (ref.second <= m_data[leftIndex].second) {
|
|
break;
|
|
}
|
|
m_data[parent] = move (m_data[leftIndex]);
|
|
|
|
parent = leftIndex;
|
|
leftIndex = getLeft (parent);
|
|
}
|
|
m_data[parent] = move (ref);
|
|
}
|
|
|
|
BinaryHeap &operator = (BinaryHeap &&other) noexcept {
|
|
base::operator = (move (other));
|
|
return *this;
|
|
}
|
|
|
|
private:
|
|
static constexpr size_t getLeft (size_t index) {
|
|
return index << 1 | 1;
|
|
}
|
|
|
|
static constexpr size_t getRight (size_t index) {
|
|
return ++index << 1;
|
|
}
|
|
|
|
static constexpr size_t getParent (size_t index) {
|
|
return --index >> 1;
|
|
}
|
|
};
|
|
|
|
class String final : private Array <char> {
|
|
private:
|
|
using base = Array <char>;
|
|
|
|
private:
|
|
bool isTrimChar (char chr, const char *chars) {
|
|
do {
|
|
if (*chars == chr) {
|
|
return true;
|
|
}
|
|
chars++;
|
|
} while (*chars);
|
|
return false;
|
|
}
|
|
|
|
public:
|
|
|
|
String (void) = default;
|
|
String (String &&other) noexcept : base (move (other)) { }
|
|
~String (void) = default;
|
|
|
|
public:
|
|
String (const char chr) {
|
|
assign (chr);
|
|
}
|
|
|
|
String (const char *str, size_t length = 0) {
|
|
assign (str, length);
|
|
}
|
|
|
|
String (const String &str, size_t length = 0) : base () {
|
|
assign (str.chars (), length);
|
|
}
|
|
|
|
public:
|
|
String &assign (const char *str, size_t length = 0) {
|
|
length = length > 0 ? length : strlen (str);
|
|
|
|
clear ();
|
|
resize (length);
|
|
|
|
memcpy (m_data, str, length);
|
|
terminate ();
|
|
|
|
return *this;
|
|
}
|
|
|
|
String &assign (const String &str, size_t length = 0) {
|
|
return assign (str.chars (), length);
|
|
}
|
|
|
|
String &assign (const char chr) {
|
|
resize (2);
|
|
m_data[0] = chr;
|
|
|
|
return *this;
|
|
}
|
|
|
|
String &append (const char *str) {
|
|
if (empty ()) {
|
|
return assign (str);
|
|
}
|
|
const size_t maxLength = strlen (str) + 1;
|
|
|
|
resize (length () + maxLength);
|
|
strncat (m_data, str, maxLength);
|
|
|
|
return *this;
|
|
}
|
|
|
|
String &append (const String &str) {
|
|
return append (str.chars ());
|
|
}
|
|
|
|
String &append (const char chr) {
|
|
const char app[] = { chr, '\0' };
|
|
return append (app);
|
|
}
|
|
|
|
const char *chars (void) const {
|
|
return empty () ? "" : m_data;
|
|
}
|
|
|
|
size_t length (void) const {
|
|
return base::length ();
|
|
}
|
|
|
|
bool empty (void) const {
|
|
return !length ();
|
|
}
|
|
|
|
void clear (void) {
|
|
base::clear ();
|
|
|
|
if (m_data) {
|
|
m_data[0] = '\0';
|
|
}
|
|
}
|
|
|
|
void erase (size_t index, size_t count = 1) {
|
|
base::erase (index, count);
|
|
terminate ();
|
|
}
|
|
|
|
void reserve (size_t count) {
|
|
base::reserve (count);
|
|
}
|
|
|
|
void resize (size_t count) {
|
|
base::resize (count);
|
|
terminate ();
|
|
}
|
|
|
|
int32 toInt32 (void) const {
|
|
return atoi (chars ());
|
|
}
|
|
|
|
inline void terminate (void) {
|
|
m_data[m_length] = '\0';
|
|
}
|
|
|
|
inline char &at (size_t index) {
|
|
return m_data[index];
|
|
}
|
|
|
|
int32 compare (const String &what) const {
|
|
return strcmp (m_data, what.begin ());
|
|
}
|
|
|
|
int32 compare (const char *what) const {
|
|
return strcmp (begin (), what);
|
|
}
|
|
|
|
bool contains (const String &what) const {
|
|
return strstr (m_data, what.begin ()) != nullptr;
|
|
}
|
|
|
|
template <size_t BufferSize = 512> void format (const char *fmt, ...) {
|
|
va_list ap;
|
|
char buffer[BufferSize];
|
|
|
|
va_start (ap, fmt);
|
|
vsnprintf (buffer, bufsize (buffer), fmt, ap);
|
|
va_end (ap);
|
|
|
|
assign (buffer);
|
|
}
|
|
|
|
template <size_t BufferSize = 512> void formatAppend (const char *fmt, ...) {
|
|
va_list ap;
|
|
char buffer[BufferSize];
|
|
|
|
va_start (ap, fmt);
|
|
vsnprintf (buffer, bufsize (buffer), fmt, ap);
|
|
va_end (ap);
|
|
|
|
append (buffer);
|
|
}
|
|
|
|
size_t insert (size_t at, const String &str) {
|
|
if (base::insert (at, str.begin (), str.length ())) {
|
|
terminate ();
|
|
return length ();
|
|
}
|
|
return 0;
|
|
}
|
|
|
|
size_t find (const String &search, size_t pos) const {
|
|
if (pos > length ()) {
|
|
return INVALID_INDEX;
|
|
}
|
|
size_t len = search.length ();
|
|
|
|
for (size_t i = pos; len + i <= length (); i++) {
|
|
if (strncmp (m_data + i, search.chars (), len) == 0) {
|
|
return i;
|
|
}
|
|
}
|
|
return INVALID_INDEX;
|
|
}
|
|
|
|
size_t replace (const String &what, const String &to) {
|
|
if (!what.length () || !to.length ()) {
|
|
return 0;
|
|
}
|
|
size_t numReplaced = 0, posIndex = 0;
|
|
|
|
while (posIndex < length ()) {
|
|
posIndex = find (what, posIndex);
|
|
|
|
if (posIndex == INVALID_INDEX) {
|
|
break;
|
|
}
|
|
erase (posIndex, what.length ());
|
|
insert (posIndex, to);
|
|
|
|
posIndex += to.length ();
|
|
numReplaced++;
|
|
}
|
|
return numReplaced;
|
|
}
|
|
|
|
String substr (size_t start, size_t count = INVALID_INDEX) {
|
|
String result;
|
|
|
|
if (start >= length () || empty ()) {
|
|
return result;
|
|
}
|
|
if (count == INVALID_INDEX) {
|
|
count = length () - start;
|
|
}
|
|
else if (start + count >= length ()) {
|
|
count = length () - start;
|
|
}
|
|
result.resize (count);
|
|
|
|
transfer (&result[0], &m_data[start], count);
|
|
result[count] = '\0';
|
|
|
|
return result;
|
|
}
|
|
|
|
char &operator[] (size_t index) {
|
|
return at (index);
|
|
}
|
|
|
|
friend String operator + (const String &lhs, const String &rhs) {
|
|
return String (lhs).append (rhs);
|
|
}
|
|
|
|
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 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;
|
|
}
|
|
|
|
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 = (String &&other) noexcept {
|
|
base::operator = (move (other));
|
|
return *this;
|
|
}
|
|
|
|
String &operator += (const String &rhs) {
|
|
return append (rhs);
|
|
}
|
|
|
|
String &operator += (const char *rhs) {
|
|
return append (rhs);
|
|
}
|
|
|
|
String &trimRight (const char *chars = "\r\n\t ") {
|
|
if (empty ()) {
|
|
return *this;
|
|
}
|
|
char *str = end () - 1;
|
|
|
|
while (*str != 0) {
|
|
if (isTrimChar (*str, chars)) {
|
|
erase (str - begin ());
|
|
str--;
|
|
}
|
|
else {
|
|
break;
|
|
}
|
|
}
|
|
return *this;
|
|
}
|
|
|
|
String &trimLeft (const char *chars = "\r\n\t ") {
|
|
if (empty ()) {
|
|
return *this;
|
|
}
|
|
char *str = begin ();
|
|
|
|
while (isTrimChar (*str, chars)) {
|
|
str++;
|
|
}
|
|
|
|
if (begin () != str) {
|
|
erase (0, str - begin ());
|
|
}
|
|
return *this;
|
|
}
|
|
|
|
String &trim (const char *chars = "\r\n\t ") {
|
|
return trimLeft (chars).trimRight (chars);
|
|
}
|
|
|
|
Array <String> split (const char *delimiter) {
|
|
Array <String> tokens;
|
|
size_t length, index = 0;
|
|
|
|
do {
|
|
index += strspn (&m_data[index], delimiter);
|
|
length = strcspn (&m_data[index], delimiter);
|
|
|
|
if (length > 0) {
|
|
tokens.push (move (substr (index, length)));
|
|
}
|
|
index += length;
|
|
} while (length > 0);
|
|
|
|
return tokens;
|
|
}
|
|
|
|
public:
|
|
static void trimChars (char *str) {
|
|
size_t pos = 0;
|
|
char *dest = str;
|
|
|
|
while (str[pos] <= ' ' && str[pos] > 0) {
|
|
pos++;
|
|
}
|
|
|
|
while (str[pos]) {
|
|
*(dest++) = str[pos];
|
|
pos++;
|
|
}
|
|
*(dest--) = '\0';
|
|
|
|
while (dest >= str && *dest <= ' ' && *dest > 0) {
|
|
*(dest--) = '\0';
|
|
}
|
|
}
|
|
};
|
|
|
|
template <typename K> struct StringHash {
|
|
unsigned long operator () (const K &key) const {
|
|
char *str = const_cast <char *> (key.chars ());
|
|
size_t hash = key.length ();
|
|
|
|
while (*str++) {
|
|
hash = ((hash << 5) + hash) + *str;
|
|
}
|
|
return hash;
|
|
}
|
|
};
|
|
|
|
template <typename K> struct IntHash {
|
|
unsigned long operator () (K key) const {
|
|
key = ((key >> 16) ^ key) * 0x119de1f3;
|
|
key = ((key >> 16) ^ key) * 0x119de1f3;
|
|
key = (key >> 16) ^ key;
|
|
|
|
return key;
|
|
}
|
|
};
|
|
|
|
template <typename K, typename V, typename H = StringHash <K>, size_t I = 256> class HashMap final : private NonCopyable {
|
|
public:
|
|
using Bucket = Pair <K, V>;
|
|
|
|
private:
|
|
Array <Bucket> m_buckets[I];
|
|
H m_hash;
|
|
|
|
Array <Bucket> &getBucket (const K &key) {
|
|
return m_buckets[m_hash (key) % I];
|
|
}
|
|
|
|
public:
|
|
HashMap (void) = default;
|
|
~HashMap (void) = default;
|
|
|
|
public:
|
|
bool exists (const K &key) {
|
|
return get (key, nullptr);
|
|
}
|
|
|
|
bool get (const K &key, V *val) {
|
|
for (auto &entry : getBucket (key)) {
|
|
if (entry.first == key) {
|
|
if (val != nullptr) {
|
|
*val = entry.second;
|
|
}
|
|
return true;
|
|
}
|
|
}
|
|
return false;
|
|
}
|
|
|
|
bool get (const K &key, V &val) {
|
|
return get (key, &val);
|
|
}
|
|
|
|
bool put (const K &key, const V &val) {
|
|
if (exists (key)) {
|
|
return false;
|
|
}
|
|
getBucket (key).push (move (Pair <K, V> (key, val)));
|
|
return true;
|
|
}
|
|
|
|
bool erase (const K &key) {
|
|
auto &bucket = getBucket (key);
|
|
|
|
for (auto &entry : getBucket (key)) {
|
|
if (entry.first == key) {
|
|
bucket.erase (entry);
|
|
return true;
|
|
}
|
|
}
|
|
return false;
|
|
}
|
|
|
|
public:
|
|
V &operator [] (const K &key) {
|
|
for (auto &entry : getBucket (key)) {
|
|
if (entry.first == key) {
|
|
return entry.second;
|
|
}
|
|
}
|
|
static V ret;
|
|
return ret;
|
|
}
|
|
};
|
|
|
|
class File : private NonCopyable {
|
|
private:
|
|
FILE *m_handle;
|
|
size_t m_size;
|
|
|
|
public:
|
|
File (void) : m_handle (nullptr), m_size (0) {}
|
|
|
|
File (const String &fileName, const String &mode = "rt") : m_handle (nullptr), m_size (0){
|
|
open (fileName, mode);
|
|
}
|
|
|
|
virtual ~File (void) {
|
|
close ();
|
|
}
|
|
|
|
bool open (const String &fileName, const String &mode) {
|
|
if ((m_handle = fopen (fileName.chars (), mode.chars ())) == nullptr) {
|
|
return false;
|
|
}
|
|
fseek (m_handle, 0L, SEEK_END);
|
|
m_size = ftell (m_handle);
|
|
fseek (m_handle, 0L, SEEK_SET);
|
|
|
|
return true;
|
|
}
|
|
|
|
void close (void) {
|
|
if (m_handle != nullptr) {
|
|
fclose (m_handle);
|
|
m_handle = nullptr;
|
|
}
|
|
m_size = 0;
|
|
}
|
|
|
|
bool eof (void) {
|
|
return feof (m_handle) ? true : false;
|
|
}
|
|
|
|
bool flush (void) {
|
|
return fflush (m_handle) ? false : true;
|
|
}
|
|
|
|
int getch (void) {
|
|
return fgetc (m_handle);
|
|
}
|
|
|
|
char *gets (char *buffer, int count) {
|
|
return fgets (buffer, count, m_handle);
|
|
}
|
|
|
|
int writeFormat (const char *format, ...) {
|
|
assert (m_handle != nullptr);
|
|
|
|
va_list ap;
|
|
va_start (ap, format);
|
|
int written = vfprintf (m_handle, format, ap);
|
|
va_end (ap);
|
|
|
|
if (written < 0) {
|
|
return 0;
|
|
}
|
|
return written;
|
|
}
|
|
|
|
char putch (char ch) {
|
|
return static_cast <char> (fputc (ch, m_handle));
|
|
}
|
|
|
|
bool puts (const String &buffer) {
|
|
assert (m_handle != nullptr);
|
|
|
|
if (fputs (buffer.chars (), m_handle) < 0) {
|
|
return false;
|
|
}
|
|
return true;
|
|
}
|
|
|
|
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 (void) {
|
|
::rewind (m_handle);
|
|
}
|
|
|
|
size_t getSize (void) const {
|
|
return m_size;
|
|
}
|
|
|
|
bool isValid (void) const {
|
|
return m_handle != nullptr;
|
|
}
|
|
|
|
public:
|
|
static inline bool exists (const String &filename) {
|
|
File fp;
|
|
|
|
if (fp.open (filename, "rb")) {
|
|
fp.close ();
|
|
return true;
|
|
}
|
|
return false;
|
|
}
|
|
|
|
static inline void pathCreate (char *path) {
|
|
for (char *str = path + 1; *str; str++) {
|
|
if (*str == '/') {
|
|
*str = 0;
|
|
_mkdir (path);
|
|
*str = '/';
|
|
}
|
|
}
|
|
_mkdir (path);
|
|
}
|
|
};
|
|
|
|
class MemoryLoader : public Singleton <MemoryLoader> {
|
|
private:
|
|
using Load = uint8 * (*) (const char *, int *);
|
|
using Unload = void (*) (void *);
|
|
|
|
private:
|
|
Load m_load;
|
|
Unload m_unload;
|
|
|
|
public:
|
|
MemoryLoader (void) = default;
|
|
~MemoryLoader (void) = default;
|
|
|
|
public:
|
|
void setup (Load load, Unload unload) {
|
|
m_load = load;
|
|
m_unload = unload;
|
|
}
|
|
|
|
uint8 *load (const char *name, int *size) {
|
|
if (m_load) {
|
|
return m_load (name, size);
|
|
}
|
|
return nullptr;
|
|
}
|
|
|
|
void unload (void *buffer) {
|
|
if (m_unload) {
|
|
m_unload (buffer);
|
|
}
|
|
}
|
|
};
|
|
|
|
class MemFile : private NonCopyable {
|
|
private:
|
|
size_t m_size;
|
|
size_t m_pos;
|
|
uint8 *m_buffer;
|
|
|
|
public:
|
|
MemFile (void) : m_size (0) , m_pos (0) , m_buffer (nullptr) {}
|
|
|
|
MemFile (const String &filename) : m_size (0) , m_pos (0) , m_buffer (nullptr) {
|
|
open (filename);
|
|
}
|
|
|
|
virtual ~MemFile (void) {
|
|
close ();
|
|
}
|
|
|
|
bool open (const String &filename) {
|
|
m_size = 0;
|
|
m_pos = 0;
|
|
m_buffer = MemoryLoader::ref ().load (filename.chars (), reinterpret_cast <int *> (&m_size));
|
|
|
|
if (!m_buffer) {
|
|
return false;
|
|
}
|
|
return true;
|
|
}
|
|
|
|
void close (void) {
|
|
MemoryLoader::ref ().unload (m_buffer);
|
|
|
|
m_size = 0;
|
|
m_pos = 0;
|
|
m_buffer = nullptr;
|
|
}
|
|
|
|
int getch (void) {
|
|
if (!m_buffer || m_pos >= m_size) {
|
|
return -1;
|
|
}
|
|
int readCh = m_buffer[m_pos];
|
|
m_pos++;
|
|
|
|
return readCh;
|
|
}
|
|
|
|
char *gets (char *buffer, size_t count) {
|
|
if (!m_buffer || m_pos >= m_size) {
|
|
return nullptr;
|
|
}
|
|
size_t index = 0;
|
|
buffer[0] = 0;
|
|
|
|
for (; index < count - 1;) {
|
|
if (m_pos < m_size) {
|
|
buffer[index] = m_buffer[m_pos++];
|
|
|
|
if (buffer[index++] == '\n') {
|
|
break;
|
|
}
|
|
}
|
|
else {
|
|
break;
|
|
}
|
|
}
|
|
buffer[index] = 0;
|
|
return index ? buffer : nullptr;
|
|
}
|
|
|
|
size_t read (void *buffer, size_t size, size_t count = 1) {
|
|
if (!m_buffer || m_size <= m_pos || !buffer || !size || !count) {
|
|
return 0;
|
|
}
|
|
size_t blocksRead = size * count <= m_size - m_pos ? size * count : m_size - m_pos;
|
|
|
|
memcpy (buffer, &m_buffer[m_pos], blocksRead);
|
|
m_pos += blocksRead;
|
|
|
|
return blocksRead / size;
|
|
}
|
|
|
|
bool seek (size_t offset, int origin) {
|
|
if (!m_buffer || m_pos >= m_size) {
|
|
return false;
|
|
}
|
|
if (origin == SEEK_SET) {
|
|
if (offset >= m_size) {
|
|
return false;
|
|
}
|
|
m_pos = offset;
|
|
}
|
|
else if (origin == SEEK_END) {
|
|
if (offset >= m_size) {
|
|
return false;
|
|
}
|
|
m_pos = m_size - offset;
|
|
}
|
|
else {
|
|
if (m_pos + offset >= m_size) {
|
|
return false;
|
|
}
|
|
m_pos += offset;
|
|
}
|
|
return true;
|
|
}
|
|
|
|
inline size_t getSize (void) const {
|
|
return m_size;
|
|
}
|
|
|
|
bool isValid (void) const {
|
|
return m_buffer && m_size > 0;
|
|
}
|
|
};
|
|
|
|
}}
|
|
|
|
|
|
namespace cr {
|
|
namespace types {
|
|
|
|
using StringArray = cr::classes::Array <cr::classes::String>;
|
|
using IntArray = cr::classes::Array <int>;
|
|
|
|
}}
|
|
|
|
|