mirror of
https://github.com/J-D-K/JKSV.git
synced 2026-03-22 01:34:13 -05:00
159 lines
5.8 KiB
C++
159 lines
5.8 KiB
C++
#include "stringutil.hpp"
|
||
|
||
#include <algorithm>
|
||
#include <cstdarg>
|
||
#include <cstring>
|
||
#include <ctime>
|
||
#include <span>
|
||
#include <string>
|
||
#include <switch.h>
|
||
#include <unordered_map>
|
||
#include <unordered_set>
|
||
|
||
// Defined at bottom.
|
||
static std::unordered_set<uint32_t> &get_forbidden_characters();
|
||
static std::unordered_map<uint32_t, std::string_view> &get_replacement_table();
|
||
|
||
std::string stringutil::get_formatted_string(const char *format, ...)
|
||
{
|
||
static constexpr size_t VA_BUFFER_SIZE = 0x1000;
|
||
|
||
std::array<char, VA_BUFFER_SIZE> vaBuffer = {0};
|
||
|
||
std::va_list vaList;
|
||
va_start(vaList, format);
|
||
vsnprintf(vaBuffer.data(), VA_BUFFER_SIZE, format, vaList);
|
||
va_end(vaList);
|
||
|
||
return std::string(vaBuffer.data());
|
||
}
|
||
|
||
void stringutil::replace_in_string(std::string &target, std::string_view find, std::string_view replace)
|
||
{
|
||
const size_t findLength = find.length();
|
||
const size_t replaceLength = replace.length();
|
||
|
||
for (size_t i = target.find(find); i != target.npos; i = target.find(find, i + replaceLength))
|
||
{
|
||
target.replace(i, findLength, replace);
|
||
}
|
||
}
|
||
|
||
void stringutil::strip_character(char c, std::string &target)
|
||
{
|
||
for (size_t i = target.find_first_of(c); i != target.npos; i = target.find_first_of(c, i))
|
||
{
|
||
target.erase(target.begin() + i);
|
||
}
|
||
}
|
||
|
||
bool stringutil::sanitize_string_for_path(const char *stringIn, char *stringOut, size_t stringOutSize)
|
||
{
|
||
uint32_t codepoint{};
|
||
const int length = std::char_traits<char>::length(stringIn);
|
||
auto &forbiddenChars = get_forbidden_characters();
|
||
auto &replacementTable = get_replacement_table();
|
||
|
||
for (int i = 0, outOffset = 0; i < length;)
|
||
{
|
||
const uint8_t *point = reinterpret_cast<const uint8_t *>(&stringIn[i]);
|
||
const ssize_t count = decode_utf8(&codepoint, point);
|
||
if (count <= 0 || outOffset + count >= static_cast<int>(stringOutSize)) { return false; }
|
||
|
||
// If it's forbidden, skip.
|
||
const bool isForbidden = forbiddenChars.find(codepoint) != forbiddenChars.end();
|
||
if (isForbidden)
|
||
{
|
||
i += count;
|
||
continue;
|
||
}
|
||
|
||
// Check for replacing.
|
||
const auto replace = replacementTable.find(codepoint);
|
||
if (replace != replacementTable.end())
|
||
{
|
||
const auto &[tablePoint, replacement] = *replace;
|
||
const size_t replacementLength = replacement.length();
|
||
|
||
const size_t remainingSize = stringOutSize - outOffset;
|
||
const std::span<char> stringSpan{&stringOut[outOffset], remainingSize};
|
||
std::copy(replacement.data(), replacement.data() + replacementLength, stringSpan.begin());
|
||
|
||
outOffset += replacementLength;
|
||
i += count;
|
||
continue;
|
||
}
|
||
|
||
// Final valid ASCII check.
|
||
const bool asciiCheck = codepoint < 0x20 || codepoint >= 0x7F;
|
||
if (asciiCheck) { return false; }
|
||
|
||
// Just copy it over.
|
||
const size_t remainingSpace = stringOutSize - outOffset;
|
||
const std::span<const char> stringSpan{&stringIn[i], static_cast<size_t>(count)};
|
||
const std::span<char> stringOutSpan{&stringOut[outOffset], remainingSpace};
|
||
std::copy(stringSpan.begin(), stringSpan.end(), stringOutSpan.begin());
|
||
|
||
outOffset += count;
|
||
i += count;
|
||
}
|
||
|
||
const int outLength = std::char_traits<char>::length(stringOut) - 1;
|
||
for (int i = outLength; i > 0 && (stringOut[i] == ' ' || stringOut[i] == '.'); i--) { stringOut[i] = '\0'; }
|
||
|
||
return true;
|
||
}
|
||
|
||
std::string stringutil::get_date_string(stringutil::DateFormat format)
|
||
{
|
||
static constexpr size_t STRING_BUFFER_SIZE = 0x80;
|
||
|
||
char stringBuffer[STRING_BUFFER_SIZE] = {0};
|
||
|
||
std::time_t timer{};
|
||
std::time(&timer);
|
||
const std::tm localTime = *std::localtime(&timer);
|
||
|
||
switch (format)
|
||
{
|
||
case stringutil::DateFormat::YearMonthDay:
|
||
{
|
||
std::strftime(stringBuffer, STRING_BUFFER_SIZE, "%Y-%m-%d_%H-%M-%S", &localTime);
|
||
}
|
||
break;
|
||
|
||
case stringutil::DateFormat::YearDayMonth:
|
||
{
|
||
std::strftime(stringBuffer, STRING_BUFFER_SIZE, "%Y-%d-%m_%H-%M-%S", &localTime);
|
||
}
|
||
break;
|
||
}
|
||
|
||
return std::string(stringBuffer);
|
||
}
|
||
|
||
static std::unordered_set<uint32_t> &get_forbidden_characters()
|
||
{
|
||
static std::unordered_set<uint32_t> forbidden =
|
||
{L',', L'/', L'\\', L'<', L'>', L':', L'"', L'|', L'?', L'*', L'™', L'©', L'®'};
|
||
|
||
return forbidden;
|
||
}
|
||
|
||
static std::unordered_map<uint32_t, std::string_view> &get_replacement_table()
|
||
{
|
||
static std::unordered_map<uint32_t, std::string_view> replacementTable = {
|
||
{L'Á', "A"}, {L'À', "A"}, {L'Â', "A"}, {L'Ä', "A"}, {L'Ã', "A"}, {L'Å', "A"}, {L'á', "a"}, {L'à', "a"},
|
||
{L'â', "a"}, {L'ä', "a"}, {L'ã', "a"}, {L'å', "a"}, {L'É', "E"}, {L'È', "E"}, {L'Ê', "E"}, {L'Ë', "E"},
|
||
{L'é', "e"}, {L'è', "e"}, {L'ê', "e"}, {L'ë', "e"}, {L'Í', "I"}, {L'Ì', "I"}, {L'Î', "I"}, {L'Ï', "I"},
|
||
{L'í', "i"}, {L'ì', "i"}, {L'î', "i"}, {L'ï', "i"}, {L'Ó', "O"}, {L'Ò', "O"}, {L'Ô', "O"}, {L'Ö', "O"},
|
||
{L'Õ', "O"}, {L'Ø', "O"}, {L'ó', "o"}, {L'ò', "o"}, {L'ô', "o"}, {L'ö', "o"}, {L'õ', "o"}, {L'ø', "o"},
|
||
{L'Ú', "U"}, {L'Ù', "U"}, {L'Û', "U"}, {L'Ü', "U"}, {L'ú', "u"}, {L'ù', "u"}, {L'û', "u"}, {L'ü', "u"},
|
||
{L'Ñ', "N"}, {L'ñ', "n"}, {L'Ç', "C"}, {L'ç', "c"}, {L'ẞ', "Ss"}, {L'ß', "ss"}, {L'Œ', "OE"}, {L'œ', "oe"},
|
||
{L'Æ', "AE"}, {L'æ', "ae"}, {L'‐', "-"}, {L'–', "-"}, {L'—', "-"}, {L'―', "-"}, {L' ', " "}, {L' ', " "},
|
||
{L' ', " "}, {L'Ⅰ', "I"}, {L'Ⅱ', "II"}, {L'Ⅲ', "III"}, {L'Ⅳ', "IV"}, {L'Ⅴ', "V"}, {L'※', "*"}, {L'×', "x"},
|
||
{L'‘', "'"}, {L'’', "'"}, {L'‛', "'"}, {L'′', "'"}, {L'ʼ', "'"}};
|
||
|
||
return replacementTable;
|
||
}
|