/* Copyright (c) 2013 yvt This file is part of OpenSpades. OpenSpades is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version. OpenSpades is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with OpenSpades. If not, see . */ #pragma once #include #include #include #include #include "Exception.h" #include "Math.h" namespace spades { std::string Intern(const std::string &); template std::string ToString(const T &v) { return std::to_string(v); } template std::size_t StringSpan(const char *str, F predicate) { auto *ptr = str; while (*ptr != 0 && predicate(*ptr)) ptr++; return ptr - str; } class StandardTokenizer { const char *ptr; public: StandardTokenizer(const char *ptr); class Iterator { const char *ptr; StandardTokenizer *tokenizer; std::string token; std::string prevToken; bool nowPrev; void SkipWhitespace(); void AnalyzeToken(); public: Iterator(const char *ptr, StandardTokenizer *tokenizer); std::string operator*(); void operator++(); void operator--(); bool operator==(const Iterator &it) const { return ptr == it.ptr && nowPrev == it.nowPrev; } bool operator!=(const Iterator &it) const { return ptr != it.ptr || nowPrev != it.nowPrev; } const char *GetPointer() const { return ptr - token.size(); } StandardTokenizer &GetTokenizer() const { return *tokenizer; } }; Iterator begin(); Iterator end(); const char *GetString() { return ptr; } }; template class StaticArray { std::array staticArray; std::vector dynamicArray; std::size_t count; public: StaticArray() : count(0) {} std::size_t size() const { return count; } void push_back(const T &e) { if (count == N) { dynamicArray.push_back(e); count++; } else { staticArray[count++] = e; } } void push_back(T &&e) { if (count == N) { dynamicArray.push_back(e); count++; } else { staticArray[count++] = e; } } T &operator[](std::size_t i) { if (i < N) return staticArray[i]; else return dynamicArray[i - N]; } const T &operator[](std::size_t i) const { if (i < N) return staticArray[i]; else return dynamicArray[i - N]; } class iterator { StaticArray &arr; mutable std::size_t index; iterator(StaticArray &arr, std::size_t index) : arr(arr), index(index) {} T &operator*() { return arr[index]; } const T &operator*() const { return arr[index]; } iterator operator+(std::size_t i) { return iterator(arr, index + i); } iterator operator-(std::size_t i) { return iterator(arr, index - i); } const iterator operator+(std::size_t i) const { return iterator(arr, index + i); } const iterator operator-(std::size_t i) const { return iterator(arr, index - i); } void operator++() const { ++index; } void operator--() const { --index; } T &operator[](std::size_t i) { return arr[index + i]; } const T &operator[](std::size_t i) const { return arr[index + i]; } bool operator==(const iterator &it) const { return &arr == &it.arr && index == it.index; } bool operator!=(const iterator &it) const { return &arr != &it.arr || index != it.index; } }; typedef const iterator const_iterator; iterator begin() { return iterator(*this, 0); } iterator end() { return iterator(*this, size()); } const_iterator begin() const { return iterator(*this, 0); } const_iterator end() const { return iterator(*this, size()); } }; template class Formatter { struct Segment { std::size_t start; std::size_t length; }; struct Placeholder { std::size_t parameterIndex; std::string format; }; const char *str; StaticArray segments; StaticArray placeholders; std::array parameters; public: Formatter(const char *str) : str(str) { auto *firstPlaceholder = std::strchr(str, '{'); if (firstPlaceholder) { std::size_t index = firstPlaceholder - str; segments.push_back({0, index}); while (str[index] != '\0') { // parse placeholder. index++; auto endIndex = index + std::strcspn(str + index, ":}"); if (str[endIndex] == '\0') SPRaise("Malformed format string (placeholder is not closed): %s", str); char *endptr; auto idx = static_cast(strtol(str + index, &endptr, 10)); if (endptr != str + endIndex) { SPRaise("Malformed format string (failed to parse parameter index): %s", str); } Placeholder p; p.parameterIndex = idx; if (idx >= numArgs) { SPRaise( "Malformed format string (using non-existent parameter index %d): %s", static_cast(idx), str); } if (str[endIndex] == ':') { index = endIndex + 1; endptr = const_cast(std::strchr(str + index, '}')); if (!endptr) SPRaise("Malformed format string (placeholder is not closed): %s", str); endIndex = endptr - str; p.format = std::string(str + index, endIndex - index); index = endIndex + 1; } else { index = endIndex + 1; } placeholders.push_back(p); // find next placeholder auto *nextPlaceholder = std::strchr(str + index, '{'); if (nextPlaceholder) { endIndex = nextPlaceholder - str; } else { endIndex = std::strlen(str); } segments.push_back({index, endIndex - index}); index = endIndex; } } else { segments.push_back({0, std::strlen(str)}); } } private: template void SetParameterValue() { static_assert(index <= numArgs, "index <= numArgs"); } template void SetParameterValue(Head head, T... args) { static_assert(index < numArgs, "index < numArgs"); parameters[index] = ToString(head); SetParameterValue(args...); } public: template std::string Format(T... args) { SetParameterValue<>(args...); std::string ret; ret.append(str + segments[0].start, segments[0].length); for (std::size_t i = 0; i < placeholders.size(); i++) { ret += parameters[placeholders[i].parameterIndex]; ret.append(str + segments[i + 1].start, segments[i + 1].length); } return ret; } }; template <> class Formatter<0> { const char *fmt; public: Formatter(const char *fmt) : fmt(fmt) { if (strchr(fmt, '{')) { SPRaise("Malformed format string (using non-existent parameter. no parameter " "provided): %s", fmt); } } std::string Format() { return fmt; } }; template std::string Format(const char *str, T... args) { return Formatter(str).Format(args...); } template std::string Format(const std::string &str, T... args) { return Format(str.c_str(), args...); } template <> std::string ToString(const std::string &s); template <> std::string ToString(const char *const &s); template <> std::string ToString(const Vector2 &v); template <> std::string ToString(const Vector3 &v); template <> std::string ToString(const Vector4 &v); template <> std::string ToString(const IntVector3 &v); // `CheckPlural` converts the given value to an integer for plural form identification. // Let's ignore huge numbers for now... template int CheckPlural(T... args) { return 1; } template int CheckPlural(int v, T... args) { return v; } template int CheckPlural(long v, T... args) { return static_cast(v); } template int CheckPlural(unsigned int v, T... args) { return v; } template int CheckPlural(unsigned long v, T... args) { return static_cast(v); } template int CheckPlural(short v, T... args) { return v; } template int CheckPlural(unsigned short v, T... args) { return v; } template int CheckPlural(char v, T... args) { return v; } template int CheckPlural(unsigned char v, T... args) { return v; } std::string GetTextRaw(const std::string &domain, const std::string &ctx, const std::string &text, int plural); std::string GetTextRawPlural(const std::string &domain, const std::string &ctx, const std::string &text, const std::string &textPlural, int plural); template std::string GetText(const std::string &domain, const std::string &context, const std::string &text, T... args) { int plural = CheckPlural(args...); auto s = GetTextRaw(domain, context, text, plural); return Format(s, args...); } template std::string GetTextPlural(const std::string &domain, const std::string &context, const std::string &text, const std::string &textPl, T... args) { int plural = CheckPlural(args...); auto s = GetTextRawPlural(domain, context, text, textPl, plural); return Format(s, args...); } class CatalogDomainHandle { std::string domain; public: CatalogDomainHandle(const std::string &domainName); template std::string Get(const std::string &context, const std::string &text, T... args) { return GetText(domain, context, text, args...); } template std::string GetPlural(const std::string &context, const std::string &text, const std::string &textPl, T... args) { return GetTextPlural(domain, context, text, textPl, args...); } }; extern CatalogDomainHandle defaultDomain; void LoadCurrentLocale(); /** * Returns a current local identifier in this format: `[language[_territory]]`. */ std::string GetCurrentLocaleAndRegion(); } #define _Tr(...) ::spades::defaultDomain.Get(__VA_ARGS__) #define _TrN(...) ::spades::defaultDomain.GetPlural(__VA_ARGS__) #define SPADES_DEFINE_GETTEXT_DOMAIN(name) static CatalogDomainHandle _##name(#name)