Files
ppsspp/Common/StringUtils.h
T
Henrik RydgårdandClaude Opus 5.5 b4a9462d92 CwCheat: Ignore invisible characters, and show parse errors
- Fixes #14955

Cheats copied from websites can carry invisible Unicode characters, like a
pop directional formatting mark after each address. That broke the hex
parse of every line, and a cheat with no valid lines didn't show up at all.
The parser now drops such characters (a new SanitizeString mode), and the
cheat screen shows the first few parse errors instead of silently leaving
cheats out.

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
2026-10-18 13:40:18 +02:00

257 lines
8.1 KiB
C++

// Copyright (C) 2003 Dolphin Project.
// This program is free software: you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
// the Free Software Foundation, version 2.0 or later versions.
// This program is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU General Public License 2.0 for more details.
// A copy of the GPL 2.0 should have been included with the program.
// If not, see http://www.gnu.org/licenses/
// Official SVN repository and contact information can be found at
// http://code.google.com/p/dolphin-emu/
#pragma once
#include <cstdarg>
#include <cstdint>
#include <string>
#include <cstring>
#include <string_view>
#include <vector>
#ifdef _MSC_VER
#define strncasecmp _strnicmp
#define strcasecmp _stricmp
#else
#include <strings.h>
#endif
// Useful for shaders with error messages..
std::string LineNumberString(const std::string &str);
std::string IndentString(const std::string &str, std::string_view sep, bool skipFirst = false);
// Other simple string utilities.
inline bool startsWith(std::string_view str, std::string_view key) {
if (str.size() < key.size())
return false;
return !memcmp(str.data(), key.data(), key.size());
}
inline bool endsWith(std::string_view str, std::string_view what) {
if (str.size() < what.size())
return false;
return str.substr(str.size() - what.size()) == what;
}
// Only use on strings where you're only concerned about ASCII.
inline bool startsWithNoCase(std::string_view str, std::string_view key) {
if (str.size() < key.size())
return false;
return strncasecmp(str.data(), key.data(), key.size()) == 0;
}
inline bool endsWithNoCase(std::string_view str, std::string_view key) {
if (str.size() < key.size())
return false;
const size_t offset = str.size() - key.size();
return strncasecmp(str.data() + offset, key.data(), key.size()) == 0;
}
inline bool equals(std::string_view str, std::string_view key) {
return str == key;
}
inline bool equalsNoCase(std::string_view str, std::string_view key) {
if (str.size() != key.size())
return false;
if (str.empty())
return true; // due to the check above, the other one is also empty.
return strncasecmp(str.data(), key.data(), key.size()) == 0;
}
bool containsNoCase(std::string_view haystack, std::string_view needle);
// Sigh
#ifdef None
#undef None
#endif
enum class StringRestriction {
None,
AlphaNumDashUnderscore, // Used for infrastructure usernames
// For deriving a filename from untrusted data (an ELF's module name, a disc ID). Unlike the
// above, disallowed characters become '_' rather than disappearing, so two different names
// can't silently collapse onto the same filename.
FileName,
NoLineBreaksOrSpecials, // Used for savedata UI. Removes line breaks, backslashes and similar.
ConvertToUnixEndings,
NoInvisibleFormatting, // Drops zero-width characters, direction marks and similar, turns no-break spaces into spaces.
};
std::string SanitizeString(std::string_view username, StringRestriction restriction, int minLength = 0, int maxLength = -1);
void DataToHexString(const uint8_t *data, size_t size, std::string *output, bool lineBreaks = true);
void DataToHexString(int indent, uint32_t startAddr, const uint8_t* data, size_t size, std::string *output);
std::string StringFromFormat(const char* format, ...);
std::string StringFromInt(int value);
std::string_view KeepAfterLast(std::string_view s, char c);
std::string_view KeepIncludingLast(std::string_view s, char c);
std::string_view StripSpaces(std::string_view s);
std::string_view StripQuotes(std::string_view s);
std::string_view StripPrefix(std::string_view prefix, std::string_view s);
int CountChar(std::string_view haystack, char needle);
// NOTE: str must live at least as long as all uses of output.
void SplitString(std::string_view str, const char delim, std::vector<std::string_view> &output);
// Try to avoid this when possible, in favor of the string_view version.
void SplitString(std::string_view str, const char delim, std::vector<std::string> &output, bool trimOutput = false);
// Splits on the first occurrence of delim. Returns true if the delimiter was found.
bool SplitStringOnce(std::string_view str, std::string_view *firstPart, std::string_view *secondPart, char delim);
void GetQuotedStrings(std::string_view str, std::vector<std::string> &output);
std::string ReplaceAll(std::string_view input, std::string_view src, std::string_view dest);
// Takes something like R&eplace and returns Replace, plus writes 'e' to *shortcutChar
// if not nullptr. Useful for Windows menu strings.
std::string UnescapeMenuString(std::string_view input, char *shortcutChar);
void SkipSpace(const char **ptr);
size_t truncate_cpy(char *dest, size_t destSize, const char *src);
template<size_t Count>
inline size_t truncate_cpy(char(&out)[Count], const char *src) {
return truncate_cpy(out, Count, src);
}
size_t truncate_cpy(char *dest, size_t destSize, std::string_view src);
template<size_t Count>
inline size_t truncate_cpy(char(&out)[Count], std::string_view src) {
return truncate_cpy(out, Count, src);
}
template<size_t Count>
inline std::string_view StringViewFromFixedSizeField(const char(&field)[Count]) {
return std::string_view(field, strnlen(field, Count));
}
inline std::string join(std::string_view a, std::string_view b) {
std::string result;
result.reserve(a.size() + b.size());
result.append(a);
result.append(b);
return result;
}
inline const char *safe_string(const char *s) {
return s ? s : "(null)";
}
template<size_t Count>
inline size_t truncate_cpy_len(char(&out)[Count], const char *src, size_t srcLen) {
if (srcLen >= Count) {
memcpy(out, src, Count - 1);
out[Count - 1] = '\0';
return Count - 1;
} else {
memcpy(out, src, srcLen);
out[srcLen] = '\0';
return srcLen;
}
}
inline size_t truncate_cat(char *out, size_t outLen, const char *src1, size_t src1Len, const char *src2, size_t src2Len) {
if (src1Len >= outLen) {
memcpy(out, src1, outLen);
out[outLen - 1] = '\0';
return outLen - 1;
}
memcpy(out, src1, src1Len);
size_t pos = src1Len;
size_t remaining = outLen - src1Len;
if (src2Len >= remaining) {
memcpy(out + pos, src2, remaining);
out[outLen - 1] = '\0';
return outLen - 1;
}
memcpy(out + pos, src2, src2Len);
out[pos + src2Len] = '\0';
return src1Len + src2Len;
}
long parseHexLong(const std::string &s);
long parseLong(std::string s);
// Cheap!
bool CharArrayFromFormatV(char* out, int outsize, const char* format, va_list args);
template<size_t Count>
inline void CharArrayFromFormat(char (& out)[Count], const char* format, ...)
{
va_list args;
va_start(args, format);
CharArrayFromFormatV(out, Count, format, args);
va_end(args);
}
inline void CopyStrings(std::vector<std::string> *output, const std::vector<std::string_view> &input) {
output->clear();
output->reserve(input.size());
for (auto str : input) {
output->emplace_back(str);
}
}
void MakeUnique(std::vector<std::string> &vec);
size_t SplitSearch(std::string_view needle, std::string_view part1, std::string_view part2);
// Replaces %1, %2, %3 in format with arg1, arg2, arg3.
// Much safer than snprintf and friends.
// For mixes of strings and ints, manually convert the ints to strings.
std::string ApplySafeSubstitutions(std::string_view format, std::string_view string1, std::string_view string2 = "", std::string_view string3 = "", std::string_view string4 = "");
std::string ApplySafeSubstitutions(std::string_view format, int i1, int i2 = 0, int i3 = 0, int i4 = 0);
// Not really a string util.
template<class T>
bool Contains(const std::vector<T> &vec, const T &needle) {
for (const auto &item : vec) {
if (item == needle) {
return true;
}
}
return false;
}
inline bool ContainsNoCase(const std::vector<std::string> &vec, std::string_view needle) {
for (const auto &item : vec) {
if (equalsNoCase(item, needle)) {
return true;
}
}
return false;
}
inline bool RemoveNoCase(std::vector<std::string> &vec, std::string_view needle) {
for (auto it = vec.begin(); it != vec.end(); ++it) {
if (equalsNoCase(*it, needle)) {
vec.erase(it);
return true;
}
}
return false;
}