diff --git a/src/console.cpp b/src/console.cpp index 450fabd3fe..80ba5fa439 100644 --- a/src/console.cpp +++ b/src/console.cpp @@ -255,8 +255,7 @@ std::string RemoveUnderscores(std::string name) */ static void IConsoleAliasExec(const IConsoleAlias *alias, byte tokencount, char *tokens[ICON_TOKEN_COUNT], const uint recurse_count) { - char alias_buffer[ICON_MAX_STREAMSIZE] = { '\0' }; - char *alias_stream = alias_buffer; + std::string alias_buffer; DEBUG(console, 6, "Requested command is an alias; parsing..."); @@ -268,14 +267,13 @@ static void IConsoleAliasExec(const IConsoleAlias *alias, byte tokencount, char for (const char *cmdptr = alias->cmdline.c_str(); *cmdptr != '\0'; cmdptr++) { switch (*cmdptr) { case '\'': // ' will double for "" - alias_stream = strecpy(alias_stream, "\"", lastof(alias_buffer)); + alias_buffer += '\"'; break; case ';': // Cmd separator; execute previous and start new command - IConsoleCmdExec(alias_buffer, recurse_count); + IConsoleCmdExec(alias_buffer.c_str(), recurse_count); - alias_stream = alias_buffer; - *alias_stream = '\0'; // Make sure the new command is terminated. + alias_buffer.clear(); cmdptr++; break; @@ -285,21 +283,21 @@ static void IConsoleAliasExec(const IConsoleAlias *alias, byte tokencount, char switch (*cmdptr) { case '+': { // All parameters separated: "[param 1]" "[param 2]" for (uint i = 0; i != tokencount; i++) { - if (i != 0) alias_stream = strecpy(alias_stream, " ", lastof(alias_buffer)); - alias_stream = strecpy(alias_stream, "\"", lastof(alias_buffer)); - alias_stream = strecpy(alias_stream, tokens[i], lastof(alias_buffer)); - alias_stream = strecpy(alias_stream, "\"", lastof(alias_buffer)); + if (i != 0) alias_buffer += ' '; + alias_buffer += '\"'; + alias_buffer += tokens[i]; + alias_buffer += '\"'; } break; } case '!': { // Merge the parameters to one: "[param 1] [param 2] [param 3...]" - alias_stream = strecpy(alias_stream, "\"", lastof(alias_buffer)); + alias_buffer += '\"'; for (uint i = 0; i != tokencount; i++) { - if (i != 0) alias_stream = strecpy(alias_stream, " ", lastof(alias_buffer)); - alias_stream = strecpy(alias_stream, tokens[i], lastof(alias_buffer)); + if (i != 0) alias_buffer += " "; + alias_buffer += tokens[i]; } - alias_stream = strecpy(alias_stream, "\"", lastof(alias_buffer)); + alias_buffer += '\"'; break; } @@ -312,27 +310,26 @@ static void IConsoleAliasExec(const IConsoleAlias *alias, byte tokencount, char return; } - alias_stream = strecpy(alias_stream, "\"", lastof(alias_buffer)); - alias_stream = strecpy(alias_stream, tokens[param], lastof(alias_buffer)); - alias_stream = strecpy(alias_stream, "\"", lastof(alias_buffer)); + alias_buffer += '\"'; + alias_buffer += tokens[param]; + alias_buffer += '\"'; break; } } break; default: - *alias_stream++ = *cmdptr; - *alias_stream = '\0'; + alias_buffer += *cmdptr; break; } - if (alias_stream >= lastof(alias_buffer) - 1) { + if (alias_buffer.size() >= ICON_MAX_STREAMSIZE - 1) { IConsoleError("Requested alias execution would overflow execution buffer"); return; } } - IConsoleCmdExec(alias_buffer, recurse_count); + IConsoleCmdExec(alias_buffer.c_str(), recurse_count); } /** diff --git a/src/core/mem_func.hpp b/src/core/mem_func.hpp index 12acce13fc..81bf903fea 100644 --- a/src/core/mem_func.hpp +++ b/src/core/mem_func.hpp @@ -65,37 +65,4 @@ static inline int MemCmpT(const T *ptr1, const T *ptr2, size_t num = 1) return memcmp(ptr1, ptr2, num * sizeof(T)); } -/** - * Type safe memory reverse operation. - * Reverse a block of memory in steps given by the - * type of the pointers. - * - * @param ptr1 Start-pointer to the block of memory. - * @param ptr2 End-pointer to the block of memory. - */ -template -static inline void MemReverseT(T *ptr1, T *ptr2) -{ - assert(ptr1 != nullptr && ptr2 != nullptr); - assert(ptr1 < ptr2); - - do { - Swap(*ptr1, *ptr2); - } while (++ptr1 < --ptr2); -} - -/** - * Type safe memory reverse operation (overloaded) - * - * @param ptr Pointer to the block of memory. - * @param num The number of items we want to reverse. - */ -template -static inline void MemReverseT(T *ptr, size_t num) -{ - assert(ptr != nullptr); - - MemReverseT(ptr, ptr + (num - 1)); -} - #endif /* MEM_FUNC_HPP */ diff --git a/src/game/game_text.cpp b/src/game/game_text.cpp index a905c75864..3b4e3d7fa5 100644 --- a/src/game/game_text.cpp +++ b/src/game/game_text.cpp @@ -288,10 +288,9 @@ static void ExtractStringParams(const StringData &data, StringParamsList ¶ms if (ls != nullptr) { StringParams ¶m = params.emplace_back(); - ParsedCommandStruct pcs; - ExtractCommandString(&pcs, ls->english, false); + ParsedCommandStruct pcs = ExtractCommandString(ls->english.c_str(), false); - for (const CmdStruct *cs : pcs.cmd) { + for (const CmdStruct *cs : pcs.consuming_commands) { if (cs == nullptr) break; param.emplace_back(GetParamType(cs), cs->consumes); } diff --git a/src/gfx.cpp b/src/gfx.cpp index ba8a87368f..e8d2ee94f7 100644 --- a/src/gfx.cpp +++ b/src/gfx.cpp @@ -2230,77 +2230,39 @@ void SetAnimatedMouseCursor(const AnimCursor *table) } /** - * Update cursor position on mouse movement for relative modes. + * Update cursor position based on a relative change. + * * @param delta_x How much change in the X position. * @param delta_y How much change in the Y position. */ void CursorVars::UpdateCursorPositionRelative(int delta_x, int delta_y) { - if (this->fix_at) { - this->delta.x = delta_x; - this->delta.y = delta_y; - } else { - int last_position_x = this->pos.x; - int last_position_y = this->pos.y; + assert(this->fix_at); - this->pos.x = Clamp(this->pos.x + delta_x, 0, _cur_resolution.width - 1); - this->pos.y = Clamp(this->pos.y + delta_y, 0, _cur_resolution.height - 1); - - this->delta.x = last_position_x - this->pos.x; - this->delta.y = last_position_y - this->pos.y; - - this->dirty = true; - } + this->delta.x = delta_x; + this->delta.y = delta_y; } /** * Update cursor position on mouse movement. * @param x New X position. * @param y New Y position. - * @param queued_warp True, if the OS queues mouse warps after pending mouse movement events. - * False, if the warp applies instantaneous. * @return true, if the OS cursor position should be warped back to this->pos. */ -bool CursorVars::UpdateCursorPosition(int x, int y, bool queued_warp) +bool CursorVars::UpdateCursorPosition(int x, int y) { - /* Detecting relative mouse movement is somewhat tricky. - * - There may be multiple mouse move events in the video driver queue (esp. when OpenTTD lags a bit). - * - When we request warping the mouse position (return true), a mouse move event is appended at the end of the queue. - * - * So, when this->fix_at is active, we use the following strategy: - * - The first movement triggers the warp to reset the mouse position. - * - Subsequent events have to compute movement relative to the previous event. - * - The relative movement is finished, when we receive the event matching the warp. - */ + this->delta.x = x - this->pos.x; + this->delta.y = y - this->pos.y; - if (x == this->pos.x && y == this->pos.y) { - /* Warp finished. */ - this->queued_warp = false; - } - - this->delta.x = x - (this->queued_warp ? this->last_position.x : this->pos.x); - this->delta.y = y - (this->queued_warp ? this->last_position.y : this->pos.y); - - this->last_position.x = x; - this->last_position.y = y; - - bool need_warp = false; if (this->fix_at) { - if (this->delta.x != 0 || this->delta.y != 0) { - /* Trigger warp. - * Note: We also trigger warping again, if there is already a pending warp. - * This makes it more tolerant about the OS or other software in between - * botchering the warp. */ - this->queued_warp = queued_warp; - need_warp = true; - } + return this->delta.x != 0 || this->delta.y != 0; } else if (this->pos.x != x || this->pos.y != y) { - this->queued_warp = false; // Cancel warping, we are no longer confining the position. this->dirty = true; this->pos.x = x; this->pos.y = y; } - return need_warp; + + return false; } bool ChangeResInGame(int width, int height) diff --git a/src/gfx_type.h b/src/gfx_type.h index 62434cd2ac..71870c7a13 100644 --- a/src/gfx_type.h +++ b/src/gfx_type.h @@ -145,11 +145,7 @@ struct CursorVars { bool vehchain; ///< vehicle chain is dragged void UpdateCursorPositionRelative(int delta_x, int delta_y); - bool UpdateCursorPosition(int x, int y, bool queued_warp); - -private: - bool queued_warp; - Point last_position; + bool UpdateCursorPosition(int x, int y); }; /** Data about how and where to blit pixels. */ diff --git a/src/network/network_survey.cpp b/src/network/network_survey.cpp index 82ed9cba81..59f361ba12 100644 --- a/src/network/network_survey.cpp +++ b/src/network/network_survey.cpp @@ -270,6 +270,33 @@ static void SurveyGameScript(nlohmann::json &survey) survey = fmt::format("{}.{}", Game::GetInfo()->GetName(), Game::GetInfo()->GetVersion()); } +/** + * Change the bytes of memory into a textual version rounded up to the biggest unit. + * + * For example, 16751108096 would become 16 GiB. + * + * @param memory The bytes of memory. + * @return std::string A textual representation. + */ +std::string SurveyMemoryToText(uint64_t memory) +{ + memory = memory / 1024; // KiB + memory = CeilDiv(memory, 1024); // MiB + + /* Anything above 512 MiB we represent in GiB. */ + if (memory > 512) { + return fmt::format("{} GiB", CeilDiv(memory, 1024)); + } + + /* Anything above 64 MiB we represent in a multiplier of 128 MiB. */ + if (memory > 64) { + return fmt::format("{} MiB", Ceil(memory, 128)); + } + + /* Anything else in a multiplier of 4 MiB. */ + return fmt::format("{} MiB", Ceil(memory, 4)); +} + #endif /* WITH_NLOHMANN_JSON */ /** @@ -300,8 +327,6 @@ std::string NetworkSurveyHandler::CreatePayload(Reason reason, bool for_preview) { auto &info = survey["info"]; SurveyOS(info["os"]); - info["os"]["hardware_concurrency"] = std::thread::hardware_concurrency(); - SurveyOpenTTD(info["openttd"]); SurveyConfiguration(info["configuration"]); SurveyFont(info["font"]); diff --git a/src/openttd.cpp b/src/openttd.cpp index ff7373fa75..c37631caa8 100644 --- a/src/openttd.cpp +++ b/src/openttd.cpp @@ -595,9 +595,9 @@ void MakeNewgameSettingsLive() void OpenBrowser(const char *url) { /* Make sure we only accept urls that are sure to open a browser. */ - if (strstr(url, "http://") != url && strstr(url, "https://") != url) return; - - OSOpenBrowser(url); + if (StrStartsWith(url, "http://") || StrStartsWith(url, "https://")) { + OSOpenBrowser(url); + } } /** Callback structure of statements to be executed after the NewGRF scan. */ diff --git a/src/os/macosx/macos.mm b/src/os/macosx/macos.mm index 4eda4c6562..21f58341bd 100644 --- a/src/os/macosx/macos.mm +++ b/src/os/macosx/macos.mm @@ -189,25 +189,21 @@ const char *GetCurrentLocale(const char *) /** * Return the contents of the clipboard (COCOA). * - * @param buffer Clipboard content. - * @param last The pointer to the last element of the destination buffer - * @return Whether clipboard is empty or not. + * @return The (optional) clipboard contents. */ -bool GetClipboardContents(char *buffer, const char *last) +std::optional GetClipboardContents() { NSPasteboard *pb = [ NSPasteboard generalPasteboard ]; NSArray *types = [ NSArray arrayWithObject:NSPasteboardTypeString ]; NSString *bestType = [ pb availableTypeFromArray:types ]; /* Clipboard has no text data available. */ - if (bestType == nil) return false; + if (bestType == nil) return std::nullopt; NSString *string = [ pb stringForType:NSPasteboardTypeString ]; - if (string == nil || [ string length ] == 0) return false; + if (string == nil || [ string length ] == 0) return std::nullopt; - strecpy(buffer, [ string UTF8String ], last); - - return true; + return [ string UTF8String ]; } /** Set the application's bundle directory. diff --git a/src/os/macosx/survey_osx.cpp b/src/os/macosx/survey_osx.cpp index d553a81989..8affddd34b 100644 --- a/src/os/macosx/survey_osx.cpp +++ b/src/os/macosx/survey_osx.cpp @@ -16,9 +16,12 @@ #include #include +#include #include "../../safeguards.h" +extern std::string SurveyMemoryToText(uint64_t memory); + void SurveyOS(nlohmann::json &json) { int ver_maj, ver_min, ver_bug; @@ -32,7 +35,8 @@ void SurveyOS(nlohmann::json &json) json["min_ver"] = MAC_OS_X_VERSION_MIN_REQUIRED; json["max_ver"] = MAC_OS_X_VERSION_MAX_ALLOWED; - json["memory"] = MacOSGetPhysicalMemory(); + json["memory"] = SurveyMemoryToText(MacOSGetPhysicalMemory()); + json["hardware_concurrency"] = std::thread::hardware_concurrency(); } #endif /* WITH_NLOHMANN_JSON */ diff --git a/src/os/os2/os2.cpp b/src/os/os2/os2.cpp index 546a06ad5d..bcb056e1b2 100644 --- a/src/os/os2/os2.cpp +++ b/src/os/os2/os2.cpp @@ -174,27 +174,25 @@ int CDECL main(int argc, char *argv[]) return openttd_main(argc, argv); } -bool GetClipboardContents(char *buffer, const char *last) +std::optional GetClipboardContents() { /* XXX -- Currently no clipboard support implemented with GCC */ #ifndef __INNOTEK_LIBC__ HAB hab = 0; - if (WinOpenClipbrd(hab)) - { - const char *text = (const char*)WinQueryClipbrdData(hab, CF_TEXT); + if (WinOpenClipbrd(hab)) { + const char *text = (const char *)WinQueryClipbrdData(hab, CF_TEXT); - if (text != nullptr) - { - strecpy(buffer, text, last); + if (text != nullptr) { + std::string result = text; WinCloseClipbrd(hab); - return true; + return result; } WinCloseClipbrd(hab); } #endif - return false; + return std::nullopt; } diff --git a/src/os/unix/survey_unix.cpp b/src/os/unix/survey_unix.cpp index d86b58e98c..d831a78390 100644 --- a/src/os/unix/survey_unix.cpp +++ b/src/os/unix/survey_unix.cpp @@ -13,10 +13,13 @@ #include #include +#include #include #include "../../safeguards.h" +extern std::string SurveyMemoryToText(uint64_t memory); + void SurveyOS(nlohmann::json &json) { struct utsname name; @@ -32,7 +35,8 @@ void SurveyOS(nlohmann::json &json) long pages = sysconf(_SC_PHYS_PAGES); long page_size = sysconf(_SC_PAGE_SIZE); - json["memory"] = pages * page_size; + json["memory"] = SurveyMemoryToText(pages * page_size); + json["hardware_concurrency"] = std::thread::hardware_concurrency(); } #endif /* WITH_NLOHMANN_JSON */ diff --git a/src/os/unix/unix.cpp b/src/os/unix/unix.cpp index 0d6ff73d74..aa1349f8d2 100644 --- a/src/os/unix/unix.cpp +++ b/src/os/unix/unix.cpp @@ -270,22 +270,20 @@ int CDECL main(int argc, char *argv[]) } #ifndef WITH_COCOA -bool GetClipboardContents(char *buffer, const char *last) +std::optional GetClipboardContents() { #ifdef WITH_SDL2 - if (SDL_HasClipboardText() == SDL_FALSE) { - return false; - } + if (SDL_HasClipboardText() == SDL_FALSE) return std::nullopt; char *clip = SDL_GetClipboardText(); if (clip != nullptr) { - strecpy(buffer, clip, last); + std::string result = clip; SDL_free(clip); - return true; + return result; } #endif - return false; + return std::nullopt; } #endif diff --git a/src/os/windows/survey_win.cpp b/src/os/windows/survey_win.cpp index 3bb7f7cde2..97895edd9d 100644 --- a/src/os/windows/survey_win.cpp +++ b/src/os/windows/survey_win.cpp @@ -14,10 +14,13 @@ #include "../../core/format.hpp" #include +#include #include #include "../../safeguards.h" +extern std::string SurveyMemoryToText(uint64_t memory); + void SurveyOS(nlohmann::json &json) { _OSVERSIONINFOA os; @@ -31,7 +34,8 @@ void SurveyOS(nlohmann::json &json) status.dwLength = sizeof(status); GlobalMemoryStatusEx(&status); - json["memory"] = status.ullTotalPhys; + json["memory"] = SurveyMemoryToText(status.ullTotalPhys); + json["hardware_concurrency"] = std::thread::hardware_concurrency(); } #endif /* WITH_NLOHMANN_JSON */ diff --git a/src/os/windows/win32.cpp b/src/os/windows/win32.cpp index 7aa917bd0e..de3e8e573d 100644 --- a/src/os/windows/win32.cpp +++ b/src/os/windows/win32.cpp @@ -549,26 +549,19 @@ void DetermineBasePaths(const char *exe) } -bool GetClipboardContents(char *buffer, const char *last) +std::optional GetClipboardContents() { - HGLOBAL cbuf; - const char *ptr; + if (!IsClipboardFormatAvailable(CF_UNICODETEXT)) return std::nullopt; - if (IsClipboardFormatAvailable(CF_UNICODETEXT)) { - OpenClipboard(nullptr); - cbuf = GetClipboardData(CF_UNICODETEXT); + OpenClipboard(nullptr); + HGLOBAL cbuf = GetClipboardData(CF_UNICODETEXT); - ptr = (const char*)GlobalLock(cbuf); - int out_len = WideCharToMultiByte(CP_UTF8, 0, (LPCWSTR)ptr, -1, buffer, (last - buffer) + 1, nullptr, nullptr); - GlobalUnlock(cbuf); - CloseClipboard(); + std::string result = FS2OTTD(static_cast(GlobalLock(cbuf))); + GlobalUnlock(cbuf); + CloseClipboard(); - if (out_len == 0) return false; - } else { - return false; - } - - return true; + if (result.empty()) return std::nullopt; + return result; } diff --git a/src/sortlist_type.h b/src/sortlist_type.h index 554478fd89..ab0e562903 100644 --- a/src/sortlist_type.h +++ b/src/sortlist_type.h @@ -242,7 +242,7 @@ public: { this->flags ^= VL_DESC; - if (this->IsSortable()) MemReverseT(std::vector::data(), std::vector::size()); + if (this->IsSortable()) std::reverse(std::vector::begin(), std::vector::end()); } /** diff --git a/src/strgen/strgen.h b/src/strgen/strgen.h index 6881e72232..88bd8ca52a 100644 --- a/src/strgen/strgen.h +++ b/src/strgen/strgen.h @@ -16,40 +16,39 @@ #include #include +#include +#include + /** Container for the different cases of a string. */ struct Case { - int caseidx; ///< The index of the case. - char *string; ///< The translation of the case. - Case *next; ///< The next, chained, case. + int caseidx; ///< The index of the case. + std::string string; ///< The translation of the case. - Case(int caseidx, const char *string, Case *next); - ~Case(); + Case(int caseidx, std::string string); }; /** Information about a single string. */ struct LangString { - char *name; ///< Name of the string. - char *english; ///< English text. - char *translated; ///< Translated text. - LangString *hash_next; ///< Next hash entry. - int index; ///< The index in the language file. - int line; ///< Line of string in source-file. - Case *translated_case; ///< Cases of the translation. + std::string name; ///< Name of the string. + std::string english; ///< English text. + std::string translated; ///< Translated text. + int index; ///< The index in the language file. + int line; ///< Line of string in source-file. + std::vector translated_cases; ///< Cases of the translation. std::unique_ptr chain_before; std::unique_ptr chain_after; bool no_translate_mode = false; LangString *default_translation = nullptr; - LangString(const char *name, const char *english, int index, int line); - void ReplaceDefinition(const char *name, const char *english, int line); - ~LangString(); + LangString(std::string name, std::string english, size_t index, int line); + void ReplaceDefinition(std::string english, int line); void FreeTranslation(); }; /** Information about the currently known strings. */ struct StringData { - LangString **strings; ///< Array of all known strings. - LangString **hash_heads; ///< Hash table for the strings. + std::vector strings; ///< List of all known strings. + std::unordered_map name_to_string; ///< Lookup table for the strings. size_t tabs; ///< The number of 'tabs' of strings. size_t max_strings; ///< The maximum number of strings. int next_string_id; ///< The next string ID to allocate. @@ -62,11 +61,8 @@ struct StringData { LangString *default_translation = nullptr; StringData(size_t tabs); - ~StringData(); void FreeTranslation(); - uint HashStr(const char *s) const; - void Add(const char *s, LangString *ls); - LangString *Find(const char *s); + LangString *Find(const std::string_view s); uint VersionHashStr(uint hash, const char *s) const; uint Version() const; uint CountInUse(uint tab) const; @@ -79,8 +75,8 @@ struct StringReader { bool master; ///< Are we reading the master file? bool translation; ///< Are we reading a translation, implies !master. However, the base translation will have this false. - StringReader(StringData &data, const char *file, bool master, bool translation); - virtual ~StringReader(); + StringReader(StringData &data, std::string file, bool master, bool translation); + virtual ~StringReader() {} void HandleString(char *str); /** @@ -157,18 +153,17 @@ struct LanguageWriter { struct CmdStruct; struct CmdPair { - const CmdStruct *a; - const char *v; + const CmdStruct *cmd; + std::string param; }; struct ParsedCommandStruct { - uint np; - CmdPair pairs[32]; - const CmdStruct *cmd[32]; // ordered by param # + std::vector non_consuming_commands; + std::array consuming_commands{ nullptr }; // ordered by param # }; const CmdStruct *TranslateCmdForCompare(const CmdStruct *a); -void ExtractCommandString(ParsedCommandStruct *p, const char *s, bool warnings); +ParsedCommandStruct ExtractCommandString(const char *s, bool warnings); void CDECL strgen_warning(const char *s, ...) WARN_FORMAT(1, 2); void CDECL strgen_error(const char *s, ...) WARN_FORMAT(1, 2); diff --git a/src/strgen/strgen_base.cpp b/src/strgen/strgen_base.cpp index 7c818f1e41..b6b47152b0 100644 --- a/src/strgen/strgen_base.cpp +++ b/src/strgen/strgen_base.cpp @@ -37,20 +37,12 @@ static const CmdStruct *ParseCommandString(const char **str, char *param, int *a * Create a new case. * @param caseidx The index of the case. * @param string The translation of the case. - * @param next The next chained case. */ -Case::Case(int caseidx, const char *string, Case *next) : - caseidx(caseidx), string(stredup(string)), next(next) +Case::Case(int caseidx, std::string string) : + caseidx(caseidx), string(std::move(string)) { } -/** Free everything we allocated. */ -Case::~Case() -{ - free(this->string); - delete this->next; -} - /** * Create a new string. * @param name The name of the string. @@ -58,38 +50,22 @@ Case::~Case() * @param index The index in the string table. * @param line The line this string was found on. */ -LangString::LangString(const char *name, const char *english, int index, int line) : - name(stredup(name)), english(stredup(english)), translated(nullptr), - hash_next(0), index(index), line(line), translated_case(nullptr) +LangString::LangString(std::string name, std::string english, size_t index, int line) : + name(std::move(name)), english(std::move(english)), index(index), line(line) { } -/** Free everything we allocated. */ -LangString::~LangString() +void LangString::ReplaceDefinition(std::string english, int line) { - free(this->name); - free(this->english); - free(this->translated); - delete this->translated_case; -} - -void LangString::ReplaceDefinition(const char *name, const char *english, int line) -{ - free(this->name); - free(this->english); - this->name = stredup(name); - this->english = stredup(english); + this->english = std::move(english); this->line = line; } /** Free all data related to the translation. */ void LangString::FreeTranslation() { - free(this->translated); - this->translated = nullptr; - - delete this->translated_case; - this->translated_case = nullptr; + this->translated.clear(); + this->translated_cases.clear(); } /** @@ -98,18 +74,10 @@ void LangString::FreeTranslation() */ StringData::StringData(size_t tabs) : tabs(tabs), max_strings(tabs * TAB_SIZE) { - this->strings = CallocT(max_strings); - this->hash_heads = CallocT(max_strings); + this->strings.resize(max_strings); this->next_string_id = 0; } -/** Free everything we allocated. */ -StringData::~StringData() -{ - free(this->strings); - free(this->hash_heads); -} - /** Free all data related to the translation. */ void StringData::FreeTranslation() { @@ -119,45 +87,17 @@ void StringData::FreeTranslation() } } -/** - * Create a hash of the string for finding them back quickly. - * @param s The string to hash. - * @return The hashed string. - */ -uint StringData::HashStr(const char *s) const -{ - uint hash = 0; - for (; *s != '\0'; s++) hash = ROL(hash, 3) ^ *s; - return hash % this->max_strings; -} - -/** - * Add a newly created LangString. - * @param s The name of the string. - * @param ls The string to add. - */ -void StringData::Add(const char *s, LangString *ls) -{ - uint hash = this->HashStr(s); - ls->hash_next = this->hash_heads[hash]; - /* Off-by-one for hash find. */ - this->hash_heads[hash] = ls; -} - /** * Find a LangString based on the string name. * @param s The string name to search on. * @return The LangString or nullptr if it is not known. */ -LangString *StringData::Find(const char *s) +LangString *StringData::Find(const std::string_view s) { - LangString *ls = this->hash_heads[this->HashStr(s)]; + auto it = this->name_to_string.find(s); + if (it == this->name_to_string.end()) return nullptr; - while (ls != nullptr) { - if (strcmp(ls->name, s) == 0) return ls; - ls = ls->hash_next; - } - return nullptr; + return it->second; } /** @@ -193,12 +133,12 @@ uint StringData::Version() const int argno; int casei; - s = ls->name; + s = ls->name.c_str(); hash ^= i * 0x717239; hash = (hash & 1 ? hash >> 1 ^ 0xDEADBEEF : hash >> 1); hash = this->VersionHashStr(hash, s + 1); - s = ls->english; + s = ls->english.c_str(); while ((cs = ParseCommandString(&s, buf, &argno, &casei)) != nullptr) { if (cs->flags & C_DONTCOUNT) continue; @@ -390,7 +330,7 @@ void EmitPlural(Buffer *buffer, char *buf, int value) /* Parse out the number, if one exists. Otherwise default to prev arg. */ if (!ParseRelNum(&buf, &argidx, &offset)) argidx--; - const CmdStruct *cmd = _cur_pcs.cmd[argidx]; + const CmdStruct *cmd = _cur_pcs.consuming_commands[argidx]; if (offset == -1) { /* Use default offset */ if (cmd == nullptr || cmd->default_plural_offset < 0) { @@ -455,7 +395,7 @@ void EmitGender(Buffer *buffer, char *buf, int value) * If no relative number exists, default to +0 */ ParseRelNum(&buf, &argidx, &offset); - const CmdStruct *cmd = _cur_pcs.cmd[argidx]; + const CmdStruct *cmd = _cur_pcs.consuming_commands[argidx]; if (cmd == nullptr || (cmd->flags & C_GENDER) == 0) { strgen_fatal("Command '%s' can't have a gender", cmd == nullptr ? "" : cmd->cmd); } @@ -579,24 +519,19 @@ static const CmdStruct *ParseCommandString(const char **str, char *param, int *a * @param master Are we reading the master file? * @param translation Are we reading a translation? */ -StringReader::StringReader(StringData &data, const char *file, bool master, bool translation) : - data(data), file(file), master(master), translation(translation) +StringReader::StringReader(StringData &data, std::string file, bool master, bool translation) : + data(data), file(std::move(file)), master(master), translation(translation) { } -/** Make sure the right reader gets freed. */ -StringReader::~StringReader() -{ -} - -void ExtractCommandString(ParsedCommandStruct *p, const char *s, bool warnings) +ParsedCommandStruct ExtractCommandString(const char *s, bool warnings) { char param[MAX_COMMAND_PARAM_SIZE]; int argno; int argidx = 0; int casei; - memset(p, 0, sizeof(*p)); + ParsedCommandStruct p; for (;;) { /* read until next command from a. */ @@ -609,17 +544,16 @@ void ExtractCommandString(ParsedCommandStruct *p, const char *s, bool warnings) if (ar->consumes) { if (argno != -1) argidx = argno; - if (argidx < 0 || (uint)argidx >= lengthof(p->cmd)) strgen_fatal("invalid param idx %d", argidx); - if (p->cmd[argidx] != nullptr && p->cmd[argidx] != ar) strgen_fatal("duplicate param idx %d", argidx); + if (argidx < 0 || (uint)argidx >= p.consuming_commands.max_size()) strgen_fatal("invalid param idx %d", argidx); + if (p.consuming_commands[argidx] != nullptr && p.consuming_commands[argidx] != ar) strgen_fatal("duplicate param idx %d", argidx); - p->cmd[argidx++] = ar; + p.consuming_commands[argidx++] = ar; } else if (!(ar->flags & C_DONTCOUNT)) { // Ignore some of them - if (p->np >= lengthof(p->pairs)) strgen_fatal("too many commands in string, max " PRINTF_SIZE, lengthof(p->pairs)); - p->pairs[p->np].a = ar; - p->pairs[p->np].v = param[0] != '\0' ? stredup(param) : ""; - p->np++; + p.non_consuming_commands.emplace_back(CmdPair{ar, param}); } } + + return p; } @@ -643,7 +577,7 @@ const CmdStruct *TranslateCmdForCompare(const CmdStruct *a) } -static bool CheckCommandsMatch(char *a, char *b, const char *name) +static bool CheckCommandsMatch(const char *a, const char *b, const char *name) { /* If we're not translating, i.e. we're compiling the base language, * it is pointless to do all these checks as it'll always be correct. @@ -651,45 +585,42 @@ static bool CheckCommandsMatch(char *a, char *b, const char *name) */ if (!_translation) return true; - ParsedCommandStruct templ; - ParsedCommandStruct lang; bool result = true; - ExtractCommandString(&templ, b, true); - ExtractCommandString(&lang, a, true); + ParsedCommandStruct templ = ExtractCommandString(b, true); + ParsedCommandStruct lang = ExtractCommandString(a, true); /* For each string in templ, see if we find it in lang */ - if (templ.np != lang.np) { + if (templ.non_consuming_commands.max_size() != lang.non_consuming_commands.max_size()) { strgen_warning("%s: template string and language string have a different # of commands", name); result = false; } - for (uint i = 0; i < templ.np; i++) { + for (auto &templ_nc : templ.non_consuming_commands) { /* see if we find it in lang, and zero it out */ bool found = false; - for (uint j = 0; j < lang.np; j++) { - if (templ.pairs[i].a == lang.pairs[j].a && - strcmp(templ.pairs[i].v, lang.pairs[j].v) == 0) { + for (auto &lang_nc : lang.non_consuming_commands) { + if (templ_nc.cmd == lang_nc.cmd && templ_nc.param == lang_nc.param) { /* it was found in both. zero it out from lang so we don't find it again */ - lang.pairs[j].a = nullptr; + lang_nc.cmd = nullptr; found = true; break; } } if (!found) { - strgen_warning("%s: command '%s' exists in template file but not in language file", name, templ.pairs[i].a->cmd); + strgen_warning("%s: command '%s' exists in template file but not in language file", name, templ_nc.cmd->cmd); result = false; } } /* if we reach here, all non consumer commands match up. * Check if the non consumer commands match up also. */ - for (uint i = 0; i < lengthof(templ.cmd); i++) { - if (TranslateCmdForCompare(templ.cmd[i]) != lang.cmd[i]) { + for (uint i = 0; i < templ.consuming_commands.max_size(); i++) { + if (TranslateCmdForCompare(templ.consuming_commands[i]) != lang.consuming_commands[i]) { strgen_warning("%s: Param idx #%d '%s' doesn't match with template command '%s'", name, i, - lang.cmd[i] == nullptr ? "" : TranslateCmdForCompare(lang.cmd[i])->cmd, - templ.cmd[i] == nullptr ? "" : templ.cmd[i]->cmd); + lang.consuming_commands[i] == nullptr ? "" : TranslateCmdForCompare(lang.consuming_commands[i])->cmd, + templ.consuming_commands[i] == nullptr ? "" : templ.consuming_commands[i]->cmd); result = false; } } @@ -754,7 +685,7 @@ void StringReader::HandleString(char *str) if (ent != nullptr) { if (this->data.override_mode) { - ent->ReplaceDefinition(str, s, _cur_line); + ent->ReplaceDefinition(s, _cur_line); return; } strgen_error("String name '%s' is used multiple times", str); @@ -772,7 +703,7 @@ void StringReader::HandleString(char *str) std::unique_ptr ls(new LangString(str, s, this->data.next_string_id, _cur_line)); if (this->data.no_translate_mode) ls->no_translate_mode = true; this->data.next_string_id = -1; - this->data.Add(str, ls.get()); + this->data.name_to_string[ls->name] = ls.get(); if (this->data.default_translation != nullptr) { ls->default_translation = this->data.default_translation; @@ -802,10 +733,9 @@ void StringReader::HandleString(char *str) return; } - if (ent->translated && casep == nullptr) { + if (!ent->translated.empty() && casep == nullptr) { if (this->data.override_mode) { - free(ent->translated); - ent->translated = nullptr; + ent->translated.clear(); } else { strgen_error("String name '%s' is used multiple times", str); return; @@ -813,12 +743,12 @@ void StringReader::HandleString(char *str) } /* make sure that the commands match */ - if (!CheckCommandsMatch(s, ent->english, str)) return; + if (!CheckCommandsMatch(s, ent->english.c_str(), str)) return; if (casep != nullptr) { - ent->translated_case = new Case(ResolveCaseName(casep, strlen(casep)), s, ent->translated_case); + ent->translated_cases.emplace_back(ResolveCaseName(casep, strlen(casep)), s); } else { - ent->translated = stredup(s); + ent->translated = s; /* If the string was translated, use the line from the * translated language so errors in the translated file * are properly referenced to. */ @@ -895,7 +825,7 @@ void StringReader::AssignIDs(size_t &next_id, LangString *ls) strgen_error("Too many strings, maximum allowed is " PRINTF_SIZE, this->data.max_strings); return; } else if (this->data.strings[ls->index] != nullptr) { - strgen_error("String ID 0x%X for '%s' already in use by '%s'", (uint)ls->index, ls->name, this->data.strings[ls->index]->name); + strgen_error("String ID 0x%X for '%s' already in use by '%s'", (uint)ls->index, ls->name.c_str(), this->data.strings[ls->index]->name.c_str()); return; } else { this->data.strings[ls->index] = ls; @@ -915,7 +845,7 @@ void HeaderWriter::WriteHeader(const StringData &data) int last = 0; for (size_t i = 0; i < data.max_strings; i++) { if (data.strings[i] != nullptr) { - this->WriteStringID(data.strings[i]->name, (int)i); + this->WriteStringID(data.strings[i]->name.c_str(), (int)i); last = (int)i; } } @@ -927,20 +857,20 @@ static int TranslateArgumentIdx(int argidx, int offset) { int sum; - if (argidx < 0 || (uint)argidx >= lengthof(_cur_pcs.cmd)) { + if (argidx < 0 || (uint)argidx >= _cur_pcs.consuming_commands.max_size()) { strgen_fatal("invalid argidx %d", argidx); } - const CmdStruct *cs = _cur_pcs.cmd[argidx]; + const CmdStruct *cs = _cur_pcs.consuming_commands[argidx]; if (cs != nullptr && cs->consumes <= offset) { strgen_fatal("invalid argidx offset %d:%d", argidx, offset); } - if (_cur_pcs.cmd[argidx] == nullptr) { + if (_cur_pcs.consuming_commands[argidx] == nullptr) { strgen_fatal("no command for this argidx %d", argidx); } for (int i = sum = 0; i < argidx; i++) { - cs = _cur_pcs.cmd[i]; + cs = _cur_pcs.consuming_commands[i]; sum += (cs != nullptr) ? cs->consumes : 1; } @@ -986,7 +916,7 @@ static void PutCommandString(Buffer *buffer, const char *str) } /* Output the one from the master string... it's always accurate. */ - cs = _cur_pcs.cmd[_cur_argidx++]; + cs = _cur_pcs.consuming_commands[_cur_argidx++]; if (cs == nullptr) { strgen_fatal("%s: No argument exists at position %d", _cur_ident, _cur_argidx - 1); } @@ -1030,7 +960,7 @@ void LanguageWriter::WriteLang(const StringData &data) for (uint j = 0; j != in_use[tab]; j++) { const LangString *ls = data.strings[(tab * TAB_SIZE) + j]; - if (ls != nullptr && ls->translated == nullptr && ls->default_translation == nullptr && !ls->no_translate_mode) { + if (ls != nullptr && ls->translated.empty() && ls->default_translation == nullptr && !ls->no_translate_mode) { _lang.missing++; } } @@ -1047,8 +977,7 @@ void LanguageWriter::WriteLang(const StringData &data) for (size_t tab = 0; tab < data.tabs; tab++) { for (uint j = 0; j != in_use[tab]; j++) { const LangString *ls = data.strings[(tab * TAB_SIZE) + j]; - const Case *casep; - const char *cmdp; + const std::string *cmdp; /* For undefined strings, just set that it's an empty string */ if (ls == nullptr) { @@ -1056,13 +985,13 @@ void LanguageWriter::WriteLang(const StringData &data) continue; } - _cur_ident = ls->name; + _cur_ident = ls->name.c_str(); _cur_line = ls->line; /* Produce a message if a string doesn't have a translation. */ - if (_show_todo > 0 && ls->translated == nullptr) { + if (_show_todo > 0 && ls->translated.empty()) { if ((_show_todo & 2) != 0) { - strgen_warning("'%s' is untranslated", ls->name); + strgen_warning("'%s' is untranslated", ls->name.c_str()); } if ((_show_todo & 1) != 0) { const char *s = " "; @@ -1071,43 +1000,36 @@ void LanguageWriter::WriteLang(const StringData &data) } /* Extract the strings and stuff from the english command string */ - ExtractCommandString(&_cur_pcs, ls->english, false); + _cur_pcs = ExtractCommandString(ls->english.c_str(), false); - if (ls->translated_case != nullptr || ls->translated != nullptr) { - casep = ls->translated_case; - cmdp = ls->translated; + if (!ls->translated_cases.empty() || !ls->translated.empty()) { + cmdp = &ls->translated; } else { - casep = nullptr; - cmdp = ls->english; - if (ls->default_translation != nullptr && ls->default_translation->translated != nullptr) { - cmdp = ls->default_translation->translated; + cmdp = &ls->english; + if (ls->default_translation != nullptr && !ls->default_translation->translated.empty()) { + cmdp = &ls->default_translation->translated; } } - _translated = cmdp != ls->english; - - if (casep != nullptr) { - const Case *c; - uint num; + _translated = cmdp != &ls->english; + if (!ls->translated_cases.empty()) { /* Need to output a case-switch. * It has this format * <0x9E> * Each LEN is printed using 2 bytes in big endian order. */ buffer.AppendUtf8(SCC_SWITCH_CASE); - /* Count the number of cases */ - for (num = 0, c = casep; c; c = c->next) num++; - buffer.AppendByte(num); + buffer.AppendByte((byte)ls->translated_cases.size()); /* Write each case */ - for (c = casep; c != nullptr; c = c->next) { - buffer.AppendByte(c->caseidx); + for (const Case &c : ls->translated_cases) { + buffer.AppendByte(c.caseidx); /* Make some space for the 16-bit length */ uint pos = (uint)buffer.size(); buffer.AppendByte(0); buffer.AppendByte(0); /* Write string */ - PutCommandString(&buffer, c->string); + PutCommandString(&buffer, c.string.c_str()); buffer.AppendByte(0); // terminate with a zero /* Fill in the length */ uint size = (uint)buffer.size() - (pos + 2); @@ -1116,7 +1038,7 @@ void LanguageWriter::WriteLang(const StringData &data) } } - if (cmdp != nullptr) PutCommandString(&buffer, cmdp); + if (!cmdp->empty()) PutCommandString(&buffer, cmdp->c_str()); this->WriteLength((uint)buffer.size()); this->Write(buffer.data(), buffer.size()); diff --git a/src/table/settings/settings.ini b/src/table/settings/settings.ini index 8cbf82f230..eebb9c0018 100644 --- a/src/table/settings/settings.ini +++ b/src/table/settings/settings.ini @@ -4474,7 +4474,7 @@ strval = STR_CONFIG_SETTING_AUTOSCROLL_DISABLED cat = SC_BASIC [SDTC_VAR] -ifdef = __EMSCRIPTEN__ +ifdef = UNIX var = gui.scroll_mode type = SLE_UINT8 flags = SF_NOT_IN_SAVE | SF_NO_NETWORK_SYNC | SF_GUI_DROPDOWN @@ -4487,7 +4487,7 @@ strval = STR_CONFIG_SETTING_SCROLLMODE_DEFAULT cat = SC_BASIC [SDTC_VAR] -ifndef = __EMSCRIPTEN__ +ifndef = UNIX var = gui.scroll_mode type = SLE_UINT8 flags = SF_NOT_IN_SAVE | SF_NO_NETWORK_SYNC | SF_GUI_DROPDOWN diff --git a/src/textbuf.cpp b/src/textbuf.cpp index 2edff11f94..a8d02920df 100644 --- a/src/textbuf.cpp +++ b/src/textbuf.cpp @@ -24,11 +24,9 @@ * Try to retrieve the current clipboard contents. * * @note OS-specific function. - * @param buffer Clipboard content. - * @param last The pointer to the last element of the destination buffer - * @return True if some text could be retrieved. + * @return The (optional) clipboard contents. */ -bool GetClipboardContents(char *buffer, const char *last); +std::optional GetClipboardContents(); int _caret_timer; @@ -225,11 +223,10 @@ bool Textbuf::InsertString(const char *str, bool marked, const char *caret, cons */ bool Textbuf::InsertClipboard() { - char utf8_buf[512]; + auto contents = GetClipboardContents(); + if (!contents.has_value()) return false; - if (!GetClipboardContents(utf8_buf, lastof(utf8_buf))) return false; - - return this->InsertString(utf8_buf, false); + return this->InsertString(contents.value().c_str(), false); } /** diff --git a/src/video/allegro_v.cpp b/src/video/allegro_v.cpp index d8b8e403ce..7944394461 100644 --- a/src/video/allegro_v.cpp +++ b/src/video/allegro_v.cpp @@ -390,7 +390,7 @@ bool VideoDriver_Allegro::PollEvent() } /* Mouse movement */ - if (_cursor.UpdateCursorPosition(mouse_x, mouse_y, false)) { + if (_cursor.UpdateCursorPosition(mouse_x, mouse_y)) { position_mouse(_cursor.pos.x, _cursor.pos.y); } if (_cursor.delta.x != 0 || _cursor.delta.y) mouse_action = true; diff --git a/src/video/cocoa/cocoa_wnd.mm b/src/video/cocoa/cocoa_wnd.mm index d2d00d2ce2..1a0233fa1d 100644 --- a/src/video/cocoa/cocoa_wnd.mm +++ b/src/video/cocoa/cocoa_wnd.mm @@ -661,7 +661,7 @@ void CocoaDialog(const char *title, const char *message, const char *buttonLabel _cursor.UpdateCursorPositionRelative(event.deltaX * self.getContentsScale, event.deltaY * self.getContentsScale); } else { NSPoint pt = [ self mousePositionFromEvent:event ]; - _cursor.UpdateCursorPosition(pt.x, pt.y, false); + _cursor.UpdateCursorPosition(pt.x, pt.y); } HandleMouseEvents(); diff --git a/src/video/sdl2_v.cpp b/src/video/sdl2_v.cpp index 2affa0858d..e496e4d8cb 100644 --- a/src/video/sdl2_v.cpp +++ b/src/video/sdl2_v.cpp @@ -653,12 +653,25 @@ bool VideoDriver_SDL_Base::PollEvent() if (!SDL_PollEvent(&ev)) return false; switch (ev.type) { - case SDL_MOUSEMOTION: - if (_cursor.UpdateCursorPosition(ev.motion.x, ev.motion.y, true)) { + case SDL_MOUSEMOTION: { + int32_t x = ev.motion.x; + int32_t y = ev.motion.y; + + if (_cursor.fix_at) { + /* Get all queued mouse events now in case we have to warp the cursor. In the + * end, we only care about the current mouse position and not bygone events. */ + while (SDL_PeepEvents(&ev, 1, SDL_GETEVENT, SDL_MOUSEMOTION, SDL_MOUSEMOTION)) { + x = ev.motion.x; + y = ev.motion.y; + } + } + + if (_cursor.UpdateCursorPosition(x, y)) { SDL_WarpMouseInWindow(this->sdl_window, _cursor.pos.x, _cursor.pos.y); } HandleMouseEvents(); break; + } case SDL_MOUSEWHEEL: if (ev.wheel.y > 0) { @@ -790,10 +803,8 @@ bool VideoDriver_SDL_Base::PollEvent() } else if (ev.window.event == SDL_WINDOWEVENT_ENTER) { // mouse entered the window, enable cursor _cursor.in_window = true; -#ifdef __EMSCRIPTEN__ /* Ensure pointer lock will not occur. */ SDL_SetRelativeMouseMode(SDL_FALSE); -#endif } else if (ev.window.event == SDL_WINDOWEVENT_LEAVE) { // mouse left the window, undraw cursor UndrawMouseCursor(); @@ -832,9 +843,6 @@ static const char *InitializeSDL() * UpdateWindowSurface() to update the window's texture instead of * its surface. */ SDL_SetHint(SDL_HINT_FRAMEBUFFER_ACCELERATION, "0"); -#ifndef __EMSCRIPTEN__ - SDL_SetHint(SDL_HINT_MOUSE_RELATIVE_MODE_WARP, "1"); -#endif /* Check if the video-driver is already initialized. */ if (SDL_WasInit(SDL_INIT_VIDEO) != 0) return nullptr; diff --git a/src/video/sdl_v.cpp b/src/video/sdl_v.cpp index eff915112f..c75e3536f4 100644 --- a/src/video/sdl_v.cpp +++ b/src/video/sdl_v.cpp @@ -484,12 +484,25 @@ bool VideoDriver_SDL::PollEvent() if (!SDL_PollEvent(&ev)) return false; switch (ev.type) { - case SDL_MOUSEMOTION: - if (_cursor.UpdateCursorPosition(ev.motion.x, ev.motion.y, true)) { + case SDL_MOUSEMOTION: { + int32_t x = ev.motion.x; + int32_t y = ev.motion.y; + + if (_cursor.fix_at) { + /* Get all queued mouse events now in case we have to warp the cursor. In the + * end, we only care about the current mouse position and not bygone events. */ + while (SDL_PeepEvents(&ev, 1, SDL_GETEVENT, SDL_MOUSEMOTION)) { + x = ev.motion.x; + y = ev.motion.y; + } + } + + if (_cursor.UpdateCursorPosition(x, y)) { SDL_WarpMouse(_cursor.pos.x, _cursor.pos.y); } HandleMouseEvents(); break; + } case SDL_MOUSEBUTTONDOWN: if (_rightclick_emulate && SDL_GetModState() & KMOD_CTRL) { diff --git a/src/video/win32_v.cpp b/src/video/win32_v.cpp index 6e7ea8109d..2379889658 100644 --- a/src/video/win32_v.cpp +++ b/src/video/win32_v.cpp @@ -491,7 +491,7 @@ LRESULT CALLBACK WndProcGdi(HWND hwnd, UINT msg, WPARAM wParam, LPARAM lParam) } } - if (_cursor.UpdateCursorPosition(x, y, false)) { + if (_cursor.UpdateCursorPosition(x, y)) { POINT pt; pt.x = _cursor.pos.x; pt.y = _cursor.pos.y;