From 99f6576a120212eb3b5f5b18fed52e6a9f4d5f09 Mon Sep 17 00:00:00 2001 From: superp00t Date: Sat, 15 Mar 2025 22:25:07 -0400 Subject: [PATCH] refactor(bc): improve consistency (across platforms esp.) and readability of string conversion --- bc/CMakeLists.txt | 23 +-- bc/String.cpp | 281 ---------------------------------- bc/String.hpp | 72 ++------- bc/Unicode.cpp | 239 +++++++++++++++++++++++++++++ bc/Unicode.hpp | 20 +++ bc/os/CommandLine.cpp | 100 +++++++----- bc/os/CommandLine.hpp | 4 +- bc/os/File.cpp | 166 -------------------- bc/os/Path.cpp | 55 ++++--- bc/os/Path.hpp | 6 +- bc/string/Append.cpp | 31 ++++ bc/string/Append.hpp | 14 ++ bc/string/Copy.cpp | 42 +++++ bc/string/Copy.hpp | 14 ++ bc/string/Defines.hpp | 13 ++ bc/string/Equal.cpp | 23 +++ bc/string/Equal.hpp | 16 ++ bc/string/Find.cpp | 39 +++++ bc/string/Find.hpp | 16 ++ bc/string/Format.cpp | 60 ++++++++ bc/string/Format.hpp | 17 ++ bc/string/Length.cpp | 10 ++ bc/string/Length.hpp | 15 ++ bc/string/Memory.cpp | 22 +++ bc/string/Memory.hpp | 18 +++ bc/string/Path.cpp | 192 +++++++++++++++++++++++ bc/string/Path.hpp | 30 ++++ bc/string/QuickFormat.hpp | 32 ++++ bc/string/QuickNativePath.hpp | 46 ++++++ bc/string/Translate.cpp | 49 ++++++ bc/string/Translate.hpp | 14 ++ bc/system/System_Time.cpp | 2 +- bc/time/Time.cpp | 41 ++--- bc/time/Time.hpp | 4 +- bc/time/TimeConst.hpp | 4 +- 35 files changed, 1106 insertions(+), 624 deletions(-) delete mode 100644 bc/String.cpp create mode 100644 bc/Unicode.cpp create mode 100644 bc/Unicode.hpp delete mode 100644 bc/os/File.cpp create mode 100644 bc/string/Append.cpp create mode 100644 bc/string/Append.hpp create mode 100644 bc/string/Copy.cpp create mode 100644 bc/string/Copy.hpp create mode 100644 bc/string/Defines.hpp create mode 100644 bc/string/Equal.cpp create mode 100644 bc/string/Equal.hpp create mode 100644 bc/string/Find.cpp create mode 100644 bc/string/Find.hpp create mode 100644 bc/string/Format.cpp create mode 100644 bc/string/Format.hpp create mode 100644 bc/string/Length.cpp create mode 100644 bc/string/Length.hpp create mode 100644 bc/string/Memory.cpp create mode 100644 bc/string/Memory.hpp create mode 100644 bc/string/Path.cpp create mode 100644 bc/string/Path.hpp create mode 100644 bc/string/QuickFormat.hpp create mode 100644 bc/string/QuickNativePath.hpp create mode 100644 bc/string/Translate.cpp create mode 100644 bc/string/Translate.hpp diff --git a/bc/CMakeLists.txt b/bc/CMakeLists.txt index 09c1dc0..3900b22 100644 --- a/bc/CMakeLists.txt +++ b/bc/CMakeLists.txt @@ -1,25 +1,18 @@ file(GLOB BC_SOURCES "*.cpp" - "memory/*.cpp" - "lock/*.cpp" - "os/*.cpp" + "string/*.cpp" "file/*.cpp" - "file/path/*.cpp" - "time/*.cpp" + "lock/*.cpp" + "memory/*.cpp" + "os/*.cpp" + "os/file/*.cpp" "system/*.cpp" "system/file/*.cpp" + "system/file/posix/*.cpp" + "system/file/win/*.cpp" + "time/*.cpp" ) -if(DEFINED WHOA_SYSTEM_WIN) - file(GLOB BC_FILE_SYSTEM_SOURCES "system/file/win/*.cpp") -endif() - -if(DEFINED WHOA_SYSTEM_LINUX OR WHOA_SYSTEM_MAC) - file(GLOB BC_FILE_SYSTEM_SOURCES "system/file/posix/*.cpp") -endif() - -list(APPEND BC_SOURCES ${BC_FILE_SYSTEM_SOURCES}) - add_library(bc STATIC ${BC_SOURCES} ) diff --git a/bc/String.cpp b/bc/String.cpp deleted file mode 100644 index 2efab25..0000000 --- a/bc/String.cpp +++ /dev/null @@ -1,281 +0,0 @@ -#include "bc/String.hpp" -#include "bc/Debug.hpp" -#include -#include -#include - -namespace Blizzard { -namespace String { - -int32_t Append(char* buf, const char* appended, size_t cap) { - const char* dx = nullptr; - int bytesAppended = 0; - char* outBytes = nullptr; - char* ceiling = nullptr; - char inByte = '\0'; - - if (!cap || !buf) { - return 0; - } - - ceiling = buf + (cap - 1); - - if (buf > ceiling) { - return 0; - } - - inByte = *buf; - outBytes = buf; - - while (inByte != '\0') { - outBytes = outBytes + 1; - if (ceiling < outBytes) { - return outBytes - buf; - } - inByte = *outBytes; - } - - if ((outBytes <= ceiling) && appended != nullptr) { - if (outBytes < ceiling) { - inByte = *appended; - - while (inByte != '\0') { - *outBytes = inByte; - outBytes = outBytes + 1; - if (ceiling <= outBytes) { - break; - } - appended = appended + 1; - inByte = *appended; - } - } - *outBytes = '\0'; - } - - return int32_t(reinterpret_cast(outBytes) - reinterpret_cast(buf)); -} - -int32_t Copy(char* dst, const char* src, size_t len) { - if (!len || !dst) { - return 0; - } - - if (!src) { - *dst = 0; - return 0; - } - - char* v3 = dst + len - 1; - - char v4; - const char* v5; - char* v6; - - int32_t result; - - if (dst < v3 && (v4 = *src, v5 = src, v6 = dst, *src)) { - do { - *v6++ = v4; - - if (v3 <= v6) { - break; - } - - v4 = (v5++)[1]; - } while (v4); - - result = v6 - dst; - } else { - v6 = dst; - result = 0; - } - - *v6 = 0; - - return result; -} - -// Find first occurence of char ch within string str -// returns ptr to first occurence, or null if it is not found -char* Find(char* str, char ch, size_t len) { - // Check if the string is null or empty. - if (str == nullptr || len == 0) { - return nullptr; - } - - auto ptr = str; - auto end = str + len; - - // Loop through the string, looking for the character. - while (ptr < end) { - if (*ptr == '\0' && ch != '\0') { - return nullptr; - } - - if (*ptr == ch) { - // Return the address of the first occurrence. - return ptr; - } - - // Increment the pointer. - ptr++; - } - - // The character was not found. - return nullptr; -} - -const char* FindFilename(const char* str) { - char ch = 0; - const char* result = nullptr; - auto ptr = str; - - if (str == nullptr) { - return ""; - } - - do { - do { - result = ptr; - ch = *str; - str = str + 1; - ptr = str; - } while (ch == '/'); - } while ((ch == '\\') || (ptr = result, ch != '\0')); - return result; -} - -// Format a string into char* dest, not exceeding uint32_t length. -void Format(char* dst, size_t capacity, const char* format, ...) { - if (!dst || capacity == 0) { - return; - } - - if (!format && capacity >= 1) { - *dst = '\0'; - return; - } - - auto formatNative = format; - -#if defined(WHOA_SYSTEM_MAC) || defined(WHOA_SYSTEM_LINUX) - constexpr size_t translatedSize = 2048; - - // POSIX formatting convention requires %ll for 64-bit printing - // Search-and-replace all instances of %I64 with %ll - char translated[translatedSize] = {0}; - - Translate(format, translated, translatedSize, "%I64", "%ll"); - - formatNative = translated; -#endif - va_list args; - va_start(args, format); - - vsnprintf(dst, capacity, formatNative, args); - - *dst = '\0'; -} - -uint32_t Length(const char* str) { - if (str) { - return strlen(str); - } - - return 0; -} - -int32_t MemCompare(void* p1, void *p2, size_t len) { - return memcmp(p1, p2, len); -} - -void MemCopy(void* dst, const void* src, size_t len) { - memmove(dst, src, len); -} - -void MemFill(void* dst, uint32_t len, uint8_t fill) { - memset(dst, fill, len); -} - -void Translate(const char* src, char* dest, size_t destSize, const char* pattern, const char* replacement) { - if (dest == nullptr || pattern == nullptr || replacement == nullptr) { - return; - } - - if (src == nullptr) { - src = dest; - } - - // Cap-1 because we need to always affix a null character. - auto destCeiling = dest + destSize - 1; - auto srcCeiling = src + Length(src) + 1; - - auto patternLen = Length(pattern); - auto replacementLen = Length(replacement); - - // Current read pointer - auto srcPtr = src; - // Current write pointer - auto destPtr = dest; - - // Process string byte by byte - // Until dest ceiling is reached - while (destPtr < destCeiling && srcPtr < srcCeiling) { - auto byte = *srcPtr; - - if (byte == '\0') { - break; - } - - // If this byte is found at the start of pattern - if (byte == *pattern) { - // Calculate size of replacement bytes to copy - // Read size must not read outside of bounds - size_t copySize = replacementLen; - if ((destPtr + copySize) >= destCeiling) { - copySize = destCeiling - destPtr; - } - - // Look for the rest of the pattern - auto substring = strstr(srcPtr, pattern); - // If the read pointer is indeed pointing to an instance of the pattern, - if (substring == srcPtr) { - // Copy replacement data instead of original pattern. - MemCopy(destPtr, replacement, copySize); - destPtr += replacementLen; - srcPtr += patternLen; - continue; - } - } - - // copy a single byte - *destPtr = *srcPtr; - srcPtr++; - destPtr++; - } - - *destPtr = '\0'; -} - -void VFormat(char* dst, size_t capacity, const char* format, va_list args) { - char buffer[0x800] = {0}; - - auto formatNative = format; - -#if !defined(WHOA_SYSTEM_WIN) - Translate(format, buffer, 0x800, "%I64", "%ll"); - formatNative = buffer; -#endif - - if (format == nullptr) { - if (capacity != 0) { - *dst = '\0'; - } - } else { - vsnprintf(dst, capacity, formatNative, args); - dst[capacity - 1] = '\0'; - } -} - -} // namespace String -} // namespace Blizzard diff --git a/bc/String.hpp b/bc/String.hpp index 865dea9..ad8e0cd 100644 --- a/bc/String.hpp +++ b/bc/String.hpp @@ -1,66 +1,16 @@ #ifndef BC_STRING_HPP #define BC_STRING_HPP -#include -#include -#include - -#if defined(WHOA_SYSTEM_WIN) -#define BC_FILE_SYSTEM_PATH_SEPARATOR '\\' -#else -#define BC_FILE_SYSTEM_PATH_SEPARATOR '/' -#endif - -namespace Blizzard { -namespace String { - -// Types - -template -class QuickFormat { - public: - char buffer[Cap]; - - QuickFormat(const char* format, ...); - const char* Str(); -}; - -// Functions -int32_t Append(char* dst, const char* src, size_t cap); - -int32_t Copy(char* dst, const char* src, size_t len); - -char* Find(char* str, char ch, size_t len); - -const char* FindFilename(const char* str); - -void Format(char* dest, size_t capacity, const char* format, ...); - -uint32_t Length(const char* str); - -void MemFill(void* dst, uint32_t len, uint8_t fill); - -void MemCopy(void* dst, const void* src, size_t len); - -int32_t MemCompare(void* p1, void *p2, size_t len); - -void Translate(const char* src, char* dest, size_t destSize, const char* pattern, const char* replacement); - -void VFormat(char* dst, size_t capacity, const char* format, va_list args); - -template -QuickFormat::QuickFormat(const char* format, ...) { - va_list args; - va_start(args, format); - VFormat(this->buffer, Cap, format, args); -} - -template -const char* QuickFormat::Str() { - return static_cast(this->buffer); -} - -} // namespace String -} // namespace Blizzard +#include "bc/string/Append.hpp" +#include "bc/string/Copy.hpp" +#include "bc/string/Defines.hpp" +#include "bc/string/Find.hpp" +#include "bc/string/Format.hpp" +#include "bc/string/Length.hpp" +#include "bc/string/Memory.hpp" +#include "bc/string/Path.hpp" +#include "bc/string/QuickFormat.hpp" +#include "bc/string/QuickNativePath.hpp" +#include "bc/string/Translate.hpp" #endif diff --git a/bc/Unicode.cpp b/bc/Unicode.cpp new file mode 100644 index 0000000..09023bd --- /dev/null +++ b/bc/Unicode.cpp @@ -0,0 +1,239 @@ +#include "bc/Unicode.hpp" +#include "bc/Debug.hpp" +#include +#include + +namespace Blizzard { +namespace Unicode { + +static const uint32_t offsetsFromUTF8[] = { + 0x0, + 0x0, + 0x3080, + 0xE2080, + 0x3C82080, + 0xFA082080, + 0x82082080 +}; + +static const uint32_t firstByteMark[] = { + 0x0, + 0x0, + 0xC0, + 0xE0, + 0xF0, + 0xF8, + 0xFC +}; + +static const uint8_t bytesFromUTF8[] = { + 0x00, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, + 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, + 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, + 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, + 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, + 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, + 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, + 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, + 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, + 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, + 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, + 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, + 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, + 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, + 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, + 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, + 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, + 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, + 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, + 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, + 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, + 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, + 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, + 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, + 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, + 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, + 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, + 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, + 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, + 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, + 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, + 0x05, 0x05, 0x05, 0x05, 0x06, 0x06, 0x06, 0x06 +}; + +int32_t ConvertUTF16to8(uint8_t* dst, uint32_t dstmaxchars, const uint16_t* src, uint32_t srcmaxchars, uint32_t* dstchars, uint32_t* srcchars) { + auto srcend = srcmaxchars & 0x80000000 ? reinterpret_cast(0xFFFFFFFF) : &src[srcmaxchars]; + auto dstend = &dst[dstmaxchars]; + auto dststart = dst; + auto srcstart = src; + + int32_t result; + + while (src < srcend) { + uint32_t widechars = 1; + auto grapheme = static_cast(src[0]); + if (0xD7FF < grapheme && grapheme < 0xDC00) { + if (src + 1 >= srcend) { + goto fail; + } + auto char2 = static_cast(src[1]); + if (0xDBFF < char2 && char2 < 0xE000) { + grapheme = ((grapheme - 0xD7F7) * 1024) + char2; + widechars = 2; + } + } + + uint32_t chars; + + if (grapheme < 0x80) { + chars = 1; + if (grapheme == 0) { + if (dst < dstend) { + *dst++ = '\0'; + result = 0; + } + goto done; + } + } else if (grapheme < 0x800) { + chars = 2; + } else if (grapheme < 0x10000) { + chars = 3; + } else if (grapheme < 0x200000) { + chars = 4; + } else if (grapheme < 0x4000000) { + chars = 5; + } else if (grapheme > 0x7FFFFFFF) { + grapheme = 0xFFFD; + chars = 2; + } else { + chars = 6; + } + + result = chars; + + dst += chars; + + if (dst > dstend) { + goto done; + } + + switch (chars) { + case 6: + *--dst = (grapheme & 0x3F) | 0x80; + grapheme >>= 6; + case 5: + *--dst = (grapheme & 0x3F) | 0x80; + grapheme >>= 6; + case 4: + *--dst = (grapheme & 0x3F) | 0x80; + grapheme >>= 6; + case 3: + *--dst = (grapheme & 0x3F) | 0x80; + grapheme >>= 6; + case 2: + *--dst = (grapheme & 0x3F) | 0x80; + grapheme >>= 6; + case 1: + *--dst = grapheme | firstByteMark[chars]; + } + + src += widechars; + dst += chars; + } + +fail: + result = -1; + +done: + if (srcchars) { + *srcchars = src - srcstart; + } + + if (dstchars) { + *dstchars = dst - dststart; + } + return result; +} + +int32_t ConvertUTF8to16(uint16_t* dst, uint32_t dstmaxchars, const uint8_t* src, uint32_t srcmaxchars, uint32_t* dstchars, uint32_t* srcchars) { + auto srcend = srcmaxchars & 0x80000000 ? std::numeric_limits::max() : src + srcmaxchars; + auto dstend = dst + dstmaxchars; + auto dststart = dst; + auto srcstart = src; + + int32_t result = 0; + + while (src < srcend) { + auto bytes = bytesFromUTF8[*src]; + if ((src + bytes) > srcend) { + result = -bytes; + goto done; + } + + uint32_t grapheme = 0; + + switch (bytes) { + case 6: + grapheme = *src++ << 6; + case 5: + grapheme = (grapheme + *src++) << 6; + case 4: + grapheme = (grapheme + *src++) << 6; + case 3: + grapheme = (grapheme + *src++) << 6; + case 2: + grapheme = (grapheme + *src++) << 6; + case 1: + grapheme = (grapheme + *src++) - offsetsFromUTF8[bytes]; + } + + if (dst >= dstend) { + result = 1; + goto done; + } + + if (grapheme < 0x10000) { + *dst++ = static_cast(grapheme); + if (grapheme == 0x0) { + goto done; + } + } else if (grapheme < 0x110000) { + if (dst >= dstend) { + result = 1; + goto done; + } + auto v16 = grapheme - 0x10000; + *dst++ = static_cast(v16 >> 10) - 0x2800; + *dst++ = static_cast(v16 & 0x3FF) - 0x2400; + } else { + *dst++ = 0xFFFD; + } + } + + result = -1; + +done: + if (srcchars) { + *srcchars = src - srcstart; + } + + if (dstchars) { + *dstchars = dst - dststart; + } + return result; +} + +int32_t ConvertUTF8to16Len(const uint8_t* src, uint32_t srcmaxchars, uint32_t* srcchars) { + // TODO + BLIZZARD_ASSERT(0); + return -1; +} + +int32_t GetCodepointFromUTF8(const uint8_t** c) { + // TODO + BLIZZARD_ASSERT(0); + return -1; +} + +} // namespace Unicode +} // namespace Blizzard diff --git a/bc/Unicode.hpp b/bc/Unicode.hpp new file mode 100644 index 0000000..999adfa --- /dev/null +++ b/bc/Unicode.hpp @@ -0,0 +1,20 @@ +#ifndef BC_UNICODE_HPP +#define BC_UNICODE_HPP + +#include + +namespace Blizzard { +namespace Unicode { + +int32_t ConvertUTF16to8(uint8_t* dst, uint32_t dstmaxchars, const uint16_t* src, uint32_t srcmaxchars, uint32_t* dstchars, uint32_t* srchars); + +int32_t ConvertUTF8to16(uint16_t* dst, uint32_t dstmaxchars, const uint8_t* src, uint32_t srcmaxchars, uint32_t* dstchars, uint32_t* srcchars); + +int32_t ConvertUTF8to16Len(const uint8_t* src, uint32_t srcmaxchars, uint32_t* srcchars); + +int32_t GetCodepointFromUTF8(const uint8_t** c); + +} // namespace Unicode +} // namespace Blizzard + +#endif diff --git a/bc/os/CommandLine.cpp b/bc/os/CommandLine.cpp index b534396..8ce0bd7 100644 --- a/bc/os/CommandLine.cpp +++ b/bc/os/CommandLine.cpp @@ -1,7 +1,6 @@ #include "bc/os/CommandLine.hpp" -#include "bc/Debug.hpp" +#include "bc/string/Append.hpp" -#include #include #if defined(WHOA_SYSTEM_WIN) @@ -10,10 +9,66 @@ // Variables -char commandline[1024] = {0}; +#if defined(WHOA_SYSTEM_MAC) || defined(WHOA_SYSTEM_LINUX) +char commandline[65535]; +#endif // Functions +// CUSTOM: appends a command-line argument to the buffer, +void append_commandline(char* buffer, int32_t buffersize, const char* argument) { + bool escape = false; + if (*buffer) { + Blizzard::String::Append(buffer, " ", buffersize); + + auto a = argument; + while (*a++) { + if (*a == ' ' || *a == '"') { + escape = true; + break; + } + } + } else { + // first argument is always quoted + escape = true; + } + + if (escape) { + auto a = argument; + + auto dst = buffer; + auto dstend = buffer + buffersize - 1; + while (dst < dstend && *dst) { + dst++; + } + + if (dst < dstend) { + *dst++ = '"'; + } + + while (dst < dstend && *a) { + if (*a == '"') { + if (dst+1 == dstend) { + break; + } + *dst++ = '\\'; + *dst++ = '"'; + } else { + *dst++ = *a; + } + a++; + } + + if (dst < dstend) { + *dst++ = '"'; + } + + *dst = '\0'; + } else { + Blizzard::String::Append(buffer, argument, buffersize); + } +} + const char* OsGetCommandLine() { #if defined(WHOA_SYSTEM_WIN) return GetCommandLine(); @@ -24,40 +79,11 @@ const char* OsGetCommandLine() { #endif } -std::string QuoteArgument(std::string argument) { - std::string result = ""; - - result += "\""; - result += argument; - result += "\""; - - return result; -} - -std::string CheckArgument(std::string argument) { - for (size_t i = 0; i < argument.length(); i++) { - switch (argument.at(i)) { - case '\"': - case ' ': - return QuoteArgument(argument); - } - } - - return argument; -} - -void OsSetCommandLine(int32_t argc, char** argv) { - int32_t i = 0; - std::string result = ""; - +void OsSetCommandLine(int argc, char* argv[]) { +#if defined(WHOA_SYSTEM_MAC) || defined(WHOA_SYSTEM_LINUX) + int i = 0; while (i < argc) { - if (i > 0) { - result += " "; - } - - result += CheckArgument(argv[i]); - i++; + append_commandline(commandline, sizeof(commandline), argv[i]); } - - strncpy(commandline, result.c_str(), sizeof(commandline)); +#endif } diff --git a/bc/os/CommandLine.hpp b/bc/os/CommandLine.hpp index 53ccd3a..d4bd24a 100644 --- a/bc/os/CommandLine.hpp +++ b/bc/os/CommandLine.hpp @@ -1,10 +1,8 @@ #ifndef BC_OS_COMMAND_LINE_HPP #define BC_OS_COMMAND_LINE_HPP -#include - const char* OsGetCommandLine(); -void OsSetCommandLine(int32_t argc, char** argv); +void OsSetCommandLine(int argc, char* argv[]); #endif diff --git a/bc/os/File.cpp b/bc/os/File.cpp deleted file mode 100644 index 215cf35..0000000 --- a/bc/os/File.cpp +++ /dev/null @@ -1,166 +0,0 @@ -#include "bc/os/File.hpp" -#include "bc/file/Defines.hpp" -#include "bc/file/File.hpp" -#include "bc/Debug.hpp" - -HOSFILE OsCreateFile(const char* fileName, uint32_t desiredAccess, uint32_t shareMode, uint32_t createDisposition, uint32_t flagsAndAttributes, uint32_t extendedFileType) { - // Ensure sanity - BLIZZARD_VALIDATE(fileName, "invalid filename", HOSFILE_INVALID); - BLIZZARD_VALIDATE(desiredAccess != 0, "invalid desired access", HOSFILE_INVALID); - BLIZZARD_VALIDATE(createDisposition <= OS_TRUNCATE_EXISTING, "invalid create disposition", HOSFILE_INVALID); - - uint32_t flags = 0; - - // Read/write flags - if (desiredAccess & OS_GENERIC_READ) { - flags |= BC_FILE_OPEN_READ; - } - if (desiredAccess & OS_GENERIC_WRITE) { - flags |= BC_FILE_OPEN_WRITE; - } - - // Allow other users to access the file in read and/or write mode - if (shareMode & OS_FILE_SHARE_READ) { - flags |= BC_FILE_OPEN_SHARE_READ; - } - if (shareMode & OS_FILE_SHARE_WRITE) { - flags |= BC_FILE_OPEN_SHARE_WRITE; - } - - // Convert createDisposition into BC flags - switch (createDisposition) { - case OS_CREATE_NEW: - // flags |= 0xC00; - flags |= BC_FILE_OPEN_CREATE | BC_FILE_OPEN_MUST_NOT_EXIST; - break; - case OS_CREATE_ALWAYS: - // flags |= 0x400; - flags |= BC_FILE_OPEN_CREATE; - break; - case OS_OPEN_EXISTING: - // flags |= 0x1000 - flags |= BC_FILE_OPEN_MUST_EXIST; - break; - case OS_OPEN_ALWAYS: - // flags |= 0x200; - flags |= BC_FILE_OPEN_ALWAYS; - break; - case OS_TRUNCATE_EXISTING: - // flags |= 0x100; - flags |= BC_FILE_OPEN_TRUNCATE; - break; - } - - // Open file - Blizzard::File::StreamRecord* stream; - bool success = Blizzard::File::Open(fileName, flags, stream); - if (!success) { - return HOSFILE_INVALID; - } - - // Set attributes - OsSetFileAttributes(fileName, flagsAndAttributes); - return stream; -} - -int32_t OsSetFileAttributes(const char* fileName, uint32_t attributes) { - BLIZZARD_ASSERT(fileName); - - // Translate OS file attribute bits into BlizzardCore attribute bits. - uint32_t flags = 0; - - if (attributes & OS_FILE_ATTRIBUTE_READONLY) { - // flags |= 1; - flags |= BC_FILE_ATTRIBUTE_READONLY; - } - if (attributes & OS_FILE_ATTRIBUTE_HIDDEN) { - // flags |= 2; - flags |= BC_FILE_ATTRIBUTE_HIDDEN; - } - if (attributes & OS_FILE_ATTRIBUTE_SYSTEM) { - // flags |= 4 - flags |= BC_FILE_ATTRIBUTE_SYSTEM; - } - if (attributes & OS_FILE_ATTRIBUTE_ARCHIVE) { - // flags |= 8; - flags |= BC_FILE_ATTRIBUTE_ARCHIVE; - } - if (attributes & OS_FILE_ATTRIBUTE_TEMPORARY) { - // flags |= 0x10; - flags |= BC_FILE_ATTRIBUTE_TEMPORARY; - } - if (attributes & OS_FILE_ATTRIBUTE_NORMAL) { - // flags |= 0x20; - flags |= BC_FILE_ATTRIBUTE_NORMAL; - } - if (attributes & OS_FILE_ATTRIBUTE_DIRECTORY) { - // flags |= 0x40; - flags |= BC_FILE_ATTRIBUTE_DIRECTORY; - } - - return Blizzard::File::SetAttributes(fileName, flags); -} - -uint64_t OsGetFileSize(HOSFILE fileHandle) { - auto info = Blizzard::File::GetFileInfo(reinterpret_cast(fileHandle)); - return info->size; -} - -int32_t OsReadFile(HOSFILE fileHandle, void* buffer, size_t bytesToRead, size_t* bytesRead) { - BLIZZARD_ASSERT(buffer); - BLIZZARD_ASSERT(bytesToRead); - - return Blizzard::File::Read(fileHandle, buffer, bytesToRead, bytesRead); -} - -int32_t OsWriteFile(HOSFILE fileHandle, void* buffer, size_t bytesToWrite, size_t* bytesWritten) { - if (buffer != nullptr && bytesToWrite != 0) { - return Blizzard::File::Write(fileHandle, buffer, bytesToWrite, bytesWritten); - } - - return 0; -} - -int64_t OsSetFilePointer(HOSFILE fileHandle, int64_t distanceToMove, uint32_t moveMethod) { - BLIZZARD_ASSERT(moveMethod <= 2); - - int64_t position; - - if (moveMethod != 0 && !Blizzard::File::GetPos(fileHandle, position)) { - return -1; - } - - uint32_t seeks[3] = { - BC_FILE_SEEK_START, - BC_FILE_SEEK_CURRENT, - BC_FILE_SEEK_END - }; - - if (!Blizzard::File::SetPos(fileHandle, position, seeks[moveMethod])) { - return -1; - } - - return position + distanceToMove; -} - -void OsCloseFile(HOSFILE fileHandle) { - Blizzard::File::Close(static_cast(fileHandle)); -} - -int32_t OsSetCurrentDirectory(const char* pathName) { - BLIZZARD_ASSERT(pathName); - - return Blizzard::File::SetWorkingDirectory(pathName); -} - -int32_t OsGetCurrentDirectory(size_t pathLen, char* pathName) { - BLIZZARD_ASSERT(pathName); - - return Blizzard::File::GetWorkingDirectory(pathName, pathLen); -} - -int32_t OsCreateDirectory(const char* pathName, int32_t recursive) { - BLIZZARD_ASSERT(pathName); - - return Blizzard::File::CreateDirectory(pathName, recursive == 1); -} diff --git a/bc/os/Path.cpp b/bc/os/Path.cpp index b75812b..7e8ecb5 100644 --- a/bc/os/Path.cpp +++ b/bc/os/Path.cpp @@ -1,6 +1,5 @@ #include "bc/os/Path.hpp" #include "bc/String.hpp" -#include "bc/file/Path.hpp" // Win32 #if defined(WHOA_SYSTEM_WIN) @@ -12,54 +11,54 @@ #include #endif -// procfs #if defined(WHOA_SYSTEM_LINUX) #include #endif // Get the full path of the currently running .exe/ELF/Mach-O executable -void OsGetExeName(char* buffer, size_t chars) { +void OsGetExeName(char* buffer, uint32_t buffersize) { #if defined(WHOA_SYSTEM_WIN) - // Win32 - GetModuleFileName(nullptr, buffer, chars); + ::GetModuleFileName(nullptr, buffer, buffersize); #endif #if defined(WHOA_SYSTEM_MAC) - // Darwin - char path[1024] = {0}; - uint32_t bufsize = 1024; - _NSGetExecutablePath(path, &bufsize); + char executablepathbuffer[4096]; + uint32_t executablepathbuffersize = sizeof(executablepathbuffer); + ::_NSGetExecutablePath(executable, &executablepathbuffersize); - char actualPath[1024] = {0}; - realpath(path, actualPath); + char realpathbuffer[4096]; + ::realpath(executablepathbuffer, realpathbuffer); - Blizzard::File::Path::MakeBackslashPath(actualPath, buffer, chars); + Blizzard::String::Copy(buffer, realpathbuffer, buffersize); #endif #if defined(WHOA_SYSTEM_LINUX) // procfs - char actualPath[4096] = {0}; - readlink("/proc/self/exe", actualPath, chars); - Blizzard::File::Path::MakeBackslashPath(actualPath, buffer, chars); + if (::readlink("/proc/self/exe", buffer, buffersize) == -1) { + *buffer = '\0'; + } #endif } -void OsPathStripFilename(char* path) { - auto length = Blizzard::String::Length(path); - - char* head = &path[length-1]; - - while ((head != (path-1)) && (*head != '\0')) { - if (*head == '\\') { - *(head + 1) = '\0'; - break; +void OsPathStripFilename(char* name) { + auto n = name; + while (*n) { + n++; + } + if (n > name) { + while (n > name) { + if (*n == '/' || *n == '\\') { + break; + } + n--; } - head--; + + *n = '\0'; } } // Get the directory containing the currently running executable -void OsGetExePath(char* dest, size_t chars) { - OsGetExeName(dest, chars); - OsPathStripFilename(dest); +void OsGetExePath(char* buffer, uint32_t buffersize) { + OsGetExeName(buffer, buffersize); + OsPathStripFilename(buffer); } diff --git a/bc/os/Path.hpp b/bc/os/Path.hpp index 9ffebbd..ba214d3 100644 --- a/bc/os/Path.hpp +++ b/bc/os/Path.hpp @@ -1,10 +1,10 @@ #ifndef BC_OS_PATH_HPP #define BC_OS_PATH_HPP -#include +#include -void OsGetExePath(char* buffer, size_t chars); +void OsGetExePath(char* buffer, uint32_t buffersize); -void OsGetExeName(char* buffer, size_t chars); +void OsGetExeName(char* buffer, uint32_t buffersize); #endif diff --git a/bc/string/Append.cpp b/bc/string/Append.cpp new file mode 100644 index 0000000..1f61bcf --- /dev/null +++ b/bc/string/Append.cpp @@ -0,0 +1,31 @@ +#include "bc/string/Append.hpp" +#include + +namespace Blizzard { +namespace String { + +int32_t Append(char* dst, const char* src, uint32_t count) { + if (count == 0 || dst == nullptr) { + return 0; + } + + auto dstend = dst + count; + while (dst < dstend && *dst) { + dst++; + } + if (dstend <= dst) { + return 0; + } + + auto dststart = dst; + + while (dst < dstend && *src) { + *dst++ = *src++; + } + *dst = '\0'; + + return dst - dststart; +} + +} // namespace String +} // namespace Blizzard diff --git a/bc/string/Append.hpp b/bc/string/Append.hpp new file mode 100644 index 0000000..19e4895 --- /dev/null +++ b/bc/string/Append.hpp @@ -0,0 +1,14 @@ +#ifndef BC_STRING_APPEND_HPP +#define BC_STRING_APPEND_HPP + +#include + +namespace Blizzard { +namespace String { + +int32_t Append(char* dst, const char* src, uint32_t count); + +} // namespace String +} // namespace Blizzard + +#endif diff --git a/bc/string/Copy.cpp b/bc/string/Copy.cpp new file mode 100644 index 0000000..8a8c36c --- /dev/null +++ b/bc/string/Copy.cpp @@ -0,0 +1,42 @@ +#include "bc/string/Copy.hpp" +#include + +int32_t Blizzard::String::Copy(char* dst, const char* src, uint32_t count) { + if (!count || !dst) { + return 0; + } + + if (!src) { + *dst = 0; + return 0; + } + + char* v3 = dst + count - 1; + + char v4; + const char* v5; + char* v6; + + int32_t result; + + if (dst < v3 && (v4 = *src, v5 = src, v6 = dst, *src)) { + do { + *v6++ = v4; + + if (v3 <= v6) { + break; + } + + v4 = (v5++)[1]; + } while (v4); + + result = v6 - dst; + } else { + v6 = dst; + result = 0; + } + + *v6 = 0; + + return result; +} diff --git a/bc/string/Copy.hpp b/bc/string/Copy.hpp new file mode 100644 index 0000000..aa57014 --- /dev/null +++ b/bc/string/Copy.hpp @@ -0,0 +1,14 @@ +#ifndef BC_STRING_COPY_HPP +#define BC_STRING_COPY_HPP + +#include + +namespace Blizzard { +namespace String { + +int32_t Copy(char* dst, const char* src, uint32_t count); + +} // namespace String +} // namespace Blizzard + +#endif diff --git a/bc/string/Defines.hpp b/bc/string/Defines.hpp new file mode 100644 index 0000000..aa7f420 --- /dev/null +++ b/bc/string/Defines.hpp @@ -0,0 +1,13 @@ +#ifndef BC_STRING_DEFINES_HPP +#define BC_STRING_DEFINES_HPP + +// How many bytes to when translating stacked filesystem names to large, native file paths +#define BC_STRING_MAX_PATH 1024 + +#if defined(WHOA_SYSTEM_WIN) +#define BC_STRING_PATH_SEPARATOR '\\' +#else +#define BC_STRING_PATH_SEPARATOR '/' +#endif + +#endif diff --git a/bc/string/Equal.cpp b/bc/string/Equal.cpp new file mode 100644 index 0000000..84e998f --- /dev/null +++ b/bc/string/Equal.cpp @@ -0,0 +1,23 @@ +#include "bc/string/Equal.hpp" +#include + +namespace Blizzard { +namespace String { + +bool Equal(const char* a, const char* b) { + if (a && b) { + return strcmp(a, b); + } + return false; +} + +bool EqualI(const char* a, const char* b, uint32_t count) { + if (a && b) { + return strncasecmp(a, b, count); + } + return false; +} + +} // namespace String +} // namespace Blizzard + diff --git a/bc/string/Equal.hpp b/bc/string/Equal.hpp new file mode 100644 index 0000000..0f4a309 --- /dev/null +++ b/bc/string/Equal.hpp @@ -0,0 +1,16 @@ +#ifndef BC_STRING_EQUAL_HPP +#define BC_STRING_EQUAL_HPP + +#include + +namespace Blizzard { +namespace String { + +bool Equal(const char* a, const char* b); + +bool EqualI(const char* a, const char* b, uint32_t count); + +} // namespace String +} // namespace Blizzard + +#endif diff --git a/bc/string/Find.cpp b/bc/string/Find.cpp new file mode 100644 index 0000000..af6cd0a --- /dev/null +++ b/bc/string/Find.cpp @@ -0,0 +1,39 @@ +#include "bc/string/Find.hpp" + +char* Blizzard::String::Find(char* str, char ch, int32_t count) { + if (!str || !ch) { + return nullptr; + } + + auto ptr = str; + auto end = str + count; + + while (ptr != end && *ptr) { + if (ch == *ptr) { + return ptr; + } + + ptr++; + } + + return nullptr; +} + +const char* Find(const char* str, char ch, int32_t count) { + if (!str || !ch) { + return nullptr; + } + + auto ptr = str; + auto end = str + count; + + while (ptr != end && *ptr) { + if (ch == *ptr) { + return ptr; + } + + ptr++; + } + + return nullptr; +} diff --git a/bc/string/Find.hpp b/bc/string/Find.hpp new file mode 100644 index 0000000..d51eca2 --- /dev/null +++ b/bc/string/Find.hpp @@ -0,0 +1,16 @@ +#ifndef BC_STRING_FIND_HPP +#define BC_STRING_FIND_HPP + +#include + +namespace Blizzard { +namespace String { + +char* Find(char* str, char ch, int32_t count); + +const char* Find(const char* str, char ch, int32_t count); + +} // namespace String +} // namespace Blizzard + +#endif diff --git a/bc/string/Format.cpp b/bc/string/Format.cpp new file mode 100644 index 0000000..6409af5 --- /dev/null +++ b/bc/string/Format.cpp @@ -0,0 +1,60 @@ + +#include "bc/string/Format.hpp" +#include "bc/string/Translate.hpp" +#include +#include + +namespace Blizzard { +namespace String { + +void Format(char* buffer, uint32_t buffersize, const char* format, ...) { + if (!buffer || buffersize == 0) { + return; + } + + if (!format && buffersize >= 1) { + *buffer = '\0'; + return; + } + + auto formatNative = format; + +#if defined(WHOA_SYSTEM_MAC) || defined(WHOA_SYSTEM_LINUX) + constexpr size_t translatedSize = 2048; + + // POSIX formatting convention requires %ll for 64-bit printing + // find & replace all instances of %I64 with %ll + char translated[translatedSize] = {0}; + + Translate(format, translated, translatedSize, "%I64", "%ll"); + + formatNative = translated; +#endif + va_list args; + va_start(args, format); + + vsnprintf(buffer, buffersize, formatNative, args); + + *buffer = '\0'; +} + +void VFormat(char* buffer, uint32_t buffersize, const char* format, va_list args) { + auto formatNative = format; + +#if defined(WHOA_SYSTEM_MAC) || defined(WHOA_SYSTEM_LINUX) + char translatedformat[0x800]; + Translate(format, translatedformat, 0x800, "%I64", "%ll"); + formatNative = buffer; +#endif + if (format == nullptr) { + if (buffer) { + *buffer = '\0'; + } + } else { + vsnprintf(buffer, buffersize, formatNative, args); + } +} + + +} // namespace String +} // namespace Blizzard diff --git a/bc/string/Format.hpp b/bc/string/Format.hpp new file mode 100644 index 0000000..44797f6 --- /dev/null +++ b/bc/string/Format.hpp @@ -0,0 +1,17 @@ +#ifndef BC_STRING_FORMAT_HPP +#define BC_STRING_FORMAT_HPP + +#include +#include + +namespace Blizzard { +namespace String { + +void Format(char* buffer, uint32_t buffersize, const char* format, ...); + +void VFormat(char* buffer, uint32_t buffersize, const char* format, va_list args); + +} // namespace String +} // namespace Blizzard + +#endif diff --git a/bc/string/Length.cpp b/bc/string/Length.cpp new file mode 100644 index 0000000..25165d3 --- /dev/null +++ b/bc/string/Length.cpp @@ -0,0 +1,10 @@ +#include "bc/string/Length.hpp" +#include + +uint32_t Blizzard::String::Length(const char* str) { + if (str) { + return strlen(str); + } + + return 0; +} diff --git a/bc/string/Length.hpp b/bc/string/Length.hpp new file mode 100644 index 0000000..cac09a3 --- /dev/null +++ b/bc/string/Length.hpp @@ -0,0 +1,15 @@ +#ifndef BC_STRING_LENGTH_HPP +#define BC_STRING_LENGTH_HPP + +#include + +namespace Blizzard { +namespace String { + +uint32_t Length(const char* str); + +} // namespace String +} // namespace Blizzard + + +#endif diff --git a/bc/string/Memory.cpp b/bc/string/Memory.cpp new file mode 100644 index 0000000..a8dd2a7 --- /dev/null +++ b/bc/string/Memory.cpp @@ -0,0 +1,22 @@ +#include "bc/string/Memory.hpp" + +#include +#include + +namespace Blizzard { +namespace String { + +void MemFill(void* dst, int32_t count, uint8_t fill) { + memset(dst, fill, count); +} + +int32_t MemCompare(void* p1, void *p2, int32_t count) { + return memcmp(p1, p2, count); +} + +void MemCopy(void* dst, const void* src, int32_t count) { + memmove(dst, src, count); +} + +} // namespace String +} // namespace Blizzard diff --git a/bc/string/Memory.hpp b/bc/string/Memory.hpp new file mode 100644 index 0000000..b2d36de --- /dev/null +++ b/bc/string/Memory.hpp @@ -0,0 +1,18 @@ +#ifndef BC_STRING_MEMORY_HPP +#define BC_STRING_MEMORY_HPP + +#include + +namespace Blizzard { +namespace String { + +void MemFill(void* dst, int32_t count, uint8_t fill); + +int32_t MemCompare(void* p1, void *p2, int32_t count); + +void MemCopy(void* dst, const void* src, int32_t count); + +} // namespace String +} // namespace Blizzard + +#endif diff --git a/bc/string/Path.cpp b/bc/string/Path.cpp new file mode 100644 index 0000000..76a4a96 --- /dev/null +++ b/bc/string/Path.cpp @@ -0,0 +1,192 @@ +#include "bc/string/Path.hpp" +#include "bc/Debug.hpp" +#include "bc/string/Defines.hpp" +#include "bc/string/Length.hpp" +#include "bc/string/Copy.hpp" +#include "bc/string/Append.hpp" + +namespace Blizzard { +namespace String { + +static char s_empty[] = { 0 }; + +char* FindFilename(char* str) { + char ch = 0; + char* result = nullptr; + auto ptr = str; + + if (str == nullptr) { + return s_empty; + } + + do { + do { + result = ptr; + ch = *str; + ptr = ++str; + } while (ch == '/'); + } while ((ch == '\\') || (ptr = result, ch != '\0')); + return result; +} + +const char* FindFilename(const char* str) { + char ch = 0; + const char* result = nullptr; + auto ptr = str; + + if (str == nullptr) { + return s_empty; + } + + do { + do { + result = ptr; + ch = *str; + ptr = ++str; + } while (ch == '/'); + } while ((ch == '\\') || (ptr = result, ch != '\0')); + return result; +} + +char FirstPathSeparator(const char* str) { + while (*str) { + if (*str == '/' || *str == '\\') { + return *str; + } + + str++; + } + + return BC_STRING_PATH_SEPARATOR; +} + +void ForceTrailingSeparator(char* buf, int32_t bufMax, char separator) { + if (buf == nullptr) { + return; + } + + if (separator == '\0') { + separator = FirstPathSeparator(buf); + } + + // buffer needs at least two characters. + if (bufMax < 2) { + return; + } + + // slice off the end of the path buffer, + // so that any existing* trailing separator is discarded. + auto len = Length(buf); + char last = '\0'; + if (len > 0) { + last = buf[len - 1]; + } + switch (last) { + case '/': + case '\\': + len--; + break; + default: + break; + } + + // Add (back*) trailing separator + BLIZZARD_ASSERT(len <= bufMax - 2); + buf[len] = separator; + buf[len + 1] = '\0'; // pad null +} + +void JoinPath(char* dst, int32_t count, const char* str1, const char* str2) { + if (dst) { + Copy(dst, str1, count); + if (*dst) { + ForceTrailingSeparator(dst, count, 0); + } + if (str2) { + while (*str2 == '/' || *str2 == '\\') { + str2++; + } + } + Append(dst, str2, count); + MakeConsistentPath(dst, dst, count); + } +} + +char* MakeBackslashPath(const char* src, char* buffer, int32_t buffersize) { + auto dst = buffer; + if (buffer) { + auto dstend = buffer + buffersize - 1; + while (dst < dstend) { + auto c = *src++; + if (c == '\0') { + break; + } else if (c == '/') { + *dst = '\\'; + } else { + *dst = c; + } + dst++; + } + } + + *dst = '\0'; + return buffer; +} + +char* MakeUnivPath(const char* src, char* buffer, int32_t buffersize) { + auto dst = buffer; + if (buffer) { + auto dstend = buffer + buffersize - 1; + while (dst < dstend) { + auto c = *src++; + if (c == '\0') { + break; + } else if (c == '\\') { + *dst = '/'; + } else { + *dst = c; + } + dst++; + } + } + + *dst = '\0'; + return buffer; +} + +char* MakeConsistentPath(const char* src, char* buffer, int32_t buffersize) { + if (!buffer || (buffersize <= 0)) { + return nullptr; + } + + if (!src || (buffersize == 1)) { + *buffer = '\0'; + return buffer; + } + + switch (FirstPathSeparator(src)) { + case '\\': + return MakeBackslashPath(src, buffer, buffersize); + case '/': + return MakeUnivPath(src, buffer, buffersize); + } + + Copy(buffer, src, buffersize); + return buffer; +} + +char* MakeNativePath(const char* src, char* buffer, int32_t buffersize) { + if (!src) { + *buffer = '\0'; + return buffer; + } else { +#if defined(WHOA_SYSTEM_WIN) + return MakeBackslashPath(src, buffer, buffersize); +#else + return MakeUnivPath(src, buffer, buffersize); +#endif + } +} + +} // namespace String +} // namespace Blizzard diff --git a/bc/string/Path.hpp b/bc/string/Path.hpp new file mode 100644 index 0000000..c173ef1 --- /dev/null +++ b/bc/string/Path.hpp @@ -0,0 +1,30 @@ +#ifndef BC_STRING_PATH_HPP +#define BC_STRING_PATH_HPP + +#include + +namespace Blizzard { +namespace String { + +const char* FindFilename(const char* str); + +char* FindFilename(char* str); + +char FirstPathSeparator(const char* str); + +void ForceTrailingSeparator(char* buf, int32_t bufMax, char sep); + +void JoinPath(char* dst, int32_t count, const char* str1, const char* str2); + +char* MakeBackslashPath(const char* src, char* buffer, int32_t buffersize); + +char* MakeConsistentPath(const char* src, char* buffer, int32_t buffersize); + +char* MakeNativePath(const char* src, char* buffer, int32_t buffersize); + +char* MakeUnivPath(const char* src, char* buffer, int32_t buffersize); + +} // namespace String +} // namespace Blizzard + +#endif diff --git a/bc/string/QuickFormat.hpp b/bc/string/QuickFormat.hpp new file mode 100644 index 0000000..29bbe20 --- /dev/null +++ b/bc/string/QuickFormat.hpp @@ -0,0 +1,32 @@ +#ifndef BC_STRING_QUICK_FORMAT_HPP +#define BC_STRING_QUICK_FORMAT_HPP + +#include "bc/string/Format.hpp" +#include +#include +#include + +namespace Blizzard { +namespace String { + +template +class QuickFormat { + private: + char buffer[N]; + public: + QuickFormat(const char* format, ...) { + va_list args; + va_start(args, format); + VFormat(this->buffer, N, format, args); + printf("QI %d '%s', '%s'\n", N, format, this->buffer); + } + + const char* ToString() { + return this->buffer; + } +}; + +} // namespace String +} // namespace Blizzard + +#endif diff --git a/bc/string/QuickNativePath.hpp b/bc/string/QuickNativePath.hpp new file mode 100644 index 0000000..0e2d2c9 --- /dev/null +++ b/bc/string/QuickNativePath.hpp @@ -0,0 +1,46 @@ +#ifndef BC_STRING_QUICK_NATIVE_PATH_HPP +#define BC_STRING_QUICK_NATIVE_PATH_HPP + +#include "bc/String.hpp" +#include "bc/Memory.hpp" +#include + +namespace Blizzard { +namespace String { + +template +class QuickNativePath { + private: + // string length of path, including NULL character + uint32_t length; + char* path; + char buffer[N]; + + public: + QuickNativePath(const char* name) { + this->length = String::Length(name) + 1; + + if (this->length > N) { + this->path = reinterpret_cast(Memory::Allocate(this->length)); + MakeNativePath(name, this->path, this->length); + } else { + this->path = this->buffer; + MakeNativePath(name, this->path, N); + } + } + + ~QuickNativePath() { + if (this->path != this->buffer) { + Memory::Free(this->path); + } + } + + const char* ToString() { + return this->path; + } +}; + +} // namespace String +} // namespace Blizzard + +#endif diff --git a/bc/string/Translate.cpp b/bc/string/Translate.cpp new file mode 100644 index 0000000..8186d41 --- /dev/null +++ b/bc/string/Translate.cpp @@ -0,0 +1,49 @@ +#include "bc/string/Translate.hpp" +#include "bc/string/Length.hpp" +#include "bc/string/Memory.hpp" +#include + +void Blizzard::String::Translate(const char* src, char* dst, int32_t dstmaxchars, const char* find, const char* replace) { + if (dst == nullptr || find == nullptr || replace == nullptr) { + return; + } + + if (src == nullptr) { + src = dst; + } + + auto srcstart = src; + auto dststart = dst; + + auto dstmax = &dst[dstmaxchars - 1]; + auto srcmax = &src[Length(src) + 1]; + + auto findlength = Length(find); + auto replacelength = Length(replace); + while (dst < dstmax && src < srcmax) { + auto srcchar = *src; + + if (srcchar == '\0') { + break; + } + + if (srcchar == find[0]) { + auto copylength = replacelength; + if ((dst + copylength) >= dstmax) { + copylength = dstmax - dst; + } + + auto substring = strstr(src, find); + if (substring == src) { + MemCopy(dst, replace, copylength); + dst += replacelength; + src += findlength; + continue; + } + } + + *dst++ = *src++; + } + + *dst = '\0'; +} diff --git a/bc/string/Translate.hpp b/bc/string/Translate.hpp new file mode 100644 index 0000000..8b80097 --- /dev/null +++ b/bc/string/Translate.hpp @@ -0,0 +1,14 @@ +#ifndef BC_STRING_TRANSLATE_HPP +#define BC_STRING_TRANSLATE_HPP + +#include + +namespace Blizzard { +namespace String { + +void Translate(const char* src, char* dst, int32_t dstmaxchars, const char* find, const char* replace); + +} // namespace String +} // namespace Blizzard + +#endif diff --git a/bc/system/System_Time.cpp b/bc/system/System_Time.cpp index 9b6e811..04b3816 100644 --- a/bc/system/System_Time.cpp +++ b/bc/system/System_Time.cpp @@ -104,7 +104,7 @@ void TimeInit() { ULARGE_INTEGER ul = {}; ul.HighPart = ft.dwHighDateTime; ul.LowPart = ft.dwLowDateTime; - s_gmBegin = Time::FromWinFiletime(ul.QuadPart); + s_gmBegin = Time::FromFileTime(ul.QuadPart); #endif // Attempt to figure out the scale of TSC durations in real-time diff --git a/bc/time/Time.cpp b/bc/time/Time.cpp index b041934..aed0d8d 100644 --- a/bc/time/Time.cpp +++ b/bc/time/Time.cpp @@ -1,6 +1,6 @@ #include "bc/time/Time.hpp" -#include "bc/time/TimeConst.hpp" #include "bc/system/System_Time.hpp" +#include "bc/time/TimeConst.hpp" #if defined(WHOA_SYSTEM_WIN) #include @@ -70,25 +70,20 @@ Timestamp FromUnixTime(int32_t unixTime) { } // Win32 FILETIME to y2k -Timestamp FromWinFiletime(uint64_t winTime) { - if (winTime < 33677863631452242ULL) { +Timestamp FromFileTime(uint64_t filetime) { + if (filetime < 33677863631452242ULL) { return std::numeric_limits::min(); } - if (winTime >= 218145301729837567ULL) { - return std::numeric_limits::max();; + if (filetime >= 218145301729837567ULL) { + return std::numeric_limits::max(); } - // 1601 (Gregorian) 100-nsec - auto gregorian = static_cast(winTime); - // Convert filetime from 1601 epoch to 2000 epoch. - auto y2k = gregorian - TimeConst::WinFiletimeY2kDifference; - // Convert 100-nsec intervals into nsec intervals - return static_cast(y2k * 100LL); + return (static_cast(filetime) * 100LL) + ~(TimeConst::FileTimeDelta*100LL) + 1; } -uint64_t ToWinFiletime(Timestamp y2k) { - return (y2k + TimeConst::WinFiletimeY2kDifference) / 100ULL; +uint64_t ToFileTime(Timestamp y2k) { + return (static_cast(y2k) + TimeConst::FileTimeDelta) / 100ULL; } int32_t GetTimeElapsed(uint32_t start, uint32_t end) { @@ -123,7 +118,7 @@ Timestamp MakeTime(const TimeRec& date) { #if defined(WHOA_SYSTEM_WIN) // Win32 implementation - FILETIME fileTime = {}; + FILETIME fileTime = {}; SYSTEMTIME systemTime = {}; systemTime.wYear = static_cast(date.year); @@ -135,19 +130,14 @@ Timestamp MakeTime(const TimeRec& date) { systemTime.wMilliseconds = 0; ::SystemTimeToFileTime(&systemTime, &fileTime); - - ULARGE_INTEGER ul = {}; - ul.HighPart = fileTime.dwHighDateTime; - ul.LowPart = fileTime.dwLowDateTime; - - timestamp = FromWinFiletime(ul.QuadPart); + timestamp = FromFileTime(static_cast(fileTime.dwHighDateTime) << 32 | static_cast(fileTime.dwLowDateTime)); #endif #if defined(WHOA_SYSTEM_MAC) || defined(WHOA_SYSTEM_LINUX) // UNIX implementation struct tm t; - t.tm_year = date.year - 1900; + t.tm_year = date.year - 1900; t.tm_mon = date.month - 1; t.tm_mday = date.day; t.tm_hour = date.hour; @@ -156,7 +146,7 @@ Timestamp MakeTime(const TimeRec& date) { // Convert date into UNIX timestamp auto unixTime = ::timegm(&t); - timestamp = FromUnixTime(unixTime); + timestamp = FromUnixTime(unixTime); #endif // Add nsec to result auto nsec = date.nsec; @@ -182,12 +172,12 @@ void BreakTime(Timestamp timestamp, TimeRec& date) { #if defined(WHOA_SYSTEM_WIN) // Win32 implementation ULARGE_INTEGER ul = {}; - ul.QuadPart = ToWinFiletime(timestamp); + ul.QuadPart = ToFileTime(timestamp); - FILETIME fileTime = {}; + FILETIME fileTime = {}; fileTime.dwHighDateTime = ul.HighPart; fileTime.dwLowDateTime = ul.LowPart; - SYSTEMTIME systemTime = {}; + SYSTEMTIME systemTime = {}; ::FileTimeToSystemTime(&fileTime, &systemTime); date.day = static_cast(systemTime.wDay); @@ -201,6 +191,7 @@ void BreakTime(Timestamp timestamp, TimeRec& date) { bool leapYear = (date.year % 400 == 0) || (date.year % 100 != 0 && ((systemTime.wYear & 3) == 0)); auto yearDay = s_monthDays[date.month] + -1 + static_cast(systemTime.wDay); + date.yday = yearDay; if (leapYear && date.month > 2) { date.yday++; diff --git a/bc/time/Time.hpp b/bc/time/Time.hpp index edf957d..6950b60 100644 --- a/bc/time/Time.hpp +++ b/bc/time/Time.hpp @@ -13,9 +13,9 @@ int32_t ToUnixTime(Timestamp timestamp); Timestamp FromUnixTime(int32_t unixTime); // Win32 FILETIME to y2k -Timestamp FromWinFiletime(uint64_t winTime); +Timestamp FromFileTime(uint64_t filetime); -uint64_t ToWinFiletime(Timestamp y2k); +uint64_t ToFileTime(Timestamp y2k); Timestamp GetTimestamp(); diff --git a/bc/time/TimeConst.hpp b/bc/time/TimeConst.hpp index a972223..a01bedd 100644 --- a/bc/time/TimeConst.hpp +++ b/bc/time/TimeConst.hpp @@ -9,10 +9,10 @@ namespace TimeConst { constexpr int64_t TimestampsPerSecond = 1000000000ULL; // amount of win32 filetime units in a second -constexpr int64_t WinUnitsPerSecond = (TimestampsPerSecond / 100ULL); +constexpr int64_t FileTimeUnitsPerSecond = (TimestampsPerSecond / 100LL); // the FILETIME value needed to move from 1601 epoch to the Year 2000 epoch that Blizzard prefers -constexpr int64_t WinFiletimeY2kDifference = 125911584000000000ULL; +constexpr uint64_t FileTimeDelta = 125911584000000000ULL; } // namespace TimeConst