2 * This file is part of OpenTTD.
3 * 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.
4 * 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.
5 * 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/>.
8 /** @file survey.cpp Functions to survey the current game / system, for crashlog and network-survey. */
14 #include "settings_table.h"
15 #include "network/network.h"
17 #include "settings_type.h"
18 #include "timer/timer_game_tick.h"
19 #include "timer/timer_game_calendar.h"
20 #include "timer/timer_game_economy.h"
23 #include "fontcache.h"
26 #include "ai/ai_info.hpp"
27 #include "game/game.hpp"
28 #include "game/game_info.hpp"
30 #include "music/music_driver.hpp"
31 #include "sound/sound_driver.hpp"
32 #include "video/video_driver.hpp"
34 #include "base_media_base.h"
35 #include "blitter/factory.hpp"
37 #include "social_integration.h"
41 #endif /* WITH_ALLEGRO */
42 #ifdef WITH_FONTCONFIG
43 # include <fontconfig/fontconfig.h>
44 #endif /* WITH_FONTCONFIG */
46 /* pngconf.h, included by png.h doesn't like something in the
47 * freetype headers. As such it's not alphabetically sorted. */
51 # include <ft2build.h>
52 # include FT_FREETYPE_H
53 #endif /* WITH_FREETYPE */
56 #endif /* WITH_HARFBUZZ */
58 # include <unicode/uversion.h>
59 #endif /* WITH_ICU_I18N */
64 #include <lzo/lzo1x.h>
66 #if defined(WITH_SDL) || defined(WITH_SDL2)
68 #endif /* WITH_SDL || WITH_SDL2 */
73 # include <curl/curl.h>
76 #include "safeguards.h"
78 NLOHMANN_JSON_SERIALIZE_ENUM(GRFStatus
, {
79 {GRFStatus::GCS_UNKNOWN
, "unknown"},
80 {GRFStatus::GCS_DISABLED
, "disabled"},
81 {GRFStatus::GCS_NOT_FOUND
, "not found"},
82 {GRFStatus::GCS_INITIALISED
, "initialised"},
83 {GRFStatus::GCS_ACTIVATED
, "activated"},
86 NLOHMANN_JSON_SERIALIZE_ENUM(SocialIntegrationPlugin::State
, {
87 {SocialIntegrationPlugin::State::RUNNING
, "running"},
88 {SocialIntegrationPlugin::State::FAILED
, "failed"},
89 {SocialIntegrationPlugin::State::PLATFORM_NOT_RUNNING
, "platform_not_running"},
90 {SocialIntegrationPlugin::State::UNLOADED
, "unloaded"},
91 {SocialIntegrationPlugin::State::DUPLICATE
, "duplicate"},
92 {SocialIntegrationPlugin::State::UNSUPPORTED_API
, "unsupported_api"},
93 {SocialIntegrationPlugin::State::INVALID_SIGNATURE
, "invalid_signature"},
97 /** Lookup table to convert a VehicleType to a string. */
98 static const std::string _vehicle_type_to_string
[] = {
106 * List of all the generic setting tables.
108 * There are a few tables that are special and not processed like the rest:
109 * - _currency_settings
111 * - _company_settings
113 * As such, they are not part of this list.
115 static auto &GenericSettingTables()
117 static const SettingTable _generic_setting_tables
[] = {
118 _difficulty_settings
,
124 _multimedia_settings
,
126 _news_display_settings
,
127 _pathfinding_settings
,
131 return _generic_setting_tables
;
135 * Convert a settings table to JSON.
137 * @param survey The JSON object.
138 * @param table The settings table to convert.
139 * @param object The object to get the settings from.
140 * @param skip_if_default If true, skip any settings that are on their default value.
142 static void SurveySettingsTable(nlohmann::json
&survey
, const SettingTable
&table
, void *object
, bool skip_if_default
)
144 for (auto &desc
: table
) {
145 const SettingDesc
*sd
= GetSettingDesc(desc
);
146 /* Skip any old settings we no longer save/load. */
147 if (!SlIsObjectCurrentlyValid(sd
->save
.version_from
, sd
->save
.version_to
)) continue;
149 auto name
= sd
->GetName();
150 if (skip_if_default
&& sd
->IsDefaultValue(object
)) continue;
151 survey
[name
] = sd
->FormatValue(object
);
156 * Convert settings to JSON.
158 * @param survey The JSON object.
160 void SurveySettings(nlohmann::json
&survey
, bool skip_if_default
)
162 SurveySettingsTable(survey
, _misc_settings
, nullptr, skip_if_default
);
163 #if defined(_WIN32) && !defined(DEDICATED)
164 SurveySettingsTable(survey
, _win32_settings
, nullptr, skip_if_default
);
166 for (auto &table
: GenericSettingTables()) {
167 SurveySettingsTable(survey
, table
, &_settings_game
, skip_if_default
);
169 SurveySettingsTable(survey
, _currency_settings
, &GetCustomCurrency(), skip_if_default
);
170 SurveySettingsTable(survey
, _company_settings
, &_settings_client
.company
, skip_if_default
);
174 * Convert compiler information to JSON.
176 * @param survey The JSON object.
178 void SurveyCompiler(nlohmann::json
&survey
)
180 #if defined(_MSC_VER)
181 survey
["name"] = "MSVC";
182 survey
["version"] = _MSC_VER
;
183 #elif defined(__ICC) && defined(__GNUC__)
184 survey
["name"] = "ICC";
185 survey
["version"] = __ICC
;
186 # if defined(__GNUC__)
187 survey
["extra"] = fmt::format("GCC {}.{}.{} mode", __GNUC__
, __GNUC_MINOR__
, __GNUC_PATCHLEVEL__
);
189 #elif defined(__GNUC__)
190 survey
["name"] = "GCC";
191 survey
["version"] = fmt::format("{}.{}.{}", __GNUC__
, __GNUC_MINOR__
, __GNUC_PATCHLEVEL__
);
193 survey
["name"] = "unknown";
196 #if defined(__VERSION__)
197 survey
["extra"] = __VERSION__
;
202 * Convert generic OpenTTD information to JSON.
204 * @param survey The JSON object.
206 void SurveyOpenTTD(nlohmann::json
&survey
)
208 survey
["version"]["revision"] = std::string(_openttd_revision
);
209 survey
["version"]["modified"] = _openttd_revision_modified
;
210 survey
["version"]["tagged"] = _openttd_revision_tagged
;
211 survey
["version"]["hash"] = std::string(_openttd_revision_hash
);
212 survey
["version"]["newgrf"] = fmt::format("{:X}", _openttd_newgrf_version
);
213 survey
["version"]["content"] = std::string(_openttd_content_version
);
214 survey
["build_date"] = std::string(_openttd_build_date
);
216 #ifdef POINTER_IS_64BIT
222 if constexpr (std::endian::native
== std::endian::little
) survey
["endian"] = "little";
223 if constexpr (std::endian::native
== std::endian::big
) survey
["endian"] = "big";
224 survey
["dedicated_build"] =
234 * Convert game session information to JSON.
236 * @param survey The JSON object.
238 void SurveyGameSession(nlohmann::json
&survey
)
240 survey
["id"] = _game_session_stats
.savegame_id
;
241 survey
["seconds"] = std::chrono::duration_cast
<std::chrono::seconds
>(std::chrono::steady_clock::now() - _game_session_stats
.start_time
).count();
242 if (_game_session_stats
.savegame_size
.has_value()) {
243 survey
["savegame_size"] = _game_session_stats
.savegame_size
.value();
248 * Convert generic game information to JSON.
250 * @param survey The JSON object.
252 void SurveyConfiguration(nlohmann::json
&survey
)
254 survey
["network"] = _networking
? (_network_server
? "server" : "client") : "no";
255 if (_current_language
!= nullptr) {
256 survey
["language"]["filename"] = FS2OTTD(_current_language
->file
.filename());
257 survey
["language"]["name"] = _current_language
->name
;
258 survey
["language"]["isocode"] = _current_language
->isocode
;
260 if (BlitterFactory::GetCurrentBlitter() != nullptr) {
261 survey
["blitter"] = BlitterFactory::GetCurrentBlitter()->GetName();
263 if (MusicDriver::GetInstance() != nullptr) {
264 survey
["music_driver"] = MusicDriver::GetInstance()->GetName();
266 if (SoundDriver::GetInstance() != nullptr) {
267 survey
["sound_driver"] = SoundDriver::GetInstance()->GetName();
269 if (VideoDriver::GetInstance() != nullptr) {
270 survey
["video_driver"] = VideoDriver::GetInstance()->GetName();
271 survey
["video_info"] = VideoDriver::GetInstance()->GetInfoString();
273 if (BaseGraphics::GetUsedSet() != nullptr) {
274 survey
["graphics_set"] = fmt::format("{}.{}", BaseGraphics::GetUsedSet()->name
, BaseGraphics::GetUsedSet()->version
);
275 const GRFConfig
*extra_cfg
= BaseGraphics::GetUsedSet()->GetExtraConfig();
276 if (extra_cfg
!= nullptr && extra_cfg
->num_params
> 0) {
277 survey
["graphics_set_parameters"] = std::span
<const uint32_t>(extra_cfg
->param
.data(), extra_cfg
->num_params
);
279 survey
["graphics_set_parameters"] = std::span
<const uint32_t>();
282 if (BaseMusic::GetUsedSet() != nullptr) {
283 survey
["music_set"] = fmt::format("{}.{}", BaseMusic::GetUsedSet()->name
, BaseMusic::GetUsedSet()->version
);
285 if (BaseSounds::GetUsedSet() != nullptr) {
286 survey
["sound_set"] = fmt::format("{}.{}", BaseSounds::GetUsedSet()->name
, BaseSounds::GetUsedSet()->version
);
291 * Convert font information to JSON.
293 * @param survey The JSON object.
295 void SurveyFont(nlohmann::json
&survey
)
297 survey
["small"] = FontCache::Get(FS_SMALL
)->GetFontName();
298 survey
["medium"] = FontCache::Get(FS_NORMAL
)->GetFontName();
299 survey
["large"] = FontCache::Get(FS_LARGE
)->GetFontName();
300 survey
["mono"] = FontCache::Get(FS_MONO
)->GetFontName();
304 * Convert company information to JSON.
306 * @param survey The JSON object.
308 void SurveyCompanies(nlohmann::json
&survey
)
310 for (const Company
*c
: Company::Iterate()) {
311 auto &company
= survey
[std::to_string(c
->index
)];
312 if (c
->ai_info
== nullptr) {
313 company
["type"] = "human";
315 company
["type"] = "ai";
316 company
["script"] = fmt::format("{}.{}", c
->ai_info
->GetName(), c
->ai_info
->GetVersion());
319 for (VehicleType type
= VEH_BEGIN
; type
< VEH_COMPANY_END
; type
++) {
320 uint amount
= c
->group_all
[type
].num_vehicle
;
321 company
["vehicles"][_vehicle_type_to_string
[type
]] = amount
;
324 company
["infrastructure"]["road"] = c
->infrastructure
.GetRoadTotal();
325 company
["infrastructure"]["tram"] = c
->infrastructure
.GetTramTotal();
326 company
["infrastructure"]["rail"] = c
->infrastructure
.GetRailTotal();
327 company
["infrastructure"]["signal"] = c
->infrastructure
.signal
;
328 company
["infrastructure"]["water"] = c
->infrastructure
.water
;
329 company
["infrastructure"]["station"] = c
->infrastructure
.station
;
330 company
["infrastructure"]["airport"] = c
->infrastructure
.airport
;
335 * Convert timer information to JSON.
337 * @param survey The JSON object.
339 void SurveyTimers(nlohmann::json
&survey
)
341 survey
["ticks"] = TimerGameTick::counter
;
343 TimerGameEconomy::YearMonthDay economy_ymd
= TimerGameEconomy::ConvertDateToYMD(TimerGameEconomy::date
);
344 survey
["economy"] = fmt::format("{:04}-{:02}-{:02} ({})", economy_ymd
.year
, economy_ymd
.month
+ 1, economy_ymd
.day
, TimerGameEconomy::date_fract
);
346 TimerGameCalendar::YearMonthDay ymd
= TimerGameCalendar::ConvertDateToYMD(TimerGameCalendar::date
);
347 survey
["calendar"] = fmt::format("{:04}-{:02}-{:02} ({})", ymd
.year
, ymd
.month
+ 1, ymd
.day
, TimerGameCalendar::date_fract
);
351 * Convert GRF information to JSON.
353 * @param survey The JSON object.
355 void SurveyGrfs(nlohmann::json
&survey
)
357 for (GRFConfig
*c
= _grfconfig
; c
!= nullptr; c
= c
->next
) {
358 auto grfid
= fmt::format("{:08x}", BSWAP32(c
->ident
.grfid
));
359 auto &grf
= survey
[grfid
];
361 grf
["md5sum"] = FormatArrayAsHex(c
->ident
.md5sum
);
362 grf
["status"] = c
->status
;
364 if ((c
->palette
& GRFP_GRF_MASK
) == GRFP_GRF_UNSET
) grf
["palette"] = "unset";
365 if ((c
->palette
& GRFP_GRF_MASK
) == GRFP_GRF_DOS
) grf
["palette"] = "dos";
366 if ((c
->palette
& GRFP_GRF_MASK
) == GRFP_GRF_WINDOWS
) grf
["palette"] = "windows";
367 if ((c
->palette
& GRFP_GRF_MASK
) == GRFP_GRF_ANY
) grf
["palette"] = "any";
369 if ((c
->palette
& GRFP_BLT_MASK
) == GRFP_BLT_UNSET
) grf
["blitter"] = "unset";
370 if ((c
->palette
& GRFP_BLT_MASK
) == GRFP_BLT_32BPP
) grf
["blitter"] = "32bpp";
372 grf
["is_static"] = HasBit(c
->flags
, GCF_STATIC
);
373 grf
["parameters"] = std::span
<const uint32_t>(c
->param
.data(), c
->num_params
);
378 * Convert game-script information to JSON.
380 * @param survey The JSON object.
382 void SurveyGameScript(nlohmann::json
&survey
)
384 if (Game::GetInfo() == nullptr) return;
386 survey
= fmt::format("{}.{}", Game::GetInfo()->GetName(), Game::GetInfo()->GetVersion());
390 * Convert compiled libraries information to JSON.
392 * @param survey The JSON object.
394 void SurveyLibraries(nlohmann::json
&survey
)
397 survey
["allegro"] = std::string(allegro_id
);
398 #endif /* WITH_ALLEGRO */
400 #ifdef WITH_FONTCONFIG
401 int version
= FcGetVersion();
402 survey
["fontconfig"] = fmt::format("{}.{}.{}", version
/ 10000, (version
/ 100) % 100, version
% 100);
403 #endif /* WITH_FONTCONFIG */
407 int major
, minor
, patch
;
408 FT_Init_FreeType(&library
);
409 FT_Library_Version(library
, &major
, &minor
, &patch
);
410 FT_Done_FreeType(library
);
411 survey
["freetype"] = fmt::format("{}.{}.{}", major
, minor
, patch
);
412 #endif /* WITH_FREETYPE */
414 #if defined(WITH_HARFBUZZ)
415 survey
["harfbuzz"] = hb_version_string();
416 #endif /* WITH_HARFBUZZ */
418 #if defined(WITH_ICU_I18N)
419 /* 4 times 0-255, separated by dots (.) and a trailing '\0' */
420 char buf
[4 * 3 + 3 + 1];
423 u_versionToString(ver
, buf
);
424 survey
["icu_i18n"] = buf
;
425 #endif /* WITH_ICU_I18N */
428 survey
["lzma"] = lzma_version_string();
432 survey
["lzo"] = lzo_version_string();
436 survey
["png"] = png_get_libpng_ver(nullptr);
437 #endif /* WITH_PNG */
440 const SDL_version
*sdl_v
= SDL_Linked_Version();
441 survey
["sdl"] = fmt::format("{}.{}.{}", sdl_v
->major
, sdl_v
->minor
, sdl_v
->patch
);
442 #elif defined(WITH_SDL2)
444 SDL_GetVersion(&sdl2_v
);
445 survey
["sdl2"] = fmt::format("{}.{}.{}", sdl2_v
.major
, sdl2_v
.minor
, sdl2_v
.patch
);
449 survey
["zlib"] = zlibVersion();
453 auto *curl_v
= curl_version_info(CURLVERSION_NOW
);
454 survey
["curl"] = curl_v
->version
;
455 survey
["curl_ssl"] = curl_v
->ssl_version
== nullptr ? "none" : curl_v
->ssl_version
;
460 * Convert plugin information to JSON.
462 * @param survey The JSON object.
464 void SurveyPlugins(nlohmann::json
&survey
)
466 auto _plugins
= SocialIntegration::GetPlugins();
468 for (auto &plugin
: _plugins
) {
469 auto &platform
= survey
[plugin
->social_platform
];
471 {"name", plugin
->name
},
472 {"version", plugin
->version
},
473 {"basepath", plugin
->basepath
},
474 {"state", plugin
->state
},
480 * Change the bytes of memory into a textual version rounded up to the biggest unit.
482 * For example, 16751108096 would become 16 GiB.
484 * @param memory The bytes of memory.
485 * @return std::string A textual representation.
487 std::string
SurveyMemoryToText(uint64_t memory
)
489 memory
= memory
/ 1024; // KiB
490 memory
= CeilDiv(memory
, 1024); // MiB
492 /* Anything above 512 MiB we represent in GiB. */
494 return fmt::format("{} GiB", CeilDiv(memory
, 1024));
497 /* Anything above 64 MiB we represent in a multiplier of 128 MiB. */
499 return fmt::format("{} MiB", Ceil(memory
, 128));
502 /* Anything else in a multiplier of 4 MiB. */
503 return fmt::format("{} MiB", Ceil(memory
, 4));