mirror of
https://github.com/MonsterDruide1/OdysseyDecomp
synced 2026-04-23 09:04:21 +00:00
357 lines
9.8 KiB
C++
357 lines
9.8 KiB
C++
#include "Library/Base/StringUtil.h"
|
|
|
|
#include <prim/seadStringUtil.h>
|
|
|
|
namespace al {
|
|
const char* createStringIfInStack(const char* str) {
|
|
if (!isInStack(str))
|
|
return str;
|
|
|
|
s32 len = strlen(str) + 1;
|
|
char* buffer = new char[len];
|
|
snprintf(buffer, len, "%s", str);
|
|
|
|
return buffer;
|
|
}
|
|
|
|
const char* createConcatString(const char* start, const char* end) {
|
|
s32 len = strlen(start) + strlen(end) + 1;
|
|
char* buffer = new char[len];
|
|
snprintf(buffer, len, "%s%s", start, end);
|
|
|
|
return buffer;
|
|
}
|
|
|
|
void createFileNameBySuffix(sead::BufferedSafeString* out, const char* name, const char* suffix) {
|
|
out->clear();
|
|
if (!suffix) {
|
|
out->append(name);
|
|
return;
|
|
}
|
|
out->append(name);
|
|
out->append(suffix);
|
|
}
|
|
|
|
u32 outputValueWithComma(char* out, u32 size, u64 value, bool usePadding, bool padToThousands) {
|
|
if (value > 999999999) {
|
|
return sead::StringUtil::snprintf(out, size, "%3d,%03d,%03d,%03d",
|
|
(u32)(value / 1000000000), (u32)(value / 1000000 % 1000),
|
|
(u32)(value / 1000 % 1000), (u32)(value % 1000));
|
|
}
|
|
if (value > 999999) {
|
|
if (usePadding) {
|
|
return sead::StringUtil::snprintf(out, size, "%3d,%03d,%03d", (u32)(value / 1000000),
|
|
(u32)(value / 1000 % 1000), (u32)(value % 1000));
|
|
}
|
|
return sead::StringUtil::snprintf(out, size, "%d,%03d,%03d", (u32)(value / 1000000),
|
|
(u32)(value / 1000 % 1000), (u32)(value % 1000));
|
|
}
|
|
if (value > 999) {
|
|
if (usePadding) {
|
|
if (padToThousands) {
|
|
return sead::StringUtil::snprintf(out, size, "%3d,%03d", (u32)(value / 1000),
|
|
(u32)(value % 1000));
|
|
}
|
|
return sead::StringUtil::snprintf(out, size, " %3d,%03d", (u32)(value / 1000),
|
|
(u32)(value % 1000));
|
|
}
|
|
return sead::StringUtil::snprintf(out, size, "%d,%03d", (u32)(value / 1000),
|
|
(u32)(value % 1000));
|
|
}
|
|
if (usePadding) {
|
|
if (padToThousands)
|
|
return sead::StringUtil::snprintf(out, size, " %3d", (u32)value);
|
|
return sead::StringUtil::snprintf(out, size, " %3d", (u32)value);
|
|
}
|
|
return sead::StringUtil::snprintf(out, size, "%d", (u32)value);
|
|
}
|
|
|
|
void extractString(char* out, const char* str, u32 len, u32 unused) {
|
|
strncpy(out, str, len);
|
|
out[len] = '\0';
|
|
}
|
|
|
|
const char* searchSubString(const char* str, const char* substr) {
|
|
return searchSubString(str, substr, strlen(substr));
|
|
}
|
|
|
|
const char* searchSubString(const char* str, const char* substr, s32 substrLen) {
|
|
while (str[0] != '\0') {
|
|
s32 size = 0;
|
|
for (s32 index = 0; index < substrLen; index++) {
|
|
if (str[index] == '\0' || str[index] != substr[index])
|
|
break;
|
|
size++;
|
|
}
|
|
|
|
if (size == substrLen)
|
|
return str;
|
|
|
|
str++;
|
|
}
|
|
|
|
return nullptr;
|
|
}
|
|
|
|
// const char* getSubStringUnmatched(const char**, const char*, const MatchStr&,
|
|
// void (*)(const char*, const char*, void*), void*);
|
|
// const char* getSubStringUnmatched(const char*, const MatchStr&);
|
|
// void extractBaseNameW(sead::WBufferedSafeString*, const sead::WSafeString&);
|
|
|
|
void removeExtensionString(char* out, u32 len, const char* str) {
|
|
snprintf(out, len, "%s", str);
|
|
char* dot = strchr(out, '.');
|
|
char* dirSeparator = strchr(out, '/');
|
|
|
|
if (!dot || dot < dirSeparator || ++dirSeparator == dot)
|
|
return;
|
|
|
|
*dot = '\0';
|
|
}
|
|
|
|
void removeStringFromEnd(char* out, u32 len, const char* end, const char* str) {
|
|
snprintf(out, len, "%s", str);
|
|
|
|
s32 lenStr = strlen(out);
|
|
s32 lenEnd = strlen(end);
|
|
|
|
if (lenEnd > lenStr)
|
|
return;
|
|
|
|
out[lenStr - lenEnd] = '\0';
|
|
}
|
|
|
|
void translateCharacters(char* string, const char* charmap, const char* newCharmap) {
|
|
while (charmap[0] != '\0') {
|
|
for (s32 index = 0; string[index] != '\0'; index++)
|
|
if (string[index] == *charmap)
|
|
string[index] = *newCharmap;
|
|
|
|
newCharmap++;
|
|
charmap++;
|
|
}
|
|
}
|
|
|
|
bool tryReplaceString(sead::BufferedSafeString* out, const char* oldStr, const char* newStr) {
|
|
return tryReplaceString(out, out->cstr(), oldStr, newStr);
|
|
}
|
|
|
|
bool tryReplaceString(sead::BufferedSafeString* out, const char* targetStr, const char* oldStr,
|
|
const char* newStr) {
|
|
const char* subStr = searchSubString(targetStr, oldStr);
|
|
|
|
if (!subStr)
|
|
return false;
|
|
|
|
StringTmp<1024> before;
|
|
StringTmp<1024> after;
|
|
|
|
if (subStr != targetStr)
|
|
before.copy(targetStr, subStr - targetStr);
|
|
after.copy(subStr + std::strlen(oldStr));
|
|
|
|
tryReplaceString(&after, oldStr, newStr);
|
|
out->format("%s%s%s", before.cstr(), newStr, after.cstr());
|
|
return true;
|
|
}
|
|
|
|
bool tryReplaceStringNoRecursive(sead::BufferedSafeString* out, const char* targetStr,
|
|
const char* oldStr, const char* newStr) {
|
|
const char* subStr = searchSubString(targetStr, oldStr);
|
|
|
|
if (!subStr)
|
|
return false;
|
|
|
|
StringTmp<256> before;
|
|
StringTmp<256> after;
|
|
|
|
if (subStr != targetStr)
|
|
before.copy(targetStr, subStr - targetStr);
|
|
after.copy(subStr + std::strlen(oldStr));
|
|
|
|
out->format("%s%s%s", before.cstr(), newStr, after.cstr());
|
|
return true;
|
|
}
|
|
|
|
bool isEqualString(const char16* str1, const char16* str2) {
|
|
while (*str1 == *str2) {
|
|
char16 val = *str1;
|
|
|
|
if (!val)
|
|
return true;
|
|
|
|
str2++;
|
|
str1++;
|
|
}
|
|
|
|
return false;
|
|
}
|
|
|
|
bool isEqualSubString(const char* str, const char* subStr) {
|
|
return strstr(str, subStr) != nullptr;
|
|
}
|
|
|
|
bool isEqualSubString(const sead::SafeString& str, const sead::SafeString& subStr) {
|
|
return isEqualSubString(str.cstr(), subStr.cstr());
|
|
}
|
|
|
|
bool isStartWithString(const char* str, const char* start) {
|
|
if (*start == '\0')
|
|
return true;
|
|
|
|
while (*str != '\0' && *str == *start) {
|
|
str++;
|
|
start++;
|
|
|
|
if (*start == '\0')
|
|
return true;
|
|
}
|
|
|
|
return false;
|
|
}
|
|
|
|
bool isEndWithString(const char* str, const char* end) {
|
|
s32 lenStr = strlen(str);
|
|
s32 lenEnd = strlen(end);
|
|
|
|
if (lenStr < lenEnd)
|
|
return false;
|
|
|
|
return isEqualString(&str[lenStr - lenEnd], end);
|
|
}
|
|
|
|
bool isMatchString(const char* str, const MatchStr& matchStr) {
|
|
const char* subStr = getSubStringUnmatched(str, matchStr);
|
|
|
|
return subStr && subStr[0] == '\0';
|
|
}
|
|
|
|
s32 compareStringIgnoreCase(const char* str1, const char* str2) {
|
|
return strcasecmp(str1, str2);
|
|
}
|
|
|
|
inline bool isLetter(char ch) {
|
|
return ch - 'A' < 26u || ch - 'a' < 26u;
|
|
}
|
|
|
|
inline bool isDigit(char ch) {
|
|
return ch - '0' < 10u;
|
|
}
|
|
|
|
// NON_MATCHING: Using inverse sub operation https://decomp.me/scratch/rPdbr
|
|
void makeUrlEncodeString(char* out, u32 outLen, const char* str) {
|
|
out[0] = '\0';
|
|
|
|
for (; str[0] != '\0'; str++) {
|
|
char newSymbols[4] = {'\0', '\0', '\0', '\0'};
|
|
s64 bytesWritten = 1;
|
|
|
|
if (str[0] == ' ') {
|
|
newSymbols[0] = '+';
|
|
} else if (isLetter(str[0]) || isDigit(str[0])) {
|
|
newSymbols[0] = str[0];
|
|
} else {
|
|
char digit1 = (str[0] & 0xf0) >> 4;
|
|
char digit2 = (str[0] & 0x0f) >> 0;
|
|
|
|
newSymbols[0] = '%';
|
|
newSymbols[1] = digit1 < 10 ? digit1 + '0' : digit1 + 'A' - 10;
|
|
newSymbols[2] = digit2 < 10 ? digit2 + '0' : digit2 + 'A' - 10;
|
|
bytesWritten = 3;
|
|
}
|
|
|
|
if (strlen(out) + bytesWritten > outLen) {
|
|
out[outLen - 1] = '\0';
|
|
return;
|
|
}
|
|
|
|
strcat(out, newSymbols);
|
|
}
|
|
}
|
|
|
|
void makeUrlDecodeString(char* out, u32 outLen, const char* str) {
|
|
out[0] = '\0';
|
|
std::memset(out, 0, outLen);
|
|
|
|
s32 bytesRead = 1;
|
|
for (; str[0] != '\0'; str += bytesRead) {
|
|
char newSymbols[2] = {'\0', '\0'};
|
|
|
|
if (str[0] == '+') {
|
|
newSymbols[0] = ' ';
|
|
bytesRead = 1;
|
|
} else if (str[0] == '%') {
|
|
u8 val1 = str[1] < 'A' ? str[1] - '0' : str[1] - 'A' + 10;
|
|
u8 val2 = str[2] < 'A' ? str[2] - '0' : str[2] - 'A' + 10;
|
|
|
|
newSymbols[0] = (val1 << 4) | val2;
|
|
bytesRead = 3;
|
|
} else {
|
|
newSymbols[0] = str[0];
|
|
bytesRead = 1;
|
|
}
|
|
|
|
if (strlen(out) + bytesRead > outLen) {
|
|
out[outLen - 1] = '\0';
|
|
return;
|
|
}
|
|
|
|
strcat(out, newSymbols);
|
|
}
|
|
}
|
|
|
|
void copyString(char* out, const char* str, u32 len) {
|
|
strncpy(out, str, len);
|
|
}
|
|
|
|
void copyStringW(char16* out, const char16* str, u32 len) {
|
|
sead::StringUtil::wcs16cpy(out, len, str);
|
|
}
|
|
|
|
// Note: different cpp file from here?
|
|
|
|
// Attr required for createStringIfInStack
|
|
__attribute__((noinline)) bool isInStack(const void* element) {
|
|
return sead::MemUtil::isStack(element);
|
|
}
|
|
|
|
// Attr required for isEndWithString
|
|
__attribute__((noinline)) bool isEqualString(const char* str1, const char* str2) {
|
|
while (*str1 == *str2) {
|
|
char val = *str1;
|
|
|
|
if (!val)
|
|
return true;
|
|
|
|
str2++;
|
|
str1++;
|
|
}
|
|
|
|
return false;
|
|
}
|
|
|
|
bool isEqualString(const sead::SafeString& safestr1, const sead::SafeString& safestr2) {
|
|
const char* str1 = safestr1.cstr();
|
|
const char* str2 = safestr2.cstr();
|
|
while (*str1 == *str2) {
|
|
char val = *str1;
|
|
|
|
if (!val)
|
|
return true;
|
|
|
|
str2++;
|
|
str1++;
|
|
}
|
|
|
|
return false;
|
|
}
|
|
|
|
bool isEqualStringCase(const char* str1, const char* str2) {
|
|
return compareStringIgnoreCase(str1, str2) == 0;
|
|
}
|
|
|
|
bool isEqualStringCase(const sead::SafeString& str1, const sead::SafeString& str2) {
|
|
return isEqualStringCase(str1.cstr(), str2.cstr());
|
|
}
|
|
} // namespace al
|