diff --git a/src/duckstation-mini/mini_host.cpp b/src/duckstation-mini/mini_host.cpp index a289763ad..07cc81bad 100644 --- a/src/duckstation-mini/mini_host.cpp +++ b/src/duckstation-mini/mini_host.cpp @@ -48,6 +48,7 @@ #include #include #include +#include #include LOG_CHANNEL(Host); @@ -57,8 +58,8 @@ namespace MiniHost { /// Use two async worker threads, should be enough for most tasks. static constexpr u32 NUM_ASYNC_WORKER_THREADS = 2; -//static constexpr u32 DEFAULT_WINDOW_WIDTH = 1280; -//static constexpr u32 DEFAULT_WINDOW_HEIGHT = 720; +// static constexpr u32 DEFAULT_WINDOW_WIDTH = 1280; +// static constexpr u32 DEFAULT_WINDOW_HEIGHT = 720; static constexpr u32 DEFAULT_WINDOW_WIDTH = 1920; static constexpr u32 DEFAULT_WINDOW_HEIGHT = 1080; @@ -1375,6 +1376,64 @@ bool Host::CopyTextToClipboard(std::string_view text) return true; } +std::string Host::FormatNumber(NumberFormatType type, s64 value) +{ + std::string ret; + + if (type >= NumberFormatType::ShortDate && type <= NumberFormatType::LongDateTime) + { + const char* format; + switch (type) + { + case NumberFormatType::ShortDate: + format = "%x"; + break; + + case NumberFormatType::LongDate: + format = "%A %B %e %Y"; + break; + + case NumberFormatType::ShortTime: + case NumberFormatType::LongTime: + format = "%X"; + break; + + case NumberFormatType::ShortDateTime: + format = "%X %x"; + break; + + case NumberFormatType::LongDateTime: + format = "%c"; + break; + + DefaultCaseIsUnreachable(); + } + + struct tm ttime = {}; + const std::time_t tvalue = static_cast(value); +#ifdef _MSC_VER + localtime_s(&ttime, &tvalue); +#else + localtime_r(&tvalue, &ttime); +#endif + + char buf[128]; + std::strftime(buf, std::size(buf), "%x", &ttime); + ret.assign(buf); + } + else + { + ret = fmt::format("{}", value); + } + + return ret; +} + +std::string Host::FormatNumber(NumberFormatType type, double value) +{ + return fmt::format("{}", value); +} + std::optional InputManager::ConvertHostKeyboardStringToCode(std::string_view str) { return SDLKeyNames::GetKeyCodeForName(str); diff --git a/src/duckstation-qt/qthost.cpp b/src/duckstation-qt/qthost.cpp index 04bc6e9f7..d9d8f28e5 100644 --- a/src/duckstation-qt/qthost.cpp +++ b/src/duckstation-qt/qthost.cpp @@ -2164,6 +2164,61 @@ bool Host::CopyTextToClipboard(std::string_view text) return true; } +std::string Host::FormatNumber(NumberFormatType type, s64 value) +{ + const QLocale loc = QLocale::system(); + std::string ret; + + if (type >= NumberFormatType::ShortDate && type <= NumberFormatType::LongDateTime) + { + QString format; + switch (type) + { + case NumberFormatType::ShortDate: + case NumberFormatType::LongDate: + format = loc.dateFormat((type == NumberFormatType::LongDate) ? QLocale::LongFormat : QLocale::ShortFormat); + break; + + case NumberFormatType::ShortTime: + case NumberFormatType::LongTime: + format = loc.timeFormat((type == NumberFormatType::LongTime) ? QLocale::LongFormat : QLocale::ShortFormat); + break; + + case NumberFormatType::ShortDateTime: + case NumberFormatType::LongDateTime: + format = + loc.dateTimeFormat((type == NumberFormatType::LongDateTime) ? QLocale::LongFormat : QLocale::ShortFormat); + break; + + DefaultCaseIsUnreachable(); + } + + ret = QDateTime::fromSecsSinceEpoch(value).toString(format).toStdString(); + } + else + { + ret = loc.toString(value).toStdString(); + } + + return ret; +} + +std::string Host::FormatNumber(NumberFormatType type, double value) +{ + const QLocale loc = QLocale::system(); + std::string ret; + + switch (type) + { + case NumberFormatType::Number: + default: + ret = loc.toString(value).toStdString(); + break; + } + + return ret; +} + void Host::ReportDebuggerMessage(std::string_view message) { INFO_LOG("Debugger message: {}", message); diff --git a/src/duckstation-regtest/regtest_host.cpp b/src/duckstation-regtest/regtest_host.cpp index a9cba8d57..ca5abd817 100644 --- a/src/duckstation-regtest/regtest_host.cpp +++ b/src/duckstation-regtest/regtest_host.cpp @@ -40,6 +40,7 @@ #include #include +#include LOG_CHANNEL(Host); @@ -565,6 +566,64 @@ bool Host::CopyTextToClipboard(std::string_view text) return false; } +std::string Host::FormatNumber(NumberFormatType type, s64 value) +{ + std::string ret; + + if (type >= NumberFormatType::ShortDate && type <= NumberFormatType::LongDateTime) + { + const char* format; + switch (type) + { + case NumberFormatType::ShortDate: + format = "%x"; + break; + + case NumberFormatType::LongDate: + format = "%A %B %e %Y"; + break; + + case NumberFormatType::ShortTime: + case NumberFormatType::LongTime: + format = "%X"; + break; + + case NumberFormatType::ShortDateTime: + format = "%X %x"; + break; + + case NumberFormatType::LongDateTime: + format = "%c"; + break; + + DefaultCaseIsUnreachable(); + } + + struct tm ttime = {}; + const std::time_t tvalue = static_cast(value); +#ifdef _MSC_VER + localtime_s(&ttime, &tvalue); +#else + localtime_r(&tvalue, &ttime); +#endif + + char buf[128]; + std::strftime(buf, std::size(buf), "%x", &ttime); + ret.assign(buf); + } + else + { + ret = fmt::format("{}", value); + } + + return ret; +} + +std::string Host::FormatNumber(NumberFormatType type, double value) +{ + return fmt::format("{}", value); +} + void Host::SetMouseMode(bool relative, bool hide_cursor) { // diff --git a/src/util/host.h b/src/util/host.h index 5aecc49bc..59bf262dc 100644 --- a/src/util/host.h +++ b/src/util/host.h @@ -59,6 +59,22 @@ std::string GetClipboardText(); /// Copies the provided UTF-8 text to the host's clipboard, if present. bool CopyTextToClipboard(std::string_view text); +/// Formats a number according to the current locale. +enum class NumberFormatType : u8 +{ + ShortDate, // Date formatting + LongDate, // Date formatting + ShortTime, // Time formatting + LongTime, // Time formatting + ShortDateTime, // Date and time formatting + LongDateTime, // Date and time formatting + Number, // Number formatting + + MaxCount, +}; +std::string FormatNumber(NumberFormatType type, s64 value); +std::string FormatNumber(NumberFormatType type, double value); + /// Returns a localized version of the specified string within the specified context. /// The pointer is guaranteed to be valid until the next language change. const char* TranslateToCString(std::string_view context, std::string_view msg, std::string_view disambiguation = {});