mirror of
https://github.com/JGRennison/OpenTTD-patches.git
synced 2024-10-31 15:20:10 +00:00
Merge branch 'master' into jgrpp
# Conflicts: # src/console.cpp # src/os/os2/os2.cpp # src/os/unix/font_unix.cpp # src/strgen/strgen.h # src/strgen/strgen_base.cpp # src/table/settings/gui_settings.ini
This commit is contained in:
commit
c0598d34bf
@ -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);
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -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 <typename T>
|
||||
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 <typename T>
|
||||
static inline void MemReverseT(T *ptr, size_t num)
|
||||
{
|
||||
assert(ptr != nullptr);
|
||||
|
||||
MemReverseT(ptr, ptr + (num - 1));
|
||||
}
|
||||
|
||||
#endif /* MEM_FUNC_HPP */
|
||||
|
@ -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);
|
||||
}
|
||||
|
60
src/gfx.cpp
60
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)
|
||||
|
@ -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. */
|
||||
|
@ -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"]);
|
||||
|
@ -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. */
|
||||
|
@ -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<std::string> 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.
|
||||
|
@ -16,9 +16,12 @@
|
||||
|
||||
#include <mach-o/arch.h>
|
||||
#include <nlohmann/json.hpp>
|
||||
#include <thread>
|
||||
|
||||
#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 */
|
||||
|
@ -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<std::string> 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;
|
||||
}
|
||||
|
||||
|
||||
|
@ -13,10 +13,13 @@
|
||||
|
||||
#include <nlohmann/json.hpp>
|
||||
#include <sys/utsname.h>
|
||||
#include <thread>
|
||||
#include <unistd.h>
|
||||
|
||||
#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 */
|
||||
|
@ -270,22 +270,20 @@ int CDECL main(int argc, char *argv[])
|
||||
}
|
||||
|
||||
#ifndef WITH_COCOA
|
||||
bool GetClipboardContents(char *buffer, const char *last)
|
||||
std::optional<std::string> 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
|
||||
|
||||
|
@ -14,10 +14,13 @@
|
||||
#include "../../core/format.hpp"
|
||||
|
||||
#include <nlohmann/json.hpp>
|
||||
#include <thread>
|
||||
#include <windows.h>
|
||||
|
||||
#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 */
|
||||
|
@ -549,26 +549,19 @@ void DetermineBasePaths(const char *exe)
|
||||
}
|
||||
|
||||
|
||||
bool GetClipboardContents(char *buffer, const char *last)
|
||||
std::optional<std::string> 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<LPCWSTR>(GlobalLock(cbuf)));
|
||||
GlobalUnlock(cbuf);
|
||||
CloseClipboard();
|
||||
|
||||
if (out_len == 0) return false;
|
||||
} else {
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
if (result.empty()) return std::nullopt;
|
||||
return result;
|
||||
}
|
||||
|
||||
|
||||
|
@ -242,7 +242,7 @@ public:
|
||||
{
|
||||
this->flags ^= VL_DESC;
|
||||
|
||||
if (this->IsSortable()) MemReverseT(std::vector<T>::data(), std::vector<T>::size());
|
||||
if (this->IsSortable()) std::reverse(std::vector<T>::begin(), std::vector<T>::end());
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -16,40 +16,39 @@
|
||||
#include <string>
|
||||
#include <vector>
|
||||
|
||||
#include <unordered_map>
|
||||
#include <array>
|
||||
|
||||
/** 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<Case> translated_cases; ///< Cases of the translation.
|
||||
std::unique_ptr<LangString> chain_before;
|
||||
std::unique_ptr<LangString> 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<LangString *> strings; ///< List of all known strings.
|
||||
std::unordered_map<std::string_view, LangString *> 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<CmdPair> non_consuming_commands;
|
||||
std::array<const CmdStruct*, 32> 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);
|
||||
|
@ -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<LangString *>(max_strings);
|
||||
this->hash_heads = CallocT<LangString *>(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 ? "<empty>" : 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 ? "<empty>" : TranslateCmdForCompare(lang.cmd[i])->cmd,
|
||||
templ.cmd[i] == nullptr ? "<empty>" : templ.cmd[i]->cmd);
|
||||
lang.consuming_commands[i] == nullptr ? "<empty>" : TranslateCmdForCompare(lang.consuming_commands[i])->cmd,
|
||||
templ.consuming_commands[i] == nullptr ? "<empty>" : 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<LangString> 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 = "<TODO> ";
|
||||
@ -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> <NUM CASES> <CASE1> <LEN1> <STRING1> <CASE2> <LEN2> <STRING2> <CASE3> <LEN3> <STRING3> <STRINGDEFAULT>
|
||||
* 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());
|
||||
|
@ -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
|
||||
|
@ -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<std::string> 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);
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -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;
|
||||
|
@ -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();
|
||||
|
@ -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;
|
||||
|
@ -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) {
|
||||
|
@ -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;
|
||||
|
Loading…
Reference in New Issue
Block a user