/* =========================================================================== Copyright (C) 2025 the OpenMoHAA team This file is part of OpenMoHAA source code. OpenMoHAA source code is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 2 of the License, or (at your option) any later version. OpenMoHAA source code is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with OpenMoHAA source code; if not, write to the Free Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA =========================================================================== */ // str.cpp: Simple, DLL portable string class // #pragma once #include #include #include #include #include #ifdef _WIN32 # pragma warning(disable : 4710) // function 'blah' not inlined #endif void TestStringClass(); class strdata { public: strdata() : len(0) , refcount(0) , data(NULL) , alloced(0) {} ~strdata() { if (data) { delete[] data; } } void AddRef() { refcount++; } bool DelRef() // True if killed { refcount--; if (refcount < 0) { delete this; return true; } return false; } char *data; int refcount; size_t alloced; size_t len; }; class str { protected: friend class Archiver; strdata *m_data; void EnsureAlloced(size_t, bool keepold = true); void EnsureDataWritable(); public: ~str(); str(); str(const char *text); str(const str& string); str(const str& string, size_t start, size_t end); str(const char ch); str(const int num); str(const float num); str(const unsigned int num); str(const long num); str(const unsigned long num); str(const long long num); str(const unsigned long long num); str(str&& string); str& operator=(str&& string); size_t length(void) const; const char *c_str(void) const; void append(const char *text); void append(const str& text); char operator[](intptr_t index) const; char& operator[](intptr_t index); void operator=(const str& text); void operator=(const char *text); friend str operator+(const str& a, const str& b); friend str operator+(const str& a, const char *b); friend str operator+(const char *a, const str& b); friend str operator+(const str& a, const float b); friend str operator+(const str& a, const int b); friend str operator+(const str& a, const unsigned b); friend str operator+(const str& a, const bool b); friend str operator+(const str& a, const char b); str& operator+=(const str& a); str& operator+=(const char *a); str& operator+=(const float a); str& operator+=(const char a); str& operator+=(const int a); str& operator+=(const unsigned a); str& operator+=(const bool a); str& operator-=(int c); str& operator--(int); friend bool operator==(const str& a, const str& b); friend bool operator==(const str& a, const char *b); friend bool operator==(const char *a, const str& b); friend bool operator!=(const str& a, const str& b); friend bool operator!=(const str& a, const char *b); friend bool operator!=(const char *a, const str& b); operator const char *() const; int icmpn(const char *text, size_t n) const; int icmpn(const str& text, size_t n) const; int icmp(const char *text) const; int icmp(const str& text) const; int cmpn(const char *text, size_t n) const; int cmpn(const str& text, size_t n) const; void tolower(void); void toupper(void); static char *tolower(char *s1); static char *toupper(char *s1); static int icmpn(const char *s1, const char *s2, size_t n); static int icmp(const char *s1, const char *s2); static int cmpn(const char *s1, const char *s2, size_t n); static int cmp(const char *s1, const char *s2); static void snprintf(char *dst, int size, const char *fmt, ...); void strip(void); static bool isNumeric(const char *str); bool isNumeric(void) const; void CapLength(size_t newlen); void BackSlashesToSlashes(); void SlashesToBackSlashes(); void DefaultExtension(const char *extension); const char *GetExtension() const; void StripExtension(); void SkipFile(); void SkipPath(); }; char *strstrip(char *string); char *strlwc(char *string); inline char str::operator[](intptr_t index) const { assert(m_data); if (!m_data) { return 0; } // don't include the '/0' in the test, because technically, it's out of bounds assert((index >= 0) && (index < (int)m_data->len)); // In release mode, give them a null character // don't include the '/0' in the test, because technically, it's out of bounds if ((index < 0) || (index >= (int)m_data->len)) { return 0; } return m_data->data[index]; } inline size_t str::length(void) const { return (m_data != NULL) ? m_data->len : 0; } inline str::~str() { if (m_data) { m_data->DelRef(); m_data = NULL; } } inline str::str() : m_data(NULL) {} inline str::str(const char *text) : m_data(NULL) { size_t len; assert(text); if (*text) { len = strlen(text); if (len) { EnsureAlloced(len + 1); strcpy(m_data->data, text); m_data->len = len; } } } inline str::str(const str& text) : m_data(NULL) { if (text.m_data) { text.m_data->AddRef(); } if (m_data) { m_data->DelRef(); } m_data = text.m_data; } inline str::str(const str& text, size_t start, size_t end) : m_data(NULL) { size_t i; size_t len; if (end > text.length()) { end = text.length(); } if (start > text.length()) { start = text.length(); } if (end >= start) { len = end - start; } else { len = 0; } if (len > 0) { EnsureAlloced(len + 1); for (i = 0; i < len; i++) { m_data->data[i] = text[start + i]; } m_data->data[len] = 0; m_data->len = len; } } inline str::str(const char ch) : m_data(NULL) { EnsureAlloced(2); m_data->data[0] = ch; m_data->data[1] = 0; m_data->len = 1; } inline str::str(const float num) : m_data(NULL) { char text[32]; size_t len; snprintf(text, sizeof(text), "%.3f", num); len = strlen(text); EnsureAlloced(len + 1); strcpy(m_data->data, text); m_data->len = len; } inline str::str(const int num) : m_data(NULL) { char text[32]; size_t len; snprintf(text, sizeof(text), "%d", num); len = strlen(text); EnsureAlloced(len + 1); strcpy(m_data->data, text); m_data->len = len; } inline str::str(const unsigned int num) : m_data(NULL) { char text[32]; size_t len; snprintf(text, sizeof(text), "%u", num); len = strlen(text); EnsureAlloced(len + 1); strcpy(m_data->data, text); m_data->len = len; } inline str::str(const long num) : m_data(NULL) { char text[64]; size_t len; snprintf(text, sizeof(text), "%ld", num); len = strlen(text); EnsureAlloced(len + 1); strcpy(m_data->data, text); m_data->len = len; } inline str::str(const unsigned long num) : m_data(NULL) { char text[64]; size_t len; snprintf(text, sizeof(text), "%lu", num); len = strlen(text); EnsureAlloced(len + 1); strcpy(m_data->data, text); m_data->len = len; } inline str::str(const long long num) : m_data(NULL) { char text[64]; size_t len; snprintf(text, sizeof(text), "%lld", num); len = strlen(text); EnsureAlloced(len + 1); strcpy(m_data->data, text); m_data->len = len; } inline str::str(const unsigned long long num) : m_data(NULL) { char text[64]; size_t len; snprintf(text, sizeof(text), "%llu", num); len = strlen(text); EnsureAlloced(len + 1); strcpy(m_data->data, text); m_data->len = len; } inline const char *str::c_str(void) const { if (m_data) { return m_data->data; } else { return ""; } } inline str::str(str&& string) : m_data(string.m_data) { string.m_data = NULL; } inline str& str::operator=(str&& string) { if (m_data) { m_data->DelRef(); } m_data = string.m_data; string.m_data = NULL; return *this; } inline void str::append(const char *text) { size_t len; assert(text); if (*text) { len = length(); len += strlen(text); EnsureAlloced(len + 1); strcat(m_data->data, text); m_data->len = len; } } inline void str::append(const str& text) { append(text.c_str()); } inline char& str::operator[](intptr_t index) { // Used for result for invalid indices static char dummy = 0; assert(m_data); // We don't know if they'll write to it or not // if it's not a const object EnsureDataWritable(); if (!m_data) { return dummy; } // don't include the '/0' in the test, because technically, it's out of bounds assert((index >= 0) && (index < (int)m_data->len)); // In release mode, let them change a safe variable // don't include the '/0' in the test, because technically, it's out of bounds if ((index < 0) || (index >= (int)m_data->len)) { return dummy; } return m_data->data[index]; } inline void str::operator=(const str& text) { // adding the reference before deleting our current reference prevents // us from deleting our string if we are copying from ourself if (text.m_data) { text.m_data->AddRef(); } if (m_data) { m_data->DelRef(); } m_data = text.m_data; } inline void str::operator=(const char *text) { size_t len; assert(text); if (m_data) { if (text == m_data->data) { return; // Copying same thing. Punt. } m_data->DelRef(); m_data = NULL; } if (*text) { len = strlen(text); m_data = new strdata; m_data->len = len; m_data->alloced = len + 1; m_data->data = new char[len + 1]; strcpy(m_data->data, text); } } inline str operator+(const str& a, const str& b) { str result(a); result.append(b); return result; } inline str operator+(const str& a, const char *b) { str result(a); result.append(b); return result; } inline str operator+(const char *a, const str& b) { str result(a); result.append(b); return result; } inline str operator+(const str& a, const bool b) { str result(a); result.append(b ? "true" : "false"); return result; } inline str operator+(const str& a, const char b) { char text[2]; text[0] = b; text[1] = 0; return a + text; } inline str& str::operator+=(const str& a) { append(a); return *this; } inline str& str::operator+=(const char *a) { append(a); return *this; } inline str& str::operator+=(const char a) { char text[2]; text[0] = a; text[1] = 0; append(text); return *this; } inline str& str::operator+=(const bool a) { append(a ? "true" : "false"); return *this; } inline bool operator==(const str& a, const str& b) { return (!strcmp(a.c_str(), b.c_str())); } inline bool operator==(const str& a, const char *b) { assert(b); if (!b) { return false; } return (!strcmp(a.c_str(), b)); } inline bool operator==(const char *a, const str& b) { assert(a); if (!a) { return false; } return (!strcmp(a, b.c_str())); } inline bool operator!=(const str& a, const str& b) { return !(a == b); } inline bool operator!=(const str& a, const char *b) { return !(a == b); } inline bool operator!=(const char *a, const str& b) { return !(a == b); } inline int str::icmpn(const char *text, size_t n) const { assert(text); return str::icmpn(c_str(), text, n); } inline int str::icmpn(const str& text, size_t n) const { return str::icmpn(c_str(), text.c_str(), n); } inline int str::icmp(const char *text) const { assert(text); return str::icmp(c_str(), text); } inline int str::icmp(const str& text) const { return str::icmp(c_str(), text.c_str()); } inline int str::cmpn(const char *text, size_t n) const { assert(text); return str::cmpn(c_str(), text, n); } inline int str::cmpn(const str& text, size_t n) const { return str::cmpn(c_str(), text.c_str(), n); } inline void str::tolower(void) { if (m_data) { EnsureDataWritable(); str::tolower(m_data->data); } } inline void str::toupper(void) { if (m_data) { EnsureDataWritable(); str::toupper(m_data->data); } } inline bool str::isNumeric(void) const { assert(m_data); return str::isNumeric(m_data->data); } inline str::operator const char *(void) const { return c_str(); }