diff --git a/docs/changelog.txt b/docs/changelog.txt index 4bcd39ea2..e3684cc9f 100644 --- a/docs/changelog.txt +++ b/docs/changelog.txt @@ -74,8 +74,12 @@ Template for new versions: - `modding-guide`: Add examples for script-only and blueprint-only mods that you can upload to DF's Steam Workshop ## API +- ``random_index``, ``vector_get_random``: new ``MiscUtils`` functions, for getting a random entry in a vector +- ``capitalize_string_words``: new ``MiscUtils`` function, returns string with all words capitalized +- ``grab_token_string_pos``: new ``MiscUtils`` function, used for parsing tokens ## Lua +- ``dfhack.capitalizeStringWords``: new function, returns string with all words capitalized ## Removed diff --git a/docs/dev/Lua API.rst b/docs/dev/Lua API.rst index ee68fd7fc..b58efbd0a 100644 --- a/docs/dev/Lua API.rst +++ b/docs/dev/Lua API.rst @@ -941,6 +941,10 @@ can be omitted. Note that the returned string may be longer than the input string. For example, ``ä`` is replaced with ``a``, and ``æ`` is replaced with ``ae``. +* ``dfhack.capitalizeStringWords(string)`` + + Return a version of the string with each word capitalized. + * ``dfhack.run_command(command[, ...])`` Run an arbitrary DFHack command, with the core suspended, and send output to diff --git a/library/LuaApi.cpp b/library/LuaApi.cpp index 1769baa90..fefcd42fe 100644 --- a/library/LuaApi.cpp +++ b/library/LuaApi.cpp @@ -1450,6 +1450,7 @@ static std::string df2utf(std::string s) { return DF2UTF(s); } static std::string utf2df(std::string s) { return UTF2DF(s); } static std::string df2console(color_ostream &out, std::string s) { return DF2CONSOLE(out, s); } static std::string toSearchNormalized(std::string s) { return to_search_normalized(s); } +static std::string capitalizeStringWords(std::string s) { return capitalize_string_words(s); } #define WRAP_VERSION_FUNC(name, function) WRAPN(name, DFHack::Version::function) @@ -1468,6 +1469,7 @@ static const LuaWrapper::FunctionReg dfhack_module[] = { WRAP(utf2df), WRAP(df2console), WRAP(toSearchNormalized), + WRAP(capitalizeStringWords), WRAP_VERSION_FUNC(getDFHackVersion, dfhack_version), WRAP_VERSION_FUNC(getDFHackRelease, dfhack_release), WRAP_VERSION_FUNC(getDFHackBuildID, dfhack_build_id), diff --git a/library/MiscUtils.cpp b/library/MiscUtils.cpp index a90cf1208..ba3af1f36 100644 --- a/library/MiscUtils.cpp +++ b/library/MiscUtils.cpp @@ -48,6 +48,11 @@ distribution. #include #include +int random_int(int max) +{ + return int(int64_t(rand()) * max / (int64_t(RAND_MAX) + 1)); +} + std::string stl_sprintf(const char *fmt, ...) { va_list lst; va_start(lst, fmt); @@ -171,6 +176,64 @@ std::string to_search_normalized(const std::string &str) return result; } +std::string capitalize_string_words(const std::string& str) +{ // Cleaned up from g_src/basics.cpp, and returns new string + std::string out = str; + bool starting = true; + int32_t bracket_count = 0; + bool conf; + + for (size_t s = 0; s < out.length(); s++) + { + if (out[s] == '[') { ++bracket_count; continue; } + else if (out[s] == ']') { --bracket_count; continue; } + else if (bracket_count > 0) continue; + + conf = false; + if (!starting) + { + if (out[s - 1] == ' ' || out[s - 1] == '\"') + conf = true; + // Discount single quote if it isn't preceded by space, comma, or nothing + else if (out[s - 1] == '\'' && s >= 2 && (out[s - 2] == ' ' || out[s - 2] == ',')) + conf = true; + } + + if (starting || conf) + { + // Capitalize + if (out[s] >= 'a' && out[s] <= 'z') + out[s] += 'A' - 'a'; + else + { + switch (out[s]) + { + case (char)129: // 'ü' + out[s] = (char)154; break; // 'Ü' + case (char)164: // 'ñ' + out[s] = (char)165; break; // 'Ñ' + case (char)132: // 'ä' + out[s] = (char)142; break; // 'Ä' + case (char)134: // 'å' + out[s] = (char)143; break; // 'Å' + case (char)130: // 'é' + out[s] = (char)144; break; // 'É' + case (char)148: // 'ö' + out[s] = (char)153; break; // 'Ö' + case (char)135: // 'ç' + out[s] = (char)128; break; // 'Ç' + case (char)145: // 'æ' + out[s] = (char)146; break; // 'Æ' + } + } + + starting = false; + } + } + + return out; +} + bool word_wrap(std::vector *out, const std::string &str, size_t line_length, word_wrap_whitespace_mode mode) { @@ -230,6 +293,21 @@ bool word_wrap(std::vector *out, const std::string &str, size_t lin return true; } +std::string grab_token_string_pos(const std::string& source, int32_t pos, char compc) +{ // Cleaned up from g_src/basics.cpp, return string instead of bool + std::string out; + + // Go until you hit compc, ']', or the end + for (auto s = source.begin() + pos; s < source.end(); ++s) + { + if (*s == compc || *s == ']') + break; + out += *s; + } + + return out; +} + bool prefix_matches(const std::string &prefix, const std::string &key, std::string *tail) { size_t ksize = key.size(); @@ -253,11 +331,6 @@ bool prefix_matches(const std::string &prefix, const std::string &key, std::stri return false; } -int random_int(int max) -{ - return int(int64_t(rand())*max/(int64_t(RAND_MAX)+1)); -} - #ifdef LINUX_BUILD // Linux uint64_t GetTimeMs64() { diff --git a/library/include/MiscUtils.h b/library/include/MiscUtils.h index 06915fde5..2ecda4d9c 100644 --- a/library/include/MiscUtils.h +++ b/library/include/MiscUtils.h @@ -61,6 +61,8 @@ namespace DFHack { class color_ostream; } +DFHACK_EXPORT int random_int(int max); + template void print_bits ( T val, std::ostream& out ) { @@ -178,6 +180,17 @@ inline int binsearch_index(const std::vector &vec, typename CT::key_pointer return CT::binsearch_index(vec, key, exact); } +template +int random_index(const std::vector& vec) +{ + if (vec.empty()) + return -1; + else if (vec.size() == 1) + return 0; + + return random_int(vec.size()); +} + template inline bool vector_contains(const std::vector &vec, KT key) { @@ -199,6 +212,12 @@ inline T vector_get(const std::vector &vec, unsigned idx, const T &defval = T return defval; } +template +inline T vector_get_random(const std::vector& vec, const T& defval = T()) +{ + return vector_get(vec, random_index(vec), defval); +} + template inline void vector_insert_at(std::vector &vec, unsigned idx, const T &val) { @@ -401,6 +420,7 @@ inline std::string join_strings(const std::string &separator, T &items) { DFHACK_EXPORT std::string toUpper(const std::string &str); DFHACK_EXPORT std::string toLower(const std::string &str); DFHACK_EXPORT std::string to_search_normalized(const std::string &str); +DFHACK_EXPORT std::string capitalize_string_words(const std::string& str); static inline std::string int_to_string(const int n) { std::ostringstream ss; @@ -444,6 +464,13 @@ DFHACK_EXPORT bool word_wrap(std::vector *out, const std::string &str, size_t line_length = 80, word_wrap_whitespace_mode mode = WSMODE_KEEP_ALL); +/** +* Function to assist in parsing tokens. Returns string from source pos until next compc, ']', or end. +* pos should be set to position after '[' to start with, then incremented by the result's size+1 before subsequent calls. +* compc is usually ':', but can be '.' for parsing number ranges. +* Based on Bay12's g_src/basics.cpp +*/ +std::string grab_token_string_pos(const std::string& source, int32_t pos, char compc = ':'); inline bool bits_match(unsigned required, unsigned ok, unsigned mask) { @@ -457,8 +484,6 @@ inline T clip_range(T a, T1 minv, T2 maxv) { return a; } -DFHACK_EXPORT int random_int(int max); - /** * Returns the amount of milliseconds elapsed since the UNIX epoch. * Works on both windows and linux.