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_gui.cpp
subsidy_type.h
survey.cpp
survey.h
tar_type.h
terraform_cmd.cpp
terraform_cmd.h

View File

@ -9,297 +9,60 @@
#include "stdafx.h"
#include "crashlog.h"
#include "survey.h"
#include "gamelog.h"
#include "timer/timer_game_calendar.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 "sound/sound_driver.hpp"
#include "video/video_driver.hpp"
#include "saveload/saveload.h"
#include "screenshot.h"
#include "gfx_func.h"
#include "network/network.h"
#include "network/network_survey.h"
#include "language.h"
#include "fontcache.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 "3rdparty/fmt/chrono.h"
#include "3rdparty/fmt/std.h"
#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 "core/format.hpp"
#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
{
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");
}
/** The version of the schema of the JSON information. */
constexpr uint8_t CRASHLOG_SURVEY_VERSION = 1;
/**
* Writes the gamelog data to the buffer.
* @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) {
fmt::format_to(output_iterator, "{}\n", s);
json = nlohmann::json::array();
_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.
* @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;
for (NewsItem *news = _latest_news; i < 32 && news != nullptr; news = news->prev, i++) {
TimerGameCalendar::YearMonthDay ymd;
TimerGameCalendar::ConvertDateToYMD(news->date, &ymd);
fmt::format_to(output_iterator, "({}-{:02}-{:02}) StringID: {}, Type: {}, Ref1: {}, {}, Ref2: {}, {}\n",
ymd.year, ymd.month + 1, ymd.day, news->string_id, news->type,
news->reftype1, news->ref1, news->reftype2, news->ref2);
json.push_back(fmt::format("({}-{:02}-{:02}) StringID: {}, Type: {}, Ref1: {}, {}, Ref2: {}, {}",
ymd.year, ymd.month + 1, ymd.day, news->string_id, news->type,
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.
* @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");
fmt::format_to(output_iterator, "Crash at: {:%Y-%m-%d %H:%M:%S} (UTC)\n", fmt::gmtime(time(nullptr)));
/* Reminder: this JSON is read in an automated fashion.
* 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;
TimerGameCalendar::ConvertDateToYMD(TimerGameCalendar::date, &ymd);
fmt::format_to(output_iterator, "In game date: {}-{:02}-{:02} ({})\n\n", ymd.year, ymd.month + 1, ymd.day, TimerGameCalendar::date_fract);
/* If no internal reason was logged, it must be a crash. */
if (CrashLog::message.empty()) {
this->SurveyCrash(this->survey["crash"]);
} else {
this->survey["crash"]["reason"] = CrashLog::message;
CrashLog::message.clear();
}
this->LogError(output_iterator, CrashLog::message);
this->LogOpenTTDVersion(output_iterator);
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);
if (!this->TryExecute("stacktrace", [this]() { this->SurveyStacktrace(this->survey["stacktrace"]); return true; })) {
this->survey["stacktrace"] = "crashed while gathering information";
}
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()
{
this->crashlog_filename = this->CreateFileName(".log");
this->crashlog_filename = this->CreateFileName(".json.log");
FILE *file = FioFOpenFile(this->crashlog_filename, "w", NO_DIRECTORY);
if (file == nullptr) return false;
size_t len = this->crashlog.size();
size_t written = fwrite(this->crashlog.data(), 1, len, file);
std::string survey_json = this->survey.dump(4);
size_t len = survey_json.size();
size_t written = fwrite(survey_json.data(), 1, len, file);
FioFCloseFile(file);
return len == written;
@ -414,6 +246,9 @@ bool CrashLog::WriteScreenshot()
return res;
}
/**
* Send the survey result, noting it was a crash.
*/
void CrashLog::SendSurvey() const
{
if (_game_mode == GM_NORMAL) {
@ -433,14 +268,13 @@ void CrashLog::MakeCrashLog()
if (crashlogged) return;
crashlogged = true;
crashlog.reserve(65536);
auto output_iterator = std::back_inserter(crashlog);
fmt::print("Crash encountered, generating crash log...\n");
this->FillCrashLog(output_iterator);
fmt::print("{}\n", crashlog);
this->FillCrashLog();
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");
bool ret = this->TryExecute("crashlog", [this]() { return this->WriteCrashLog(); });
if (ret) {

View File

@ -10,6 +10,8 @@
#ifndef CRASHLOG_H
#define CRASHLOG_H
#include "3rdparty/nlohmann/json.hpp"
/**
* Helper class for creating crash logs.
*/
@ -17,40 +19,20 @@ class CrashLog {
private:
/** Error message coming from #FatalError(format, ...). */
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.
* @param output_iterator Iterator to write the output to.
* Convert system crash reason to JSON.
*
* @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.
* @param output_iterator Iterator to write the output to.
* @param message Message passed to use for errors.
* Convert stacktrace to JSON.
*
* @param survey The JSON object.
*/
virtual void LogError(std::back_insert_iterator<std::string> &output_iterator, const std::string_view message) 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;
virtual void SurveyStacktrace(nlohmann::json &survey) const = 0;
/**
* 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;
protected:
std::string CreateFileName(const char *ext, bool with_dir = true) const;
public:
/** Stub destructor to silence some compilers. */
virtual ~CrashLog() = default;
std::string crashlog;
nlohmann::json survey;
std::string crashlog_filename;
std::string crashdump_filename;
std::string savegame_filename;
std::string screenshot_filename;
void FillCrashLog(std::back_insert_iterator<std::string> &output_iterator) const;
bool WriteCrashLog();
void FillCrashLog();
void PrintCrashLog() const;
bool WriteCrashLog();
virtual bool WriteCrashDump();
bool WriteSavegame();
bool WriteScreenshot();

View File

@ -524,6 +524,7 @@ STR_NEWS_MENU_DELETE_ALL_MESSAGES :Apagar todas as
# About menu
###length 11
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_TOGGLE_CONSOLE :Alternar console
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_HIGHSCORE :{BLACK}Pontuações
STR_INTRO_HELP :{BLACK}Ajuda & Manuais
STR_INTRO_CONFIG_SETTINGS_TREE :{BLACK}Configurações
STR_INTRO_NEWGRF_SETTINGS :{BLACK}Configurar NewGRF
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_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_NEWGRF_SETTINGS :{BLACK}Exibir configs. dos NewGRF
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?
# 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
STR_CHEATS :{WHITE}Trapaças
@ -4702,6 +4716,11 @@ STR_AI_SETTINGS_SETTING :{STRING}: {ORAN
# 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_TOOLTIP :[BLACK}Quebra linhas automaticamente para que o texto caiba na janela
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_LICENCE_CAPTION :{WHITE}{STRING} licença de {STRING}
STR_TEXTFILE_SURVEY_RESULT_CAPTION :{WHITE}Visualização do resultado da pesquisa
STR_TEXTFILE_GAME_MANUAL_CAPTION :{WHITE}OpenTTD documento '{STRING}'
# Vehicle loading indicators

View File

@ -523,6 +523,7 @@ STR_NEWS_MENU_DELETE_ALL_MESSAGES :Delete all mess
# About menu
###length 11
STR_ABOUT_MENU_LAND_BLOCK_INFO :Land area information
STR_ABOUT_MENU_HELP :Help & manuals
STR_ABOUT_MENU_SEPARATOR :
STR_ABOUT_MENU_TOGGLE_CONSOLE :Toggle console
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_HIGHSCORE :{BLACK}Highscore Table
STR_INTRO_HELP :{BLACK}Help & Manuals
STR_INTRO_CONFIG_SETTINGS_TREE :{BLACK}Settings
STR_INTRO_NEWGRF_SETTINGS :{BLACK}NewGRF Settings
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_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_NEWGRF_SETTINGS :{BLACK}Display NewGRF settings
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?
# 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
STR_CHEATS :{WHITE}Cheats
@ -4701,6 +4715,11 @@ STR_AI_SETTINGS_SETTING :{STRING}: {ORAN
# 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_TOOLTIP :{BLACK}Wrap the text of the window so it all fits without having to scroll
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_LICENCE_CAPTION :{WHITE}{STRING} license of {STRING}
STR_TEXTFILE_SURVEY_RESULT_CAPTION :{WHITE}Preview of survey result
STR_TEXTFILE_GAME_MANUAL_CAPTION :{WHITE}OpenTTD document '{STRING}'
# Vehicle loading indicators

View File

@ -523,6 +523,7 @@ STR_NEWS_MENU_DELETE_ALL_MESSAGES :Poista kaikki v
# About menu
###length 11
STR_ABOUT_MENU_LAND_BLOCK_INFO :Maa-alueen tiedot
STR_ABOUT_MENU_HELP :Ohjeet ja oppaat
STR_ABOUT_MENU_SEPARATOR :
STR_ABOUT_MENU_TOGGLE_CONSOLE :Konsoli
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_HIGHSCORE :{BLACK}Pistetaulukko
STR_INTRO_HELP :{BLACK}Ohjeet ja oppaat
STR_INTRO_CONFIG_SETTINGS_TREE :{BLACK}Asetukset
STR_INTRO_NEWGRF_SETTINGS :{BLACK}NewGRF-asetukset
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_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_NEWGRF_SETTINGS :{BLACK}Näytä NewGRF-asetukset
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?
# 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
STR_CHEATS :{WHITE}Huijaukset
@ -4701,6 +4715,11 @@ STR_AI_SETTINGS_SETTING :{STRING}: {ORAN
# 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_TOOLTIP :{BLACK}Rivitä teksti ikkunaan niin, että se näkyy kokonaan rullaamatta
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_LICENCE_CAPTION :{WHITE}{STRING}:n {STRING} lisenssi
STR_TEXTFILE_SURVEY_RESULT_CAPTION :{WHITE}Tutkimustuloksen esikatselu
STR_TEXTFILE_GAME_MANUAL_CAPTION :{WHITE}OpenTTD-asiakirja ”{STRING}”
# Vehicle loading indicators

View File

@ -524,6 +524,7 @@ STR_NEWS_MENU_DELETE_ALL_MESSAGES :Effacer tous le
# About menu
###length 11
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_TOGGLE_CONSOLE :Afficher/Cacher la console
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_HIGHSCORE :{BLACK}Meilleurs scores
STR_INTRO_HELP :{BLACK}Aide et manuels
STR_INTRO_CONFIG_SETTINGS_TREE :{BLACK}Paramètres de jeu
STR_INTRO_NEWGRF_SETTINGS :{BLACK}Paramètres NewGRF
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_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_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
@ -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}?
# 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
STR_CHEATS :{WHITE}Triches
@ -4702,6 +4716,11 @@ STR_AI_SETTINGS_SETTING :{STRING}{NBSP}:
# 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_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
@ -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_LICENCE_CAPTION :{WHITE}Licence du module {STRING} {STRING}
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

View File

@ -524,6 +524,7 @@ STR_NEWS_MENU_DELETE_ALL_MESSAGES :모든 뉴스
# About menu
###length 11
STR_ABOUT_MENU_LAND_BLOCK_INFO :지형 정보
STR_ABOUT_MENU_HELP :도움말 & 매뉴얼
STR_ABOUT_MENU_SEPARATOR :
STR_ABOUT_MENU_TOGGLE_CONSOLE :콘솔 켜기/끄기
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_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_HELPTEXT :도시 소유의 도로 위에 버스 정류장 건설을 허용합니다.
STR_CONFIG_SETTING_STOP_ON_TOWN_ROAD_HELPTEXT :도시 소유의 도로 위에 버스 정류장 건설을 허용합니다
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_INFRASTRUCTURE_MAINTENANCE :기반시설 유지비: {STRING}
@ -2123,6 +2126,7 @@ STR_INTRO_MULTIPLAYER :{BLACK}멀티
STR_INTRO_GAME_OPTIONS :{BLACK}게임 설정
STR_INTRO_HIGHSCORE :{BLACK}고득점 순위표
STR_INTRO_HELP :{BLACK}도움말 & 매뉴얼
STR_INTRO_CONFIG_SETTINGS_TREE :{BLACK}설정
STR_INTRO_NEWGRF_SETTINGS :{BLACK}NewGRF 설정
STR_INTRO_ONLINE_CONTENT :{BLACK}온라인 콘텐츠 다운로드
@ -2144,6 +2148,7 @@ STR_INTRO_TOOLTIP_TOYLAND_LANDSCAPE :{BLACK}장난
STR_INTRO_TOOLTIP_GAME_OPTIONS :{BLACK}게임 기본 설정 창을 보여줍니다
STR_INTRO_TOOLTIP_HIGHSCORE :{BLACK}고득점 순위표를 보여줍니다
STR_INTRO_TOOLTIP_HELP :{BLACK}각종 문서와 온라인 링크를 확인합니다
STR_INTRO_TOOLTIP_CONFIG_SETTINGS_TREE :{BLACK}설정 창을 엽니다
STR_INTRO_TOOLTIP_NEWGRF_SETTINGS :{BLACK}NewGRF 설정 창을 엽니다
STR_INTRO_TOOLTIP_ONLINE_CONTENT :{BLACK}새로 나왔거나 업데이트된 콘텐츠를 체크하여 다운로드합니다
@ -2166,6 +2171,17 @@ STR_ABANDON_GAME_QUERY :{YELLOW}정말
STR_ABANDON_SCENARIO_QUERY :{YELLOW}정말 이 시나리오를 종료하시겠습니까?
# 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
STR_CHEATS :{WHITE}치트
@ -2654,6 +2670,7 @@ STR_TRANSPARENT_BUILDINGS_TOOLTIP :{BLACK}정거
STR_TRANSPARENT_BRIDGES_TOOLTIP :{BLACK}다리를 보여줄 지 선택합니다. 고정하려면 CTRL+클릭하세요
STR_TRANSPARENT_STRUCTURES_TOOLTIP :{BLACK}등대나 안테나 같은 구조물을 보여줄 지 선택합니다. 고정하려면 CTRL+클릭하세요
STR_TRANSPARENT_CATENARY_TOOLTIP :{BLACK}전차선을 보여줄 지 선택합니다. 고정하려면 CTRL+클릭하세요
STR_TRANSPARENT_TEXT_TOOLTIP :{BLACK}적재/하차 및 수입/지출 텍스트를 보여줄 지 선택합니다. 고정하려면 CTRL+클릭하세요
STR_TRANSPARENT_INVISIBLE_TOOLTIP :{BLACK}반투명 대신 아예 안 보이게 할 항목을 선택하세요
# Linkgraph legend window
@ -4699,6 +4716,11 @@ STR_AI_SETTINGS_SETTING :{STRING}: {ORAN
# 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_TOOLTIP :{BLACK}본문 내용에 자동 줄 바꿈을 설정하여 스크롤하지 않고도 본문의 모든 내용을 볼 수 있게 합니다.
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_LICENCE_CAPTION :{WHITE}{STRING} {STRING}의 저작권
STR_TEXTFILE_SURVEY_RESULT_CAPTION :{WHITE}설문조사 결과 미리보기
STR_TEXTFILE_GAME_MANUAL_CAPTION :{WHITE}OpenTTD '{STRING}' 문서
# Vehicle loading indicators

View File

@ -649,6 +649,7 @@ STR_NEWS_MENU_DELETE_ALL_MESSAGES :Удалить
# About menu
###length 11
STR_ABOUT_MENU_LAND_BLOCK_INFO :Информация об участке земли
STR_ABOUT_MENU_HELP :Помощь и документация
STR_ABOUT_MENU_SEPARATOR :
STR_ABOUT_MENU_TOGGLE_CONSOLE :Консоль
STR_ABOUT_MENU_AI_DEBUG :Отладка ИИ / скриптов
@ -2275,6 +2276,7 @@ STR_INTRO_MULTIPLAYER :{BLACK}Сете
STR_INTRO_GAME_OPTIONS :{BLACK}Основные настройки
STR_INTRO_HIGHSCORE :{BLACK}Таблица рекордов
STR_INTRO_HELP :{BLACK}Помощь и документация
STR_INTRO_CONFIG_SETTINGS_TREE :{BLACK}Расширенные настройки
STR_INTRO_NEWGRF_SETTINGS :{BLACK}Настройки NewGRF
STR_INTRO_ONLINE_CONTENT :{BLACK}Проверить онлайн-контент
@ -2296,6 +2298,7 @@ STR_INTRO_TOOLTIP_TOYLAND_LANDSCAPE :{BLACK}Выбр
STR_INTRO_TOOLTIP_GAME_OPTIONS :{BLACK}Изменить основные настройки игры
STR_INTRO_TOOLTIP_HIGHSCORE :{BLACK}Показать таблицу рекордов
STR_INTRO_TOOLTIP_HELP :{BLACK}Ссылки на документацию и интернет-ресурсы
STR_INTRO_TOOLTIP_CONFIG_SETTINGS_TREE :{BLACK}Изменить расширенные настройки игры
STR_INTRO_TOOLTIP_NEWGRF_SETTINGS :{BLACK}Показать настройки NewGRF
STR_INTRO_TOOLTIP_ONLINE_CONTENT :{BLACK}Найти новый и обновлённый контент для загрузки
@ -2318,6 +2321,17 @@ STR_ABANDON_GAME_QUERY :{YELLOW}Вы д
STR_ABANDON_SCENARIO_QUERY :{YELLOW}Вы уверены, что хотите выйти из этого сценария?
# 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
STR_CHEATS :{WHITE}Читы
@ -4888,6 +4902,11 @@ STR_AI_SETTINGS_SETTING :{STRING}: {ORAN
# 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_TOOLTIP :{BLACK}Переносить текст так, чтобы он помещался в окне без необходимости прокрутки
STR_TEXTFILE_VIEW_README :{BLACK}Посмотреть инструкцию
@ -4898,6 +4917,7 @@ STR_TEXTFILE_README_CAPTION :{WHITE}Инст
STR_TEXTFILE_CHANGELOG_CAPTION :{WHITE}Список изменений к {STRING} {STRING}
STR_TEXTFILE_LICENCE_CAPTION :{WHITE}Лицензия к {STRING} {STRING}
STR_TEXTFILE_SURVEY_RESULT_CAPTION :{WHITE}Собранные данные
STR_TEXTFILE_GAME_MANUAL_CAPTION :{WHITE}Документ OpenTTD «{STRING}»
# Vehicle loading indicators

View File

@ -12,26 +12,9 @@
#include "settings_table.h"
#include "network.h"
#include "../debug.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"
#include "../3rdparty/nlohmann/json.hpp"
#include "../survey.h"
#include "../3rdparty/fmt/chrono.h"
#include "../3rdparty/fmt/std.h"
#include "../safeguards.h"
@ -46,294 +29,6 @@ NLOHMANN_JSON_SERIALIZE_ENUM(NetworkSurveyHandler::Reason, {
{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.
*
@ -348,6 +43,7 @@ std::string NetworkSurveyHandler::CreatePayload(Reason reason, bool for_preview)
survey["schema"] = NETWORK_SURVEY_VERSION;
survey["reason"] = reason;
survey["id"] = _savegame_id;
survey["date"] = fmt::format("{:%Y-%m-%d %H:%M:%S} (UTC)", fmt::gmtime(time(nullptr)));
#ifdef SURVEY_KEY
/* 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"]);
SurveyConfiguration(info["configuration"]);
SurveyFont(info["font"]);
SurveyCompiler(info["compiler"]);
SurveyLibraries(info["libraries"]);
}
{
auto &game = survey["game"];
SurveyTimers(game["timers"]);
SurveyCompanies(game["companies"]);
SurveySettings(game["settings"]);
SurveySettings(game["settings"], false);
SurveyGrfs(game["grfs"]);
SurveyGameScript(game["game_script"]);
}

View File

@ -49,53 +49,24 @@ class CrashLogOSX : public CrashLog {
/** Signal that has been thrown. */
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;
GetMacOSVersion(&ver_maj, &ver_min, &ver_bug);
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
);
survey["id"] = signum;
survey["reason"] = strsignal(signum);
}
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];
int trace_size = backtrace(trace, lengthof(trace));
survey = nlohmann::json::array();
char **messages = backtrace_symbols(trace, trace_size);
for (int i = 0; i < trace_size; i++) {
fmt::format_to(output_iterator, "{}\n", messages[i]);
survey.push_back(messages[i]);
}
free(messages);
fmt::format_to(output_iterator, "\n");
}
#ifdef WITH_UNOFFICIAL_BREAKPAD
@ -153,7 +124,7 @@ public:
"A serious fault condition occurred in the game. The game will shut down.";
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"
"https://github.com/OpenTTD/OpenTTD/issues.\n\n"
"{}\n{}\n{}\n{}",

View File

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

View File

@ -49,55 +49,26 @@ class CrashLogUnix : public CrashLog {
/** Signal that has been thrown. */
int signum;
void LogOSVersion(std::back_insert_iterator<std::string> &output_iterator) const override
void SurveyCrash(nlohmann::json &survey) const override
{
struct utsname name;
if (uname(&name) < 0) {
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
);
survey["id"] = signum;
survey["reason"] = strsignal(signum);
}
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__)
void *trace[64];
int trace_size = backtrace(trace, lengthof(trace));
survey = nlohmann::json::array();
char **messages = backtrace_symbols(trace, trace_size);
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);
#else
fmt::format_to(output_iterator, " Not supported.\n");
#endif
fmt::format_to(output_iterator, "\n");
}
#ifdef WITH_UNOFFICIAL_BREAKPAD

View File

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

View File

@ -38,6 +38,33 @@
/** Exception code used for custom abort. */
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.
*
@ -57,9 +84,17 @@ class CrashLogWindows : public CrashLog {
/** Information about the encountered exception */
EXCEPTION_POINTERS *ep;
void LogOSVersion(std::back_insert_iterator<std::string> &output_iterator) 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;
void SurveyCrash(nlohmann::json &survey) 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:
#ifdef WITH_UNOFFICIAL_BREAKPAD
@ -136,41 +171,11 @@ public:
/* 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)
static const uint MAX_SYMBOL_LEN = 512;
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");
struct ProcPtrs {
@ -195,7 +200,7 @@ static const uint MAX_FRAMES = 64;
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. */
if (dbghelp.Success()) {
@ -246,7 +251,7 @@ static const uint MAX_FRAMES = 64;
hCur, GetCurrentThread(), &frame, &ctx, nullptr, proc.pSymFunctionTableAccess64, proc.pSymGetModuleBase64, nullptr)) break;
if (frame.AddrPC.Offset == frame.AddrReturn.Offset) {
fmt::format_to(output_iterator, " <infinite loop>\n");
survey.push_back("<infinite loop>");
break;
}
@ -260,33 +265,31 @@ static const uint MAX_FRAMES = 64;
}
/* 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. */
DWORD64 offset;
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;
IMAGEHLP_LINE64 line;
line.SizeOfStruct = sizeof(IMAGEHLP_LINE64);
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);
}
fmt::format_to(output_iterator, "\n");
}
#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");
fmt::format_to(output_iterator, " Not supported.\n");
/* Not supported. */
}
#endif /* _MSC_VER */
@ -430,7 +433,7 @@ static bool _expanded;
static const wchar_t _crash_desc[] =
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"https://github.com/OpenTTD/OpenTTD/issues\n\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) {
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. */
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_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);
/* 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;
char32_t c;
while ((c = Utf8Consume(&crashlog_unix_nl))) {

View File

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

View File

@ -716,6 +716,12 @@ bool IntSettingDesc::IsSameValue(const IniItem *item, void *object) const
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
{
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;
}
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
{
/* Checking for equality is way more expensive than just writing the value. */
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
* 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).
*/
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. */
@ -215,6 +223,7 @@ struct IntSettingDesc : SettingDesc {
std::string FormatValue(const void *object) const override;
void ParseValue(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;
private:
@ -307,6 +316,7 @@ struct StringSettingDesc : SettingDesc {
std::string FormatValue(const void *object) const override;
void ParseValue(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;
private:
@ -324,6 +334,7 @@ struct ListSettingDesc : SettingDesc {
std::string FormatValue(const void *object) const override;
void ParseValue(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. */
@ -334,6 +345,7 @@ struct NullSettingDesc : SettingDesc {
std::string FormatValue(const 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 IsDefaultValue(void *object) const override { NOT_REACHED(); }
};
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 */