Merge remote-tracking branch 'upstream/master'

This commit is contained in:
dP
2025-09-28 02:33:49 +05:00
926 changed files with 37902 additions and 27369 deletions

View File

@@ -28,6 +28,7 @@
#include "saveload/saveload.h"
#include "company_cmd.h"
#include "company_func.h"
#include "company_gui.h"
#include "command_func.h"
#include "news_func.h"
#include "fios.h"
@@ -80,6 +81,7 @@
#include "timer/timer_game_realtime.h"
#include "timer/timer_game_tick.h"
#include "social_integration.h"
#include "core/string_consumer.hpp"
#include "linkgraph/linkgraphschedule.h"
@@ -110,7 +112,7 @@ bool HandleBootstrap();
extern void CheckCaches();
extern Company *DoStartupNewCompany(bool is_ai, CompanyID company = CompanyID::Invalid());
extern void OSOpenBrowser(const std::string &url);
extern void ShowOSErrorBox(const char *buf, bool system);
extern void ShowOSErrorBox(std::string_view buf, bool system);
extern std::string _config_file;
bool _save_config = false;
@@ -124,7 +126,7 @@ NewGRFScanCallback *_request_newgrf_scan_callback = nullptr;
*/
void UserErrorI(const std::string &str)
{
ShowOSErrorBox(str.c_str(), false);
ShowOSErrorBox(str, false);
if (VideoDriver::GetInstance() != nullptr) VideoDriver::GetInstance()->Stop();
#ifdef __EMSCRIPTEN__
@@ -146,7 +148,7 @@ void UserErrorI(const std::string &str)
void FatalErrorI(const std::string &str)
{
if (VideoDriver::GetInstance() == nullptr || VideoDriver::GetInstance()->HasGUI()) {
ShowOSErrorBox(str.c_str(), true);
ShowOSErrorBox(str, true);
}
/* Set the error message for the crash log and then invoke it. */
@@ -246,19 +248,19 @@ static void WriteSavegameInfo(const std::string &name)
std::string message;
message.reserve(1024);
fmt::format_to(std::back_inserter(message), "Name: {}\n", name);
fmt::format_to(std::back_inserter(message), "Savegame ver: {}\n", _sl_version);
fmt::format_to(std::back_inserter(message), "NewGRF ver: 0x{:08X}\n", last_ottd_rev);
fmt::format_to(std::back_inserter(message), "Modified: {}\n", ever_modified);
format_append(message, "Name: {}\n", name);
format_append(message, "Savegame ver: {}\n", _sl_version);
format_append(message, "NewGRF ver: 0x{:08X}\n", last_ottd_rev);
format_append(message, "Modified: {}\n", ever_modified);
if (removed_newgrfs) {
fmt::format_to(std::back_inserter(message), "NewGRFs have been removed\n");
format_append(message, "NewGRFs have been removed\n");
}
message += "NewGRFs:\n";
if (_load_check_data.HasNewGrfs()) {
for (const auto &c : _load_check_data.grfconfig) {
fmt::format_to(std::back_inserter(message), "{:08X} {} {}\n", std::byteswap(c->ident.grfid),
format_append(message, "{:08X} {} {}\n", std::byteswap(c->ident.grfid),
FormatArrayAsHex(c->flags.Test(GRFConfigFlag::Compatible) ? c->original_md5sum : c->ident.md5sum), c->filename);
}
}
@@ -279,16 +281,19 @@ static void WriteSavegameInfo(const std::string &name)
* @param res variable to store the resolution in.
* @param s the string to decompose.
*/
static void ParseResolution(Dimension *res, const char *s)
static void ParseResolution(Dimension &res, std::string_view s)
{
const char *t = strchr(s, 'x');
if (t == nullptr) {
StringConsumer consumer(s);
auto width = consumer.TryReadIntegerBase<uint>(10);
auto valid = consumer.ReadIf("x");
auto height = consumer.TryReadIntegerBase<uint>(10);
if (!width.has_value() || !valid || !height.has_value() || consumer.AnyBytesLeft()) {
ShowInfo("Invalid resolution '{}'", s);
return;
}
res->width = std::max(std::strtoul(s, nullptr, 0), 64UL);
res->height = std::max(std::strtoul(t + 1, nullptr, 0), 64UL);
res.width = std::max<uint>(*width, 64);
res.height = std::max<uint>(*height, 64);
}
@@ -320,7 +325,7 @@ static void ShutdownGame()
/* No NewGRFs were loaded when it was still bootstrapping. */
if (_game_mode != GM_BOOTSTRAP) ResetNewGRFData();
UninitFontCache();
FontCache::UninitializeFontCaches();
}
/**
@@ -505,7 +510,7 @@ static std::vector<OptionData> CreateOptions()
* @param arguments The command line arguments passed to the application.
* @return 0 when there is no error.
*/
int openttd_main(std::span<char * const> arguments)
int openttd_main(std::span<std::string_view> arguments)
{
_game_session_stats.start_time = std::chrono::steady_clock::now();
_game_session_stats.savegame_size = std::nullopt;
@@ -531,7 +536,6 @@ int openttd_main(std::span<char * const> arguments)
auto options = CreateOptions();
GetOptData mgo(arguments.subspan(1), options);
int ret = 0;
int i;
while ((i = mgo.GetOpt()) != -1) {
@@ -550,7 +554,7 @@ int openttd_main(std::span<char * const> arguments)
blitter = "null";
dedicated = true;
SetDebugString("net=4", ShowInfoI);
if (mgo.opt != nullptr) {
if (!mgo.opt.empty()) {
scanner->dedicated_host = ParseFullConnectionString(mgo.opt, scanner->dedicated_port);
}
break;
@@ -561,13 +565,19 @@ int openttd_main(std::span<char * const> arguments)
case 'p':
scanner->join_server_password = mgo.opt;
break;
case 'r': ParseResolution(&resolution, mgo.opt); break;
case 't': scanner->startyear = TimerGameCalendar::Year(atoi(mgo.opt)); break;
case 'r': ParseResolution(resolution, mgo.opt); break;
case 't':
if (auto value = ParseInteger(mgo.opt); value.has_value()) {
scanner->startyear = TimerGameCalendar::Year(*value);
} else {
fmt::print(stderr, "Invalid start year: {}\n", mgo.opt);
}
break;
case 'd': {
#if defined(_WIN32)
CreateConsole();
#endif
if (mgo.opt != nullptr) SetDebugString(mgo.opt, ShowInfoI);
if (!mgo.opt.empty()) SetDebugString(mgo.opt, ShowInfoI);
break;
}
case 'e':
@@ -580,10 +590,10 @@ int openttd_main(std::span<char * const> arguments)
}
break;
case 'g':
if (mgo.opt != nullptr) {
if (!mgo.opt.empty()) {
_file_to_saveload.name = mgo.opt;
std::string extension = FS2OTTD(std::filesystem::path(OTTD2FS(_file_to_saveload.name)).extension());
std::string extension = FS2OTTD(std::filesystem::path(OTTD2FS(_file_to_saveload.name)).extension().native());
auto [ft, _] = FiosGetSavegameListCallback(SLO_LOAD, _file_to_saveload.name, extension);
if (ft == FIOS_TYPE_INVALID) {
std::tie(ft, _) = FiosGetScenarioListCallback(SLO_LOAD, _file_to_saveload.name, extension);
@@ -593,14 +603,14 @@ int openttd_main(std::span<char * const> arguments)
}
/* Allow for '-e' before or after '-g'. */
switch (GetAbstractFileType(ft)) {
switch (ft.abstract) {
case FT_SAVEGAME: _switch_mode = (_switch_mode == SM_EDITOR ? SM_LOAD_SCENARIO : SM_LOAD_GAME); break;
case FT_SCENARIO: _switch_mode = (_switch_mode == SM_EDITOR ? SM_LOAD_SCENARIO : SM_LOAD_GAME); break;
case FT_HEIGHTMAP: _switch_mode = (_switch_mode == SM_EDITOR ? SM_LOAD_HEIGHTMAP : SM_START_HEIGHTMAP); break;
default: break;
}
_file_to_saveload.SetMode(SLO_LOAD, GetAbstractFileType(ft), GetDetailedFileType(ft));
_file_to_saveload.SetMode(ft, SLO_LOAD);
break;
}
@@ -612,12 +622,11 @@ int openttd_main(std::span<char * const> arguments)
break;
case 'q': {
DeterminePaths(arguments[0], only_local_path);
if (StrEmpty(mgo.opt)) {
ret = 1;
return ret;
if (mgo.opt.empty()) {
return 1;
}
std::string extension = FS2OTTD(std::filesystem::path(OTTD2FS(mgo.opt)).extension());
std::string extension = FS2OTTD(std::filesystem::path(OTTD2FS(mgo.opt)).extension().native());
auto [_, title] = FiosGetSavegameListCallback(SLO_LOAD, mgo.opt, extension);
_load_check_data.Clear();
@@ -628,26 +637,31 @@ int openttd_main(std::span<char * const> arguments)
InitializeLanguagePacks(); // A language pack is needed for GetString()
fmt::print(stderr, "{}\n", GetString(_load_check_data.error, _load_check_data.error_msg));
}
return ret;
return 1;
}
WriteSavegameInfo(title);
return ret;
return 0;
}
case 'Q': {
extern int _skip_all_newgrf_scanning;
_skip_all_newgrf_scanning += 1;
break;
}
case 'G': scanner->generation_seed = std::strtoul(mgo.opt, nullptr, 10); break;
case 'G':
if (auto value = ParseInteger(mgo.opt); value.has_value()) {
scanner->generation_seed = *value;
} else {
fmt::print(stderr, "Invalid generation seed: {}\n", mgo.opt);
}
break;
case 'c': _config_file = mgo.opt; break;
case 'x': scanner->save_config = false; break;
case 'X': only_local_path = true; break;
case 'C': {
DeterminePaths(arguments[0], true);
if (StrEmpty(mgo.opt)) {
ret = 1;
return ret;
if (mgo.opt.empty()) {
return 1;
}
citymania::load_replay_commands(mgo.opt, [](auto error) {
fmt::print(stderr, "{}\n", error.c_str());
@@ -655,17 +669,15 @@ int openttd_main(std::span<char * const> arguments)
break;
}
case 'T': {
citymania::SetReplaySaveInterval(strtol(mgo.opt, nullptr, 10));
citymania::SetReplaySaveInterval(ParseInteger(mgo.opt).value_or(0));
break;
}
case 'h':
i = -2; // Force printing of help.
break;
case 'h': break; // handled below
}
if (i == -2) break;
if (i == 'h' || i == -2) break;
}
if (i == -2 || !mgo.arguments.empty()) {
if (i == 'h' || i == -2 || !mgo.arguments.empty()) {
/* Either the user typed '-h', they made an error, or they added unrecognized command line arguments.
* In all cases, print the help, and exit.
*
@@ -677,7 +689,10 @@ int openttd_main(std::span<char * const> arguments)
BaseSounds::FindSets();
BaseMusic::FindSets();
ShowHelp();
return ret;
/* Return zero when asked to print help, and 1 for all other cases. */
if (i == 'h') return 0;
return 1;
}
DeterminePaths(arguments[0], only_local_path);
@@ -712,7 +727,7 @@ int openttd_main(std::span<char * const> arguments)
InitializeLanguagePacks();
/* Initialize the font cache */
InitFontCache(false);
FontCache::LoadFontCaches(FONTSIZES_REQUIRED);
/* This must be done early, since functions use the SetWindowDirty* calls */
InitWindowSystem();
@@ -772,7 +787,7 @@ int openttd_main(std::span<char * const> arguments)
InitializeSpriteSorter();
/* Initialize the zoom level of the screen to normal */
_screen.zoom = ZOOM_LVL_MIN;
_screen.zoom = ZoomLevel::Min;
/* The video driver is now selected, now initialise GUI zoom */
UpdateGUIZoom();
@@ -782,7 +797,7 @@ int openttd_main(std::span<char * const> arguments)
if (!HandleBootstrap()) {
ShutdownGame();
return ret;
return 0;
}
VideoDriver::GetInstance()->ClaimMousePointer();
@@ -824,7 +839,7 @@ int openttd_main(std::span<char * const> arguments)
VideoDriver::GetInstance()->MainLoop();
PostMainLoop();
return ret;
return 0;
}
void HandleExitGameRequest()
@@ -917,7 +932,7 @@ static void MakeNewGame(bool from_heightmap, bool reset_settings)
_game_mode = GM_NORMAL;
if (!from_heightmap) {
/* "reload" command needs to know what mode we were in. */
_file_to_saveload.SetMode(SLO_INVALID, FT_INVALID, DFT_INVALID);
_file_to_saveload.SetMode(FIOS_TYPE_INVALID, SLO_INVALID);
}
ResetGRFConfig(true);
@@ -936,7 +951,7 @@ static void MakeNewEditorWorld()
{
_game_mode = GM_EDITOR;
/* "reload" command needs to know what mode we were in. */
_file_to_saveload.SetMode(SLO_INVALID, FT_INVALID, DFT_INVALID);
_file_to_saveload.SetMode(FIOS_TYPE_INVALID, SLO_INVALID);
ResetGRFConfig(true);
@@ -1088,12 +1103,12 @@ void SwitchToMode(SwitchMode new_mode)
break;
case SM_RELOADGAME: // Reload with what-ever started the game
if (_file_to_saveload.abstract_ftype == FT_SAVEGAME || _file_to_saveload.abstract_ftype == FT_SCENARIO) {
if (_file_to_saveload.ftype.abstract == FT_SAVEGAME || _file_to_saveload.ftype.abstract == FT_SCENARIO) {
/* Reload current savegame/scenario */
_switch_mode = _game_mode == GM_EDITOR ? SM_LOAD_SCENARIO : SM_LOAD_GAME;
SwitchToMode(_switch_mode);
break;
} else if (_file_to_saveload.abstract_ftype == FT_HEIGHTMAP) {
} else if (_file_to_saveload.ftype.abstract == FT_HEIGHTMAP) {
/* Restart current heightmap */
_switch_mode = _game_mode == GM_EDITOR ? SM_LOAD_HEIGHTMAP : SM_RESTART_HEIGHTMAP;
SwitchToMode(_switch_mode);
@@ -1118,10 +1133,10 @@ void SwitchToMode(SwitchMode new_mode)
ResetGRFConfig(true);
ResetWindowSystem();
if (!SafeLoad(_file_to_saveload.name, _file_to_saveload.file_op, _file_to_saveload.detail_ftype, GM_NORMAL, NO_DIRECTORY)) {
if (!SafeLoad(_file_to_saveload.name, _file_to_saveload.file_op, _file_to_saveload.ftype.detailed, GM_NORMAL, NO_DIRECTORY)) {
ShowErrorMessage(GetSaveLoadErrorType(), GetSaveLoadErrorMessage(), WL_CRITICAL);
} else {
if (_file_to_saveload.abstract_ftype == FT_SCENARIO) {
if (_file_to_saveload.ftype.abstract == FT_SCENARIO) {
OnStartScenario();
}
OnStartGame(_network_dedicated);
@@ -1154,7 +1169,7 @@ void SwitchToMode(SwitchMode new_mode)
break;
case SM_LOAD_SCENARIO: { // Load scenario from scenario editor
if (SafeLoad(_file_to_saveload.name, _file_to_saveload.file_op, _file_to_saveload.detail_ftype, GM_EDITOR, NO_DIRECTORY)) {
if (SafeLoad(_file_to_saveload.name, _file_to_saveload.file_op, _file_to_saveload.ftype.detailed, GM_EDITOR, NO_DIRECTORY)) {
SetLocalCompany(OWNER_NONE);
GenerateSavegameId();
_settings_newgame.game_creation.starting_year = TimerGameCalendar::year;
@@ -1203,7 +1218,7 @@ void SwitchToMode(SwitchMode new_mode)
break;
case SM_SAVE_HEIGHTMAP: // Save heightmap.
MakeHeightmapScreenshot(_file_to_saveload.name.c_str());
MakeHeightmapScreenshot(_file_to_saveload.name);
CloseWindowById(WC_SAVELOAD, 0);
break;
@@ -1251,8 +1266,6 @@ void StateGameLoop()
PerformanceMeasurer framerate(PFE_GAMELOOP);
PerformanceAccumulator::Reset(PFE_GL_LANDSCAPE);
Layouter::ReduceLineCache();
if (_game_mode == GM_EDITOR) {
BasePersistentStorageArray::SwitchMode(PSM_ENTER_GAMELOOP);
RunTileLoop();