1
0
Fork 0
mirror of https://github.com/beefytech/Beef.git synced 2025-06-07 19:18:19 +02:00
Beef/BeefySysLib/Common.cpp
2025-04-01 15:37:03 -04:00

1420 lines
31 KiB
C++

#include "Common.h"
#include "util/BumpAllocator.h"
#include "util/UTF8.h"
#include "util/String.h"
//#include "farts.z"
#include <string.h>
#include "platform/PlatformHelper.h"
#ifndef BF_SMALL
#define STB_SPRINTF_DECORATE(name) BF_stbsp_##name
#define STB_SPRINTF_IMPLEMENTATION
#include "third_party/stb/stb_sprintf.h"
#endif
extern "C"
{
#include "third_party/utf8proc/utf8proc.h"
}
#ifdef BF_PLATFORM_WINDOWS
#include <shellapi.h>
#include <direct.h>
#endif
#pragma warning(disable:4996)
#pragma comment(lib, "winmm.lib")
int gBFArgC;
char** gBFArgV;
#ifdef BUMPALLOC_ETW_TRACING
VSHeapTracker::CHeapTracker* Beefy::BumpAllocator::mVSHeapTracker = NULL;
#pragma comment(lib, "VSCustomNativeHeapEtwProvider.lib")
#endif
USING_NS_BF;
UTF16String Beefy::ToWString(const StringImpl& theString)
{
UTF16String aString;
aString.Reserve(theString.length() + 1);
for (intptr i = 0; i < theString.length(); ++i)
aString.Add((unsigned char)theString[i]);
aString.Add(0);
return aString;
}
// String Beefy::ToString(const UTF16String& theString)
// {
// String newString;
// newString.Reserve((intptr)theString.length());
// for (int i = 0; i < (int)theString.length(); ++i)
// {
// const unsigned int c = (unsigned int)theString[i];
// newString.Append(char(c & 0xFF));
// }
// return newString;
// }
String Beefy::ToUpper(const StringImpl& theString)
{
String aString = theString;
for (int i = 0; i < (int)aString.length(); ++i)
aString[i] = toupper(aString[i]);
return aString;
}
void Beefy::MakeUpper(StringImpl& theString)
{
for (int i = 0; i < (int)theString.length(); ++i)
theString[i] = toupper(theString[i]);
}
UTF16String Beefy::ToUpper(const UTF16String& theString)
{
UTF16String aString = theString;
for (int i = 0; i < (int)aString.length(); ++i)
aString[i] = toupper(aString[i]);
return aString;
}
UTF16String Beefy::ToLower(const UTF16String& theString)
{
UTF16String aString = theString;
for (int i = 0; i < (int)aString.length(); ++i)
aString[i] = tolower(aString[i]);
return aString;
}
String Beefy::ToLower(const StringImpl& theString)
{
String aString = theString;
for (int i = 0; i < (int)aString.length(); ++i)
aString[i] = (char)tolower((int)(uint8)aString[i]);
return aString;
}
// UTF16String Beefy::Trim(const UTF16String& theString)
// {
// int left = 0;
// while ((left < (int) theString.length() - 1) && (iswspace(theString[left])))
// left++;
//
// int right = (int)theString.length() - 1;
// while ((right >= left) && (iswspace(theString[right])))
// right--;
//
// if ((left == 0) && (right == theString.length() - 1))
// return theString;
//
// return theString.substr(left, right - left + 1);
// }
String Beefy::Trim(const StringImpl& theString)
{
int left = 0;
while ((left < (int) theString.length() - 1) && (iswspace(theString[left])))
left++;
int right = (int)theString.length() - 1;
while ((right >= left) && (iswspace(theString[right])))
right--;
if ((left == 0) && (right == theString.length() - 1))
return theString;
return theString.Substring(left, right - left + 1);
}
bool Beefy::StrReplace(StringImpl& str, const StringImpl& from, const StringImpl& to)
{
str.Replace(from, to);
return true;
}
bool Beefy::StrStartsWith(const StringImpl& str, const StringImpl& subStr)
{
if (subStr.length() < str.length())
return false;
return strncmp(str.c_str(), subStr.c_str(), subStr.length()) == 0;
}
bool Beefy::StrEndsWith(const StringImpl& str, const StringImpl& subStr)
{
if (subStr.length() < str.length())
return false;
return strncmp(str.c_str() - (str.length() - subStr.length()), subStr.c_str(), subStr.length()) == 0;
}
#ifndef BF_SMALL
String Beefy::SlashString(const StringImpl& str, bool utf8decode, bool utf8encode, bool beefString)
{
bool prevEndedInSlashedNum = false;
String outStr;
bool noNextHex = false;
bool lastWasVisibleChar = false;
for (int idx = 0; idx < (int)str.length(); idx++)
{
bool endingInSlashedNum = false;
char c = str[idx];
if (noNextHex)
{
if (((c >= '0') && (c <= '9')) ||
((c >= 'a') && (c <= 'f')) ||
((c >= 'A') && (c <= 'F')))
{
outStr += "\"\"";
}
noNextHex = false;
}
bool isVisibleChar = false;
switch (c)
{
case '\0':
outStr += "\\0";
endingInSlashedNum = true;
break;
case '\a':
outStr += "\\a";
break;
case '\b':
outStr += "\\b";
break;
case '\t':
outStr += "\\t";
break;
case '\n':
outStr += "\\n";
break;
case '\v':
outStr += "\\v";
break;
case '\f':
outStr += "\\f";
break;
case '\r':
outStr += "\\r";
break;
case '\"':
outStr += "\\\"";
break;
case '\\':
outStr += "\\\\";
break;
default:
{
if ((c >= '0') && (c <= '9'))
{
// Need to break string to allow proper evaluation of slashed string
if (prevEndedInSlashedNum)
outStr += "\" \"";
}
// May be UTF8 byte. Make sure it's valid before we add it, otherwise write the bytes in direct "\x" style
if (((uint8)c >= 0x80) && (utf8decode))
{
int seqLen = u8_seqlen((char*)&str[idx]);
if (seqLen > 1)
{
if (idx + seqLen <= str.length())
{
uint32 c32 = u8_toucs((char*)&str[idx], (int)str.length() - idx);
bool wantHex = false;
// Combining mark without previous visible char?
if ((((c32 >= L'\u0300') && (c32 <= L'\u036F')) || ((c32 >= L'\u1DC0') && (c32 <= L'\u1DFF'))) &&
(!lastWasVisibleChar))
wantHex = true;
utf8proc_category_t cat = utf8proc_category(c32);
switch (cat)
{
case UTF8PROC_CATEGORY_ZS:
case UTF8PROC_CATEGORY_ZL:
case UTF8PROC_CATEGORY_ZP:
case UTF8PROC_CATEGORY_CC:
case UTF8PROC_CATEGORY_CF:
case UTF8PROC_CATEGORY_CO:
wantHex = true;
break;
default:
break;
}
if (wantHex)
{
if (beefString)
{
char tempStr[32];
sprintf(tempStr, "\\u{%x}", c32);
outStr += tempStr;
idx += seqLen - 1;
continue;
}
else
{
char tempStr[32];
sprintf(tempStr, "\\u%04x", c32);
outStr += tempStr;
idx += seqLen - 1;
noNextHex = true;
continue;
}
}
bool passed = true;
for (int checkOfs = 0; checkOfs < seqLen - 1; checkOfs++)
{
char checkC = str[idx + checkOfs + 1];
if ((checkC & 0xC0) != 0x80) // Has trailing signature (0x10xxxxxx)?
passed = false;
}
if (passed)
{
outStr += c;
for (int checkOfs = 0; checkOfs < seqLen - 1; checkOfs++)
{
char checkC = str[idx + checkOfs + 1];
outStr += checkC;
}
idx += seqLen - 1;
continue;
}
}
}
}
if (((uint8)c >= 0x80) && (utf8encode))
{
outStr += (char)(0xC0 | (((uint8)c & 0xFF) >> 6));
outStr += (char)(0x80 | ((uint8)c & 0x3F));
isVisibleChar = true;
continue;
}
if (((uint8)c >= 32) && ((uint8)c < 0x80))
{
outStr += c;
isVisibleChar = true;
}
else
{
char tempStr[32];
sprintf(tempStr, "\\x%02x", (uint8)c);
outStr += tempStr;
endingInSlashedNum = true;
if (!beefString)
noNextHex = true;
}
}
break;
}
prevEndedInSlashedNum = endingInSlashedNum;
lastWasVisibleChar = isVisibleChar;
}
return outStr;
}
#endif
UTF16String Beefy::UTF8Decode(const StringImpl& theString)
{
UTF16String strOut;
int strLen = 0;
char* cPtr = (char*)theString.c_str();
int lenLeft = (int)theString.length();
while (lenLeft > 0)
{
int seqLen = 0;
uint32 c32 = u8_toucs(cPtr, lenLeft, &seqLen);
if ((c32 >= 0x10000) && (sizeof(wchar_t) == 2))
strLen += 2;
else
strLen += 1;
cPtr += seqLen;
lenLeft -= seqLen;
}
strOut.ResizeRaw(strLen + 1);
strOut[strLen] = 0;
cPtr = (char*)theString.c_str();
uint16* wcPtr = strOut.mVals;
int wcLenLeft = (int)strOut.length() + 1;
lenLeft = (int)theString.length();
while (lenLeft > 0)
{
int seqLen = 0;
uint32 c32 = u8_toucs(cPtr, lenLeft, &seqLen);
if ((c32 >= 0x10000) && (sizeof(wchar_t) == 2))
{
*(wcPtr++) = (wchar_t)(((c32 - 0x10000) >> 10) + 0xD800);
*(wcPtr++) = (wchar_t)(((c32 - 0x10000) & 0x3FF) + 0xDC00);
wcLenLeft -= 2;
}
else
{
*(wcPtr++) = (wchar_t)c32;
wcLenLeft -= 1;
}
cPtr += seqLen;
lenLeft -= seqLen;
}
return strOut;
}
String Beefy::UTF8Encode(const uint16* theString, int length)
{
if (length == 0)
return String();
String strOut;
int utf8Len = 0;
uint16 utf16hi = 0;
for (int i = 0; i < length; i++)
{
uint16 c = theString[i];
uint32 c32 = c;
if ((c >= 0xD800) && (c < 0xDC00))
{
utf16hi = (uint16)c;
continue;
}
else if ((c >= 0xDC00) && (c < 0xE000))
{
uint16 utf16lo = c;
c32 = 0x10000 + ((uint32)(utf16hi - 0xD800) << 10) | (uint32)(utf16lo - 0xDC00);
}
utf8Len += u8_seqlen(c32);
}
strOut.Append('?', utf8Len);
char* cPtr = (char*)&strOut[0];
int lenLeft = (int)strOut.length() + 1;
for (int i = 0; i < length; i++)
{
uint16 c = theString[i];
uint32 c32 = c;
if ((c >= 0xD800) && (c < 0xDC00))
{
utf16hi = (uint16)c;
continue;
}
else if ((c >= 0xDC00) && (c < 0xE000))
{
uint16 utf16lo = c;
c32 = 0x10000 + ((uint32)(utf16hi - 0xD800) << 10) | (uint32)(utf16lo - 0xDC00);
}
int len = u8_toutf8(cPtr, lenLeft, c32);
cPtr += len;
lenLeft -= len;
}
return strOut;
}
String Beefy::UTF8Encode(const UTF16String& theString)
{
return UTF8Encode((uint16*)theString.c_str(), (int)theString.length());
}
UTF16String Beefy::UTF16Decode(const uint16* theString)
{
if (sizeof(wchar_t) == 2)
{
UTF16String str;
str.Set((wchar_t*)theString);
return str;
}
else
{
UTF16String str;
int len = 0;
while (theString[len] != 0)
len++;
str.ResizeRaw(len + 1);
str[len] = 0;
for (int pos = 0; pos < len; pos++)
str[pos] = (wchar_t)theString[pos];
return str;
}
}
static const char gHexChar[] = { '0', '1', '2', '3', '4', '5', '6', '7', '8', '9', 'A', 'B', 'C', 'D', 'E', 'F' };
String Beefy::FileNameToURI(const StringImpl& fileName)
{
#ifdef _WIN32
String out = "file:///";
#else
String out = "file://";
#endif
for (int i = 0; i < (int)fileName.length(); i++)
{
char c = out[i];
bool isValid =
((c >= '@' && c <= 'Z') ||
(c >= 'a' && c <= 'z') ||
(c >= '&' && c < 0x3b) ||
(c == '!') || (c == '$') || (c == '_') || (c == '=') || (c == '~'));
if ((((unsigned char)c) >= 0x80) || (!isValid))
{
out += '%';
out += gHexChar[((unsigned char)(c)) >> 4];
out += gHexChar[((unsigned char)(c)) & 0xf];
}
else if (c == '\\')
out += '/';
else
out += c;
}
return out;
}
static const uint8 cCharTo64b[] = { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 62, 0, 52, 53, 54, 55, 56, 57, 58, 59, 60, 61, 0, 0, 0, 0, 0, 0,
0, 26, 27, 28, 29, 30, 31, 32, 33, 34, 35, 36, 37, 38, 39, 40, 41, 42, 43, 44, 45, 46, 47, 48, 49, 50, 51, 0, 0, 0, 0, 63,
0, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 };
int64 Beefy::DecodeULEB32(const char*& p)
{
uint64 val = 0;
unsigned shift = 0;
uint8 charVal;
do
{
charVal = cCharTo64b[(uint8)*(p++)];
val += uint64(charVal & 0x1f) << shift;
shift += 5;
} while ((charVal & 0x20) != 0);
return val;
}
static const char c64bToChar[] = { 'a', 'b', 'c', 'd', 'e', 'f', 'g', 'h', 'i', 'j', 'k', 'l', 'm', 'n', 'o', 'p',
'q', 'r', 's', 't', 'u', 'v', 'w', 'x', 'y', 'z', 'A', 'B', 'C', 'D', 'E', 'F',
'G', 'H', 'I', 'J', 'K', 'L', 'M', 'N', 'O', 'P', 'Q', 'R', 'S', 'T', 'U', 'V',
'W', 'X', 'Y', 'Z', '0', '1', '2', '3', '4', '5', '6', '7', '8', '9', '.', '_'};
void Beefy::EncodeULEB32(uint64 value, StringImpl& buffer)
{
do
{
uint8 byteVal = value & 0x1f;
value >>= 5;
if (value != 0)
byteVal |= 0x20; // Mark this byte to show that more bytes will follow
buffer.Append(c64bToChar[byteVal]);
}
while (value != 0);
}
#ifndef BF_SMALL
void Beefy::ExactMinimalFloatToStr(float f, char* str)
{
sprintf(str, "%1.9g", f);
char* cPtrLast = str;
while (true)
{
char c = cPtrLast[1];
if ((c == 0) || (c == 'e'))
break;
cPtrLast++;
}
char* cStrEnd = cPtrLast + 1;
int baseLen = (int)(cPtrLast - str) + 1;
if (baseLen < 9)
return; // Isn't "full" precision
char removeChar;
char lastChar = *cPtrLast;
char secondToLast = cPtrLast[-1];
if (secondToLast == '9')
{
removeChar = '9';
}
else if (secondToLast == '0')
{
removeChar = '0';
}
else
{
return; // Not a 'transitional' representation
}
do
{
*cPtrLast = 0;
cPtrLast--;
} while (*cPtrLast == removeChar);
strcpy(cPtrLast + 1, cStrEnd);
if (removeChar == '9')
(*cPtrLast)++;
// Verify that our pretty representation is equivalent
float checkF = 0;
sscanf(str, "%f", &checkF);
if (f == checkF)
return;
// Rollback
int endLen = (int)strlen(cPtrLast + 1);
memmove(cStrEnd, cPtrLast + 1, endLen);
if (removeChar == '9')
(*cPtrLast)--;
cPtrLast++;
while (cPtrLast < cStrEnd - 1)
{
*cPtrLast = removeChar;
cPtrLast++;
}
*cPtrLast = lastChar;
}
void Beefy::ExactMinimalDoubleToStr(double d, char* str)
{
sprintf(str, "%1.17g", d);
char* cPtrLast = str;
while (true)
{
char c = cPtrLast[1];
if ((c == 0) || (c == 'e'))
break;
cPtrLast++;
}
char* cStrEnd = cPtrLast + 1;
int baseLen = (int)(cPtrLast - str) + 1;
if (baseLen < 18)
return; // Isn't "full" precision
char removeChar;
char lastChar = *cPtrLast;
if ((lastChar == '9') || (lastChar == '8'))
{
removeChar = '9';
}
else if ((lastChar == '1') || (lastChar == '2'))
{
removeChar = '0';
}
else
{
return; // Not a 'transitional' representation
}
do
{
*cPtrLast = 0;
cPtrLast--;
}
while (*cPtrLast == removeChar);
strcpy(cPtrLast + 1, cStrEnd);
if (removeChar == '9')
(*cPtrLast)++;
// Verify that our pretty representation is equivalent
double checkD = 0;
sscanf(str, "%lf", &checkD);
if (d == checkD)
return;
// Rollback
int endLen = (int)strlen(cPtrLast + 1);
memmove(cStrEnd, cPtrLast + 1, endLen);
if (removeChar == '9')
(*cPtrLast)--;
cPtrLast++;
while (cPtrLast < cStrEnd - 1)
{
*cPtrLast = removeChar;
cPtrLast++;
}
*cPtrLast = lastChar;
}
#endif
static char* StbspCallback(char *buf, void *user, int len)
{
((StringImpl*)user)->Append(buf, len);
return buf;
}
//extern "C" int _vsnprintf(char* const _Buffer, size_t const _BufferCount, char const* const _Format, va_list _ArgList, int zz);
#ifdef BF_SMALL
String Beefy::vformat(const char* fmt, va_list argPtr)
{
// We draw the line at a 1MB string.
const int maxSize = 1000000;
//va_list checkArgPtr = argPtr;
//va_start(checkArgPtr, fmt);
int argIdx = 0;
char* newFmt = NULL;
char tempBuff[2048];
char* tempBuffPtr = tempBuff;
for (int i = 0; fmt[i] != 0; i++)
{
if (fmt[i] == '%')
{
if (fmt[i + 1] == '%')
{
i++;
continue;
}
#ifdef BF32
bool isLongAddr = false;
#else
bool isLongAddr = true;
#endif
if ((fmt[i + 1] == 'l') && (fmt[i + 2] == '@'))
{
isLongAddr = true;
i++;
}
if (fmt[i + 1] == '@')
{
if (newFmt == NULL)
{
newFmt = (char*)malloc(strlen(fmt) + 1);
strcpy(newFmt, fmt);
//newFmt = strdup(fmt);
}
newFmt[i + 1] = 's';
const char*& argValPtr = *(const char**)((intptr*)argPtr + argIdx);
if (isLongAddr)
{
int64 iVal = *(int64*)((intptr*)argPtr + argIdx);
#ifdef BF32
argIdx++; // Takes two spots
#endif
argValPtr = tempBuffPtr;
int leftVal = (int)(iVal >> 32);
if (leftVal != 0)
{
sprintf(tempBuffPtr, "%x", leftVal);
tempBuffPtr += strlen(tempBuffPtr);
}
tempBuffPtr[0] = '\'';
tempBuffPtr++;
sprintf(tempBuffPtr, "%08x", (int)iVal);
tempBuffPtr += strlen(tempBuffPtr) + 1;
}
else
{
int32 iVal = (int32)(intptr)argValPtr;
argValPtr = tempBuffPtr;
sprintf(tempBuffPtr, "%08x", iVal);
tempBuffPtr += strlen(tempBuffPtr) + 1;
}
if (newFmt[i] == 'l')
newFmt[i] = '+';
}
else
{
//const char*& argValPtr = va_arg(checkArgPtr, const char*);
}
argIdx++;
}
}
if (newFmt != NULL)
{
Beefy::String retVal = vformat(newFmt, argPtr);
free(newFmt);
return retVal;
}
// If the string is less than 161 characters,
// allocate it on the stack because this saves
// the malloc/free time.
const int bufSize = 161;
char stackBuffer[bufSize];
int attemptedSize = bufSize - 1;
int numChars = 0;
#ifdef _WIN32
numChars = _vsnprintf(stackBuffer, attemptedSize, fmt, argPtr);
#else
numChars = vsnprintf(stackBuffer, attemptedSize, fmt, argPtr);
#endif
if ((numChars >= 0) && (numChars <= attemptedSize))
{
// Needed for case of 160-character printf thing
stackBuffer[numChars] = '\0';
// Got it on the first try.
return String(stackBuffer);
}
// Now use the heap.
char* heapBuffer = NULL;
while (((numChars == -1) || (numChars > attemptedSize)) &&
(attemptedSize < maxSize))
{
// Try a bigger size
attemptedSize *= 2;
heapBuffer = (char*)realloc(heapBuffer, (attemptedSize + 1));
#ifdef _WIN32
numChars = _vsnprintf(heapBuffer, attemptedSize, fmt, argPtr);
#else
numChars = vsnprintf(heapBuffer, attemptedSize, fmt, argPtr);
#endif
}
if (numChars == -1)
{
free(heapBuffer);
return "";
}
heapBuffer[numChars] = 0;
Beefy::String aResult = Beefy::String(heapBuffer);
free(heapBuffer);
return aResult;
}
#else
String Beefy::vformat(const char* fmt, va_list argPtr)
{
String str;
char buf[STB_SPRINTF_MIN];
BF_stbsp_vsprintfcb(StbspCallback, (void*)&str, buf, fmt, argPtr);
return str;
}
void Beefy::vformat(StringImpl& str, const char* fmt, va_list argPtr)
{
char buf[STB_SPRINTF_MIN];
BF_stbsp_vsprintfcb(StbspCallback, (void*)&str, buf, fmt, argPtr);
}
#endif
String Beefy::StrFormat(const char* fmt ...)
{
va_list argList;
va_start(argList, fmt);
String aResult = vformat(fmt, argList);
va_end(argList);
return aResult;
}
String Beefy::IntPtrDynAddrFormat(intptr addr)
{
if ((addr & 0xFFFFFFFF00000000LL) == 0)
return StrFormat("%08X", addr);
return StrFormat("%p", addr);
}
void Beefy::OutputDebugStr(const StringImpl& theString)
{
const int maxLen = 0xFFFE;
if (theString.length() > maxLen)
{
OutputDebugStr(theString.Substring(0, maxLen));
OutputDebugStr(theString.Substring(maxLen));
return;
}
BfpOutput_DebugString(theString.c_str());
}
void Beefy::OutputDebugStrF(const char* fmt ...)
{
va_list argList;
va_start(argList, fmt);
String aResult = vformat(fmt, argList);
va_end(argList);
OutputDebugStr(aResult);
}
uint8* Beefy::LoadBinaryData(const StringImpl& path, int* size)
{
FILE* fP = fopen(path.c_str(), "rb");
if (fP == NULL)
return NULL;
fseek(fP, 0, SEEK_END);
int aSize = (int32)ftell(fP);
fseek(fP, 0, SEEK_SET);
uint8* data = new uint8[aSize];
int readSize = (int)fread(data, 1, aSize, fP);
(void)readSize;
fclose(fP);
if (size)
*size = aSize;
return data;
}
char* Beefy::LoadTextData(const StringImpl& path, int* size)
{
FILE* fP = fopen(path.c_str(), "rb");
if (fP == NULL)
return NULL;
fseek(fP, 0, SEEK_END);
int fileSize = (int32)ftell(fP);
int strLen = fileSize;
fseek(fP, 0, SEEK_SET);
uint8 charHeader[3] = {0};
int readSize = (int)fread(charHeader, 1, 3, fP);
if ((charHeader[0] == 0xFF) && (charHeader[1] == 0xFE))
{
//UTF16 LE
int dataLen = fileSize - 2;
char* data = new char[dataLen + 2];
data[0] = (char)charHeader[2];
data[dataLen] = 0;
data[dataLen + 1] = 0;
int readSize = (int)fread(data + 1, 1, dataLen - 1, fP);
(void)readSize;
fclose(fP);
// UTF16
UTF16String str;
str.Set((wchar_t*)data);
delete [] data;
String utf8Str = UTF8Encode(str);
char* utf8Data = new char[utf8Str.length() + 1];
strLen = (int)utf8Str.length();
if (size != NULL)
*size = strLen;
memcpy(utf8Data, utf8Str.c_str(), strLen);
return utf8Data;
}
else if ((charHeader[0] == 0xEF) && (charHeader[1] == 0xBB) && (charHeader[2] == 0xBF))
{
strLen = fileSize - 3;
char* data = new char[strLen + 1];
data[strLen] = 0;
if (size != NULL)
*size = strLen;
int readSize = (int)fread(data, 1, strLen, fP);
(void)readSize;
fclose(fP);
return data;
}
if (size != NULL)
*size = strLen;
char* data = new char[strLen + 1];
data[strLen] = 0;
for (int i = 0; i < BF_MIN(3, strLen); i++)
data[i] = charHeader[i];
if (strLen > 3)
{
int readSize = (int)fread(data + 3, 1, strLen - 3, fP);
(void)readSize;
}
fclose(fP);
return data;
}
bool Beefy::LoadTextData(const StringImpl& path, StringImpl& str)
{
int size = 0;
char* data = LoadTextData(path, &size);
if (data == NULL)
return false;
if ((str.mAllocSizeAndFlags & StringImpl::DynAllocFlag) != 0)
str.Release();
str.mPtr = data;
str.mAllocSizeAndFlags = size | StringImpl::DynAllocFlag | StringImpl::StrPtrFlag;
str.mLength = size;
return true;
}
#ifdef BF_MINGW
unsigned long long __cdecl _byteswap_uint64(unsigned long long _Int64)
{
#ifdef _WIN64
unsigned long long retval;
__asm__ __volatile__ ("bswapq %[retval]" : [retval] "=rm" (retval) : "[retval]" (_Int64));
return retval;
#else
union {
long long int64part;
struct {
unsigned long lowpart;
unsigned long hipart;
};
} retval;
retval.int64part = _Int64;
__asm__ __volatile__ ("bswapl %[lowpart]\n"
"bswapl %[hipart]\n"
: [lowpart] "=rm" (retval.hipart), [hipart] "=rm" (retval.lowpart) : "[lowpart]" (retval.lowpart), "[hipart]" (retval.hipart));
return retval.int64part;
#endif
}
#endif
#ifdef _WIN32
int64 Beefy::EndianSwap(int64 val)
{
return _byteswap_uint64(val);
}
#endif
int32 Beefy::EndianSwap(int val)
{
return ((val & 0x000000FF) << 24) | ((val & 0x0000FF00) << 8) |
((val & 0x00FF0000) >> 8) | ((val & 0xFF000000) >> 24);
}
int16 Beefy::EndianSwap(int16 val)
{
return ((val & 0x00FF) << 8) | ((val & 0xFF00) >> 8);
}
int32 Beefy::Rand()
{
return (rand() & 0xFFFF) | ((rand() & 0x7FFF) << 16);
}
int32 Beefy::GetHighestBitSet(int32 n)
{
int i = 0;
for (; n; n = (int)((uint32)n >> 1), i++)
; /* empty */
return i;
}
String Beefy::GetFileDir(const StringImpl& path)
{
int slashPos = BF_MAX((int)path.LastIndexOf('\\'), (int)path.LastIndexOf('/'));
if (slashPos == -1)
return "";
return path.Substring(0, slashPos);
}
String Beefy::GetFileName(const StringImpl& path)
{
int slashPos = BF_MAX((int)path.LastIndexOf('\\'), (int)path.LastIndexOf('/'));
if (slashPos == -1)
return path;
return path.Substring(slashPos + 1);
}
String Beefy::GetFileExtension(const StringImpl& path)
{
int dotPos = (int)path.LastIndexOf('.');
if (dotPos == -1)
return path;
return path.Substring(dotPos);
}
static String GetDriveStringTo(String path)
{
if ((path.length() >= 2) && (path[1] == ':'))
return String(path, 0, 2);
return "";
}
String Beefy::GetRelativePath(const StringImpl& fullPath, const StringImpl& curDir)
{
String curPath1 = String(curDir);
String curPath2 = String(fullPath);
for (int i = 0; i < (int)curPath1.length(); i++)
if (curPath1[i] == DIR_SEP_CHAR_ALT)
curPath1[i] = DIR_SEP_CHAR;
for (int i = 0; i < (int)curPath2.length(); i++)
if (curPath2[i] == DIR_SEP_CHAR_ALT)
curPath2[i] = DIR_SEP_CHAR;
String driveString1 = GetDriveStringTo(curPath1);
String driveString2 = GetDriveStringTo(curPath2);
#ifdef _WIN32
StringImpl::CompareKind compareType = StringImpl::CompareKind_OrdinalIgnoreCase;
#else
StringImpl::CompareKind compareType = StringImpl::CompareKind_Ordinal;
#endif
// On separate drives?
if (!driveString1.Equals(driveString2, compareType))
{
return fullPath;
}
if (driveString1.mLength > 0)
curPath1.Remove(0, BF_MIN(driveString1.mLength + 1, curPath1.mLength));
if (driveString2.mLength > 0)
curPath2.Remove(0, BF_MIN(driveString2.mLength + 1, curPath2.mLength));
while ((curPath1.mLength > 0) && (curPath2.mLength > 0))
{
int slashPos1 = (int)curPath1.IndexOf(DIR_SEP_CHAR);
if (slashPos1 == -1)
slashPos1 = curPath1.mLength;
int slashPos2 = (int)curPath2.IndexOf(DIR_SEP_CHAR);
if (slashPos2 == -1)
slashPos2 = curPath2.mLength;
String section1;
section1.Append(StringView(curPath1, 0, slashPos1));
String section2;
section2.Append(StringView(curPath2, 0, slashPos2));
if (!section1.Equals(section2, compareType))
{
// a/b/c
// d/e/f
while (curPath1.mLength > 0)
{
slashPos1 = (int)curPath1.IndexOf(DIR_SEP_CHAR);
if (slashPos1 == -1)
slashPos1 = curPath1.mLength;
if (slashPos1 + 1 >= curPath1.mLength)
curPath1.Clear();
else
curPath1.Remove(0, slashPos1 + 1);
if (DIR_SEP_CHAR == '\\')
curPath2.Insert(0, "..\\");
else
curPath2.Insert(0, "../");
}
}
else
{
if (slashPos1 + 1 >= curPath1.mLength)
curPath1.Clear();
else
curPath1.Remove(0, slashPos1 + 1);
if (slashPos2 + 2 >= curPath2.mLength)
curPath1 = "";
else
curPath2.Remove(0, slashPos2 + 1);
}
}
return curPath2;
}
String Beefy::GetAbsPath(const StringImpl& relPathIn, const StringImpl& dir)
{
String relPath = relPathIn;
String driveString = "";
String newPath;
newPath = dir;
for (int i = 0; i < (int)newPath.length(); i++)
if (newPath[i] == DIR_SEP_CHAR_ALT)
newPath[i] = DIR_SEP_CHAR;
for (int i = 0; i < (int)relPath.length(); i++)
if (relPath[i] == DIR_SEP_CHAR_ALT)
relPath[i] = DIR_SEP_CHAR;
if ((relPath.length() >= 2) && (relPath[1] == ':'))
return relPath;
char slashChar = DIR_SEP_CHAR;
if ((newPath.length() >= 2) && (newPath[1] == ':'))
{
driveString = newPath.Substring(0, 2);
newPath = newPath.Substring(2);
}
// Append a trailing slash if necessary
if ((newPath.length() > 0) && (newPath[newPath.length() - 1] != '\\') && (newPath[newPath.length() - 1] != '/'))
newPath += slashChar;
int relIdx = 0;
for (;;)
{
if (newPath.length() == 0)
break;
int firstSlash = -1;
for (int32 i = relIdx; i < (int)relPath.length(); i++)
if ((relPath[i] == '\\') || (relPath[i] == '/'))
{
firstSlash = i;
break;
}
if (firstSlash == -1)
break;
String chDir = relPath.Substring(relIdx, firstSlash - relIdx);
relIdx = firstSlash + 1;
if (chDir == "..")
{
int lastDirStart = (int)newPath.length() - 1;
while ((lastDirStart > 0) && (newPath[lastDirStart - 1] != '\\') && (newPath[lastDirStart - 1] != '/'))
lastDirStart--;
String lastDir = newPath.Substring(lastDirStart, newPath.length() - lastDirStart - 1);
if (lastDir == "..")
{
newPath += "..";
newPath += DIR_SEP_CHAR;
}
else
{
newPath.RemoveToEnd(lastDirStart);
}
}
else if (chDir == "")
{
newPath = DIR_SEP_CHAR;
break;
}
else if (chDir != ".")
{
newPath += chDir + slashChar;
break;
}
}
//newPath = driveString + newPath + tempRelPath;
newPath = driveString + newPath;
newPath += relPath.Substring(relIdx);
return newPath;
}
String Beefy::FixPath(const StringImpl& pathIn)
{
String path = pathIn;
for (int i = 0; i < (int)path.length(); i++)
{
if (path[i] == DIR_SEP_CHAR_ALT)
path[i] = DIR_SEP_CHAR;
if ((i > 0) && (path[i - 1] == '.') && (path[i] == '.'))
{
for (int checkIdx = i - 3; checkIdx >= 0; checkIdx--)
{
if ((path[checkIdx] == '\\') || (path[checkIdx] == '/'))
{
path = path.Substring(0, checkIdx) + path.Substring(i + 1);
i = checkIdx;
break;
}
}
}
}
return path;
}
String Beefy::FixPathAndCase(const StringImpl& pathIn)
{
if ((!pathIn.IsEmpty()) && (pathIn[0] == '$'))
return pathIn;
String path = FixPath(pathIn);
#ifdef _WIN32
for (int i = 0; i < (int)path.length(); ++i)
path[i] = toupper(path[i]);
#endif
return path;
}
String Beefy::EnsureEndsInSlash(const StringImpl& dir)
{
if ((dir[dir.length() - 1] != '/') && (dir[dir.length() - 1] != '\\'))
return dir + "/";
return dir;
}
String Beefy::RemoveTrailingSlash(const StringImpl& str)
{
if (str.length() != 0)
{
char c = str[str.length() - 1];
if ((c == '\\') || (c == '/'))
return str.Substring(0, str.length() - 1);
}
return str;
}
bool Beefy::FileNameEquals(const StringImpl& filePathA, const StringImpl& filePathB)
{
#ifdef _WIN32
if (filePathA.length() != filePathB.length())
return false;
const char* aPtr = filePathA.c_str();
const char* bPtr = filePathB.c_str();
while (true)
{
char a = *(aPtr++);
char b = *(bPtr++);
if (a == 0)
return true;
if (a == b)
continue;
if (a == '/')
a = '\\';
if (b == '/')
b = '\\';
if (a == b)
continue;
if (::toupper(a) == ::toupper(b))
continue;
return false;
}
#else
return strcmp(filePathA.c_str(), filePathB.c_str()) == 0;
#endif
}
bool Beefy::RecursiveCreateDirectory(const StringImpl& dirName)
{
int slashPos = -1;
for (int i = (int)dirName.length() - 1; i >= 0; i--)
{
char c = dirName[i];
if ((c == '\\') || (c == '/'))
{
slashPos = i;
break;
}
}
if (slashPos != -1)
{
RecursiveCreateDirectory(dirName.Substring(0, slashPos));
}
BfpFileResult result;
BfpDirectory_Create(dirName.c_str(), &result);
return result == BfpFileResult_Ok;
}
bool Beefy::RecursiveDeleteDirectory(const StringImpl& dirPath)
{
String findSpec = dirPath + "/*.*";
bool failed = false;
BfpFileResult result;
BfpFindFileData* findFileData = BfpFindFileData_FindFirstFile(findSpec.c_str(), (BfpFindFileFlags)(BfpFindFileFlag_Directories | BfpFindFileFlag_Files), &result);
if (result == BfpFileResult_Ok)
{
while (true)
{
Beefy::String fileName;
BFP_GETSTR_HELPER(fileName, result, BfpFindFileData_GetFileName(findFileData, __STR, __STRLEN, &result));
String filePath = dirPath + "/" + fileName;
if ((BfpFindFileData_GetFileAttributes(findFileData) & BfpFileAttribute_Directory) != 0)
{
if (!RecursiveDeleteDirectory(filePath))
failed = true;
}
else
{
BfpFile_Delete(filePath.c_str(), &result);
if (result != BfpFileResult_Ok)
failed = true;
}
if (!BfpFindFileData_FindNextFile(findFileData))
break;
}
BfpFindFileData_Release(findFileData);
}
else if (result != BfpFileResult_NoResults)
{
return false;
}
BfpDirectory_Delete(dirPath.c_str(), &result);
return (result == BfpFileResult_Ok) && (!failed);
}
void Beefy::BFFatalError(const char* message, const char* file, int line)
{
BFFatalError(String(message), String(file), line);
}
bool Beefy::ParseMemorySpan(const StringImpl& str, void*& outPtr, int& outSize, StringImpl* outKey)
{
#ifndef BF_SMALL
static int anonymousIdx = 0;
if (str.StartsWith("@"))
{
int colon = (int)str.IndexOf(':');
String addrStr = str.Substring(1, colon - 1);
String lenStr = str.Substring(colon + 1);
outPtr = (void*)(intptr)strtoll(addrStr.c_str(), NULL, 16);
outSize = (int)strtol(lenStr.c_str(), NULL, 10);
if (outKey != NULL)
{
int nextColon = (int)str.IndexOf(':', colon + 1);
if (nextColon > 0)
{
*outKey = str.Substring(nextColon + 1);
}
else
{
int dotPos = (int)str.IndexOf('.', colon + 1);
*outKey = StrFormat("ANON_%d", anonymousIdx++) + str.Substring(dotPos);
}
}
return true;
}
#endif
return false;
}