1
0
mirror of https://github.com/OpenRCT2/OpenRCT2 synced 2026-01-18 12:33:17 +01:00
Files
OpenRCT2/src/openrct2/localisation/Currency.cpp
Aaron van Geffen 2302f0d2e3 Rework Localisation includes (#23789)
* Clean up internal localisation header includes

* Remove some external localisation includes

* Remove dependency on FormatCodes.h from InteractiveConsole.h

* Reduce Formatter.h, Localisation.Date.h includes
2025-02-07 22:36:42 +01:00

183 lines
7.8 KiB
C++
Raw Blame History

This file contains invisible Unicode characters
This file contains invisible Unicode characters that are indistinguishable to humans but may be processed differently by a computer. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
/*****************************************************************************
* Copyright (c) 2014-2025 OpenRCT2 developers
*
* For a complete list of all authors, please refer to contributors.md
* Interested in contributing? Visit https://github.com/OpenRCT2/OpenRCT2
*
* OpenRCT2 is licensed under the GNU General Public License version 3.
*****************************************************************************/
#include "Currency.h"
#include "../config/Config.h"
#include "../core/EnumUtils.hpp"
#include "../core/Guard.hpp"
#include "../core/String.hpp"
#include "Language.h"
#include "StringIds.h"
#include <cstring>
using namespace OpenRCT2;
// clang-format off
CurrencyDescriptor CurrencyDescriptors[EnumValue(CurrencyType::Count)] = {
{ "GBP", 10, CurrencyAffix::Prefix, u8"£", CurrencyAffix::Suffix, "GBP", STR_POUNDS }, // British Pound
{ "USD", 10, CurrencyAffix::Prefix, u8"$", CurrencyAffix::Prefix, "$", STR_DOLLARS }, // US Dollar
{ "FRF", 10, CurrencyAffix::Suffix, u8"F", CurrencyAffix::Suffix, "F", STR_FRANC }, // French Franc
{ "DEM", 10, CurrencyAffix::Prefix, u8"DM", CurrencyAffix::Prefix, "DM", STR_DEUTSCHE_MARK }, // Deutsche Mark
{ "JPY", 1000, CurrencyAffix::Prefix, u8"¥", CurrencyAffix::Suffix, "YEN", STR_YEN }, // Japanese Yen
{ "ESP", 10, CurrencyAffix::Suffix, u8"Pts", CurrencyAffix::Suffix, "Pts", STR_PESETA }, // Spanish Peseta
{ "ITL", 1000, CurrencyAffix::Prefix, u8"L", CurrencyAffix::Prefix, "L", STR_LIRA }, // Italian Lira
{ "NLG", 10, CurrencyAffix::Prefix, u8"ƒ ", CurrencyAffix::Prefix, "fl.", STR_GUILDERS }, // Dutch Guilder
{ "SEK", 100, CurrencyAffix::Suffix, u8" kr", CurrencyAffix::Suffix, " kr", STR_KRONA }, // Swedish Krona
{ "EUR", 10, CurrencyAffix::Prefix, u8"", CurrencyAffix::Suffix, "EUR", STR_EUROS }, // Euro
{ "KRW", 10000, CurrencyAffix::Prefix, u8"", CurrencyAffix::Prefix, "W", STR_WON }, // South Korean Won
{ "RUB", 1000, CurrencyAffix::Suffix, u8"", CurrencyAffix::Prefix, "R ", STR_ROUBLE }, // Russian Rouble
{ "CZK", 100, CurrencyAffix::Suffix, u8" ", CurrencyAffix::Suffix, " Kc", STR_CZECH_KORUNA }, // Czech koruna
{ "HKD", 100, CurrencyAffix::Prefix, u8"$", CurrencyAffix::Prefix, "HKD", STR_HONG_KONG_DOLLAR}, // Hong Kong Dollar
{ "TWD", 1000, CurrencyAffix::Prefix, u8"NT$", CurrencyAffix::Prefix, "NT$", STR_NEW_TAIWAN_DOLLAR}, // New Taiwan Dollar
{ "CNY", 100, CurrencyAffix::Prefix, u8"CN¥", CurrencyAffix::Prefix, "CNY", STR_CHINESE_YUAN }, // Chinese Yuan
{ "HUF", 1000, CurrencyAffix::Suffix, u8" Ft", CurrencyAffix::Suffix, " Ft", STR_HUNGARIAN_FORINT}, // Hungarian Forint
{ "CTM", 10, CurrencyAffix::Prefix, u8"Ctm", CurrencyAffix::Prefix, "Ctm", STR_CUSTOM_CURRENCY }, // Customizable currency
};
// clang-format on
void CurrencyLoadCustomCurrencyConfig()
{
CurrencyDescriptors[EnumValue(CurrencyType::Custom)].rate = Config::Get().general.CustomCurrencyRate;
CurrencyDescriptors[EnumValue(CurrencyType::Custom)].affix_unicode = Config::Get().general.CustomCurrencyAffix;
if (!Config::Get().general.CustomCurrencySymbol.empty())
{
String::safeUtf8Copy(
CurrencyDescriptors[EnumValue(CurrencyType::Custom)].symbol_unicode,
Config::Get().general.CustomCurrencySymbol.c_str(), kCurrencySymbolMaxSize);
}
}
money64 StringToMoney(const char* string_to_monetise)
{
const char* decimal_char = LanguageGetString(STR_LOCALE_DECIMAL_POINT);
const CurrencyDescriptor* currencyDesc = &CurrencyDescriptors[EnumValue(Config::Get().general.CurrencyFormat)];
char processedString[128] = {};
Guard::Assert(strlen(string_to_monetise) < sizeof(processedString));
uint32_t numNumbers = 0;
bool hasMinus = false;
bool hasDecSep = false;
const char* src_ptr = string_to_monetise;
char* dst_ptr = processedString;
// Process the string, keeping only numbers decimal, and minus sign(s).
while (*src_ptr != '\0')
{
if (*src_ptr >= '0' && *src_ptr <= '9')
{
numNumbers++;
}
else if (*src_ptr == decimal_char[0])
{
if (hasDecSep)
return kMoney64Undefined;
hasDecSep = true;
// Replace localised decimal separator with an English one.
*dst_ptr++ = '.';
src_ptr++;
continue;
}
else if (*src_ptr == '-')
{
if (hasMinus)
return kMoney64Undefined;
hasMinus = true;
}
else
{
// Skip invalid characters.
src_ptr++;
continue;
}
// Copy numeric values.
*dst_ptr++ = *src_ptr;
src_ptr++;
}
// Terminate destination string.
*dst_ptr = '\0';
if (numNumbers == 0)
return kMoney64Undefined;
if (hasMinus && processedString[0] != '-')
{
// If there is a minus sign, it has to be at position 0 in order to be valid.
return kMoney64Undefined;
}
// Due to the nature of strstr and strtok, decimals at the very beginning will be ignored, causing
// ".1" to be interpreted as "1". To prevent this, prefix with "0" if decimal is at the beginning.
if (processedString[0] == decimal_char[0])
{
for (size_t i = strlen(processedString); i >= 1; i--)
processedString[i] = processedString[i - 1];
processedString[0] = '0';
}
auto number = std::stod(processedString, nullptr);
number /= (currencyDesc->rate / 10.0);
return ToMoney64FromGBP(number);
}
/**
*
* @param amount The amount in tens of pounds, e.g. 123 = £ 12.30
* @param buffer_to_put_value_to Output parameter.
* @param buffer_len Length of the buffer.
* @param forceDecimals Show decimals, even if the amount does not have them. Will be ignored if the current exchange
* rate is too big to have decimals.
*/
void MoneyToString(money64 amount, char* buffer_to_put_value_to, size_t buffer_len, bool forceDecimals)
{
if (amount == kMoney64Undefined)
{
snprintf(buffer_to_put_value_to, buffer_len, "0");
return;
}
const CurrencyDescriptor& currencyDesc = CurrencyDescriptors[EnumValue(Config::Get().general.CurrencyFormat)];
const char* sign = amount >= 0 ? "" : "-";
const uint64_t a = std::abs(amount) * currencyDesc.rate;
const unsigned long long whole = a / 100;
const unsigned long long decimal = a % 100;
bool amountIsInteger = (whole > 0) && decimal == 0;
// If whole and decimal exist
if ((whole > 0 && decimal > 0) || (amountIsInteger && forceDecimals && currencyDesc.rate < 100))
{
const char* decimalChar = LanguageGetString(STR_LOCALE_DECIMAL_POINT);
auto precedingZero = (decimal < 10) ? "0" : "";
snprintf(buffer_to_put_value_to, buffer_len, "%s%llu%s%s%llu", sign, whole, decimalChar, precedingZero, decimal);
}
// If whole exists, but not decimal
else if (amountIsInteger)
{
snprintf(buffer_to_put_value_to, buffer_len, "%s%llu", sign, whole);
}
// If decimal exists, but not whole
else if (whole == 0 && decimal > 0)
{
const char* decimalChar = LanguageGetString(STR_LOCALE_DECIMAL_POINT);
snprintf(buffer_to_put_value_to, buffer_len, "%s0%s%llu", sign, decimalChar, decimal);
}
else
{
snprintf(buffer_to_put_value_to, buffer_len, "0");
}
}