1
0
Fork 0

Compare commits

...

2 Commits

Author SHA1 Message Date
translators ba51a34b4c Update: Translations from eints
english (au): 20 changes by krysclarke
korean: 25 changes by telk5093
russian: 20 changes by Ln-Wolf
finnish: 20 changes by hpiirai
french: 20 changes by glx22
portuguese (brazilian): 20 changes by pasantoro
2023-09-14 18:37:08 +00:00
Patric Stout 37e2f99c09
Change: store crash logs in JSON format (#11232) 2023-09-14 20:13:27 +02:00
20 changed files with 854 additions and 753 deletions

View File

@ -432,6 +432,8 @@ add_files(
subsidy_func.h subsidy_func.h
subsidy_gui.cpp subsidy_gui.cpp
subsidy_type.h subsidy_type.h
survey.cpp
survey.h
tar_type.h tar_type.h
terraform_cmd.cpp terraform_cmd.cpp
terraform_cmd.h terraform_cmd.h

View File

@ -9,297 +9,60 @@
#include "stdafx.h" #include "stdafx.h"
#include "crashlog.h" #include "crashlog.h"
#include "survey.h"
#include "gamelog.h" #include "gamelog.h"
#include "timer/timer_game_calendar.h"
#include "map_func.h" #include "map_func.h"
#include "rev.h"
#include "strings_func.h"
#include "blitter/factory.hpp"
#include "base_media_base.h"
#include "music/music_driver.hpp" #include "music/music_driver.hpp"
#include "sound/sound_driver.hpp" #include "sound/sound_driver.hpp"
#include "video/video_driver.hpp" #include "video/video_driver.hpp"
#include "saveload/saveload.h" #include "saveload/saveload.h"
#include "screenshot.h" #include "screenshot.h"
#include "gfx_func.h"
#include "network/network.h"
#include "network/network_survey.h" #include "network/network_survey.h"
#include "language.h"
#include "fontcache.h"
#include "news_gui.h" #include "news_gui.h"
#include "fileio_func.h"
#include "fileio_type.h"
#include "ai/ai_info.hpp"
#include "game/game.hpp"
#include "game/game_info.hpp"
#include "company_base.h"
#include "company_func.h" #include "company_func.h"
#include "3rdparty/fmt/chrono.h" #include "3rdparty/fmt/chrono.h"
#include "3rdparty/fmt/std.h" #include "3rdparty/fmt/std.h"
#include "core/format.hpp"
#ifdef WITH_ALLEGRO
# include <allegro.h>
#endif /* WITH_ALLEGRO */
#ifdef WITH_FONTCONFIG
# include <fontconfig/fontconfig.h>
#endif /* WITH_FONTCONFIG */
#ifdef WITH_PNG
/* pngconf.h, included by png.h doesn't like something in the
* freetype headers. As such it's not alphabetically sorted. */
# include <png.h>
#endif /* WITH_PNG */
#ifdef WITH_FREETYPE
# include <ft2build.h>
# include FT_FREETYPE_H
#endif /* WITH_FREETYPE */
#ifdef WITH_HARFBUZZ
# include <hb.h>
#endif /* WITH_HARFBUZZ */
#ifdef WITH_ICU_I18N
# include <unicode/uversion.h>
#endif /* WITH_ICU_I18N */
#ifdef WITH_LIBLZMA
# include <lzma.h>
#endif
#ifdef WITH_LZO
#include <lzo/lzo1x.h>
#endif
#if defined(WITH_SDL) || defined(WITH_SDL2)
# include <SDL.h>
#endif /* WITH_SDL || WITH_SDL2 */
#ifdef WITH_ZLIB
# include <zlib.h>
#endif
#ifdef WITH_CURL
# include <curl/curl.h>
#endif
#include "safeguards.h" #include "safeguards.h"
/* static */ std::string CrashLog::message{ "<none>" }; /* static */ std::string CrashLog::message{};
void CrashLog::LogCompiler(std::back_insert_iterator<std::string> &output_iterator) const /** The version of the schema of the JSON information. */
{ constexpr uint8_t CRASHLOG_SURVEY_VERSION = 1;
fmt::format_to(output_iterator, " Compiler: "
#if defined(_MSC_VER)
"MSVC {}", _MSC_VER
#elif defined(__ICC) && defined(__GNUC__)
"ICC {} (GCC {}.{}.{} mode)", __ICC, __GNUC__, __GNUC_MINOR__, __GNUC_PATCHLEVEL__
#elif defined(__ICC)
"ICC {}", __ICC
#elif defined(__GNUC__)
"GCC {}.{}.{}", __GNUC__, __GNUC_MINOR__, __GNUC_PATCHLEVEL__
#else
"<unknown>"
#endif
);
#if defined(__VERSION__)
fmt::format_to(output_iterator, " \"" __VERSION__ "\"\n\n");
#else
fmt::format_to(output_iterator, "\n\n");
#endif
}
/**
* Writes OpenTTD's version to the buffer.
* @param output_iterator Iterator to write the output to.
*/
void CrashLog::LogOpenTTDVersion(std::back_insert_iterator<std::string> &output_iterator) const
{
fmt::format_to(output_iterator,
"OpenTTD version:\n"
" Version: {} ({})\n"
" NewGRF ver: {:08x}\n"
" Bits: {}\n"
" Endian: {}\n"
" Dedicated: {}\n"
" Build date: {}\n\n",
_openttd_revision,
_openttd_revision_modified,
_openttd_newgrf_version,
#ifdef POINTER_IS_64BIT
64,
#else
32,
#endif
#if (TTD_ENDIAN == TTD_LITTLE_ENDIAN)
"little",
#else
"big",
#endif
#ifdef DEDICATED
"yes",
#else
"no",
#endif
_openttd_build_date
);
}
/**
* Writes the (important) configuration settings to the buffer.
* E.g. graphics set, sound set, blitter and AIs.
* @param output_iterator Iterator to write the output to.
*/
void CrashLog::LogConfiguration(std::back_insert_iterator<std::string> &output_iterator) const
{
fmt::format_to(output_iterator,
"Configuration:\n"
" Blitter: {}\n"
" Graphics set: {} ({})\n"
" Language: {}\n"
" Music driver: {}\n"
" Music set: {} ({})\n"
" Network: {}\n"
" Sound driver: {}\n"
" Sound set: {} ({})\n"
" Video driver: {}\n\n",
BlitterFactory::GetCurrentBlitter() == nullptr ? "none" : BlitterFactory::GetCurrentBlitter()->GetName(),
BaseGraphics::GetUsedSet() == nullptr ? "none" : BaseGraphics::GetUsedSet()->name,
BaseGraphics::GetUsedSet() == nullptr ? UINT32_MAX : BaseGraphics::GetUsedSet()->version,
_current_language == nullptr ? "none" : _current_language->file.filename(),
MusicDriver::GetInstance() == nullptr ? "none" : MusicDriver::GetInstance()->GetName(),
BaseMusic::GetUsedSet() == nullptr ? "none" : BaseMusic::GetUsedSet()->name,
BaseMusic::GetUsedSet() == nullptr ? UINT32_MAX : BaseMusic::GetUsedSet()->version,
_networking ? (_network_server ? "server" : "client") : "no",
SoundDriver::GetInstance() == nullptr ? "none" : SoundDriver::GetInstance()->GetName(),
BaseSounds::GetUsedSet() == nullptr ? "none" : BaseSounds::GetUsedSet()->name,
BaseSounds::GetUsedSet() == nullptr ? UINT32_MAX : BaseSounds::GetUsedSet()->version,
VideoDriver::GetInstance() == nullptr ? "none" : VideoDriver::GetInstance()->GetInfoString()
);
fmt::format_to(output_iterator,
"Fonts:\n"
" Small: {}\n"
" Medium: {}\n"
" Large: {}\n"
" Mono: {}\n\n",
FontCache::GetName(FS_SMALL),
FontCache::GetName(FS_NORMAL),
FontCache::GetName(FS_LARGE),
FontCache::GetName(FS_MONO)
);
fmt::format_to(output_iterator, "AI Configuration (local: {}) (current: {}):\n", _local_company, _current_company);
for (const Company *c : Company::Iterate()) {
if (c->ai_info == nullptr) {
fmt::format_to(output_iterator, " {:2}: Human\n", c->index);
} else {
fmt::format_to(output_iterator, " {:2}: {} (v{})\n", (int)c->index, c->ai_info->GetName(), c->ai_info->GetVersion());
}
}
if (Game::GetInfo() != nullptr) {
fmt::format_to(output_iterator, " GS: {} (v{})\n", Game::GetInfo()->GetName(), Game::GetInfo()->GetVersion());
}
fmt::format_to(output_iterator, "\n");
}
/**
* Writes information (versions) of the used libraries.
* @param output_iterator Iterator to write the output to.
*/
void CrashLog::LogLibraries(std::back_insert_iterator<std::string> &output_iterator) const
{
fmt::format_to(output_iterator, "Libraries:\n");
#ifdef WITH_ALLEGRO
fmt::format_to(output_iterator, " Allegro: {}\n", allegro_id);
#endif /* WITH_ALLEGRO */
#ifdef WITH_FONTCONFIG
int version = FcGetVersion();
fmt::format_to(output_iterator, " FontConfig: {}.{}.{}\n", version / 10000, (version / 100) % 100, version % 100);
#endif /* WITH_FONTCONFIG */
#ifdef WITH_FREETYPE
FT_Library library;
int major, minor, patch;
FT_Init_FreeType(&library);
FT_Library_Version(library, &major, &minor, &patch);
FT_Done_FreeType(library);
fmt::format_to(output_iterator, " FreeType: {}.{}.{}\n", major, minor, patch);
#endif /* WITH_FREETYPE */
#if defined(WITH_HARFBUZZ)
fmt::format_to(output_iterator, " HarfBuzz: {}\n", hb_version_string());
#endif /* WITH_HARFBUZZ */
#if defined(WITH_ICU_I18N)
/* 4 times 0-255, separated by dots (.) and a trailing '\0' */
char buf[4 * 3 + 3 + 1];
UVersionInfo ver;
u_getVersion(ver);
u_versionToString(ver, buf);
fmt::format_to(output_iterator, " ICU i18n: {}\n", buf);
#endif /* WITH_ICU_I18N */
#ifdef WITH_LIBLZMA
fmt::format_to(output_iterator, " LZMA: {}\n", lzma_version_string());
#endif
#ifdef WITH_LZO
fmt::format_to(output_iterator, " LZO: {}\n", lzo_version_string());
#endif
#ifdef WITH_PNG
fmt::format_to(output_iterator, " PNG: {}\n", png_get_libpng_ver(nullptr));
#endif /* WITH_PNG */
#ifdef WITH_SDL
const SDL_version *sdl_v = SDL_Linked_Version();
fmt::format_to(output_iterator, " SDL1: {}.{}.{}\n", sdl_v->major, sdl_v->minor, sdl_v->patch);
#elif defined(WITH_SDL2)
SDL_version sdl2_v;
SDL_GetVersion(&sdl2_v);
fmt::format_to(output_iterator, " SDL2: {}.{}.{}\n", sdl2_v.major, sdl2_v.minor, sdl2_v.patch);
#endif
#ifdef WITH_ZLIB
fmt::format_to(output_iterator, " Zlib: {}\n", zlibVersion());
#endif
#ifdef WITH_CURL
auto *curl_v = curl_version_info(CURLVERSION_NOW);
fmt::format_to(output_iterator, " Curl: {}\n", curl_v->version);
if (curl_v->ssl_version != nullptr) {
fmt::format_to(output_iterator, " Curl SSL: {}\n", curl_v->ssl_version);
} else {
fmt::format_to(output_iterator, " Curl SSL: none\n");
}
#endif
fmt::format_to(output_iterator, "\n");
}
/** /**
* Writes the gamelog data to the buffer. * Writes the gamelog data to the buffer.
* @param output_iterator Iterator to write the output to. * @param output_iterator Iterator to write the output to.
*/ */
void CrashLog::LogGamelog(std::back_insert_iterator<std::string> &output_iterator) const static void SurveyGamelog(nlohmann::json &json)
{ {
_gamelog.Print([&output_iterator](const std::string &s) { json = nlohmann::json::array();
fmt::format_to(output_iterator, "{}\n", s);
_gamelog.Print([&json](const std::string &s) {
json.push_back(s);
}); });
fmt::format_to(output_iterator, "\n");
} }
/** /**
* Writes up to 32 recent news messages to the buffer, with the most recent first. * Writes up to 32 recent news messages to the buffer, with the most recent first.
* @param output_iterator Iterator to write the output to. * @param output_iterator Iterator to write the output to.
*/ */
void CrashLog::LogRecentNews(std::back_insert_iterator<std::string> &output_iterator) const static void SurveyRecentNews(nlohmann::json &json)
{ {
fmt::format_to(output_iterator, "Recent news messages:\n"); json = nlohmann::json::array();
int i = 0; int i = 0;
for (NewsItem *news = _latest_news; i < 32 && news != nullptr; news = news->prev, i++) { for (NewsItem *news = _latest_news; i < 32 && news != nullptr; news = news->prev, i++) {
TimerGameCalendar::YearMonthDay ymd; TimerGameCalendar::YearMonthDay ymd;
TimerGameCalendar::ConvertDateToYMD(news->date, &ymd); TimerGameCalendar::ConvertDateToYMD(news->date, &ymd);
fmt::format_to(output_iterator, "({}-{:02}-{:02}) StringID: {}, Type: {}, Ref1: {}, {}, Ref2: {}, {}\n", json.push_back(fmt::format("({}-{:02}-{:02}) StringID: {}, Type: {}, Ref1: {}, {}, Ref2: {}, {}",
ymd.year, ymd.month + 1, ymd.day, news->string_id, news->type, ymd.year, ymd.month + 1, ymd.day, news->string_id, news->type,
news->reftype1, news->ref1, news->reftype2, news->ref2); news->reftype1, news->ref1, news->reftype2, news->ref2));
} }
fmt::format_to(output_iterator, "\n");
} }
/** /**
@ -320,28 +83,95 @@ std::string CrashLog::CreateFileName(const char *ext, bool with_dir) const
/** /**
* Fill the crash log buffer with all data of a crash log. * Fill the crash log buffer with all data of a crash log.
* @param output_iterator Iterator to write the output to.
*/ */
void CrashLog::FillCrashLog(std::back_insert_iterator<std::string> &output_iterator) const void CrashLog::FillCrashLog()
{ {
fmt::format_to(output_iterator, "*** OpenTTD Crash Report ***\n\n"); /* Reminder: this JSON is read in an automated fashion.
fmt::format_to(output_iterator, "Crash at: {:%Y-%m-%d %H:%M:%S} (UTC)\n", fmt::gmtime(time(nullptr))); * If any structural changes are applied, please bump the version. */
this->survey["schema"] = CRASHLOG_SURVEY_VERSION;
this->survey["date"] = fmt::format("{:%Y-%m-%d %H:%M:%S} (UTC)", fmt::gmtime(time(nullptr)));
TimerGameCalendar::YearMonthDay ymd; /* If no internal reason was logged, it must be a crash. */
TimerGameCalendar::ConvertDateToYMD(TimerGameCalendar::date, &ymd); if (CrashLog::message.empty()) {
fmt::format_to(output_iterator, "In game date: {}-{:02}-{:02} ({})\n\n", ymd.year, ymd.month + 1, ymd.day, TimerGameCalendar::date_fract); this->SurveyCrash(this->survey["crash"]);
} else {
this->survey["crash"]["reason"] = CrashLog::message;
CrashLog::message.clear();
}
this->LogError(output_iterator, CrashLog::message); if (!this->TryExecute("stacktrace", [this]() { this->SurveyStacktrace(this->survey["stacktrace"]); return true; })) {
this->LogOpenTTDVersion(output_iterator); this->survey["stacktrace"] = "crashed while gathering information";
this->LogStacktrace(output_iterator); }
this->LogOSVersion(output_iterator);
this->LogCompiler(output_iterator);
this->LogConfiguration(output_iterator);
this->LogLibraries(output_iterator);
this->LogGamelog(output_iterator);
this->LogRecentNews(output_iterator);
fmt::format_to(output_iterator, "*** End of OpenTTD Crash Report ***\n"); {
auto &info = this->survey["info"];
if (!this->TryExecute("os", [&info]() { SurveyOS(info["os"]); return true; })) {
info["os"] = "crashed while gathering information";
}
if (!this->TryExecute("openttd", [&info]() { SurveyOpenTTD(info["openttd"]); return true; })) {
info["openttd"] = "crashed while gathering information";
}
if (!this->TryExecute("configuration", [&info]() { SurveyConfiguration(info["configuration"]); return true; })) {
info["configuration"] = "crashed while gathering information";
}
if (!this->TryExecute("font", [&info]() { SurveyFont(info["font"]); return true; })) {
info["font"] = "crashed while gathering information";
}
if (!this->TryExecute("compiler", [&info]() { SurveyCompiler(info["compiler"]); return true; })) {
info["compiler"] = "crashed while gathering information";
}
if (!this->TryExecute("libraries", [&info]() { SurveyLibraries(info["libraries"]); return true; })) {
info["libraries"] = "crashed while gathering information";
}
}
{
auto &game = this->survey["game"];
game["local_company"] = _local_company;
game["current_company"] = _current_company;
if (!this->TryExecute("timers", [&game]() { SurveyTimers(game["timers"]); return true; })) {
game["libraries"] = "crashed while gathering information";
}
if (!this->TryExecute("companies", [&game]() { SurveyCompanies(game["companies"]); return true; })) {
game["companies"] = "crashed while gathering information";
}
if (!this->TryExecute("settings", [&game]() { SurveySettings(game["settings_changed"], true); return true; })) {
game["settings"] = "crashed while gathering information";
}
if (!this->TryExecute("grfs", [&game]() { SurveyGrfs(game["grfs"]); return true; })) {
game["grfs"] = "crashed while gathering information";
}
if (!this->TryExecute("game_script", [&game]() { SurveyGameScript(game["game_script"]); return true; })) {
game["game_script"] = "crashed while gathering information";
}
if (!this->TryExecute("gamelog", [&game]() { SurveyGamelog(game["gamelog"]); return true; })) {
game["gamelog"] = "crashed while gathering information";
}
if (!this->TryExecute("news", [&game]() { SurveyRecentNews(game["news"]); return true; })) {
game["news"] = "crashed while gathering information";
}
}
}
void CrashLog::PrintCrashLog() const
{
fmt::print(" OpenTTD version:\n");
fmt::print(" Version: {}\n", this->survey["info"]["openttd"]["version"]["revision"].get<std::string>());
fmt::print(" Hash: {}\n", this->survey["info"]["openttd"]["version"]["hash"].get<std::string>());
fmt::print(" NewGRF ver: {}\n", this->survey["info"]["openttd"]["version"]["newgrf"].get<std::string>());
fmt::print(" Content ver: {}\n", this->survey["info"]["openttd"]["version"]["content"].get<std::string>());
fmt::print("\n");
fmt::print(" Crash:\n");
fmt::print(" Reason: {}\n", this->survey["crash"]["reason"].get<std::string>());
fmt::print("\n");
fmt::print(" Stacktrace:\n");
for (const auto &line : this->survey["stacktrace"]) {
fmt::print(" {}\n", line.get<std::string>());
}
fmt::print("\n");
} }
/** /**
@ -351,13 +181,15 @@ void CrashLog::FillCrashLog(std::back_insert_iterator<std::string> &output_itera
*/ */
bool CrashLog::WriteCrashLog() bool CrashLog::WriteCrashLog()
{ {
this->crashlog_filename = this->CreateFileName(".log"); this->crashlog_filename = this->CreateFileName(".json.log");
FILE *file = FioFOpenFile(this->crashlog_filename, "w", NO_DIRECTORY); FILE *file = FioFOpenFile(this->crashlog_filename, "w", NO_DIRECTORY);
if (file == nullptr) return false; if (file == nullptr) return false;
size_t len = this->crashlog.size(); std::string survey_json = this->survey.dump(4);
size_t written = fwrite(this->crashlog.data(), 1, len, file);
size_t len = survey_json.size();
size_t written = fwrite(survey_json.data(), 1, len, file);
FioFCloseFile(file); FioFCloseFile(file);
return len == written; return len == written;
@ -414,6 +246,9 @@ bool CrashLog::WriteScreenshot()
return res; return res;
} }
/**
* Send the survey result, noting it was a crash.
*/
void CrashLog::SendSurvey() const void CrashLog::SendSurvey() const
{ {
if (_game_mode == GM_NORMAL) { if (_game_mode == GM_NORMAL) {
@ -433,14 +268,13 @@ void CrashLog::MakeCrashLog()
if (crashlogged) return; if (crashlogged) return;
crashlogged = true; crashlogged = true;
crashlog.reserve(65536);
auto output_iterator = std::back_inserter(crashlog);
fmt::print("Crash encountered, generating crash log...\n"); fmt::print("Crash encountered, generating crash log...\n");
this->FillCrashLog(output_iterator); this->FillCrashLog();
fmt::print("{}\n", crashlog);
fmt::print("Crash log generated.\n\n"); fmt::print("Crash log generated.\n\n");
fmt::print("Crash in summary:\n");
this->TryExecute("crashlog", [this]() { this->PrintCrashLog(); return true; });
fmt::print("Writing crash log to disk...\n"); fmt::print("Writing crash log to disk...\n");
bool ret = this->TryExecute("crashlog", [this]() { return this->WriteCrashLog(); }); bool ret = this->TryExecute("crashlog", [this]() { return this->WriteCrashLog(); });
if (ret) { if (ret) {

View File

@ -10,6 +10,8 @@
#ifndef CRASHLOG_H #ifndef CRASHLOG_H
#define CRASHLOG_H #define CRASHLOG_H
#include "3rdparty/nlohmann/json.hpp"
/** /**
* Helper class for creating crash logs. * Helper class for creating crash logs.
*/ */
@ -17,40 +19,20 @@ class CrashLog {
private: private:
/** Error message coming from #FatalError(format, ...). */ /** Error message coming from #FatalError(format, ...). */
static std::string message; static std::string message;
protected:
/**
* Writes OS' version to the buffer.
* @param output_iterator Iterator to write the output to.
*/
virtual void LogOSVersion(std::back_insert_iterator<std::string> &output_iterator) const = 0;
/** /**
* Writes compiler (and its version, if available) to the buffer. * Convert system crash reason to JSON.
* @param output_iterator Iterator to write the output to. *
* @param survey The JSON object.
*/ */
virtual void LogCompiler(std::back_insert_iterator<std::string> &output_iterator) const; virtual void SurveyCrash(nlohmann::json &survey) const = 0;
/** /**
* Writes actually encountered error to the buffer. * Convert stacktrace to JSON.
* @param output_iterator Iterator to write the output to. *
* @param message Message passed to use for errors. * @param survey The JSON object.
*/ */
virtual void LogError(std::back_insert_iterator<std::string> &output_iterator, const std::string_view message) const = 0; virtual void SurveyStacktrace(nlohmann::json &survey) const = 0;
/**
* Writes the stack trace to the buffer, if there is information about it
* available.
* @param output_iterator Iterator to write the output to.
*/
virtual void LogStacktrace(std::back_insert_iterator<std::string> &output_iterator) const = 0;
void LogOpenTTDVersion(std::back_insert_iterator<std::string> &output_iterator) const;
void LogConfiguration(std::back_insert_iterator<std::string> &output_iterator) const;
void LogLibraries(std::back_insert_iterator<std::string> &output_iterator) const;
void LogGamelog(std::back_insert_iterator<std::string> &output_iterator) const;
void LogRecentNews(std::back_insert_iterator<std::string> &output_iterator) const;
std::string CreateFileName(const char *ext, bool with_dir = true) const;
/** /**
* Execute the func() and return its value. If any exception / signal / crash happens, * Execute the func() and return its value. If any exception / signal / crash happens,
@ -63,19 +45,23 @@ protected:
*/ */
virtual bool TryExecute(std::string_view section_name, std::function<bool()> &&func) = 0; virtual bool TryExecute(std::string_view section_name, std::function<bool()> &&func) = 0;
protected:
std::string CreateFileName(const char *ext, bool with_dir = true) const;
public: public:
/** Stub destructor to silence some compilers. */ /** Stub destructor to silence some compilers. */
virtual ~CrashLog() = default; virtual ~CrashLog() = default;
std::string crashlog; nlohmann::json survey;
std::string crashlog_filename; std::string crashlog_filename;
std::string crashdump_filename; std::string crashdump_filename;
std::string savegame_filename; std::string savegame_filename;
std::string screenshot_filename; std::string screenshot_filename;
void FillCrashLog(std::back_insert_iterator<std::string> &output_iterator) const; void FillCrashLog();
bool WriteCrashLog(); void PrintCrashLog() const;
bool WriteCrashLog();
virtual bool WriteCrashDump(); virtual bool WriteCrashDump();
bool WriteSavegame(); bool WriteSavegame();
bool WriteScreenshot(); bool WriteScreenshot();

View File

@ -524,6 +524,7 @@ STR_NEWS_MENU_DELETE_ALL_MESSAGES :Apagar todas as
# About menu # About menu
###length 11 ###length 11
STR_ABOUT_MENU_LAND_BLOCK_INFO :Informação da área do terreno STR_ABOUT_MENU_LAND_BLOCK_INFO :Informação da área do terreno
STR_ABOUT_MENU_HELP :Ajuda & manuais
STR_ABOUT_MENU_SEPARATOR : STR_ABOUT_MENU_SEPARATOR :
STR_ABOUT_MENU_TOGGLE_CONSOLE :Alternar console STR_ABOUT_MENU_TOGGLE_CONSOLE :Alternar console
STR_ABOUT_MENU_AI_DEBUG :Depurar IA/Script do jogo STR_ABOUT_MENU_AI_DEBUG :Depurar IA/Script do jogo
@ -2125,6 +2126,7 @@ STR_INTRO_MULTIPLAYER :{BLACK}Multi-jo
STR_INTRO_GAME_OPTIONS :{BLACK}Opções do Jogo STR_INTRO_GAME_OPTIONS :{BLACK}Opções do Jogo
STR_INTRO_HIGHSCORE :{BLACK}Pontuações STR_INTRO_HIGHSCORE :{BLACK}Pontuações
STR_INTRO_HELP :{BLACK}Ajuda & Manuais
STR_INTRO_CONFIG_SETTINGS_TREE :{BLACK}Configurações STR_INTRO_CONFIG_SETTINGS_TREE :{BLACK}Configurações
STR_INTRO_NEWGRF_SETTINGS :{BLACK}Configurar NewGRF STR_INTRO_NEWGRF_SETTINGS :{BLACK}Configurar NewGRF
STR_INTRO_ONLINE_CONTENT :{BLACK}Checar conteúdo on-line STR_INTRO_ONLINE_CONTENT :{BLACK}Checar conteúdo on-line
@ -2146,6 +2148,7 @@ STR_INTRO_TOOLTIP_TOYLAND_LANDSCAPE :{BLACK}Selecion
STR_INTRO_TOOLTIP_GAME_OPTIONS :{BLACK}Exibir opções de jogo STR_INTRO_TOOLTIP_GAME_OPTIONS :{BLACK}Exibir opções de jogo
STR_INTRO_TOOLTIP_HIGHSCORE :{BLACK}Exibe as pontuações STR_INTRO_TOOLTIP_HIGHSCORE :{BLACK}Exibe as pontuações
STR_INTRO_TOOLTIP_HELP :{BLACK}Obter acesso à documentação e recursos online
STR_INTRO_TOOLTIP_CONFIG_SETTINGS_TREE :{BLACK}Config. de exibição STR_INTRO_TOOLTIP_CONFIG_SETTINGS_TREE :{BLACK}Config. de exibição
STR_INTRO_TOOLTIP_NEWGRF_SETTINGS :{BLACK}Exibir configs. dos NewGRF STR_INTRO_TOOLTIP_NEWGRF_SETTINGS :{BLACK}Exibir configs. dos NewGRF
STR_INTRO_TOOLTIP_ONLINE_CONTENT :{BLACK}Checar por conteúdo novo e atualizado para baixar STR_INTRO_TOOLTIP_ONLINE_CONTENT :{BLACK}Checar por conteúdo novo e atualizado para baixar
@ -2168,6 +2171,17 @@ STR_ABANDON_GAME_QUERY :{YELLOW}Você t
STR_ABANDON_SCENARIO_QUERY :{YELLOW}Você tem certeza que quer abandonar este cenário? STR_ABANDON_SCENARIO_QUERY :{YELLOW}Você tem certeza que quer abandonar este cenário?
# Help window # Help window
STR_HELP_WINDOW_CAPTION :{WHITE}Ajuda & Manuais
STR_HELP_WINDOW_WEBSITES :{BLACK}Sites na Web
STR_HELP_WINDOW_DOCUMENTS :{BLACK}Documentos
STR_HELP_WINDOW_README :{BLACK}Leia-me
STR_HELP_WINDOW_CHANGELOG :{BLACK}Log de mudanças
STR_HELP_WINDOW_KNOWN_BUGS :{BLACK}Erros conhecidos
STR_HELP_WINDOW_LICENSE :{BLACK}Licença
STR_HELP_WINDOW_MAIN_WEBSITE :{BLACK}OpenTTD
STR_HELP_WINDOW_MANUAL_WIKI :{BLACK}Manual / Wiki
STR_HELP_WINDOW_BUGTRACKER :{BLACK}Relatar um erro
STR_HELP_WINDOW_COMMUNITY :{BLACK}Comunidade
# Cheat window # Cheat window
STR_CHEATS :{WHITE}Trapaças STR_CHEATS :{WHITE}Trapaças
@ -4702,6 +4716,11 @@ STR_AI_SETTINGS_SETTING :{STRING}: {ORAN
# Textfile window # Textfile window
STR_TEXTFILE_JUMPLIST :{WHITE}Tabela de Conteúdo
STR_TEXTFILE_JUMPLIST_TOOLTIP :{BLACK}Acesso rápido a uma seção do arquivo mostrado nesta lista
STR_TEXTFILE_JUMPLIST_ITEM :{WHITE}{STRING}
STR_TEXTFILE_NAVBACK_TOOLTIP :{BLACK}Retornar no histórico de navegação
STR_TEXTFILE_NAVFORWARD_TOOLTIP :{BLACK}Retornar adiante no histórico de navegação
STR_TEXTFILE_WRAP_TEXT :{WHITE}Quebra de linha STR_TEXTFILE_WRAP_TEXT :{WHITE}Quebra de linha
STR_TEXTFILE_WRAP_TEXT_TOOLTIP :[BLACK}Quebra linhas automaticamente para que o texto caiba na janela STR_TEXTFILE_WRAP_TEXT_TOOLTIP :[BLACK}Quebra linhas automaticamente para que o texto caiba na janela
STR_TEXTFILE_VIEW_README :{BLACK}Ver o leia-me STR_TEXTFILE_VIEW_README :{BLACK}Ver o leia-me
@ -4712,6 +4731,7 @@ STR_TEXTFILE_README_CAPTION :{WHITE}{STRING}
STR_TEXTFILE_CHANGELOG_CAPTION :{WHITE}{STRING} log de mudanças de {STRING} STR_TEXTFILE_CHANGELOG_CAPTION :{WHITE}{STRING} log de mudanças de {STRING}
STR_TEXTFILE_LICENCE_CAPTION :{WHITE}{STRING} licença de {STRING} STR_TEXTFILE_LICENCE_CAPTION :{WHITE}{STRING} licença de {STRING}
STR_TEXTFILE_SURVEY_RESULT_CAPTION :{WHITE}Visualização do resultado da pesquisa STR_TEXTFILE_SURVEY_RESULT_CAPTION :{WHITE}Visualização do resultado da pesquisa
STR_TEXTFILE_GAME_MANUAL_CAPTION :{WHITE}OpenTTD documento '{STRING}'
# Vehicle loading indicators # Vehicle loading indicators

View File

@ -523,6 +523,7 @@ STR_NEWS_MENU_DELETE_ALL_MESSAGES :Delete all mess
# About menu # About menu
###length 11 ###length 11
STR_ABOUT_MENU_LAND_BLOCK_INFO :Land area information STR_ABOUT_MENU_LAND_BLOCK_INFO :Land area information
STR_ABOUT_MENU_HELP :Help & manuals
STR_ABOUT_MENU_SEPARATOR : STR_ABOUT_MENU_SEPARATOR :
STR_ABOUT_MENU_TOGGLE_CONSOLE :Toggle console STR_ABOUT_MENU_TOGGLE_CONSOLE :Toggle console
STR_ABOUT_MENU_AI_DEBUG :AI/Game script debug STR_ABOUT_MENU_AI_DEBUG :AI/Game script debug
@ -2124,6 +2125,7 @@ STR_INTRO_MULTIPLAYER :{BLACK}Multipla
STR_INTRO_GAME_OPTIONS :{BLACK}Game Options STR_INTRO_GAME_OPTIONS :{BLACK}Game Options
STR_INTRO_HIGHSCORE :{BLACK}Highscore Table STR_INTRO_HIGHSCORE :{BLACK}Highscore Table
STR_INTRO_HELP :{BLACK}Help & Manuals
STR_INTRO_CONFIG_SETTINGS_TREE :{BLACK}Settings STR_INTRO_CONFIG_SETTINGS_TREE :{BLACK}Settings
STR_INTRO_NEWGRF_SETTINGS :{BLACK}NewGRF Settings STR_INTRO_NEWGRF_SETTINGS :{BLACK}NewGRF Settings
STR_INTRO_ONLINE_CONTENT :{BLACK}Check Online Content STR_INTRO_ONLINE_CONTENT :{BLACK}Check Online Content
@ -2145,6 +2147,7 @@ STR_INTRO_TOOLTIP_TOYLAND_LANDSCAPE :{BLACK}Select '
STR_INTRO_TOOLTIP_GAME_OPTIONS :{BLACK}Display game options STR_INTRO_TOOLTIP_GAME_OPTIONS :{BLACK}Display game options
STR_INTRO_TOOLTIP_HIGHSCORE :{BLACK}Display highscore table STR_INTRO_TOOLTIP_HIGHSCORE :{BLACK}Display highscore table
STR_INTRO_TOOLTIP_HELP :{BLACK}Get access to documentation and online resources
STR_INTRO_TOOLTIP_CONFIG_SETTINGS_TREE :{BLACK}Display settings STR_INTRO_TOOLTIP_CONFIG_SETTINGS_TREE :{BLACK}Display settings
STR_INTRO_TOOLTIP_NEWGRF_SETTINGS :{BLACK}Display NewGRF settings STR_INTRO_TOOLTIP_NEWGRF_SETTINGS :{BLACK}Display NewGRF settings
STR_INTRO_TOOLTIP_ONLINE_CONTENT :{BLACK}Check for new and updated content to download STR_INTRO_TOOLTIP_ONLINE_CONTENT :{BLACK}Check for new and updated content to download
@ -2167,6 +2170,17 @@ STR_ABANDON_GAME_QUERY :{YELLOW}Are you
STR_ABANDON_SCENARIO_QUERY :{YELLOW}Are you sure you want to abandon this scenario? STR_ABANDON_SCENARIO_QUERY :{YELLOW}Are you sure you want to abandon this scenario?
# Help window # Help window
STR_HELP_WINDOW_CAPTION :{WHITE}Help & Manuals
STR_HELP_WINDOW_WEBSITES :{BLACK}Websites
STR_HELP_WINDOW_DOCUMENTS :{BLACK}Documents
STR_HELP_WINDOW_README :{BLACK}Readme
STR_HELP_WINDOW_CHANGELOG :{BLACK}Changelog
STR_HELP_WINDOW_KNOWN_BUGS :{BLACK}Known Bugs
STR_HELP_WINDOW_LICENSE :{BLACK}License
STR_HELP_WINDOW_MAIN_WEBSITE :{BLACK}OpenTTD
STR_HELP_WINDOW_MANUAL_WIKI :{BLACK}Manual / Wiki
STR_HELP_WINDOW_BUGTRACKER :{BLACK}Report a Bug
STR_HELP_WINDOW_COMMUNITY :{BLACK}Community
# Cheat window # Cheat window
STR_CHEATS :{WHITE}Cheats STR_CHEATS :{WHITE}Cheats
@ -4701,6 +4715,11 @@ STR_AI_SETTINGS_SETTING :{STRING}: {ORAN
# Textfile window # Textfile window
STR_TEXTFILE_JUMPLIST :{WHITE}Table of Contents
STR_TEXTFILE_JUMPLIST_TOOLTIP :{BLACK}Quickly jump to a section in the displayed file via this list
STR_TEXTFILE_JUMPLIST_ITEM :{WHITE}{STRING}
STR_TEXTFILE_NAVBACK_TOOLTIP :{BLACK}Go back in navigation history
STR_TEXTFILE_NAVFORWARD_TOOLTIP :{BLACK}Return forward in navigation history
STR_TEXTFILE_WRAP_TEXT :{WHITE}Wrap text STR_TEXTFILE_WRAP_TEXT :{WHITE}Wrap text
STR_TEXTFILE_WRAP_TEXT_TOOLTIP :{BLACK}Wrap the text of the window so it all fits without having to scroll STR_TEXTFILE_WRAP_TEXT_TOOLTIP :{BLACK}Wrap the text of the window so it all fits without having to scroll
STR_TEXTFILE_VIEW_README :{BLACK}View readme STR_TEXTFILE_VIEW_README :{BLACK}View readme
@ -4711,6 +4730,7 @@ STR_TEXTFILE_README_CAPTION :{WHITE}{STRING}
STR_TEXTFILE_CHANGELOG_CAPTION :{WHITE}{STRING} changelog of {STRING} STR_TEXTFILE_CHANGELOG_CAPTION :{WHITE}{STRING} changelog of {STRING}
STR_TEXTFILE_LICENCE_CAPTION :{WHITE}{STRING} license of {STRING} STR_TEXTFILE_LICENCE_CAPTION :{WHITE}{STRING} license of {STRING}
STR_TEXTFILE_SURVEY_RESULT_CAPTION :{WHITE}Preview of survey result STR_TEXTFILE_SURVEY_RESULT_CAPTION :{WHITE}Preview of survey result
STR_TEXTFILE_GAME_MANUAL_CAPTION :{WHITE}OpenTTD document '{STRING}'
# Vehicle loading indicators # Vehicle loading indicators

View File

@ -523,6 +523,7 @@ STR_NEWS_MENU_DELETE_ALL_MESSAGES :Poista kaikki v
# About menu # About menu
###length 11 ###length 11
STR_ABOUT_MENU_LAND_BLOCK_INFO :Maa-alueen tiedot STR_ABOUT_MENU_LAND_BLOCK_INFO :Maa-alueen tiedot
STR_ABOUT_MENU_HELP :Ohjeet ja oppaat
STR_ABOUT_MENU_SEPARATOR : STR_ABOUT_MENU_SEPARATOR :
STR_ABOUT_MENU_TOGGLE_CONSOLE :Konsoli STR_ABOUT_MENU_TOGGLE_CONSOLE :Konsoli
STR_ABOUT_MENU_AI_DEBUG :Tekoälyn/peliskriptin virheenjäljitys STR_ABOUT_MENU_AI_DEBUG :Tekoälyn/peliskriptin virheenjäljitys
@ -2124,6 +2125,7 @@ STR_INTRO_MULTIPLAYER :{BLACK}Moninpel
STR_INTRO_GAME_OPTIONS :{BLACK}Pelin valinnat STR_INTRO_GAME_OPTIONS :{BLACK}Pelin valinnat
STR_INTRO_HIGHSCORE :{BLACK}Pistetaulukko STR_INTRO_HIGHSCORE :{BLACK}Pistetaulukko
STR_INTRO_HELP :{BLACK}Ohjeet ja oppaat
STR_INTRO_CONFIG_SETTINGS_TREE :{BLACK}Asetukset STR_INTRO_CONFIG_SETTINGS_TREE :{BLACK}Asetukset
STR_INTRO_NEWGRF_SETTINGS :{BLACK}NewGRF-asetukset STR_INTRO_NEWGRF_SETTINGS :{BLACK}NewGRF-asetukset
STR_INTRO_ONLINE_CONTENT :{BLACK}Tarkista online-sisältö STR_INTRO_ONLINE_CONTENT :{BLACK}Tarkista online-sisältö
@ -2145,6 +2147,7 @@ STR_INTRO_TOOLTIP_TOYLAND_LANDSCAPE :{BLACK}Valitse
STR_INTRO_TOOLTIP_GAME_OPTIONS :{BLACK}Näytä pelin valinnat STR_INTRO_TOOLTIP_GAME_OPTIONS :{BLACK}Näytä pelin valinnat
STR_INTRO_TOOLTIP_HIGHSCORE :{BLACK}Näytä pistetaulukko STR_INTRO_TOOLTIP_HIGHSCORE :{BLACK}Näytä pistetaulukko
STR_INTRO_TOOLTIP_HELP :{BLACK}Tutustu dokumentaatioon ja online-resursseihin
STR_INTRO_TOOLTIP_CONFIG_SETTINGS_TREE :{BLACK}Näytä asetukset STR_INTRO_TOOLTIP_CONFIG_SETTINGS_TREE :{BLACK}Näytä asetukset
STR_INTRO_TOOLTIP_NEWGRF_SETTINGS :{BLACK}Näytä NewGRF-asetukset STR_INTRO_TOOLTIP_NEWGRF_SETTINGS :{BLACK}Näytä NewGRF-asetukset
STR_INTRO_TOOLTIP_ONLINE_CONTENT :{BLACK}Tarkista uutta ja päivitettyä sisältöä ladattavaksi STR_INTRO_TOOLTIP_ONLINE_CONTENT :{BLACK}Tarkista uutta ja päivitettyä sisältöä ladattavaksi
@ -2167,6 +2170,17 @@ STR_ABANDON_GAME_QUERY :{YELLOW}Lopetet
STR_ABANDON_SCENARIO_QUERY :{YELLOW}Haluatko varmasti hylätä tämän skenaarion? STR_ABANDON_SCENARIO_QUERY :{YELLOW}Haluatko varmasti hylätä tämän skenaarion?
# Help window # Help window
STR_HELP_WINDOW_CAPTION :{WHITE}Ohjeet ja oppaat
STR_HELP_WINDOW_WEBSITES :{BLACK}Verkkosivustot
STR_HELP_WINDOW_DOCUMENTS :{BLACK}Asiakirjat
STR_HELP_WINDOW_README :{BLACK}Lueminut-tiedosto
STR_HELP_WINDOW_CHANGELOG :{BLACK}Muutosloki
STR_HELP_WINDOW_KNOWN_BUGS :{BLACK}Tiedossa olevat virheet
STR_HELP_WINDOW_LICENSE :{BLACK}Lisenssi
STR_HELP_WINDOW_MAIN_WEBSITE :{BLACK}OpenTTD
STR_HELP_WINDOW_MANUAL_WIKI :{BLACK}Käyttöopas/wiki
STR_HELP_WINDOW_BUGTRACKER :{BLACK}Ilmoita virheestä
STR_HELP_WINDOW_COMMUNITY :{BLACK}Yhteisö
# Cheat window # Cheat window
STR_CHEATS :{WHITE}Huijaukset STR_CHEATS :{WHITE}Huijaukset
@ -4701,6 +4715,11 @@ STR_AI_SETTINGS_SETTING :{STRING}: {ORAN
# Textfile window # Textfile window
STR_TEXTFILE_JUMPLIST :{WHITE}Sisällysluettelo
STR_TEXTFILE_JUMPLIST_TOOLTIP :{BLACK}Tämän luettelon avulla voit nopeasti siirtyä näytettävän tiedoston johonkin osioon
STR_TEXTFILE_JUMPLIST_ITEM :{WHITE}{STRING}
STR_TEXTFILE_NAVBACK_TOOLTIP :{BLACK}Siirry taaksepäin selaushistoriassa
STR_TEXTFILE_NAVFORWARD_TOOLTIP :{BLACK}Siirry eteenpäin selaushistoriassa
STR_TEXTFILE_WRAP_TEXT :{WHITE}Tekstin rivitys STR_TEXTFILE_WRAP_TEXT :{WHITE}Tekstin rivitys
STR_TEXTFILE_WRAP_TEXT_TOOLTIP :{BLACK}Rivitä teksti ikkunaan niin, että se näkyy kokonaan rullaamatta STR_TEXTFILE_WRAP_TEXT_TOOLTIP :{BLACK}Rivitä teksti ikkunaan niin, että se näkyy kokonaan rullaamatta
STR_TEXTFILE_VIEW_README :{BLACK}Näytä readme-tiedosto STR_TEXTFILE_VIEW_README :{BLACK}Näytä readme-tiedosto
@ -4711,6 +4730,7 @@ STR_TEXTFILE_README_CAPTION :{WHITE}{STRING}
STR_TEXTFILE_CHANGELOG_CAPTION :{WHITE}{STRING}:n {STRING} muutosloki STR_TEXTFILE_CHANGELOG_CAPTION :{WHITE}{STRING}:n {STRING} muutosloki
STR_TEXTFILE_LICENCE_CAPTION :{WHITE}{STRING}:n {STRING} lisenssi STR_TEXTFILE_LICENCE_CAPTION :{WHITE}{STRING}:n {STRING} lisenssi
STR_TEXTFILE_SURVEY_RESULT_CAPTION :{WHITE}Tutkimustuloksen esikatselu STR_TEXTFILE_SURVEY_RESULT_CAPTION :{WHITE}Tutkimustuloksen esikatselu
STR_TEXTFILE_GAME_MANUAL_CAPTION :{WHITE}OpenTTD-asiakirja ”{STRING}”
# Vehicle loading indicators # Vehicle loading indicators

View File

@ -524,6 +524,7 @@ STR_NEWS_MENU_DELETE_ALL_MESSAGES :Effacer tous le
# About menu # About menu
###length 11 ###length 11
STR_ABOUT_MENU_LAND_BLOCK_INFO :Outil d'inspection du paysage STR_ABOUT_MENU_LAND_BLOCK_INFO :Outil d'inspection du paysage
STR_ABOUT_MENU_HELP :Aide et manuels
STR_ABOUT_MENU_SEPARATOR : STR_ABOUT_MENU_SEPARATOR :
STR_ABOUT_MENU_TOGGLE_CONSOLE :Afficher/Cacher la console STR_ABOUT_MENU_TOGGLE_CONSOLE :Afficher/Cacher la console
STR_ABOUT_MENU_AI_DEBUG :Débogage de scripts STR_ABOUT_MENU_AI_DEBUG :Débogage de scripts
@ -2125,6 +2126,7 @@ STR_INTRO_MULTIPLAYER :{BLACK}Jouer en
STR_INTRO_GAME_OPTIONS :{BLACK}Options globales STR_INTRO_GAME_OPTIONS :{BLACK}Options globales
STR_INTRO_HIGHSCORE :{BLACK}Meilleurs scores STR_INTRO_HIGHSCORE :{BLACK}Meilleurs scores
STR_INTRO_HELP :{BLACK}Aide et manuels
STR_INTRO_CONFIG_SETTINGS_TREE :{BLACK}Paramètres de jeu STR_INTRO_CONFIG_SETTINGS_TREE :{BLACK}Paramètres de jeu
STR_INTRO_NEWGRF_SETTINGS :{BLACK}Paramètres NewGRF STR_INTRO_NEWGRF_SETTINGS :{BLACK}Paramètres NewGRF
STR_INTRO_ONLINE_CONTENT :{BLACK}Contenu additionnel en ligne STR_INTRO_ONLINE_CONTENT :{BLACK}Contenu additionnel en ligne
@ -2146,6 +2148,7 @@ STR_INTRO_TOOLTIP_TOYLAND_LANDSCAPE :{BLACK}Sélecti
STR_INTRO_TOOLTIP_GAME_OPTIONS :{BLACK}Afficher les options du jeu STR_INTRO_TOOLTIP_GAME_OPTIONS :{BLACK}Afficher les options du jeu
STR_INTRO_TOOLTIP_HIGHSCORE :{BLACK}Afficher le tableau des meilleurs scores STR_INTRO_TOOLTIP_HIGHSCORE :{BLACK}Afficher le tableau des meilleurs scores
STR_INTRO_TOOLTIP_HELP :{BLACK}Accéder à la documentation et aux ressources en ligne
STR_INTRO_TOOLTIP_CONFIG_SETTINGS_TREE :{BLACK}Afficher les paramètres STR_INTRO_TOOLTIP_CONFIG_SETTINGS_TREE :{BLACK}Afficher les paramètres
STR_INTRO_TOOLTIP_NEWGRF_SETTINGS :{BLACK}Afficher la configuration des NewGRF STR_INTRO_TOOLTIP_NEWGRF_SETTINGS :{BLACK}Afficher la configuration des NewGRF
STR_INTRO_TOOLTIP_ONLINE_CONTENT :{BLACK}Vérifier les contenus nouveaux ou mis à jour téléchargeables STR_INTRO_TOOLTIP_ONLINE_CONTENT :{BLACK}Vérifier les contenus nouveaux ou mis à jour téléchargeables
@ -2168,6 +2171,17 @@ STR_ABANDON_GAME_QUERY :{YELLOW}Êtes-v
STR_ABANDON_SCENARIO_QUERY :{YELLOW}Êtes-vous sûr de vouloir abandonner ce scénario{NBSP}? STR_ABANDON_SCENARIO_QUERY :{YELLOW}Êtes-vous sûr de vouloir abandonner ce scénario{NBSP}?
# Help window # Help window
STR_HELP_WINDOW_CAPTION :{WHITE}Aide et manuels
STR_HELP_WINDOW_WEBSITES :{BLACK}Sites web
STR_HELP_WINDOW_DOCUMENTS :{BLACK}Documents
STR_HELP_WINDOW_README :{BLACK}Lisez-moi
STR_HELP_WINDOW_CHANGELOG :{BLACK}Journal des modifications
STR_HELP_WINDOW_KNOWN_BUGS :{BLACK}Bugs connus
STR_HELP_WINDOW_LICENSE :{BLACK}Licence
STR_HELP_WINDOW_MAIN_WEBSITE :{BLACK}OpenTTD
STR_HELP_WINDOW_MANUAL_WIKI :{BLACK}Manuel / Wiki
STR_HELP_WINDOW_BUGTRACKER :{BLACK}Rapporter un bug
STR_HELP_WINDOW_COMMUNITY :{BLACK}Communauté
# Cheat window # Cheat window
STR_CHEATS :{WHITE}Triches STR_CHEATS :{WHITE}Triches
@ -4702,6 +4716,11 @@ STR_AI_SETTINGS_SETTING :{STRING}{NBSP}:
# Textfile window # Textfile window
STR_TEXTFILE_JUMPLIST :{WHITE}Table des matières
STR_TEXTFILE_JUMPLIST_TOOLTIP :{BLACK}Sauter rapidement à une section dans le fichier affiché via cette liste
STR_TEXTFILE_JUMPLIST_ITEM :{WHITE}{STRING}
STR_TEXTFILE_NAVBACK_TOOLTIP :{BLACK}Reculer dans l'historique de navigation
STR_TEXTFILE_NAVFORWARD_TOOLTIP :{BLACK}Avancer dans l'historique de navigation
STR_TEXTFILE_WRAP_TEXT :{WHITE}Retour à la ligne automatique STR_TEXTFILE_WRAP_TEXT :{WHITE}Retour à la ligne automatique
STR_TEXTFILE_WRAP_TEXT_TOOLTIP :{BLACK}Insérer des retours à la ligne dans le texte de la fenêtre afin qu'il s'affiche entièrement sans faire défiler STR_TEXTFILE_WRAP_TEXT_TOOLTIP :{BLACK}Insérer des retours à la ligne dans le texte de la fenêtre afin qu'il s'affiche entièrement sans faire défiler
STR_TEXTFILE_VIEW_README :{BLACK}Voir le Lisez-moi STR_TEXTFILE_VIEW_README :{BLACK}Voir le Lisez-moi
@ -4712,6 +4731,7 @@ STR_TEXTFILE_README_CAPTION :{WHITE}Lisez-mo
STR_TEXTFILE_CHANGELOG_CAPTION :{WHITE}Journal des modifications pour le module {STRING} {STRING} STR_TEXTFILE_CHANGELOG_CAPTION :{WHITE}Journal des modifications pour le module {STRING} {STRING}
STR_TEXTFILE_LICENCE_CAPTION :{WHITE}Licence du module {STRING} {STRING} STR_TEXTFILE_LICENCE_CAPTION :{WHITE}Licence du module {STRING} {STRING}
STR_TEXTFILE_SURVEY_RESULT_CAPTION :{WHITE}Prévisualisation des données d'analyse STR_TEXTFILE_SURVEY_RESULT_CAPTION :{WHITE}Prévisualisation des données d'analyse
STR_TEXTFILE_GAME_MANUAL_CAPTION :{WHITE}Document OpenTTD '{STRING}'
# Vehicle loading indicators # Vehicle loading indicators

View File

@ -524,6 +524,7 @@ STR_NEWS_MENU_DELETE_ALL_MESSAGES :모든 뉴스
# About menu # About menu
###length 11 ###length 11
STR_ABOUT_MENU_LAND_BLOCK_INFO :지형 정보 STR_ABOUT_MENU_LAND_BLOCK_INFO :지형 정보
STR_ABOUT_MENU_HELP :도움말 & 매뉴얼
STR_ABOUT_MENU_SEPARATOR : STR_ABOUT_MENU_SEPARATOR :
STR_ABOUT_MENU_TOGGLE_CONSOLE :콘솔 켜기/끄기 STR_ABOUT_MENU_TOGGLE_CONSOLE :콘솔 켜기/끄기
STR_ABOUT_MENU_AI_DEBUG :인공지능/게임 스크립트 디버그 STR_ABOUT_MENU_AI_DEBUG :인공지능/게임 스크립트 디버그
@ -1427,11 +1428,13 @@ STR_CONFIG_SETTING_PLANE_CRASHES_NONE :없음*
STR_CONFIG_SETTING_PLANE_CRASHES_REDUCED :적음 STR_CONFIG_SETTING_PLANE_CRASHES_REDUCED :적음
STR_CONFIG_SETTING_PLANE_CRASHES_NORMAL :보통 STR_CONFIG_SETTING_PLANE_CRASHES_NORMAL :보통
STR_CONFIG_SETTING_CROSSING_WITH_COMPETITOR :경쟁사 소유의 도로나 선로에 건널목 건설 허용: {STRING}
STR_CONFIG_SETTING_CROSSING_WITH_COMPETITOR_HELPTEXT :다른 회사 소유의 도로나 선로에 건널목을 건설하는 것을 허용합니다
STR_CONFIG_SETTING_STOP_ON_TOWN_ROAD :도시 소유의 도로 위에 버스 정류장 건설 허용: {STRING} STR_CONFIG_SETTING_STOP_ON_TOWN_ROAD :도시 소유의 도로 위에 버스 정류장 건설 허용: {STRING}
STR_CONFIG_SETTING_STOP_ON_TOWN_ROAD_HELPTEXT :도시 소유의 도로 위에 버스 정류장 건설을 허용합니다. STR_CONFIG_SETTING_STOP_ON_TOWN_ROAD_HELPTEXT :도시 소유의 도로 위에 버스 정류장 건설을 허용합니다
STR_CONFIG_SETTING_STOP_ON_COMPETITOR_ROAD :경쟁자 소유의 도로 위에 버스 정류장 건설 허용: {STRING} STR_CONFIG_SETTING_STOP_ON_COMPETITOR_ROAD :경쟁자 소유의 도로 위에 버스 정류장 건설 허용: {STRING}
STR_CONFIG_SETTING_STOP_ON_COMPETITOR_ROAD_HELPTEXT :다른 회사 소유의 도로 위에 버스 정류장을 건설하는 것을 허용합니다. STR_CONFIG_SETTING_STOP_ON_COMPETITOR_ROAD_HELPTEXT :다른 회사 소유의 도로 위에 버스 정류장을 건설하는 것을 허용합니다
STR_CONFIG_SETTING_DYNAMIC_ENGINES_EXISTING_VEHICLES :{WHITE}이미 차량이 존재하면 이 설정을 변경하실 수 없습니다. STR_CONFIG_SETTING_DYNAMIC_ENGINES_EXISTING_VEHICLES :{WHITE}이미 차량이 존재하면 이 설정을 변경하실 수 없습니다.
STR_CONFIG_SETTING_INFRASTRUCTURE_MAINTENANCE :기반시설 유지비: {STRING} STR_CONFIG_SETTING_INFRASTRUCTURE_MAINTENANCE :기반시설 유지비: {STRING}
@ -2123,6 +2126,7 @@ STR_INTRO_MULTIPLAYER :{BLACK}멀티
STR_INTRO_GAME_OPTIONS :{BLACK}게임 설정 STR_INTRO_GAME_OPTIONS :{BLACK}게임 설정
STR_INTRO_HIGHSCORE :{BLACK}고득점 순위표 STR_INTRO_HIGHSCORE :{BLACK}고득점 순위표
STR_INTRO_HELP :{BLACK}도움말 & 매뉴얼
STR_INTRO_CONFIG_SETTINGS_TREE :{BLACK}설정 STR_INTRO_CONFIG_SETTINGS_TREE :{BLACK}설정
STR_INTRO_NEWGRF_SETTINGS :{BLACK}NewGRF 설정 STR_INTRO_NEWGRF_SETTINGS :{BLACK}NewGRF 설정
STR_INTRO_ONLINE_CONTENT :{BLACK}온라인 콘텐츠 다운로드 STR_INTRO_ONLINE_CONTENT :{BLACK}온라인 콘텐츠 다운로드
@ -2144,6 +2148,7 @@ STR_INTRO_TOOLTIP_TOYLAND_LANDSCAPE :{BLACK}장난
STR_INTRO_TOOLTIP_GAME_OPTIONS :{BLACK}게임 기본 설정 창을 보여줍니다 STR_INTRO_TOOLTIP_GAME_OPTIONS :{BLACK}게임 기본 설정 창을 보여줍니다
STR_INTRO_TOOLTIP_HIGHSCORE :{BLACK}고득점 순위표를 보여줍니다 STR_INTRO_TOOLTIP_HIGHSCORE :{BLACK}고득점 순위표를 보여줍니다
STR_INTRO_TOOLTIP_HELP :{BLACK}각종 문서와 온라인 링크를 확인합니다
STR_INTRO_TOOLTIP_CONFIG_SETTINGS_TREE :{BLACK}설정 창을 엽니다 STR_INTRO_TOOLTIP_CONFIG_SETTINGS_TREE :{BLACK}설정 창을 엽니다
STR_INTRO_TOOLTIP_NEWGRF_SETTINGS :{BLACK}NewGRF 설정 창을 엽니다 STR_INTRO_TOOLTIP_NEWGRF_SETTINGS :{BLACK}NewGRF 설정 창을 엽니다
STR_INTRO_TOOLTIP_ONLINE_CONTENT :{BLACK}새로 나왔거나 업데이트된 콘텐츠를 체크하여 다운로드합니다 STR_INTRO_TOOLTIP_ONLINE_CONTENT :{BLACK}새로 나왔거나 업데이트된 콘텐츠를 체크하여 다운로드합니다
@ -2166,6 +2171,17 @@ STR_ABANDON_GAME_QUERY :{YELLOW}정말
STR_ABANDON_SCENARIO_QUERY :{YELLOW}정말 이 시나리오를 종료하시겠습니까? STR_ABANDON_SCENARIO_QUERY :{YELLOW}정말 이 시나리오를 종료하시겠습니까?
# Help window # Help window
STR_HELP_WINDOW_CAPTION :{WHITE}도움말 & 매뉴얼
STR_HELP_WINDOW_WEBSITES :{BLACK}웹사이트
STR_HELP_WINDOW_DOCUMENTS :{BLACK}문서
STR_HELP_WINDOW_README :{BLACK}Readme
STR_HELP_WINDOW_CHANGELOG :{BLACK}변경기록
STR_HELP_WINDOW_KNOWN_BUGS :{BLACK}알려진 버그
STR_HELP_WINDOW_LICENSE :{BLACK}라이선스
STR_HELP_WINDOW_MAIN_WEBSITE :{BLACK}OpenTTD
STR_HELP_WINDOW_MANUAL_WIKI :{BLACK}매뉴얼 / 위키
STR_HELP_WINDOW_BUGTRACKER :{BLACK}버그 신고
STR_HELP_WINDOW_COMMUNITY :{BLACK}커뮤니티
# Cheat window # Cheat window
STR_CHEATS :{WHITE}치트 STR_CHEATS :{WHITE}치트
@ -2654,6 +2670,7 @@ STR_TRANSPARENT_BUILDINGS_TOOLTIP :{BLACK}정거
STR_TRANSPARENT_BRIDGES_TOOLTIP :{BLACK}다리를 보여줄 지 선택합니다. 고정하려면 CTRL+클릭하세요 STR_TRANSPARENT_BRIDGES_TOOLTIP :{BLACK}다리를 보여줄 지 선택합니다. 고정하려면 CTRL+클릭하세요
STR_TRANSPARENT_STRUCTURES_TOOLTIP :{BLACK}등대나 안테나 같은 구조물을 보여줄 지 선택합니다. 고정하려면 CTRL+클릭하세요 STR_TRANSPARENT_STRUCTURES_TOOLTIP :{BLACK}등대나 안테나 같은 구조물을 보여줄 지 선택합니다. 고정하려면 CTRL+클릭하세요
STR_TRANSPARENT_CATENARY_TOOLTIP :{BLACK}전차선을 보여줄 지 선택합니다. 고정하려면 CTRL+클릭하세요 STR_TRANSPARENT_CATENARY_TOOLTIP :{BLACK}전차선을 보여줄 지 선택합니다. 고정하려면 CTRL+클릭하세요
STR_TRANSPARENT_TEXT_TOOLTIP :{BLACK}적재/하차 및 수입/지출 텍스트를 보여줄 지 선택합니다. 고정하려면 CTRL+클릭하세요
STR_TRANSPARENT_INVISIBLE_TOOLTIP :{BLACK}반투명 대신 아예 안 보이게 할 항목을 선택하세요 STR_TRANSPARENT_INVISIBLE_TOOLTIP :{BLACK}반투명 대신 아예 안 보이게 할 항목을 선택하세요
# Linkgraph legend window # Linkgraph legend window
@ -4699,6 +4716,11 @@ STR_AI_SETTINGS_SETTING :{STRING}: {ORAN
# Textfile window # Textfile window
STR_TEXTFILE_JUMPLIST :{WHITE}목차
STR_TEXTFILE_JUMPLIST_TOOLTIP :{BLACK}이 목록을 이용하여 표시된 파일의 특정 단락으로 빠르게 이동합니다
STR_TEXTFILE_JUMPLIST_ITEM :{WHITE}{STRING}
STR_TEXTFILE_NAVBACK_TOOLTIP :{BLACK}탐색 기록 뒤로 가기
STR_TEXTFILE_NAVFORWARD_TOOLTIP :{BLACK}탐색 기록 앞으로 가기
STR_TEXTFILE_WRAP_TEXT :{WHITE}자동 줄 바꿈 STR_TEXTFILE_WRAP_TEXT :{WHITE}자동 줄 바꿈
STR_TEXTFILE_WRAP_TEXT_TOOLTIP :{BLACK}본문 내용에 자동 줄 바꿈을 설정하여 스크롤하지 않고도 본문의 모든 내용을 볼 수 있게 합니다. STR_TEXTFILE_WRAP_TEXT_TOOLTIP :{BLACK}본문 내용에 자동 줄 바꿈을 설정하여 스크롤하지 않고도 본문의 모든 내용을 볼 수 있게 합니다.
STR_TEXTFILE_VIEW_README :{BLACK}Readme 보기 STR_TEXTFILE_VIEW_README :{BLACK}Readme 보기
@ -4709,6 +4731,7 @@ STR_TEXTFILE_README_CAPTION :{WHITE}{STRING}
STR_TEXTFILE_CHANGELOG_CAPTION :{WHITE}{STRING} {STRING}의 변경기록 STR_TEXTFILE_CHANGELOG_CAPTION :{WHITE}{STRING} {STRING}의 변경기록
STR_TEXTFILE_LICENCE_CAPTION :{WHITE}{STRING} {STRING}의 저작권 STR_TEXTFILE_LICENCE_CAPTION :{WHITE}{STRING} {STRING}의 저작권
STR_TEXTFILE_SURVEY_RESULT_CAPTION :{WHITE}설문조사 결과 미리보기 STR_TEXTFILE_SURVEY_RESULT_CAPTION :{WHITE}설문조사 결과 미리보기
STR_TEXTFILE_GAME_MANUAL_CAPTION :{WHITE}OpenTTD '{STRING}' 문서
# Vehicle loading indicators # Vehicle loading indicators

View File

@ -649,6 +649,7 @@ STR_NEWS_MENU_DELETE_ALL_MESSAGES :Удалить
# About menu # About menu
###length 11 ###length 11
STR_ABOUT_MENU_LAND_BLOCK_INFO :Информация об участке земли STR_ABOUT_MENU_LAND_BLOCK_INFO :Информация об участке земли
STR_ABOUT_MENU_HELP :Помощь и документация
STR_ABOUT_MENU_SEPARATOR : STR_ABOUT_MENU_SEPARATOR :
STR_ABOUT_MENU_TOGGLE_CONSOLE :Консоль STR_ABOUT_MENU_TOGGLE_CONSOLE :Консоль
STR_ABOUT_MENU_AI_DEBUG :Отладка ИИ / скриптов STR_ABOUT_MENU_AI_DEBUG :Отладка ИИ / скриптов
@ -2275,6 +2276,7 @@ STR_INTRO_MULTIPLAYER :{BLACK}Сете
STR_INTRO_GAME_OPTIONS :{BLACK}Основные настройки STR_INTRO_GAME_OPTIONS :{BLACK}Основные настройки
STR_INTRO_HIGHSCORE :{BLACK}Таблица рекордов STR_INTRO_HIGHSCORE :{BLACK}Таблица рекордов
STR_INTRO_HELP :{BLACK}Помощь и документация
STR_INTRO_CONFIG_SETTINGS_TREE :{BLACK}Расширенные настройки STR_INTRO_CONFIG_SETTINGS_TREE :{BLACK}Расширенные настройки
STR_INTRO_NEWGRF_SETTINGS :{BLACK}Настройки NewGRF STR_INTRO_NEWGRF_SETTINGS :{BLACK}Настройки NewGRF
STR_INTRO_ONLINE_CONTENT :{BLACK}Проверить онлайн-контент STR_INTRO_ONLINE_CONTENT :{BLACK}Проверить онлайн-контент
@ -2296,6 +2298,7 @@ STR_INTRO_TOOLTIP_TOYLAND_LANDSCAPE :{BLACK}Выбр
STR_INTRO_TOOLTIP_GAME_OPTIONS :{BLACK}Изменить основные настройки игры STR_INTRO_TOOLTIP_GAME_OPTIONS :{BLACK}Изменить основные настройки игры
STR_INTRO_TOOLTIP_HIGHSCORE :{BLACK}Показать таблицу рекордов STR_INTRO_TOOLTIP_HIGHSCORE :{BLACK}Показать таблицу рекордов
STR_INTRO_TOOLTIP_HELP :{BLACK}Ссылки на документацию и интернет-ресурсы
STR_INTRO_TOOLTIP_CONFIG_SETTINGS_TREE :{BLACK}Изменить расширенные настройки игры STR_INTRO_TOOLTIP_CONFIG_SETTINGS_TREE :{BLACK}Изменить расширенные настройки игры
STR_INTRO_TOOLTIP_NEWGRF_SETTINGS :{BLACK}Показать настройки NewGRF STR_INTRO_TOOLTIP_NEWGRF_SETTINGS :{BLACK}Показать настройки NewGRF
STR_INTRO_TOOLTIP_ONLINE_CONTENT :{BLACK}Найти новый и обновлённый контент для загрузки STR_INTRO_TOOLTIP_ONLINE_CONTENT :{BLACK}Найти новый и обновлённый контент для загрузки
@ -2318,6 +2321,17 @@ STR_ABANDON_GAME_QUERY :{YELLOW}Вы д
STR_ABANDON_SCENARIO_QUERY :{YELLOW}Вы уверены, что хотите выйти из этого сценария? STR_ABANDON_SCENARIO_QUERY :{YELLOW}Вы уверены, что хотите выйти из этого сценария?
# Help window # Help window
STR_HELP_WINDOW_CAPTION :{WHITE}Помощь и документация
STR_HELP_WINDOW_WEBSITES :{BLACK}Веб-сайты
STR_HELP_WINDOW_DOCUMENTS :{BLACK}Документы
STR_HELP_WINDOW_README :{BLACK}Readme
STR_HELP_WINDOW_CHANGELOG :{BLACK}История изменений
STR_HELP_WINDOW_KNOWN_BUGS :{BLACK}Известные ошибки
STR_HELP_WINDOW_LICENSE :{BLACK}Лицензия
STR_HELP_WINDOW_MAIN_WEBSITE :{BLACK}OpenTTD
STR_HELP_WINDOW_MANUAL_WIKI :{BLACK}Инструкция / Wiki
STR_HELP_WINDOW_BUGTRACKER :{BLACK}Сообщить об ошибке
STR_HELP_WINDOW_COMMUNITY :{BLACK}Сообщество
# Cheat window # Cheat window
STR_CHEATS :{WHITE}Читы STR_CHEATS :{WHITE}Читы
@ -4888,6 +4902,11 @@ STR_AI_SETTINGS_SETTING :{STRING}: {ORAN
# Textfile window # Textfile window
STR_TEXTFILE_JUMPLIST :{WHITE}Содержание
STR_TEXTFILE_JUMPLIST_TOOLTIP :{BLACK}Список для быстрого перехода к определённой секции отображаемого файла
STR_TEXTFILE_JUMPLIST_ITEM :{WHITE}{STRING}
STR_TEXTFILE_NAVBACK_TOOLTIP :{BLACK}Назад
STR_TEXTFILE_NAVFORWARD_TOOLTIP :{BLACK}Вперёд
STR_TEXTFILE_WRAP_TEXT :{WHITE}Переносить текст STR_TEXTFILE_WRAP_TEXT :{WHITE}Переносить текст
STR_TEXTFILE_WRAP_TEXT_TOOLTIP :{BLACK}Переносить текст так, чтобы он помещался в окне без необходимости прокрутки STR_TEXTFILE_WRAP_TEXT_TOOLTIP :{BLACK}Переносить текст так, чтобы он помещался в окне без необходимости прокрутки
STR_TEXTFILE_VIEW_README :{BLACK}Посмотреть инструкцию STR_TEXTFILE_VIEW_README :{BLACK}Посмотреть инструкцию
@ -4898,6 +4917,7 @@ STR_TEXTFILE_README_CAPTION :{WHITE}Инст
STR_TEXTFILE_CHANGELOG_CAPTION :{WHITE}Список изменений к {STRING} {STRING} STR_TEXTFILE_CHANGELOG_CAPTION :{WHITE}Список изменений к {STRING} {STRING}
STR_TEXTFILE_LICENCE_CAPTION :{WHITE}Лицензия к {STRING} {STRING} STR_TEXTFILE_LICENCE_CAPTION :{WHITE}Лицензия к {STRING} {STRING}
STR_TEXTFILE_SURVEY_RESULT_CAPTION :{WHITE}Собранные данные STR_TEXTFILE_SURVEY_RESULT_CAPTION :{WHITE}Собранные данные
STR_TEXTFILE_GAME_MANUAL_CAPTION :{WHITE}Документ OpenTTD «{STRING}»
# Vehicle loading indicators # Vehicle loading indicators

View File

@ -12,26 +12,9 @@
#include "settings_table.h" #include "settings_table.h"
#include "network.h" #include "network.h"
#include "../debug.h" #include "../debug.h"
#include "../rev.h" #include "../survey.h"
#include "../settings_type.h" #include "../3rdparty/fmt/chrono.h"
#include "../timer/timer_game_tick.h" #include "../3rdparty/fmt/std.h"
#include "../currency.h"
#include "../fontcache.h"
#include "../language.h"
#include "../ai/ai_info.hpp"
#include "../game/game.hpp"
#include "../game/game_info.hpp"
#include "../music/music_driver.hpp"
#include "../sound/sound_driver.hpp"
#include "../video/video_driver.hpp"
#include "../base_media_base.h"
#include "../blitter/factory.hpp"
#include "../3rdparty/nlohmann/json.hpp"
#include "../safeguards.h" #include "../safeguards.h"
@ -46,294 +29,6 @@ NLOHMANN_JSON_SERIALIZE_ENUM(NetworkSurveyHandler::Reason, {
{NetworkSurveyHandler::Reason::CRASH, "crash"}, {NetworkSurveyHandler::Reason::CRASH, "crash"},
}) })
NLOHMANN_JSON_SERIALIZE_ENUM(GRFStatus, {
{GRFStatus::GCS_UNKNOWN, "unknown"},
{GRFStatus::GCS_DISABLED, "disabled"},
{GRFStatus::GCS_NOT_FOUND, "not found"},
{GRFStatus::GCS_INITIALISED, "initialised"},
{GRFStatus::GCS_ACTIVATED, "activated"},
})
static const std::string _vehicle_type_to_string[] = {
"train",
"roadveh",
"ship",
"aircraft",
};
/* Defined in one of the os/ survey files. */
extern void SurveyOS(nlohmann::json &json);
/**
* List of all the generic setting tables.
*
* There are a few tables that are special and not processed like the rest:
* - _currency_settings
* - _misc_settings
* - _company_settings
* - _win32_settings
* As such, they are not part of this list.
*/
static auto &GenericSettingTables()
{
static const SettingTable _generic_setting_tables[] = {
_difficulty_settings,
_economy_settings,
_game_settings,
_gui_settings,
_linkgraph_settings,
_locale_settings,
_multimedia_settings,
_network_settings,
_news_display_settings,
_pathfinding_settings,
_script_settings,
_world_settings,
};
return _generic_setting_tables;
}
/**
* Convert a settings table to JSON.
*
* @param survey The JSON object.
* @param table The settings table to convert.
* @param object The object to get the settings from.
*/
static void SurveySettingsTable(nlohmann::json &survey, const SettingTable &table, void *object)
{
for (auto &desc : table) {
const SettingDesc *sd = GetSettingDesc(desc);
/* Skip any old settings we no longer save/load. */
if (!SlIsObjectCurrentlyValid(sd->save.version_from, sd->save.version_to)) continue;
auto name = sd->GetName();
survey[name] = sd->FormatValue(object);
}
}
/**
* Convert settings to JSON.
*
* @param survey The JSON object.
*/
static void SurveySettings(nlohmann::json &survey)
{
SurveySettingsTable(survey, _misc_settings, nullptr);
#if defined(_WIN32) && !defined(DEDICATED)
SurveySettingsTable(survey, _win32_settings, nullptr);
#endif
for (auto &table : GenericSettingTables()) {
SurveySettingsTable(survey, table, &_settings_game);
}
SurveySettingsTable(survey, _currency_settings, &_custom_currency);
SurveySettingsTable(survey, _company_settings, &_settings_client.company);
}
/**
* Convert generic OpenTTD information to JSON.
*
* @param survey The JSON object.
*/
static void SurveyOpenTTD(nlohmann::json &survey)
{
survey["version"]["revision"] = std::string(_openttd_revision);
survey["version"]["modified"] = _openttd_revision_modified;
survey["version"]["tagged"] = _openttd_revision_tagged;
survey["version"]["hash"] = std::string(_openttd_revision_hash);
survey["version"]["newgrf"] = fmt::format("{:X}", _openttd_newgrf_version);
survey["version"]["content"] = std::string(_openttd_content_version);
survey["build_date"] = std::string(_openttd_build_date);
survey["bits"] =
#ifdef POINTER_IS_64BIT
64
#else
32
#endif
;
survey["endian"] =
#if (TTD_ENDIAN == TTD_LITTLE_ENDIAN)
"little"
#else
"big"
#endif
;
survey["dedicated_build"] =
#ifdef DEDICATED
"yes"
#else
"no"
#endif
;
}
/**
* Convert generic game information to JSON.
*
* @param survey The JSON object.
*/
static void SurveyConfiguration(nlohmann::json &survey)
{
survey["network"] = _networking ? (_network_server ? "server" : "client") : "no";
if (_current_language != nullptr) {
survey["language"]["filename"] = _current_language->file.filename().string();
survey["language"]["name"] = _current_language->name;
survey["language"]["isocode"] = _current_language->isocode;
}
if (BlitterFactory::GetCurrentBlitter() != nullptr) {
survey["blitter"] = BlitterFactory::GetCurrentBlitter()->GetName();
}
if (MusicDriver::GetInstance() != nullptr) {
survey["music_driver"] = MusicDriver::GetInstance()->GetName();
}
if (SoundDriver::GetInstance() != nullptr) {
survey["sound_driver"] = SoundDriver::GetInstance()->GetName();
}
if (VideoDriver::GetInstance() != nullptr) {
survey["video_driver"] = VideoDriver::GetInstance()->GetName();
survey["video_info"] = VideoDriver::GetInstance()->GetInfoString();
}
if (BaseGraphics::GetUsedSet() != nullptr) {
survey["graphics_set"] = fmt::format("{}.{}", BaseGraphics::GetUsedSet()->name, BaseGraphics::GetUsedSet()->version);
}
if (BaseMusic::GetUsedSet() != nullptr) {
survey["music_set"] = fmt::format("{}.{}", BaseMusic::GetUsedSet()->name, BaseMusic::GetUsedSet()->version);
}
if (BaseSounds::GetUsedSet() != nullptr) {
survey["sound_set"] = fmt::format("{}.{}", BaseSounds::GetUsedSet()->name, BaseSounds::GetUsedSet()->version);
}
}
/**
* Convert font information to JSON.
*
* @param survey The JSON object.
*/
static void SurveyFont(nlohmann::json &survey)
{
survey["small"] = FontCache::Get(FS_SMALL)->GetFontName();
survey["medium"] = FontCache::Get(FS_NORMAL)->GetFontName();
survey["large"] = FontCache::Get(FS_LARGE)->GetFontName();
survey["mono"] = FontCache::Get(FS_MONO)->GetFontName();
}
/**
* Convert company information to JSON.
*
* @param survey The JSON object.
*/
static void SurveyCompanies(nlohmann::json &survey)
{
for (const Company *c : Company::Iterate()) {
auto &company = survey[std::to_string(c->index)];
if (c->ai_info == nullptr) {
company["type"] = "human";
} else {
company["type"] = "ai";
company["script"] = fmt::format("{}.{}", c->ai_info->GetName(), c->ai_info->GetVersion());
}
for (VehicleType type = VEH_BEGIN; type < VEH_COMPANY_END; type++) {
uint amount = c->group_all[type].num_vehicle;
company["vehicles"][_vehicle_type_to_string[type]] = amount;
}
company["infrastructure"]["road"] = c->infrastructure.GetRoadTotal();
company["infrastructure"]["tram"] = c->infrastructure.GetTramTotal();
company["infrastructure"]["rail"] = c->infrastructure.GetRailTotal();
company["infrastructure"]["signal"] = c->infrastructure.signal;
company["infrastructure"]["water"] = c->infrastructure.water;
company["infrastructure"]["station"] = c->infrastructure.station;
company["infrastructure"]["airport"] = c->infrastructure.airport;
}
}
/**
* Convert timer information to JSON.
*
* @param survey The JSON object.
*/
static void SurveyTimers(nlohmann::json &survey)
{
survey["ticks"] = TimerGameTick::counter;
survey["seconds"] = std::chrono::duration_cast<std::chrono::seconds>(std::chrono::steady_clock::now() - _switch_mode_time).count();
TimerGameCalendar::YearMonthDay ymd;
TimerGameCalendar::ConvertDateToYMD(TimerGameCalendar::date, &ymd);
survey["calendar"] = fmt::format("{:04}-{:02}-{:02} ({})", ymd.year, ymd.month + 1, ymd.day, TimerGameCalendar::date_fract);
}
/**
* Convert GRF information to JSON.
*
* @param survey The JSON object.
*/
static void SurveyGrfs(nlohmann::json &survey)
{
for (GRFConfig *c = _grfconfig; c != nullptr; c = c->next) {
auto grfid = fmt::format("{:08x}", BSWAP32(c->ident.grfid));
auto &grf = survey[grfid];
grf["md5sum"] = FormatArrayAsHex(c->ident.md5sum);
grf["status"] = c->status;
if ((c->palette & GRFP_GRF_MASK) == GRFP_GRF_UNSET) grf["palette"] = "unset";
if ((c->palette & GRFP_GRF_MASK) == GRFP_GRF_DOS) grf["palette"] = "dos";
if ((c->palette & GRFP_GRF_MASK) == GRFP_GRF_WINDOWS) grf["palette"] = "windows";
if ((c->palette & GRFP_GRF_MASK) == GRFP_GRF_ANY) grf["palette"] = "any";
if ((c->palette & GRFP_BLT_MASK) == GRFP_BLT_UNSET) grf["blitter"] = "unset";
if ((c->palette & GRFP_BLT_MASK) == GRFP_BLT_32BPP) grf["blitter"] = "32bpp";
grf["is_static"] = HasBit(c->flags, GCF_STATIC);
std::vector<uint32_t> parameters;
for (int i = 0; i < c->num_params; i++) {
parameters.push_back(c->param[i]);
}
grf["parameters"] = parameters;
}
}
/**
* Convert game-script information to JSON.
*
* @param survey The JSON object.
*/
static void SurveyGameScript(nlohmann::json &survey)
{
if (Game::GetInfo() == nullptr) return;
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));
}
/** /**
* Create the payload for the survey. * Create the payload for the survey.
* *
@ -348,6 +43,7 @@ std::string NetworkSurveyHandler::CreatePayload(Reason reason, bool for_preview)
survey["schema"] = NETWORK_SURVEY_VERSION; survey["schema"] = NETWORK_SURVEY_VERSION;
survey["reason"] = reason; survey["reason"] = reason;
survey["id"] = _savegame_id; survey["id"] = _savegame_id;
survey["date"] = fmt::format("{:%Y-%m-%d %H:%M:%S} (UTC)", fmt::gmtime(time(nullptr)));
#ifdef SURVEY_KEY #ifdef SURVEY_KEY
/* We censor the key to avoid people trying to be "clever" and use it to send their own surveys. */ /* We censor the key to avoid people trying to be "clever" and use it to send their own surveys. */
@ -362,13 +58,15 @@ std::string NetworkSurveyHandler::CreatePayload(Reason reason, bool for_preview)
SurveyOpenTTD(info["openttd"]); SurveyOpenTTD(info["openttd"]);
SurveyConfiguration(info["configuration"]); SurveyConfiguration(info["configuration"]);
SurveyFont(info["font"]); SurveyFont(info["font"]);
SurveyCompiler(info["compiler"]);
SurveyLibraries(info["libraries"]);
} }
{ {
auto &game = survey["game"]; auto &game = survey["game"];
SurveyTimers(game["timers"]); SurveyTimers(game["timers"]);
SurveyCompanies(game["companies"]); SurveyCompanies(game["companies"]);
SurveySettings(game["settings"]); SurveySettings(game["settings"], false);
SurveyGrfs(game["grfs"]); SurveyGrfs(game["grfs"]);
SurveyGameScript(game["game_script"]); SurveyGameScript(game["game_script"]);
} }

View File

@ -49,53 +49,24 @@ class CrashLogOSX : public CrashLog {
/** Signal that has been thrown. */ /** Signal that has been thrown. */
int signum; int signum;
void LogOSVersion(std::back_insert_iterator<std::string> &output_iterator) const override void SurveyCrash(nlohmann::json &survey) const override
{ {
int ver_maj, ver_min, ver_bug; survey["id"] = signum;
GetMacOSVersion(&ver_maj, &ver_min, &ver_bug); survey["reason"] = strsignal(signum);
const NXArchInfo *arch = NXGetLocalArchInfo();
fmt::format_to(output_iterator,
"Operating system:\n"
" Name: Mac OS X\n"
" Release: {}.{}.{}\n"
" Machine: {}\n"
" Min Ver: {}\n"
" Max Ver: {}\n",
ver_maj, ver_min, ver_bug,
arch != nullptr ? arch->description : "unknown",
MAC_OS_X_VERSION_MIN_REQUIRED,
MAC_OS_X_VERSION_MAX_ALLOWED
);
} }
void LogError(std::back_insert_iterator<std::string> &output_iterator, const std::string_view message) const override void SurveyStacktrace(nlohmann::json &survey) const override
{ {
fmt::format_to(output_iterator,
"Crash reason:\n"
" Signal: {} ({})\n"
" Message: {}\n\n",
strsignal(this->signum),
this->signum,
message
);
}
void LogStacktrace(std::back_insert_iterator<std::string> &output_iterator) const override
{
fmt::format_to(output_iterator, "\nStacktrace:\n");
void *trace[64]; void *trace[64];
int trace_size = backtrace(trace, lengthof(trace)); int trace_size = backtrace(trace, lengthof(trace));
survey = nlohmann::json::array();
char **messages = backtrace_symbols(trace, trace_size); char **messages = backtrace_symbols(trace, trace_size);
for (int i = 0; i < trace_size; i++) { for (int i = 0; i < trace_size; i++) {
fmt::format_to(output_iterator, "{}\n", messages[i]); survey.push_back(messages[i]);
} }
free(messages); free(messages);
fmt::format_to(output_iterator, "\n");
} }
#ifdef WITH_UNOFFICIAL_BREAKPAD #ifdef WITH_UNOFFICIAL_BREAKPAD
@ -153,7 +124,7 @@ public:
"A serious fault condition occurred in the game. The game will shut down."; "A serious fault condition occurred in the game. The game will shut down.";
std::string message = fmt::format( std::string message = fmt::format(
"Please send crash.log, crash.dmp, and crash.sav to the developers. " "Please send crash.json.log, crash.dmp, and crash.sav to the developers. "
"This will greatly help debugging.\n\n" "This will greatly help debugging.\n\n"
"https://github.com/OpenTTD/OpenTTD/issues.\n\n" "https://github.com/OpenTTD/OpenTTD/issues.\n\n"
"{}\n{}\n{}\n{}", "{}\n{}\n{}\n{}",

View File

@ -10,7 +10,7 @@
#include "../../stdafx.h" #include "../../stdafx.h"
#include "../../3rdparty/fmt/format.h" #include "../../3rdparty/fmt/format.h"
#include "../../3rdparty/nlohmann/json.hpp" #include "../../survey.h"
#include "macos.h" #include "macos.h"
#include <mach-o/arch.h> #include <mach-o/arch.h>
@ -18,8 +18,6 @@
#include "../../safeguards.h" #include "../../safeguards.h"
extern std::string SurveyMemoryToText(uint64_t memory);
void SurveyOS(nlohmann::json &json) void SurveyOS(nlohmann::json &json)
{ {
int ver_maj, ver_min, ver_bug; int ver_maj, ver_min, ver_bug;

View File

@ -49,55 +49,26 @@ class CrashLogUnix : public CrashLog {
/** Signal that has been thrown. */ /** Signal that has been thrown. */
int signum; int signum;
void LogOSVersion(std::back_insert_iterator<std::string> &output_iterator) const override void SurveyCrash(nlohmann::json &survey) const override
{ {
struct utsname name; survey["id"] = signum;
if (uname(&name) < 0) { survey["reason"] = strsignal(signum);
fmt::format_to(output_iterator, "Could not get OS version: {}\n", strerror(errno));
return;
}
fmt::format_to(output_iterator,
"Operating system:\n"
" Name: {}\n"
" Release: {}\n"
" Version: {}\n"
" Machine: {}\n",
name.sysname,
name.release,
name.version,
name.machine
);
} }
void LogError(std::back_insert_iterator<std::string> &output_iterator, const std::string_view message) const override void SurveyStacktrace(nlohmann::json &survey) const override
{ {
fmt::format_to(output_iterator,
"Crash reason:\n"
" Signal: {} ({})\n"
" Message: {}\n\n",
strsignal(this->signum),
this->signum,
message
);
}
void LogStacktrace(std::back_insert_iterator<std::string> &output_iterator) const override
{
fmt::format_to(output_iterator, "Stacktrace:\n");
#if defined(__GLIBC__) #if defined(__GLIBC__)
void *trace[64]; void *trace[64];
int trace_size = backtrace(trace, lengthof(trace)); int trace_size = backtrace(trace, lengthof(trace));
survey = nlohmann::json::array();
char **messages = backtrace_symbols(trace, trace_size); char **messages = backtrace_symbols(trace, trace_size);
for (int i = 0; i < trace_size; i++) { for (int i = 0; i < trace_size; i++) {
fmt::format_to(output_iterator, " [{:02}] {}\n", i, messages[i]); survey.push_back(messages[i]);
} }
free(messages); free(messages);
#else
fmt::format_to(output_iterator, " Not supported.\n");
#endif #endif
fmt::format_to(output_iterator, "\n");
} }
#ifdef WITH_UNOFFICIAL_BREAKPAD #ifdef WITH_UNOFFICIAL_BREAKPAD

View File

@ -8,8 +8,7 @@
/** @file survey_unix.cpp Unix implementation of OS-specific survey information. */ /** @file survey_unix.cpp Unix implementation of OS-specific survey information. */
#include "../../stdafx.h" #include "../../stdafx.h"
#include "../../survey.h"
#include "../../3rdparty/nlohmann/json.hpp"
#include <sys/utsname.h> #include <sys/utsname.h>
#include <thread> #include <thread>
@ -17,8 +16,6 @@
#include "../../safeguards.h" #include "../../safeguards.h"
extern std::string SurveyMemoryToText(uint64_t memory);
void SurveyOS(nlohmann::json &json) void SurveyOS(nlohmann::json &json)
{ {
struct utsname name; struct utsname name;

View File

@ -38,6 +38,33 @@
/** Exception code used for custom abort. */ /** Exception code used for custom abort. */
static constexpr DWORD CUSTOM_ABORT_EXCEPTION = 0xE1212012; static constexpr DWORD CUSTOM_ABORT_EXCEPTION = 0xE1212012;
/** A map between exception code and its name. */
static const std::map<DWORD, std::string> exception_code_to_name{
{EXCEPTION_ACCESS_VIOLATION, "EXCEPTION_ACCESS_VIOLATION"},
{EXCEPTION_ARRAY_BOUNDS_EXCEEDED, "EXCEPTION_ARRAY_BOUNDS_EXCEEDED"},
{EXCEPTION_BREAKPOINT, "EXCEPTION_BREAKPOINT"},
{EXCEPTION_DATATYPE_MISALIGNMENT, "EXCEPTION_DATATYPE_MISALIGNMENT"},
{EXCEPTION_FLT_DENORMAL_OPERAND, "EXCEPTION_FLT_DENORMAL_OPERAND"},
{EXCEPTION_FLT_DIVIDE_BY_ZERO, "EXCEPTION_FLT_DIVIDE_BY_ZERO"},
{EXCEPTION_FLT_INEXACT_RESULT, "EXCEPTION_FLT_INEXACT_RESULT"},
{EXCEPTION_FLT_INVALID_OPERATION, "EXCEPTION_FLT_INVALID_OPERATION"},
{EXCEPTION_FLT_OVERFLOW, "EXCEPTION_FLT_OVERFLOW"},
{EXCEPTION_FLT_STACK_CHECK, "EXCEPTION_FLT_STACK_CHECK"},
{EXCEPTION_FLT_UNDERFLOW, "EXCEPTION_FLT_UNDERFLOW"},
{EXCEPTION_GUARD_PAGE, "EXCEPTION_GUARD_PAGE"},
{EXCEPTION_ILLEGAL_INSTRUCTION, "EXCEPTION_ILLEGAL_INSTRUCTION"},
{EXCEPTION_IN_PAGE_ERROR, "EXCEPTION_IN_PAGE_ERROR"},
{EXCEPTION_INT_DIVIDE_BY_ZERO, "EXCEPTION_INT_DIVIDE_BY_ZERO"},
{EXCEPTION_INT_OVERFLOW, "EXCEPTION_INT_OVERFLOW"},
{EXCEPTION_INVALID_DISPOSITION, "EXCEPTION_INVALID_DISPOSITION"},
{EXCEPTION_INVALID_HANDLE, "EXCEPTION_INVALID_HANDLE"},
{EXCEPTION_NONCONTINUABLE_EXCEPTION, "EXCEPTION_NONCONTINUABLE_EXCEPTION"},
{EXCEPTION_PRIV_INSTRUCTION, "EXCEPTION_PRIV_INSTRUCTION"},
{EXCEPTION_SINGLE_STEP, "EXCEPTION_SINGLE_STEP"},
{EXCEPTION_STACK_OVERFLOW, "EXCEPTION_STACK_OVERFLOW"},
{STATUS_UNWIND_CONSOLIDATE, "STATUS_UNWIND_CONSOLIDATE"},
};
/** /**
* Forcefully try to terminate the application. * Forcefully try to terminate the application.
* *
@ -57,9 +84,17 @@ class CrashLogWindows : public CrashLog {
/** Information about the encountered exception */ /** Information about the encountered exception */
EXCEPTION_POINTERS *ep; EXCEPTION_POINTERS *ep;
void LogOSVersion(std::back_insert_iterator<std::string> &output_iterator) const override; void SurveyCrash(nlohmann::json &survey) const override
void LogError(std::back_insert_iterator<std::string> &output_iterator, const std::string_view message) const override; {
void LogStacktrace(std::back_insert_iterator<std::string> &output_iterator) const override; survey["id"] = ep->ExceptionRecord->ExceptionCode;
if (exception_code_to_name.count(ep->ExceptionRecord->ExceptionCode) > 0) {
survey["reason"] = exception_code_to_name.at(ep->ExceptionRecord->ExceptionCode);
} else {
survey["reason"] = "Unknown exception code";
}
}
void SurveyStacktrace(nlohmann::json &survey) const override;
public: public:
#ifdef WITH_UNOFFICIAL_BREAKPAD #ifdef WITH_UNOFFICIAL_BREAKPAD
@ -136,41 +171,11 @@ public:
/* static */ CrashLogWindows *CrashLogWindows::current = nullptr; /* static */ CrashLogWindows *CrashLogWindows::current = nullptr;
/* virtual */ void CrashLogWindows::LogOSVersion(std::back_insert_iterator<std::string> &output_iterator) const
{
_OSVERSIONINFOA os;
os.dwOSVersionInfoSize = sizeof(os);
GetVersionExA(&os);
fmt::format_to(output_iterator,
"Operating system:\n"
" Name: Windows\n"
" Release: {}.{}.{} ({})\n",
os.dwMajorVersion,
os.dwMinorVersion,
os.dwBuildNumber,
os.szCSDVersion
);
}
/* virtual */ void CrashLogWindows::LogError(std::back_insert_iterator<std::string> &output_iterator, const std::string_view message) const
{
fmt::format_to(output_iterator,
"Crash reason:\n"
" Exception: {:08X}\n"
" Location: {:X}\n"
" Message: {}\n\n",
ep->ExceptionRecord->ExceptionCode,
(size_t)ep->ExceptionRecord->ExceptionAddress,
message
);
}
#if defined(_MSC_VER) #if defined(_MSC_VER)
static const uint MAX_SYMBOL_LEN = 512; static const uint MAX_SYMBOL_LEN = 512;
static const uint MAX_FRAMES = 64; static const uint MAX_FRAMES = 64;
/* virtual */ void CrashLogWindows::LogStacktrace(std::back_insert_iterator<std::string> &output_iterator) const /* virtual */ void CrashLogWindows::SurveyStacktrace(nlohmann::json &survey) const
{ {
DllLoader dbghelp(L"dbghelp.dll"); DllLoader dbghelp(L"dbghelp.dll");
struct ProcPtrs { struct ProcPtrs {
@ -195,7 +200,7 @@ static const uint MAX_FRAMES = 64;
dbghelp.GetProcAddress("SymGetLineFromAddr64"), dbghelp.GetProcAddress("SymGetLineFromAddr64"),
}; };
fmt::format_to(output_iterator, "Stack trace:\n"); survey = nlohmann::json::array();
/* Try to load the functions from the DLL, if that fails because of a too old dbghelp.dll, just skip it. */ /* Try to load the functions from the DLL, if that fails because of a too old dbghelp.dll, just skip it. */
if (dbghelp.Success()) { if (dbghelp.Success()) {
@ -246,7 +251,7 @@ static const uint MAX_FRAMES = 64;
hCur, GetCurrentThread(), &frame, &ctx, nullptr, proc.pSymFunctionTableAccess64, proc.pSymGetModuleBase64, nullptr)) break; hCur, GetCurrentThread(), &frame, &ctx, nullptr, proc.pSymFunctionTableAccess64, proc.pSymGetModuleBase64, nullptr)) break;
if (frame.AddrPC.Offset == frame.AddrReturn.Offset) { if (frame.AddrPC.Offset == frame.AddrReturn.Offset) {
fmt::format_to(output_iterator, " <infinite loop>\n"); survey.push_back("<infinite loop>");
break; break;
} }
@ -260,33 +265,31 @@ static const uint MAX_FRAMES = 64;
} }
/* Print module and instruction pointer. */ /* Print module and instruction pointer. */
fmt::format_to(output_iterator, "[{:02}] {:20s} {:X}", num, mod_name, frame.AddrPC.Offset); std::string message = fmt::format("{:20s} {:X}", mod_name, frame.AddrPC.Offset);
/* Get symbol name and line info if possible. */ /* Get symbol name and line info if possible. */
DWORD64 offset; DWORD64 offset;
if (proc.pSymGetSymFromAddr64(hCur, frame.AddrPC.Offset, &offset, sym_info)) { if (proc.pSymGetSymFromAddr64(hCur, frame.AddrPC.Offset, &offset, sym_info)) {
fmt::format_to(output_iterator, " {} + {}", sym_info->Name, offset); message += fmt::format(" {} + {}", sym_info->Name, offset);
DWORD line_offs; DWORD line_offs;
IMAGEHLP_LINE64 line; IMAGEHLP_LINE64 line;
line.SizeOfStruct = sizeof(IMAGEHLP_LINE64); line.SizeOfStruct = sizeof(IMAGEHLP_LINE64);
if (proc.pSymGetLineFromAddr64(hCur, frame.AddrPC.Offset, &line_offs, &line)) { if (proc.pSymGetLineFromAddr64(hCur, frame.AddrPC.Offset, &line_offs, &line)) {
fmt::format_to(output_iterator, " ({}:{})", line.FileName, line.LineNumber); message += fmt::format(" ({}:{})", line.FileName, line.LineNumber);
} }
} }
fmt::format_to(output_iterator, "\n");
survey.push_back(message);
} }
proc.pSymCleanup(hCur); proc.pSymCleanup(hCur);
} }
fmt::format_to(output_iterator, "\n");
} }
#else #else
/* virtual */ void CrashLogWindows::LogStacktrace(std::back_insert_iterator<std::string> &output_iterator) const /* virtual */ void CrashLogWindows::SurveyStacktrace(nlohmann::json &survey) const
{ {
fmt::format_to(output_iterator, "Stack trace:\n"); /* Not supported. */
fmt::format_to(output_iterator, " Not supported.\n");
} }
#endif /* _MSC_VER */ #endif /* _MSC_VER */
@ -430,7 +433,7 @@ static bool _expanded;
static const wchar_t _crash_desc[] = static const wchar_t _crash_desc[] =
L"A serious fault condition occurred in the game. The game will shut down.\n" L"A serious fault condition occurred in the game. The game will shut down.\n"
L"Please send crash.log, crash.dmp, and crash.sav to the developers.\n" L"Please send crash.json.log, crash.dmp, and crash.sav to the developers.\n"
L"This will greatly help debugging.\n\n" L"This will greatly help debugging.\n\n"
L"https://github.com/OpenTTD/OpenTTD/issues\n\n" L"https://github.com/OpenTTD/OpenTTD/issues\n\n"
L"%s\n%s\n%s\n%s\n"; L"%s\n%s\n%s\n%s\n";
@ -462,9 +465,10 @@ static INT_PTR CALLBACK CrashDialogFunc(HWND wnd, UINT msg, WPARAM wParam, LPARA
{ {
switch (msg) { switch (msg) {
case WM_INITDIALOG: { case WM_INITDIALOG: {
size_t crashlog_length = CrashLogWindows::current->crashlog.size() + 1; std::string crashlog = CrashLogWindows::current->survey.dump(4);
size_t crashlog_length = crashlog.size() + 1;
/* Reserve extra space for LF to CRLF conversion. */ /* Reserve extra space for LF to CRLF conversion. */
crashlog_length += std::count(CrashLogWindows::current->crashlog.begin(), CrashLogWindows::current->crashlog.end(), '\n'); crashlog_length += std::count(crashlog.begin(), crashlog.end(), '\n');
const size_t filename_count = 4; const size_t filename_count = 4;
const size_t filename_buf_length = MAX_PATH + 1; const size_t filename_buf_length = MAX_PATH + 1;
@ -486,7 +490,7 @@ static INT_PTR CALLBACK CrashDialogFunc(HWND wnd, UINT msg, WPARAM wParam, LPARA
char *crashlog_dos_nl = reinterpret_cast<char *>(filename_buf + filename_buf_length * filename_count); char *crashlog_dos_nl = reinterpret_cast<char *>(filename_buf + filename_buf_length * filename_count);
/* Convert unix -> dos newlines because the edit box only supports that properly. */ /* Convert unix -> dos newlines because the edit box only supports that properly. */
const char *crashlog_unix_nl = CrashLogWindows::current->crashlog.data(); const char *crashlog_unix_nl = crashlog.data();
char *p = crashlog_dos_nl; char *p = crashlog_dos_nl;
char32_t c; char32_t c;
while ((c = Utf8Consume(&crashlog_unix_nl))) { while ((c = Utf8Consume(&crashlog_unix_nl))) {

View File

@ -10,15 +10,13 @@
#include "../../stdafx.h" #include "../../stdafx.h"
#include "../../3rdparty/fmt/format.h" #include "../../3rdparty/fmt/format.h"
#include "../../3rdparty/nlohmann/json.hpp" #include "../../survey.h"
#include <thread> #include <thread>
#include <windows.h> #include <windows.h>
#include "../../safeguards.h" #include "../../safeguards.h"
extern std::string SurveyMemoryToText(uint64_t memory);
void SurveyOS(nlohmann::json &json) void SurveyOS(nlohmann::json &json)
{ {
_OSVERSIONINFOA os; _OSVERSIONINFOA os;

View File

@ -716,6 +716,12 @@ bool IntSettingDesc::IsSameValue(const IniItem *item, void *object) const
return item_value == object_value; return item_value == object_value;
} }
bool IntSettingDesc::IsDefaultValue(void *object) const
{
int32_t object_value = this->Read(object);
return this->def == object_value;
}
std::string StringSettingDesc::FormatValue(const void *object) const std::string StringSettingDesc::FormatValue(const void *object) const
{ {
const std::string &str = this->Read(object); const std::string &str = this->Read(object);
@ -742,12 +748,24 @@ bool StringSettingDesc::IsSameValue(const IniItem *item, void *object) const
return item->value->compare(str) == 0; return item->value->compare(str) == 0;
} }
bool StringSettingDesc::IsDefaultValue(void *object) const
{
const std::string &str = this->Read(object);
return this->def == str;
}
bool ListSettingDesc::IsSameValue(const IniItem *item, void *object) const bool ListSettingDesc::IsSameValue(const IniItem *item, void *object) const
{ {
/* Checking for equality is way more expensive than just writing the value. */ /* Checking for equality is way more expensive than just writing the value. */
return false; return false;
} }
bool ListSettingDesc::IsDefaultValue(void *object) const
{
/* Defaults of lists are often complicated, and hard to compare. */
return false;
}
/** /**
* Loads all items from a 'grpname' section into a list * Loads all items from a 'grpname' section into a list
* The list parameter can be a nullptr pointer, in this case nothing will be * The list parameter can be a nullptr pointer, in this case nothing will be

View File

@ -130,6 +130,14 @@ struct SettingDesc {
* @return True if the value is definitely the same (might be false when the same). * @return True if the value is definitely the same (might be false when the same).
*/ */
virtual bool IsSameValue(const IniItem *item, void *object) const = 0; virtual bool IsSameValue(const IniItem *item, void *object) const = 0;
/**
* Check whether the value is the same as the default value.
*
* @param object The object the setting is in.
* @return true iff the value is the default value.
*/
virtual bool IsDefaultValue(void *object) const = 0;
}; };
/** Base integer type, including boolean, settings. Only these are shown in the settings UI. */ /** Base integer type, including boolean, settings. Only these are shown in the settings UI. */
@ -215,6 +223,7 @@ struct IntSettingDesc : SettingDesc {
std::string FormatValue(const void *object) const override; std::string FormatValue(const void *object) const override;
void ParseValue(const IniItem *item, void *object) const override; void ParseValue(const IniItem *item, void *object) const override;
bool IsSameValue(const IniItem *item, void *object) const override; bool IsSameValue(const IniItem *item, void *object) const override;
bool IsDefaultValue(void *object) const override;
int32_t Read(const void *object) const; int32_t Read(const void *object) const;
private: private:
@ -307,6 +316,7 @@ struct StringSettingDesc : SettingDesc {
std::string FormatValue(const void *object) const override; std::string FormatValue(const void *object) const override;
void ParseValue(const IniItem *item, void *object) const override; void ParseValue(const IniItem *item, void *object) const override;
bool IsSameValue(const IniItem *item, void *object) const override; bool IsSameValue(const IniItem *item, void *object) const override;
bool IsDefaultValue(void *object) const override;
const std::string &Read(const void *object) const; const std::string &Read(const void *object) const;
private: private:
@ -324,6 +334,7 @@ struct ListSettingDesc : SettingDesc {
std::string FormatValue(const void *object) const override; std::string FormatValue(const void *object) const override;
void ParseValue(const IniItem *item, void *object) const override; void ParseValue(const IniItem *item, void *object) const override;
bool IsSameValue(const IniItem *item, void *object) const override; bool IsSameValue(const IniItem *item, void *object) const override;
bool IsDefaultValue(void *object) const override;
}; };
/** Placeholder for settings that have been removed, but might still linger in the savegame. */ /** Placeholder for settings that have been removed, but might still linger in the savegame. */
@ -334,6 +345,7 @@ struct NullSettingDesc : SettingDesc {
std::string FormatValue(const void *object) const override { NOT_REACHED(); } std::string FormatValue(const void *object) const override { NOT_REACHED(); }
void ParseValue(const IniItem *item, void *object) const override { NOT_REACHED(); } void ParseValue(const IniItem *item, void *object) const override { NOT_REACHED(); }
bool IsSameValue(const IniItem *item, void *object) const override { NOT_REACHED(); } bool IsSameValue(const IniItem *item, void *object) const override { NOT_REACHED(); }
bool IsDefaultValue(void *object) const override { NOT_REACHED(); }
}; };
typedef std::variant<IntSettingDesc, BoolSettingDesc, OneOfManySettingDesc, ManyOfManySettingDesc, StringSettingDesc, ListSettingDesc, NullSettingDesc> SettingVariant; typedef std::variant<IntSettingDesc, BoolSettingDesc, OneOfManySettingDesc, ManyOfManySettingDesc, StringSettingDesc, ListSettingDesc, NullSettingDesc> SettingVariant;

458
src/survey.cpp 100644
View File

@ -0,0 +1,458 @@
/*
* This file is part of OpenTTD.
* OpenTTD 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, version 2.
* OpenTTD 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 OpenTTD. If not, see <http://www.gnu.org/licenses/>.
*/
/** @file survey.cpp Functions to survey the current game / system, for crashlog and network-survey. */
#include "stdafx.h"
#include "survey.h"
#include "settings_table.h"
#include "network/network.h"
#include "rev.h"
#include "settings_type.h"
#include "timer/timer_game_tick.h"
#include "currency.h"
#include "fontcache.h"
#include "language.h"
#include "ai/ai_info.hpp"
#include "game/game.hpp"
#include "game/game_info.hpp"
#include "music/music_driver.hpp"
#include "sound/sound_driver.hpp"
#include "video/video_driver.hpp"
#include "base_media_base.h"
#include "blitter/factory.hpp"
#ifdef WITH_ALLEGRO
# include <allegro.h>
#endif /* WITH_ALLEGRO */
#ifdef WITH_FONTCONFIG
# include <fontconfig/fontconfig.h>
#endif /* WITH_FONTCONFIG */
#ifdef WITH_PNG
/* pngconf.h, included by png.h doesn't like something in the
* freetype headers. As such it's not alphabetically sorted. */
# include <png.h>
#endif /* WITH_PNG */
#ifdef WITH_FREETYPE
# include <ft2build.h>
# include FT_FREETYPE_H
#endif /* WITH_FREETYPE */
#ifdef WITH_HARFBUZZ
# include <hb.h>
#endif /* WITH_HARFBUZZ */
#ifdef WITH_ICU_I18N
# include <unicode/uversion.h>
#endif /* WITH_ICU_I18N */
#ifdef WITH_LIBLZMA
# include <lzma.h>
#endif
#ifdef WITH_LZO
#include <lzo/lzo1x.h>
#endif
#if defined(WITH_SDL) || defined(WITH_SDL2)
# include <SDL.h>
#endif /* WITH_SDL || WITH_SDL2 */
#ifdef WITH_ZLIB
# include <zlib.h>
#endif
#ifdef WITH_CURL
# include <curl/curl.h>
#endif
#include "safeguards.h"
NLOHMANN_JSON_SERIALIZE_ENUM(GRFStatus, {
{GRFStatus::GCS_UNKNOWN, "unknown"},
{GRFStatus::GCS_DISABLED, "disabled"},
{GRFStatus::GCS_NOT_FOUND, "not found"},
{GRFStatus::GCS_INITIALISED, "initialised"},
{GRFStatus::GCS_ACTIVATED, "activated"},
})
/** Lookup table to convert a VehicleType to a string. */
static const std::string _vehicle_type_to_string[] = {
"train",
"roadveh",
"ship",
"aircraft",
};
/**
* List of all the generic setting tables.
*
* There are a few tables that are special and not processed like the rest:
* - _currency_settings
* - _misc_settings
* - _company_settings
* - _win32_settings
* As such, they are not part of this list.
*/
static auto &GenericSettingTables()
{
static const SettingTable _generic_setting_tables[] = {
_difficulty_settings,
_economy_settings,
_game_settings,
_gui_settings,
_linkgraph_settings,
_locale_settings,
_multimedia_settings,
_network_settings,
_news_display_settings,
_pathfinding_settings,
_script_settings,
_world_settings,
};
return _generic_setting_tables;
}
/**
* Convert a settings table to JSON.
*
* @param survey The JSON object.
* @param table The settings table to convert.
* @param object The object to get the settings from.
* @param skip_if_default If true, skip any settings that are on their default value.
*/
static void SurveySettingsTable(nlohmann::json &survey, const SettingTable &table, void *object, bool skip_if_default)
{
for (auto &desc : table) {
const SettingDesc *sd = GetSettingDesc(desc);
/* Skip any old settings we no longer save/load. */
if (!SlIsObjectCurrentlyValid(sd->save.version_from, sd->save.version_to)) continue;
auto name = sd->GetName();
if (skip_if_default && sd->IsDefaultValue(object)) continue;
survey[name] = sd->FormatValue(object);
}
}
/**
* Convert settings to JSON.
*
* @param survey The JSON object.
*/
void SurveySettings(nlohmann::json &survey, bool skip_if_default)
{
SurveySettingsTable(survey, _misc_settings, nullptr, skip_if_default);
#if defined(_WIN32) && !defined(DEDICATED)
SurveySettingsTable(survey, _win32_settings, nullptr, skip_if_default);
#endif
for (auto &table : GenericSettingTables()) {
SurveySettingsTable(survey, table, &_settings_game, skip_if_default);
}
SurveySettingsTable(survey, _currency_settings, &_custom_currency, skip_if_default);
SurveySettingsTable(survey, _company_settings, &_settings_client.company, skip_if_default);
}
/**
* Convert compiler information to JSON.
*
* @param survey The JSON object.
*/
void SurveyCompiler(nlohmann::json &survey)
{
#if defined(_MSC_VER)
survey["name"] = "MSVC";
survey["version"] = _MSC_VER;
#elif defined(__ICC) && defined(__GNUC__)
survey["name"] = "ICC";
survey["version"] = __ICC;
# if defined(__GNUC__)
survey["extra"] = fmt::format("GCC {}.{}.{} mode", __GNUC__, __GNUC_MINOR__, __GNUC_PATCHLEVEL__);
# endif
#elif defined(__GNUC__)
survey["name"] = "GCC";
survey["version"] = fmt::format("{}.{}.{}", __GNUC__, __GNUC_MINOR__, __GNUC_PATCHLEVEL__);
#else
survey["name"] = "unknown";
#endif
#if defined(__VERSION__)
survey["extra"] = __VERSION__;
#endif
}
/**
* Convert generic OpenTTD information to JSON.
*
* @param survey The JSON object.
*/
void SurveyOpenTTD(nlohmann::json &survey)
{
survey["version"]["revision"] = std::string(_openttd_revision);
survey["version"]["modified"] = _openttd_revision_modified;
survey["version"]["tagged"] = _openttd_revision_tagged;
survey["version"]["hash"] = std::string(_openttd_revision_hash);
survey["version"]["newgrf"] = fmt::format("{:X}", _openttd_newgrf_version);
survey["version"]["content"] = std::string(_openttd_content_version);
survey["build_date"] = std::string(_openttd_build_date);
survey["bits"] =
#ifdef POINTER_IS_64BIT
64
#else
32
#endif
;
survey["endian"] =
#if (TTD_ENDIAN == TTD_LITTLE_ENDIAN)
"little"
#else
"big"
#endif
;
survey["dedicated_build"] =
#ifdef DEDICATED
"yes"
#else
"no"
#endif
;
}
/**
* Convert generic game information to JSON.
*
* @param survey The JSON object.
*/
void SurveyConfiguration(nlohmann::json &survey)
{
survey["network"] = _networking ? (_network_server ? "server" : "client") : "no";
if (_current_language != nullptr) {
survey["language"]["filename"] = _current_language->file.filename().string();
survey["language"]["name"] = _current_language->name;
survey["language"]["isocode"] = _current_language->isocode;
}
if (BlitterFactory::GetCurrentBlitter() != nullptr) {
survey["blitter"] = BlitterFactory::GetCurrentBlitter()->GetName();
}
if (MusicDriver::GetInstance() != nullptr) {
survey["music_driver"] = MusicDriver::GetInstance()->GetName();
}
if (SoundDriver::GetInstance() != nullptr) {
survey["sound_driver"] = SoundDriver::GetInstance()->GetName();
}
if (VideoDriver::GetInstance() != nullptr) {
survey["video_driver"] = VideoDriver::GetInstance()->GetName();
survey["video_info"] = VideoDriver::GetInstance()->GetInfoString();
}
if (BaseGraphics::GetUsedSet() != nullptr) {
survey["graphics_set"] = fmt::format("{}.{}", BaseGraphics::GetUsedSet()->name, BaseGraphics::GetUsedSet()->version);
}
if (BaseMusic::GetUsedSet() != nullptr) {
survey["music_set"] = fmt::format("{}.{}", BaseMusic::GetUsedSet()->name, BaseMusic::GetUsedSet()->version);
}
if (BaseSounds::GetUsedSet() != nullptr) {
survey["sound_set"] = fmt::format("{}.{}", BaseSounds::GetUsedSet()->name, BaseSounds::GetUsedSet()->version);
}
}
/**
* Convert font information to JSON.
*
* @param survey The JSON object.
*/
void SurveyFont(nlohmann::json &survey)
{
survey["small"] = FontCache::Get(FS_SMALL)->GetFontName();
survey["medium"] = FontCache::Get(FS_NORMAL)->GetFontName();
survey["large"] = FontCache::Get(FS_LARGE)->GetFontName();
survey["mono"] = FontCache::Get(FS_MONO)->GetFontName();
}
/**
* Convert company information to JSON.
*
* @param survey The JSON object.
*/
void SurveyCompanies(nlohmann::json &survey)
{
for (const Company *c : Company::Iterate()) {
auto &company = survey[std::to_string(c->index)];
if (c->ai_info == nullptr) {
company["type"] = "human";
} else {
company["type"] = "ai";
company["script"] = fmt::format("{}.{}", c->ai_info->GetName(), c->ai_info->GetVersion());
}
for (VehicleType type = VEH_BEGIN; type < VEH_COMPANY_END; type++) {
uint amount = c->group_all[type].num_vehicle;
company["vehicles"][_vehicle_type_to_string[type]] = amount;
}
company["infrastructure"]["road"] = c->infrastructure.GetRoadTotal();
company["infrastructure"]["tram"] = c->infrastructure.GetTramTotal();
company["infrastructure"]["rail"] = c->infrastructure.GetRailTotal();
company["infrastructure"]["signal"] = c->infrastructure.signal;
company["infrastructure"]["water"] = c->infrastructure.water;
company["infrastructure"]["station"] = c->infrastructure.station;
company["infrastructure"]["airport"] = c->infrastructure.airport;
}
}
/**
* Convert timer information to JSON.
*
* @param survey The JSON object.
*/
void SurveyTimers(nlohmann::json &survey)
{
survey["ticks"] = TimerGameTick::counter;
survey["seconds"] = std::chrono::duration_cast<std::chrono::seconds>(std::chrono::steady_clock::now() - _switch_mode_time).count();
TimerGameCalendar::YearMonthDay ymd;
TimerGameCalendar::ConvertDateToYMD(TimerGameCalendar::date, &ymd);
survey["calendar"] = fmt::format("{:04}-{:02}-{:02} ({})", ymd.year, ymd.month + 1, ymd.day, TimerGameCalendar::date_fract);
}
/**
* Convert GRF information to JSON.
*
* @param survey The JSON object.
*/
void SurveyGrfs(nlohmann::json &survey)
{
for (GRFConfig *c = _grfconfig; c != nullptr; c = c->next) {
auto grfid = fmt::format("{:08x}", BSWAP32(c->ident.grfid));
auto &grf = survey[grfid];
grf["md5sum"] = FormatArrayAsHex(c->ident.md5sum);
grf["status"] = c->status;
if ((c->palette & GRFP_GRF_MASK) == GRFP_GRF_UNSET) grf["palette"] = "unset";
if ((c->palette & GRFP_GRF_MASK) == GRFP_GRF_DOS) grf["palette"] = "dos";
if ((c->palette & GRFP_GRF_MASK) == GRFP_GRF_WINDOWS) grf["palette"] = "windows";
if ((c->palette & GRFP_GRF_MASK) == GRFP_GRF_ANY) grf["palette"] = "any";
if ((c->palette & GRFP_BLT_MASK) == GRFP_BLT_UNSET) grf["blitter"] = "unset";
if ((c->palette & GRFP_BLT_MASK) == GRFP_BLT_32BPP) grf["blitter"] = "32bpp";
grf["is_static"] = HasBit(c->flags, GCF_STATIC);
std::vector<uint32_t> parameters;
for (int i = 0; i < c->num_params; i++) {
parameters.push_back(c->param[i]);
}
grf["parameters"] = parameters;
}
}
/**
* Convert game-script information to JSON.
*
* @param survey The JSON object.
*/
void SurveyGameScript(nlohmann::json &survey)
{
if (Game::GetInfo() == nullptr) return;
survey = fmt::format("{}.{}", Game::GetInfo()->GetName(), Game::GetInfo()->GetVersion());
}
/**
* Convert compiled libraries information to JSON.
*
* @param survey The JSON object.
*/
void SurveyLibraries(nlohmann::json &survey)
{
#ifdef WITH_ALLEGRO
survey["allegro"] = std::string(allegro_id);
#endif /* WITH_ALLEGRO */
#ifdef WITH_FONTCONFIG
int version = FcGetVersion();
survey["fontconfig"] = fmt::format("{}.{}.{}", version / 10000, (version / 100) % 100, version % 100);
#endif /* WITH_FONTCONFIG */
#ifdef WITH_FREETYPE
FT_Library library;
int major, minor, patch;
FT_Init_FreeType(&library);
FT_Library_Version(library, &major, &minor, &patch);
FT_Done_FreeType(library);
survey["freetype"] = fmt::format("{}.{}.{}", major, minor, patch);
#endif /* WITH_FREETYPE */
#if defined(WITH_HARFBUZZ)
survey["harfbuzz"] = hb_version_string();
#endif /* WITH_HARFBUZZ */
#if defined(WITH_ICU_I18N)
/* 4 times 0-255, separated by dots (.) and a trailing '\0' */
char buf[4 * 3 + 3 + 1];
UVersionInfo ver;
u_getVersion(ver);
u_versionToString(ver, buf);
survey["icu_i18n"] = buf;
#endif /* WITH_ICU_I18N */
#ifdef WITH_LIBLZMA
survey["lzma"] = lzma_version_string();
#endif
#ifdef WITH_LZO
survey["lzo"] = lzo_version_string();
#endif
#ifdef WITH_PNG
survey["png"] = png_get_libpng_ver(nullptr);
#endif /* WITH_PNG */
#ifdef WITH_SDL
const SDL_version *sdl_v = SDL_Linked_Version();
survey["sdl"] = fmt::format("{}.{}.{}", sdl_v->major, sdl_v->minor, sdl_v->patch);
#elif defined(WITH_SDL2)
SDL_version sdl2_v;
SDL_GetVersion(&sdl2_v);
survey["sdl2"] = fmt::format("{}.{}.{}", sdl2_v.major, sdl2_v.minor, sdl2_v.patch);
#endif
#ifdef WITH_ZLIB
survey["zlib"] = zlibVersion();
#endif
#ifdef WITH_CURL
auto *curl_v = curl_version_info(CURLVERSION_NOW);
survey["curl"] = curl_v->version;
survey["curl_ssl"] = curl_v->ssl_version == nullptr ? "none" : curl_v->ssl_version;
#endif
}
/**
* 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));
}

31
src/survey.h 100644
View File

@ -0,0 +1,31 @@
/*
* This file is part of OpenTTD.
* OpenTTD 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, version 2.
* OpenTTD 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 OpenTTD. If not, see <http://www.gnu.org/licenses/>.
*/
/** @file survey.h Functions to survey the current game / system, for crashlog and network-survey. */
#ifndef SURVEY_H
#define SURVEY_H
#include "3rdparty/nlohmann/json.hpp"
std::string SurveyMemoryToText(uint64_t memory);
void SurveyCompanies(nlohmann::json &survey);
void SurveyCompiler(nlohmann::json &survey);
void SurveyConfiguration(nlohmann::json &survey);
void SurveyFont(nlohmann::json &survey);
void SurveyGameScript(nlohmann::json &survey);
void SurveyGrfs(nlohmann::json &survey);
void SurveyLibraries(nlohmann::json &survey);
void SurveyOpenTTD(nlohmann::json &survey);
void SurveySettings(nlohmann::json &survey, bool skip_if_default);
void SurveyTimers(nlohmann::json &survey);
/* Defined in os/<os>/survey_<os>.cpp. */
void SurveyOS(nlohmann::json &json);
#endif /* SURVEY_H */