Changeset - r25666:da59d4ce1d64
[Not reviewed]
master
0 9 0
rubidium42 - 3 years ago 2021-06-12 19:33:01
rubidium@openttd.org
Codechange: replace IConsolePrintF with IConsolePrint and fmt formatting

Also make some strings more consistent with the rest of the console strings.
9 files changed with 97 insertions and 120 deletions:
0 comments (0 inline, 0 general)
src/console.cpp
Show inline comments
 
@@ -38,57 +38,57 @@ static const uint ICON_MAX_RECURSE = 10;
 

	
 
FILE *_iconsole_output_file;
 

	
 
void IConsoleInit()
 
{
 
	_iconsole_output_file = nullptr;
 
	_redirect_console_to_client = INVALID_CLIENT_ID;
 
	_redirect_console_to_admin  = INVALID_ADMIN_ID;
 

	
 
	IConsoleGUIInit();
 

	
 
	IConsoleStdLibRegister();
 
}
 

	
 
static void IConsoleWriteToLogFile(const char *string)
 
{
 
	if (_iconsole_output_file != nullptr) {
 
		/* if there is an console output file ... also print it there */
 
		const char *header = GetLogPrefix();
 
		if ((strlen(header) != 0 && fwrite(header, strlen(header), 1, _iconsole_output_file) != 1) ||
 
				fwrite(string, strlen(string), 1, _iconsole_output_file) != 1 ||
 
				fwrite("\n", 1, 1, _iconsole_output_file) != 1) {
 
			fclose(_iconsole_output_file);
 
			_iconsole_output_file = nullptr;
 
			IConsolePrintF(CC_DEFAULT, "cannot write to log file");
 
			IConsolePrint(CC_ERROR, "Cannot write to console log file; closing the log file.");
 
		}
 
	}
 
}
 

	
 
bool CloseConsoleLogIfActive()
 
{
 
	if (_iconsole_output_file != nullptr) {
 
		IConsolePrintF(CC_DEFAULT, "file output complete");
 
		IConsolePrint(CC_INFO, "Console log file closed.");
 
		fclose(_iconsole_output_file);
 
		_iconsole_output_file = nullptr;
 
		return true;
 
	}
 

	
 
	return false;
 
}
 

	
 
void IConsoleFree()
 
{
 
	IConsoleGUIFree();
 
	CloseConsoleLogIfActive();
 
}
 

	
 
/**
 
 * Handle the printing of text entered into the console or redirected there
 
 * by any other means. Text can be redirected to other clients in a network game
 
 * as well as to a logfile. If the network server is a dedicated server, all activities
 
 * are also logged. All lines to print are added to a temporary buffer which can be
 
 * used as a history to print them onscreen
 
 * @param colour_code The colour of the command.
 
 * @param string The message to output on the console (notice, error, etc.)
 
 */
 
void IConsolePrint(TextColour colour_code, const std::string &string)
 
@@ -105,67 +105,48 @@ void IConsolePrint(TextColour colour_cod
 
		NetworkServerSendAdminRcon(_redirect_console_to_admin, colour_code, string);
 
		return;
 
	}
 

	
 
	/* Create a copy of the string, strip if of colours and invalid
 
	 * characters and (when applicable) assign it to the console buffer */
 
	char *str = stredup(string.c_str());
 
	str_strip_colours(str);
 
	StrMakeValidInPlace(str);
 

	
 
	if (_network_dedicated) {
 
		NetworkAdminConsole("console", str);
 
		fprintf(stdout, "%s%s\n", GetLogPrefix(), str);
 
		fflush(stdout);
 
		IConsoleWriteToLogFile(str);
 
		free(str); // free duplicated string since it's not used anymore
 
		return;
 
	}
 

	
 
	IConsoleWriteToLogFile(str);
 
	IConsoleGUIPrint(colour_code, str);
 
}
 

	
 
/**
 
 * Handle the printing of text entered into the console or redirected there
 
 * by any other means. Uses printf() style format, for more information look
 
 * at IConsolePrint()
 
 */
 
void CDECL IConsolePrintF(TextColour colour_code, const char *format, ...)
 
{
 
	assert(IsValidConsoleColour(colour_code));
 

	
 
	va_list va;
 
	char buf[ICON_MAX_STREAMSIZE];
 

	
 
	va_start(va, format);
 
	vseprintf(buf, lastof(buf), format, va);
 
	va_end(va);
 

	
 
	IConsolePrint(colour_code, buf);
 
}
 

	
 
/**
 
 * Change a string into its number representation. Supports
 
 * decimal and hexadecimal numbers as well as 'on'/'off' 'true'/'false'
 
 * @param *value the variable a successful conversion will be put in
 
 * @param *arg the string to be converted
 
 * @return Return true on success or false on failure
 
 */
 
bool GetArgumentInteger(uint32 *value, const char *arg)
 
{
 
	char *endptr;
 

	
 
	if (strcmp(arg, "on") == 0 || strcmp(arg, "true") == 0) {
 
		*value = 1;
 
		return true;
 
	}
 
	if (strcmp(arg, "off") == 0 || strcmp(arg, "false") == 0) {
 
		*value = 0;
 
		return true;
 
	}
 

	
 
	*value = strtoul(arg, &endptr, 0);
 
	return arg != endptr;
 
}
 

	
 
/**
src/console_cmds.cpp
Show inline comments
 
@@ -170,49 +170,49 @@ DEF_CONSOLE_HOOK(ConHookServerOrNoNetwor
 
		if (echo) IConsolePrint(CC_ERROR, "This command is only available to a network server.");
 
		return CHR_DISALLOW;
 
	}
 
	return CHR_ALLOW;
 
}
 

	
 
DEF_CONSOLE_HOOK(ConHookNewGRFDeveloperTool)
 
{
 
	if (_settings_client.gui.newgrf_developer_tools) {
 
		if (_game_mode == GM_MENU) {
 
			if (echo) IConsolePrint(CC_ERROR, "This command is only available in-game and in the editor.");
 
			return CHR_DISALLOW;
 
		}
 
		return ConHookNoNetwork(echo);
 
	}
 
	return CHR_HIDE;
 
}
 

	
 
/**
 
 * Show help for the console.
 
 * @param str String to print in the console.
 
 */
 
static void IConsoleHelp(const char *str)
 
{
 
	IConsolePrintF(CC_HELP, "- {}", str);
 
	IConsolePrint(CC_HELP, "- {}", str);
 
}
 

	
 
/**
 
 * Reset status of all engines.
 
 * @return Will always succeed.
 
 */
 
DEF_CONSOLE_CMD(ConResetEngines)
 
{
 
	if (argc == 0) {
 
		IConsoleHelp("Reset status data of all engines. This might solve some issues with 'lost' engines. Usage: 'resetengines'");
 
		return true;
 
	}
 

	
 
	StartupEngines();
 
	return true;
 
}
 

	
 
/**
 
 * Reset status of the engine pool.
 
 * @return Will always return true.
 
 * @note Resetting the pool only succeeds when there are no vehicles ingame.
 
 */
 
DEF_CONSOLE_CMD(ConResetEnginePool)
 
{
 
@@ -304,51 +304,51 @@ DEF_CONSOLE_CMD(ConScrollToTile)
 
			break;
 
		}
 
	}
 

	
 
	return false;
 
}
 

	
 
/**
 
 * Save the map to a file.
 
 * param filename the filename to save the map to.
 
 * @return True when help was displayed or the file attempted to be saved.
 
 */
 
DEF_CONSOLE_CMD(ConSave)
 
{
 
	if (argc == 0) {
 
		IConsoleHelp("Save the current game. Usage: 'save <filename>'");
 
		return true;
 
	}
 

	
 
	if (argc == 2) {
 
		char *filename = str_fmt("%s.sav", argv[1]);
 
		IConsolePrint(CC_DEFAULT, "Saving map...");
 

	
 
		if (SaveOrLoad(filename, SLO_SAVE, DFT_GAME_FILE, SAVE_DIR) != SL_OK) {
 
			IConsolePrint(CC_ERROR, "Saving map failed");
 
			IConsolePrint(CC_ERROR, "Saving map failed.");
 
		} else {
 
			IConsolePrintF(CC_DEFAULT, "Map successfully saved to %s", filename);
 
			IConsolePrint(CC_INFO, "Map successfully saved to '{}'.", filename);
 
		}
 
		free(filename);
 
		return true;
 
	}
 

	
 
	return false;
 
}
 

	
 
/**
 
 * Explicitly save the configuration.
 
 * @return True.
 
 */
 
DEF_CONSOLE_CMD(ConSaveConfig)
 
{
 
	if (argc == 0) {
 
		IConsoleHelp("Saves the configuration for new games to the configuration file, typically 'openttd.cfg'.");
 
		IConsoleHelp("It does not save the configuration of the current game to the configuration file.");
 
		return true;
 
	}
 

	
 
	SaveToConfig();
 
	IConsolePrint(CC_DEFAULT, "Saved config.");
 
	return true;
 
}
 
@@ -396,49 +396,49 @@ DEF_CONSOLE_CMD(ConRemove)
 
	const FiosItem *item = _console_file_list.FindItem(file);
 
	if (item != nullptr) {
 
		if (!FiosDelete(item->name)) {
 
			IConsolePrint(CC_ERROR, "Failed to delete '{}'.", file);
 
		}
 
	} else {
 
		IConsolePrint(CC_ERROR, "'{}' could not be found.", file);
 
	}
 

	
 
	_console_file_list.InvalidateFileList();
 
	return true;
 
}
 

	
 

	
 
/* List all the files in the current dir via console */
 
DEF_CONSOLE_CMD(ConListFiles)
 
{
 
	if (argc == 0) {
 
		IConsoleHelp("List all loadable savegames and directories in the current dir via console. Usage: 'ls | dir'");
 
		return true;
 
	}
 

	
 
	_console_file_list.ValidateFileList(true);
 
	for (uint i = 0; i < _console_file_list.size(); i++) {
 
		IConsolePrintF(CC_DEFAULT, "%d) %s", i, _console_file_list[i].title);
 
		IConsolePrint(CC_DEFAULT, "{}) {}", i, _console_file_list[i].title);
 
	}
 

	
 
	return true;
 
}
 

	
 
/* Change the dir via console */
 
DEF_CONSOLE_CMD(ConChangeDirectory)
 
{
 
	if (argc == 0) {
 
		IConsoleHelp("Change the dir via console. Usage: 'cd <directory | number>'");
 
		return true;
 
	}
 

	
 
	if (argc != 2) return false;
 

	
 
	const char *file = argv[1];
 
	_console_file_list.ValidateFileList(true);
 
	const FiosItem *item = _console_file_list.FindItem(file);
 
	if (item != nullptr) {
 
		switch (item->type) {
 
			case FIOS_TYPE_DIR: case FIOS_TYPE_DRIVE: case FIOS_TYPE_PARENT:
 
				FiosBrowseTo(item);
 
				break;
 
			default: IConsolePrint(CC_ERROR, "{}: Not a directory.", file);
 
@@ -502,49 +502,49 @@ static bool ConKickOrBan(const char *arg
 
			return true;
 
		}
 

	
 
		NetworkClientInfo *ci = NetworkClientInfo::GetByClientID(client_id);
 
		if (ci == nullptr) {
 
			IConsolePrint(CC_ERROR, "Invalid client ID.");
 
			return true;
 
		}
 

	
 
		if (!ban) {
 
			/* Kick only this client, not all clients with that IP */
 
			NetworkServerKickClient(client_id, reason);
 
			return true;
 
		}
 

	
 
		/* When banning, kick+ban all clients with that IP */
 
		n = NetworkServerKickOrBanIP(client_id, ban, reason);
 
	} else {
 
		n = NetworkServerKickOrBanIP(argv, ban, reason);
 
	}
 

	
 
	if (n == 0) {
 
		IConsolePrint(CC_DEFAULT, ban ? "Client not online, address added to banlist." : "Client not found.");
 
	} else {
 
		IConsolePrintF(CC_DEFAULT, "%sed %u client(s).", ban ? "Bann" : "Kick", n);
 
		IConsolePrint(CC_DEFAULT, "{}ed {} client(s).", ban ? "Bann" : "Kick", n);
 
	}
 

	
 
	return true;
 
}
 

	
 
DEF_CONSOLE_CMD(ConKick)
 
{
 
	if (argc == 0) {
 
		IConsoleHelp("Kick a client from a network game. Usage: 'kick <ip | client-id> [<kick-reason>]'");
 
		IConsoleHelp("For client-id's, see the command 'clients'");
 
		return true;
 
	}
 

	
 
	if (argc != 2 && argc != 3) return false;
 

	
 
	/* No reason supplied for kicking */
 
	if (argc == 2) return ConKickOrBan(argv[1], false, {});
 

	
 
	/* Reason for kicking supplied */
 
	size_t kick_message_length = strlen(argv[2]);
 
	if (kick_message_length >= 255) {
 
		IConsolePrint(CC_ERROR, "Maximum kick message length is 254 characters. You entered {} characters.", kick_message_length);
 
		return false;
 
	} else {
 
@@ -577,72 +577,70 @@ DEF_CONSOLE_CMD(ConBan)
 
}
 

	
 
DEF_CONSOLE_CMD(ConUnBan)
 
{
 
	if (argc == 0) {
 
		IConsoleHelp("Unban a client from a network game. Usage: 'unban <ip | banlist-index>'");
 
		IConsoleHelp("For a list of banned IP's, see the command 'banlist'");
 
		return true;
 
	}
 

	
 
	if (argc != 2) return false;
 

	
 
	/* Try by IP. */
 
	uint index;
 
	for (index = 0; index < _network_ban_list.size(); index++) {
 
		if (_network_ban_list[index] == argv[1]) break;
 
	}
 

	
 
	/* Try by index. */
 
	if (index >= _network_ban_list.size()) {
 
		index = atoi(argv[1]) - 1U; // let it wrap
 
	}
 

	
 
	if (index < _network_ban_list.size()) {
 
		char msg[64];
 
		seprintf(msg, lastof(msg), "Unbanned %s", _network_ban_list[index].c_str());
 
		IConsolePrint(CC_DEFAULT, msg);
 
		IConsolePrint(CC_DEFAULT, "Unbanned {}.", _network_ban_list[index]);
 
		_network_ban_list.erase(_network_ban_list.begin() + index);
 
	} else {
 
		IConsolePrint(CC_DEFAULT, "Invalid list index or IP not in ban-list.");
 
		IConsolePrint(CC_DEFAULT, "For a list of banned IP's, see the command 'banlist'");
 
	}
 

	
 
	return true;
 
}
 

	
 
DEF_CONSOLE_CMD(ConBanList)
 
{
 
	if (argc == 0) {
 
		IConsoleHelp("List the IP's of banned clients: Usage 'banlist'");
 
		return true;
 
	}
 

	
 
	IConsolePrint(CC_DEFAULT, "Banlist: ");
 

	
 
	uint i = 1;
 
	for (const auto &entry : _network_ban_list) {
 
		IConsolePrintF(CC_DEFAULT, "  %d) %s", i, entry.c_str());
 
		IConsolePrint(CC_DEFAULT, "  {}) {}", i, entry);
 
		i++;
 
	}
 

	
 
	return true;
 
}
 

	
 
DEF_CONSOLE_CMD(ConPauseGame)
 
{
 
	if (argc == 0) {
 
		IConsoleHelp("Pause a network game. Usage: 'pause'");
 
		return true;
 
	}
 

	
 
	if (_game_mode == GM_MENU) {
 
		IConsolePrint(CC_ERROR, "This command is only available in-game and in the editor.");
 
		return true;
 
	}
 

	
 
	if ((_pause_mode & PM_PAUSED_NORMAL) == PM_UNPAUSED) {
 
		DoCommandP(0, PM_PAUSED_NORMAL, 1, CMD_PAUSE);
 
		if (!_networking) IConsolePrint(CC_DEFAULT, "Game paused.");
 
	} else {
 
		IConsolePrint(CC_DEFAULT, "Game is already paused.");
 
	}
 
@@ -692,51 +690,51 @@ DEF_CONSOLE_CMD(ConRcon)
 
		NetworkClientSendRcon(argv[1], argv[2]);
 
	}
 
	return true;
 
}
 

	
 
DEF_CONSOLE_CMD(ConStatus)
 
{
 
	if (argc == 0) {
 
		IConsoleHelp("List the status of all clients connected to the server. Usage 'status'");
 
		return true;
 
	}
 

	
 
	NetworkServerShowStatusToConsole();
 
	return true;
 
}
 

	
 
DEF_CONSOLE_CMD(ConServerInfo)
 
{
 
	if (argc == 0) {
 
		IConsoleHelp("List current and maximum client/company limits. Usage 'server_info'");
 
		IConsoleHelp("You can change these values by modifying settings 'network.max_clients', 'network.max_companies' and 'network.max_spectators'");
 
		return true;
 
	}
 

	
 
	IConsolePrintF(CC_DEFAULT, "Current/maximum clients:    %2d/%2d", _network_game_info.clients_on, _settings_client.network.max_clients);
 
	IConsolePrintF(CC_DEFAULT, "Current/maximum companies:  %2d/%2d", (int)Company::GetNumItems(), _settings_client.network.max_companies);
 
	IConsolePrintF(CC_DEFAULT, "Current/maximum spectators: %2d/%2d", NetworkSpectatorCount(), _settings_client.network.max_spectators);
 
	IConsolePrint(CC_DEFAULT, "Current/maximum clients:    {:3d}/{:3d}", _network_game_info.clients_on, _settings_client.network.max_clients);
 
	IConsolePrint(CC_DEFAULT, "Current/maximum companies:  {:3d}/{:3d}", Company::GetNumItems(), _settings_client.network.max_companies);
 
	IConsolePrint(CC_DEFAULT, "Current/maximum spectators: {:3d}/{:3d}", NetworkSpectatorCount(), _settings_client.network.max_spectators);
 

	
 
	return true;
 
}
 

	
 
DEF_CONSOLE_CMD(ConClientNickChange)
 
{
 
	if (argc != 3) {
 
		IConsoleHelp("Change the nickname of a connected client. Usage: 'client_name <client-id> <new-name>'");
 
		IConsoleHelp("For client-id's, see the command 'clients'");
 
		return true;
 
	}
 

	
 
	ClientID client_id = (ClientID)atoi(argv[1]);
 

	
 
	if (client_id == CLIENT_ID_SERVER) {
 
		IConsolePrint(CC_ERROR, "Please use the command 'name' to change your own name!");
 
		return true;
 
	}
 

	
 
	if (NetworkClientInfo::GetByClientID(client_id) == nullptr) {
 
		IConsolePrint(CC_ERROR, "Invalid client ID.");
 
		return true;
 
	}
 

	
 
@@ -901,49 +899,49 @@ DEF_CONSOLE_CMD(ConNetworkReconnect)
 
	if (argc == 0) {
 
		IConsoleHelp("Reconnect to server to which you were connected last time. Usage: 'reconnect [<company>]'");
 
		IConsoleHelp("Company 255 is spectator (default, if not specified), 0 means creating new company.");
 
		IConsoleHelp("All others are a certain company with Company 1 being #1");
 
		return true;
 
	}
 

	
 
	CompanyID playas = (argc >= 2) ? (CompanyID)atoi(argv[1]) : COMPANY_SPECTATOR;
 
	switch (playas) {
 
		case 0: playas = COMPANY_NEW_COMPANY; break;
 
		case COMPANY_SPECTATOR: /* nothing to do */ break;
 
		default:
 
			/* From a user pov 0 is a new company, internally it's different and all
 
			 * companies are offset by one to ease up on users (eg companies 1-8 not 0-7) */
 
			if (playas < COMPANY_FIRST + 1 || playas > MAX_COMPANIES + 1) return false;
 
			break;
 
	}
 

	
 
	if (_settings_client.network.last_joined.empty()) {
 
		IConsolePrint(CC_DEFAULT, "No server for reconnecting.");
 
		return true;
 
	}
 

	
 
	/* Don't resolve the address first, just print it directly as it comes from the config file. */
 
	IConsolePrintF(CC_DEFAULT, "Reconnecting to %s ...", _settings_client.network.last_joined.c_str());
 
	IConsolePrint(CC_DEFAULT, "Reconnecting to {} ...", _settings_client.network.last_joined);
 

	
 
	return NetworkClientConnectGame(_settings_client.network.last_joined, playas);
 
}
 

	
 
DEF_CONSOLE_CMD(ConNetworkConnect)
 
{
 
	if (argc == 0) {
 
		IConsoleHelp("Connect to a remote OTTD server and join the game. Usage: 'connect <ip>'");
 
		IConsoleHelp("IP can contain port and company: 'IP[:Port][#Company]', eg: 'server.ottd.org:443#2'");
 
		IConsoleHelp("Company #255 is spectator all others are a certain company with Company 1 being #1");
 
		return true;
 
	}
 

	
 
	if (argc < 2) return false;
 

	
 
	return NetworkClientConnectGame(argv[1], COMPANY_NEW_COMPANY);
 
}
 

	
 
/*********************************
 
 *  script file console commands
 
 *********************************/
 

	
 
DEF_CONSOLE_CMD(ConExec)
 
{
 
@@ -1004,51 +1002,54 @@ DEF_CONSOLE_CMD(ConReturn)
 
	}
 

	
 
	_script_current_depth--;
 
	return true;
 
}
 

	
 
/*****************************
 
 *  default console commands
 
 ******************************/
 
extern bool CloseConsoleLogIfActive();
 

	
 
DEF_CONSOLE_CMD(ConScript)
 
{
 
	extern FILE *_iconsole_output_file;
 

	
 
	if (argc == 0) {
 
		IConsoleHelp("Start or stop logging console output to a file. Usage: 'script <filename>'");
 
		IConsoleHelp("If filename is omitted, a running log is stopped if it is active");
 
		return true;
 
	}
 

	
 
	if (!CloseConsoleLogIfActive()) {
 
		if (argc < 2) return false;
 

	
 
		IConsolePrintF(CC_DEFAULT, "file output started to: %s", argv[1]);
 
		_iconsole_output_file = fopen(argv[1], "ab");
 
		if (_iconsole_output_file == nullptr) IConsolePrint(CC_ERROR, "Could not open file '{}'.", argv[1]);
 
		if (_iconsole_output_file == nullptr) {
 
			IConsolePrint(CC_ERROR, "Could not open console log file '{}'.", argv[1]);
 
		} else {
 
			IConsolePrint(CC_INFO, "Console log output started to: '{}'", argv[1]);
 
		}
 
	}
 

	
 
	return true;
 
}
 

	
 

	
 
DEF_CONSOLE_CMD(ConEcho)
 
{
 
	if (argc == 0) {
 
		IConsoleHelp("Print back the first argument to the console. Usage: 'echo <arg>'");
 
		return true;
 
	}
 

	
 
	if (argc < 2) return false;
 
	IConsolePrint(CC_DEFAULT, argv[1]);
 
	return true;
 
}
 

	
 
DEF_CONSOLE_CMD(ConEchoC)
 
{
 
	if (argc == 0) {
 
		IConsoleHelp("Print back the first argument to the console in a given colour. Usage: 'echoc <colour> <arg2>'");
 
		return true;
 
	}
 
@@ -1096,49 +1097,49 @@ DEF_CONSOLE_CMD(ConReload)
 
		IConsoleHelp(" * if you started from a savegame / scenario / heightmap, that exact same savegame / scenario / heightmap will be loaded.");
 
		IConsoleHelp(" * if you started from a new game, this acts the same as 'restart'.");
 
		return true;
 
	}
 

	
 
	/* Don't copy the _newgame pointers to the real pointers, so call SwitchToMode directly */
 
	_settings_game.game_creation.map_x = MapLogX();
 
	_settings_game.game_creation.map_y = FindFirstBit(MapSizeY());
 
	_switch_mode = SM_RELOADGAME;
 
	return true;
 
}
 

	
 
/**
 
 * Print a text buffer line by line to the console. Lines are separated by '\n'.
 
 * @param buf The buffer to print.
 
 * @note All newlines are replace by '\0' characters.
 
 */
 
static void PrintLineByLine(char *buf)
 
{
 
	char *p = buf;
 
	/* Print output line by line */
 
	for (char *p2 = buf; *p2 != '\0'; p2++) {
 
		if (*p2 == '\n') {
 
			*p2 = '\0';
 
			IConsolePrintF(CC_DEFAULT, "%s", p);
 
			IConsolePrint(CC_DEFAULT, p);
 
			p = p2 + 1;
 
		}
 
	}
 
}
 

	
 
DEF_CONSOLE_CMD(ConListAILibs)
 
{
 
	char buf[4096];
 
	AI::GetConsoleLibraryList(buf, lastof(buf));
 

	
 
	PrintLineByLine(buf);
 

	
 
	return true;
 
}
 

	
 
DEF_CONSOLE_CMD(ConListAI)
 
{
 
	char buf[4096];
 
	AI::GetConsoleList(buf, lastof(buf));
 

	
 
	PrintLineByLine(buf);
 

	
 
	return true;
 
}
 
@@ -1236,87 +1237,87 @@ DEF_CONSOLE_CMD(ConStartAI)
 

	
 
	return true;
 
}
 

	
 
DEF_CONSOLE_CMD(ConReloadAI)
 
{
 
	if (argc != 2) {
 
		IConsoleHelp("Reload an AI. Usage: 'reload_ai <company-id>'");
 
		IConsoleHelp("Reload the AI with the given company id. For company-id's, see the list of companies from the dropdown menu. Company 1 is 1, etc.");
 
		return true;
 
	}
 

	
 
	if (_game_mode != GM_NORMAL) {
 
		IConsolePrint(CC_ERROR, "AIs can only be managed in a game.");
 
		return true;
 
	}
 

	
 
	if (_networking && !_network_server) {
 
		IConsolePrint(CC_ERROR, "Only the server can reload an AI.");
 
		return true;
 
	}
 

	
 
	CompanyID company_id = (CompanyID)(atoi(argv[1]) - 1);
 
	if (!Company::IsValidID(company_id)) {
 
		IConsolePrintF(CC_DEFAULT, "Unknown company. Company range is between 1 and %d.", MAX_COMPANIES);
 
		IConsolePrint(CC_ERROR, "Unknown company. Company range is between 1 and {}.", MAX_COMPANIES);
 
		return true;
 
	}
 

	
 
	/* In singleplayer mode the player can be in an AI company, after cheating or loading network save with an AI in first slot. */
 
	if (Company::IsHumanID(company_id) || company_id == _local_company) {
 
		IConsolePrint(CC_ERROR, "Company is not controlled by an AI.");
 
		return true;
 
	}
 

	
 
	/* First kill the company of the AI, then start a new one. This should start the current AI again */
 
	DoCommandP(0, CCA_DELETE | company_id << 16 | CRR_MANUAL << 24, 0, CMD_COMPANY_CTRL);
 
	DoCommandP(0, CCA_NEW_AI | company_id << 16, 0, CMD_COMPANY_CTRL);
 
	IConsolePrint(CC_DEFAULT, "AI reloaded.");
 

	
 
	return true;
 
}
 

	
 
DEF_CONSOLE_CMD(ConStopAI)
 
{
 
	if (argc != 2) {
 
		IConsoleHelp("Stop an AI. Usage: 'stop_ai <company-id>'");
 
		IConsoleHelp("Stop the AI with the given company id. For company-id's, see the list of companies from the dropdown menu. Company 1 is 1, etc.");
 
		return true;
 
	}
 

	
 
	if (_game_mode != GM_NORMAL) {
 
		IConsolePrint(CC_ERROR, "AIs can only be managed in a game.");
 
		return true;
 
	}
 

	
 
	if (_networking && !_network_server) {
 
		IConsolePrint(CC_ERROR, "Only the server can stop an AI.");
 
		return true;
 
	}
 

	
 
	CompanyID company_id = (CompanyID)(atoi(argv[1]) - 1);
 
	if (!Company::IsValidID(company_id)) {
 
		IConsolePrintF(CC_DEFAULT, "Unknown company. Company range is between 1 and %d.", MAX_COMPANIES);
 
		IConsolePrint(CC_ERROR, "Unknown company. Company range is between 1 and {}.", MAX_COMPANIES);
 
		return true;
 
	}
 

	
 
	/* In singleplayer mode the player can be in an AI company, after cheating or loading network save with an AI in first slot. */
 
	if (Company::IsHumanID(company_id) || company_id == _local_company) {
 
		IConsolePrint(CC_ERROR, "Company is not controlled by an AI.");
 
		return true;
 
	}
 

	
 
	/* Now kill the company of the AI. */
 
	DoCommandP(0, CCA_DELETE | company_id << 16 | CRR_MANUAL << 24, 0, CMD_COMPANY_CTRL);
 
	IConsolePrint(CC_DEFAULT, "AI stopped, company deleted.");
 

	
 
	return true;
 
}
 

	
 
DEF_CONSOLE_CMD(ConRescanAI)
 
{
 
	if (argc == 0) {
 
		IConsoleHelp("Rescan the AI dir for scripts. Usage: 'rescan_ai'");
 
		return true;
 
	}
 

	
 
	if (_networking && !_network_server) {
 
@@ -1347,75 +1348,75 @@ DEF_CONSOLE_CMD(ConRescanGame)
 
}
 

	
 
DEF_CONSOLE_CMD(ConRescanNewGRF)
 
{
 
	if (argc == 0) {
 
		IConsoleHelp("Rescan the data dir for NewGRFs. Usage: 'rescan_newgrf'");
 
		return true;
 
	}
 

	
 
	if (!RequestNewGRFScan()) {
 
		IConsolePrint(CC_ERROR, "NewGRF scanning is already running. Please wait until completed to run again.");
 
	}
 

	
 
	return true;
 
}
 

	
 
DEF_CONSOLE_CMD(ConGetSeed)
 
{
 
	if (argc == 0) {
 
		IConsoleHelp("Returns the seed used to create this game. Usage: 'getseed'");
 
		IConsoleHelp("The seed can be used to reproduce the exact same map as the game started with.");
 
		return true;
 
	}
 

	
 
	IConsolePrintF(CC_DEFAULT, "Generation Seed: %u", _settings_game.game_creation.generation_seed);
 
	IConsolePrint(CC_DEFAULT, "Generation Seed: {}", _settings_game.game_creation.generation_seed);
 
	return true;
 
}
 

	
 
DEF_CONSOLE_CMD(ConGetDate)
 
{
 
	if (argc == 0) {
 
		IConsoleHelp("Returns the current date (year-month-day) of the game. Usage: 'getdate'");
 
		return true;
 
	}
 

	
 
	YearMonthDay ymd;
 
	ConvertDateToYMD(_date, &ymd);
 
	IConsolePrintF(CC_DEFAULT, "Date: %04d-%02d-%02d", ymd.year, ymd.month + 1, ymd.day);
 
	IConsolePrint(CC_DEFAULT, "Date: {:04d}-{:02d}-{:02d}", ymd.year, ymd.month + 1, ymd.day);
 
	return true;
 
}
 

	
 
DEF_CONSOLE_CMD(ConGetSysDate)
 
{
 
	if (argc == 0) {
 
		IConsoleHelp("Returns the current date (year-month-day) of your system. Usage: 'getsysdate'");
 
		return true;
 
	}
 

	
 
	char buffer[lengthof("2000-01-02 03:04:05")];
 
	LocalTime::Format(buffer, lastof(buffer), "%Y-%m-%d %H:%M:%S");
 
	IConsolePrintF(CC_DEFAULT, "System Date: %s", buffer);
 
	IConsolePrint(CC_DEFAULT, "System Date: {}", buffer);
 
	return true;
 
}
 

	
 

	
 
DEF_CONSOLE_CMD(ConAlias)
 
{
 
	IConsoleAlias *alias;
 

	
 
	if (argc == 0) {
 
		IConsoleHelp("Add a new alias, or redefine the behaviour of an existing alias . Usage: 'alias <name> <command>'");
 
		return true;
 
	}
 

	
 
	if (argc < 3) return false;
 

	
 
	alias = IConsole::AliasGet(argv[1]);
 
	if (alias == nullptr) {
 
		IConsole::AliasRegister(argv[1], argv[2]);
 
	} else {
 
		alias->cmdline = argv[2];
 
	}
 
	return true;
 
}
 

	
 
@@ -1511,49 +1512,49 @@ DEF_CONSOLE_CMD(ConInfoCmd)
 
	const IConsoleCmd *cmd = IConsole::CmdGet(argv[1]);
 
	if (cmd == nullptr) {
 
		IConsolePrint(CC_ERROR, "The given command was not found.");
 
		return true;
 
	}
 

	
 
	IConsolePrint(CC_DEFAULT, "Command name: '{}'", cmd->name);
 

	
 
	if (cmd->hook != nullptr) IConsolePrint(CC_DEFAULT, "Command is hooked.");
 

	
 
	return true;
 
}
 

	
 
DEF_CONSOLE_CMD(ConDebugLevel)
 
{
 
	if (argc == 0) {
 
		IConsoleHelp("Get/set the default debugging level for the game. Usage: 'debug_level [<level>]'");
 
		IConsoleHelp("Level can be any combination of names, levels. Eg 'net=5 ms=4'. Remember to enclose it in \"'s");
 
		return true;
 
	}
 

	
 
	if (argc > 2) return false;
 

	
 
	if (argc == 1) {
 
		IConsolePrintF(CC_DEFAULT, "Current debug-level: '%s'", GetDebugString());
 
		IConsolePrint(CC_DEFAULT, "Current debug-level: '{}'", GetDebugString());
 
	} else {
 
		SetDebugString(argv[1]);
 
	}
 

	
 
	return true;
 
}
 

	
 
DEF_CONSOLE_CMD(ConExit)
 
{
 
	if (argc == 0) {
 
		IConsoleHelp("Exit the game. Usage: 'exit'");
 
		return true;
 
	}
 

	
 
	if (_game_mode == GM_NORMAL && _settings_client.gui.autosave_on_exit) DoExitSave();
 

	
 
	_exit_game = true;
 
	return true;
 
}
 

	
 
DEF_CONSOLE_CMD(ConPart)
 
{
 
	if (argc == 0) {
 
		IConsoleHelp("Leave the currently joined/running game (only ingame). Usage: 'part'");
 
@@ -1564,217 +1565,214 @@ DEF_CONSOLE_CMD(ConPart)
 

	
 
	_switch_mode = SM_MENU;
 
	return true;
 
}
 

	
 
DEF_CONSOLE_CMD(ConHelp)
 
{
 
	if (argc == 2) {
 
		const IConsoleCmd *cmd;
 
		const IConsoleAlias *alias;
 

	
 
		cmd = IConsole::CmdGet(argv[1]);
 
		if (cmd != nullptr) {
 
			cmd->proc(0, nullptr);
 
			return true;
 
		}
 

	
 
		alias = IConsole::AliasGet(argv[1]);
 
		if (alias != nullptr) {
 
			cmd = IConsole::CmdGet(alias->cmdline);
 
			if (cmd != nullptr) {
 
				cmd->proc(0, nullptr);
 
				return true;
 
			}
 
			IConsolePrintF(CC_ERROR, "ERROR: alias is of special type, please see its execution-line: '%s'", alias->cmdline.c_str());
 
			IConsolePrint(CC_ERROR, "Alias is of special type, please see its execution-line: '{}'.", alias->cmdline);
 
			return true;
 
		}
 

	
 
		IConsolePrint(CC_ERROR, "Command not found");
 
		return true;
 
	}
 

	
 
	IConsolePrint(TC_LIGHT_BLUE, " ---- OpenTTD Console Help ---- ");
 
	IConsolePrint(CC_DEFAULT, " - commands: [command to list all commands: list_cmds]");
 
	IConsolePrint(CC_DEFAULT, " call commands with '<command> <arg2> <arg3>...'");
 
	IConsolePrint(CC_DEFAULT, " - to assign strings, or use them as arguments, enclose it within quotes");
 
	IConsolePrint(CC_DEFAULT, " like this: '<command> \"string argument with spaces\"'");
 
	IConsolePrint(CC_DEFAULT, " - use 'help <command>' to get specific information");
 
	IConsolePrint(CC_DEFAULT, " - scroll console output with shift + (up | down | pageup | pagedown)");
 
	IConsolePrint(CC_DEFAULT, " - scroll console input history with the up or down arrows");
 
	IConsolePrint(CC_DEFAULT, "");
 
	return true;
 
}
 

	
 
DEF_CONSOLE_CMD(ConListCommands)
 
{
 
	if (argc == 0) {
 
		IConsoleHelp("List all registered commands. Usage: 'list_cmds [<pre-filter>]'");
 
		return true;
 
	}
 

	
 
	for (auto &it : IConsole::Commands()) {
 
		const IConsoleCmd *cmd = &it.second;
 
		if (argv[1] == nullptr || cmd->name.find(argv[1]) != std::string::npos) {
 
			if (cmd->hook == nullptr || cmd->hook(false) != CHR_HIDE) IConsolePrintF(CC_DEFAULT, "%s", cmd->name.c_str());
 
			if (cmd->hook == nullptr || cmd->hook(false) != CHR_HIDE) IConsolePrint(CC_DEFAULT, cmd->name);
 
		}
 
	}
 

	
 
	return true;
 
}
 

	
 
DEF_CONSOLE_CMD(ConListAliases)
 
{
 
	if (argc == 0) {
 
		IConsoleHelp("List all registered aliases. Usage: 'list_aliases [<pre-filter>]'");
 
		return true;
 
	}
 

	
 
	for (auto &it : IConsole::Aliases()) {
 
		const IConsoleAlias *alias = &it.second;
 
		if (argv[1] == nullptr || alias->name.find(argv[1]) != std::string::npos) {
 
			IConsolePrintF(CC_DEFAULT, "%s => %s", alias->name.c_str(), alias->cmdline.c_str());
 
			IConsolePrint(CC_DEFAULT, "{} => {}", alias->name, alias->cmdline);
 
		}
 
	}
 

	
 
	return true;
 
}
 

	
 
DEF_CONSOLE_CMD(ConCompanies)
 
{
 
	if (argc == 0) {
 
		IConsoleHelp("List the details of all companies in the game. Usage 'companies'");
 
		return true;
 
	}
 

	
 
	for (const Company *c : Company::Iterate()) {
 
		/* Grab the company name */
 
		char company_name[512];
 
		SetDParam(0, c->index);
 
		GetString(company_name, STR_COMPANY_NAME, lastof(company_name));
 

	
 
		const char *password_state = "";
 
		if (c->is_ai) {
 
			password_state = "AI";
 
		} else if (_network_server) {
 
			password_state = _network_company_states[c->index].password.empty() ? "unprotected" : "protected";
 
		}
 

	
 
		char colour[512];
 
		GetString(colour, STR_COLOUR_DARK_BLUE + _company_colours[c->index], lastof(colour));
 
		IConsolePrintF(CC_INFO, "#:%d(%s) Company Name: '%s'  Year Founded: %d  Money: " OTTD_PRINTF64 "  Loan: " OTTD_PRINTF64 "  Value: " OTTD_PRINTF64 "  (T:%d, R:%d, P:%d, S:%d) %s",
 
		IConsolePrint(CC_INFO, "#:{}({}) Company Name: '{}'  Year Founded: {}  Money: {}  Loan: {}  Value: {}  (T:{}, R:{}, P:{}, S:{}) {}",
 
			c->index + 1, colour, company_name,
 
			c->inaugurated_year, (int64)c->money, (int64)c->current_loan, (int64)CalculateCompanyValue(c),
 
			c->group_all[VEH_TRAIN].num_vehicle,
 
			c->group_all[VEH_ROAD].num_vehicle,
 
			c->group_all[VEH_AIRCRAFT].num_vehicle,
 
			c->group_all[VEH_SHIP].num_vehicle,
 
			password_state);
 
	}
 

	
 
	return true;
 
}
 

	
 
DEF_CONSOLE_CMD(ConSay)
 
{
 
	if (argc == 0) {
 
		IConsoleHelp("Chat to your fellow players in a multiplayer game. Usage: 'say \"<msg>\"'");
 
		return true;
 
	}
 

	
 
	if (argc != 2) return false;
 

	
 
	if (!_network_server) {
 
		NetworkClientSendChat(NETWORK_ACTION_CHAT, DESTTYPE_BROADCAST, 0 /* param does not matter */, argv[1]);
 
	} else {
 
		bool from_admin = (_redirect_console_to_admin < INVALID_ADMIN_ID);
 
		NetworkServerSendChat(NETWORK_ACTION_CHAT, DESTTYPE_BROADCAST, 0, argv[1], CLIENT_ID_SERVER, from_admin);
 
	}
 

	
 
	return true;
 
}
 

	
 
DEF_CONSOLE_CMD(ConSayCompany)
 
{
 
	if (argc == 0) {
 
		IConsoleHelp("Chat to a certain company in a multiplayer game. Usage: 'say_company <company-no> \"<msg>\"'");
 
		IConsoleHelp("CompanyNo is the company that plays as company <companyno>, 1 through max_companies");
 
		return true;
 
	}
 

	
 
	if (argc != 3) return false;
 

	
 
	CompanyID company_id = (CompanyID)(atoi(argv[1]) - 1);
 
	if (!Company::IsValidID(company_id)) {
 
		IConsolePrintF(CC_DEFAULT, "Unknown company. Company range is between 1 and %d.", MAX_COMPANIES);
 
		IConsolePrint(CC_DEFAULT, "Unknown company. Company range is between 1 and {}.", MAX_COMPANIES);
 
		return true;
 
	}
 

	
 
	if (!_network_server) {
 
		NetworkClientSendChat(NETWORK_ACTION_CHAT_COMPANY, DESTTYPE_TEAM, company_id, argv[2]);
 
	} else {
 
		bool from_admin = (_redirect_console_to_admin < INVALID_ADMIN_ID);
 
		NetworkServerSendChat(NETWORK_ACTION_CHAT_COMPANY, DESTTYPE_TEAM, company_id, argv[2], CLIENT_ID_SERVER, from_admin);
 
	}
 

	
 
	return true;
 
}
 

	
 
DEF_CONSOLE_CMD(ConSayClient)
 
{
 
	if (argc == 0) {
 
		IConsoleHelp("Chat to a certain client in a multiplayer game. Usage: 'say_client <client-no> \"<msg>\"'");
 
		IConsoleHelp("For client-id's, see the command 'clients'");
 
		return true;
 
	}
 

	
 
	if (argc != 3) return false;
 

	
 
	if (!_network_server) {
 
		NetworkClientSendChat(NETWORK_ACTION_CHAT_CLIENT, DESTTYPE_CLIENT, atoi(argv[1]), argv[2]);
 
	} else {
 
		bool from_admin = (_redirect_console_to_admin < INVALID_ADMIN_ID);
 
		NetworkServerSendChat(NETWORK_ACTION_CHAT_CLIENT, DESTTYPE_CLIENT, atoi(argv[1]), argv[2], CLIENT_ID_SERVER, from_admin);
 
	}
 

	
 
	return true;
 
}
 

	
 
DEF_CONSOLE_CMD(ConCompanyPassword)
 
{
 
	if (argc == 0) {
 
		const char *helpmsg;
 

	
 
		if (_network_dedicated) {
 
			helpmsg = "Change the password of a company. Usage: 'company_pw <company-no> \"<password>\"";
 
			IConsolePrint(CC_HELP, "Change the password of a company. Usage: 'company_pw <company-no> \"<password>\".");
 
		} else if (_network_server) {
 
			helpmsg = "Change the password of your or any other company. Usage: 'company_pw [<company-no>] \"<password>\"'";
 
			IConsolePrint(CC_HELP, "Change the password of your or any other company. Usage: 'company_pw [<company-no>] \"<password>\"'.");
 
		} else {
 
			helpmsg = "Change the password of your company. Usage: 'company_pw \"<password>\"'";
 
			IConsolePrint(CC_HELP, "Change the password of your company. Usage: 'company_pw \"<password>\"'.");
 
		}
 

	
 
		IConsoleHelp(helpmsg);
 
		IConsoleHelp("Use \"*\" to disable the password.");
 
		IConsolePrint(CC_HELP, "Use \"*\" to disable the password.");
 
		return true;
 
	}
 

	
 
	CompanyID company_id;
 
	std::string password;
 
	const char *errormsg;
 

	
 
	if (argc == 2) {
 
		company_id = _local_company;
 
		password = argv[1];
 
		errormsg = "You have to own a company to make use of this command.";
 
	} else if (argc == 3 && _network_server) {
 
		company_id = (CompanyID)(atoi(argv[1]) - 1);
 
		password = argv[2];
 
		errormsg = "You have to specify the ID of a valid human controlled company.";
 
	} else {
 
		return false;
 
	}
 

	
 
	if (!Company::IsValidHumanID(company_id)) {
 
		IConsolePrint(CC_ERROR, errormsg);
 
		return false;
 
	}
 

	
 
@@ -1786,156 +1784,156 @@ DEF_CONSOLE_CMD(ConCompanyPassword)
 
		IConsolePrint(CC_INFO, "Company password changed to '{}'.", password);
 
	}
 

	
 
	return true;
 
}
 

	
 
/* Content downloading only is available with ZLIB */
 
#if defined(WITH_ZLIB)
 
#include "network/network_content.h"
 

	
 
/** Resolve a string to a content type. */
 
static ContentType StringToContentType(const char *str)
 
{
 
	static const char * const inv_lookup[] = { "", "base", "newgrf", "ai", "ailib", "scenario", "heightmap" };
 
	for (uint i = 1 /* there is no type 0 */; i < lengthof(inv_lookup); i++) {
 
		if (strcasecmp(str, inv_lookup[i]) == 0) return (ContentType)i;
 
	}
 
	return CONTENT_TYPE_END;
 
}
 

	
 
/** Asynchronous callback */
 
struct ConsoleContentCallback : public ContentCallback {
 
	void OnConnect(bool success)
 
	{
 
		IConsolePrintF(CC_DEFAULT, "Content server connection %s", success ? "established" : "failed");
 
		IConsolePrint(CC_DEFAULT, "Content server connection {}.", success ? "established" : "failed");
 
	}
 

	
 
	void OnDisconnect()
 
	{
 
		IConsolePrintF(CC_DEFAULT, "Content server connection closed");
 
		IConsolePrint(CC_DEFAULT, "Content server connection closed.");
 
	}
 

	
 
	void OnDownloadComplete(ContentID cid)
 
	{
 
		IConsolePrintF(CC_DEFAULT, "Completed download of %d", cid);
 
		IConsolePrint(CC_DEFAULT, "Completed download of {}.", cid);
 
	}
 
};
 

	
 
/**
 
 * Outputs content state information to console
 
 * @param ci the content info
 
 */
 
static void OutputContentState(const ContentInfo *const ci)
 
{
 
	static const char * const types[] = { "Base graphics", "NewGRF", "AI", "AI library", "Scenario", "Heightmap", "Base sound", "Base music", "Game script", "GS library" };
 
	static_assert(lengthof(types) == CONTENT_TYPE_END - CONTENT_TYPE_BEGIN);
 
	static const char * const states[] = { "Not selected", "Selected", "Dep Selected", "Installed", "Unknown" };
 
	static const TextColour state_to_colour[] = { CC_COMMAND, CC_INFO, CC_INFO, CC_WHITE, CC_ERROR };
 

	
 
	char buf[sizeof(ci->md5sum) * 2 + 1];
 
	md5sumToString(buf, lastof(buf), ci->md5sum);
 
	IConsolePrintF(state_to_colour[ci->state], "%d, %s, %s, %s, %08X, %s", ci->id, types[ci->type - 1], states[ci->state], ci->name.c_str(), ci->unique_id, buf);
 
	IConsolePrint(state_to_colour[ci->state], "{}, {}, {}, {}, {:08X}, {}", ci->id, types[ci->type - 1], states[ci->state], ci->name, ci->unique_id, buf);
 
}
 

	
 
DEF_CONSOLE_CMD(ConContent)
 
{
 
	static ContentCallback *cb = nullptr;
 
	if (cb == nullptr) {
 
		cb = new ConsoleContentCallback();
 
		_network_content_client.AddCallback(cb);
 
	}
 

	
 
	if (argc <= 1) {
 
		IConsoleHelp("Query, select and download content. Usage: 'content update|upgrade|select [id]|unselect [all|id]|state [filter]|download'");
 
		IConsoleHelp("  update: get a new list of downloadable content; must be run first");
 
		IConsoleHelp("  upgrade: select all items that are upgrades");
 
		IConsoleHelp("  select: select a specific item given by its id. If no parameter is given, all selected content will be listed");
 
		IConsoleHelp("  unselect: unselect a specific item given by its id or 'all' to unselect all");
 
		IConsoleHelp("  state: show the download/select state of all downloadable content. Optionally give a filter string");
 
		IConsoleHelp("  download: download all content you've selected");
 
		return true;
 
	}
 

	
 
	if (strcasecmp(argv[1], "update") == 0) {
 
		_network_content_client.RequestContentList((argc > 2) ? StringToContentType(argv[2]) : CONTENT_TYPE_END);
 
		return true;
 
	}
 

	
 
	if (strcasecmp(argv[1], "upgrade") == 0) {
 
		_network_content_client.SelectUpgrade();
 
		return true;
 
	}
 

	
 
	if (strcasecmp(argv[1], "select") == 0) {
 
		if (argc <= 2) {
 
			/* List selected content */
 
			IConsolePrintF(CC_WHITE, "id, type, state, name");
 
			IConsolePrint(CC_WHITE, "id, type, state, name");
 
			for (ConstContentIterator iter = _network_content_client.Begin(); iter != _network_content_client.End(); iter++) {
 
				if ((*iter)->state != ContentInfo::SELECTED && (*iter)->state != ContentInfo::AUTOSELECTED) continue;
 
				OutputContentState(*iter);
 
			}
 
		} else if (strcasecmp(argv[2], "all") == 0) {
 
			/* The intention of this function was that you could download
 
			 * everything after a filter was applied; but this never really
 
			 * took off. Instead, a select few people used this functionality
 
			 * to download every available package on BaNaNaS. This is not in
 
			 * the spirit of this service. Additionally, these few people were
 
			 * good for 70% of the consumed bandwidth of BaNaNaS. */
 
			IConsolePrint(CC_ERROR, "'select all' is no longer supported since 1.11.");
 
		} else {
 
			_network_content_client.Select((ContentID)atoi(argv[2]));
 
		}
 
		return true;
 
	}
 

	
 
	if (strcasecmp(argv[1], "unselect") == 0) {
 
		if (argc <= 2) {
 
			IConsolePrint(CC_ERROR, "You must enter the id.");
 
			return false;
 
		}
 
		if (strcasecmp(argv[2], "all") == 0) {
 
			_network_content_client.UnselectAll();
 
		} else {
 
			_network_content_client.Unselect((ContentID)atoi(argv[2]));
 
		}
 
		return true;
 
	}
 

	
 
	if (strcasecmp(argv[1], "state") == 0) {
 
		IConsolePrintF(CC_WHITE, "id, type, state, name");
 
		IConsolePrint(CC_WHITE, "id, type, state, name");
 
		for (ConstContentIterator iter = _network_content_client.Begin(); iter != _network_content_client.End(); iter++) {
 
			if (argc > 2 && strcasestr((*iter)->name.c_str(), argv[2]) == nullptr) continue;
 
			OutputContentState(*iter);
 
		}
 
		return true;
 
	}
 

	
 
	if (strcasecmp(argv[1], "download") == 0) {
 
		uint files;
 
		uint bytes;
 
		_network_content_client.DownloadSelectedContent(files, bytes);
 
		IConsolePrintF(CC_DEFAULT, "Downloading %d file(s) (%d bytes)", files, bytes);
 
		IConsolePrint(CC_DEFAULT, "Downloading {} file(s) ({} bytes).", files, bytes);
 
		return true;
 
	}
 

	
 
	return false;
 
}
 
#endif /* defined(WITH_ZLIB) */
 

	
 
DEF_CONSOLE_CMD(ConSetting)
 
{
 
	if (argc == 0) {
 
		IConsoleHelp("Change setting for all clients. Usage: 'setting <name> [<value>]'");
 
		IConsoleHelp("Omitting <value> will print out the current value of the setting.");
 
		return true;
 
	}
 

	
 
	if (argc == 1 || argc > 3) return false;
 

	
 
	if (argc == 2) {
 
		IConsoleGetSetting(argv[1]);
 
	} else {
 
		IConsoleSetSetting(argv[1], argv[2]);
 
	}
 

	
 
	return true;
 
@@ -2001,49 +1999,49 @@ DEF_CONSOLE_CMD(ConNewGRFProfile)
 
		IConsoleHelp("Usage: newgrf_profile unselect <grf-num>...");
 
		IConsoleHelp("  Unselect one or more GRFs from profiling. Use the keyword \"all\" instead of a GRF number to unselect all. Removing an active profiler aborts data collection.");
 
		IConsoleHelp("Usage: newgrf_profile start [<num-days>]");
 
		IConsoleHelp("  Begin profiling all selected GRFs. If a number of days is provided, profiling stops after that many in-game days.");
 
		IConsoleHelp("Usage: newgrf_profile stop");
 
		IConsoleHelp("  End profiling and write the collected data to CSV files.");
 
		IConsoleHelp("Usage: newgrf_profile abort");
 
		IConsoleHelp("  End profiling and discard all collected data.");
 
		return true;
 
	}
 

	
 
	extern const std::vector<GRFFile *> &GetAllGRFFiles();
 
	const std::vector<GRFFile *> &files = GetAllGRFFiles();
 

	
 
	/* "list" sub-command */
 
	if (argc == 1 || strncasecmp(argv[1], "lis", 3) == 0) {
 
		IConsolePrint(CC_INFO, "Loaded GRF files:");
 
		int i = 1;
 
		for (GRFFile *grf : files) {
 
			auto profiler = std::find_if(_newgrf_profilers.begin(), _newgrf_profilers.end(), [&](NewGRFProfiler &pr) { return pr.grffile == grf; });
 
			bool selected = profiler != _newgrf_profilers.end();
 
			bool active = selected && profiler->active;
 
			TextColour tc = active ? TC_LIGHT_BLUE : selected ? TC_GREEN : CC_INFO;
 
			const char *statustext = active ? " (active)" : selected ? " (selected)" : "";
 
			IConsolePrintF(tc, "%d: [%08X] %s%s", i, BSWAP32(grf->grfid), grf->filename, statustext);
 
			IConsolePrint(tc, "{}: [{:08X}] {}{}", i, BSWAP32(grf->grfid), grf->filename, statustext);
 
			i++;
 
		}
 
		return true;
 
	}
 

	
 
	/* "select" sub-command */
 
	if (strncasecmp(argv[1], "sel", 3) == 0 && argc >= 3) {
 
		for (size_t argnum = 2; argnum < argc; ++argnum) {
 
			int grfnum = atoi(argv[argnum]);
 
			if (grfnum < 1 || grfnum > (int)files.size()) { // safe cast, files.size() should not be larger than a few hundred in the most extreme cases
 
				IConsolePrint(CC_WARNING, "GRF number {} out of range, not added.", grfnum);
 
				continue;
 
			}
 
			GRFFile *grf = files[grfnum - 1];
 
			if (std::any_of(_newgrf_profilers.begin(), _newgrf_profilers.end(), [&](NewGRFProfiler &pr) { return pr.grffile == grf; })) {
 
				IConsolePrint(CC_WARNING, "GRF number {} [{:08X}] is already selected for profiling.", grfnum, BSWAP32(grf->grfid));
 
				continue;
 
			}
 
			_newgrf_profilers.emplace_back(grf);
 
		}
 
		return true;
 
	}
 

	
 
	/* "unselect" sub-command */
 
@@ -2060,57 +2058,57 @@ DEF_CONSOLE_CMD(ConNewGRFProfile)
 
			}
 
			GRFFile *grf = files[grfnum - 1];
 
			auto pos = std::find_if(_newgrf_profilers.begin(), _newgrf_profilers.end(), [&](NewGRFProfiler &pr) { return pr.grffile == grf; });
 
			if (pos != _newgrf_profilers.end()) _newgrf_profilers.erase(pos);
 
		}
 
		return true;
 
	}
 

	
 
	/* "start" sub-command */
 
	if (strncasecmp(argv[1], "sta", 3) == 0) {
 
		std::string grfids;
 
		size_t started = 0;
 
		for (NewGRFProfiler &pr : _newgrf_profilers) {
 
			if (!pr.active) {
 
				pr.Start();
 
				started++;
 

	
 
				if (!grfids.empty()) grfids += ", ";
 
				char grfidstr[12]{ 0 };
 
				seprintf(grfidstr, lastof(grfidstr), "[%08X]", BSWAP32(pr.grffile->grfid));
 
				grfids += grfidstr;
 
			}
 
		}
 
		if (started > 0) {
 
			IConsolePrintF(CC_DEBUG, "Started profiling for GRFID%s %s", (started > 1) ? "s" : "", grfids.c_str());
 
			IConsolePrint(CC_DEBUG, "Started profiling for GRFID{} {}", (started > 1) ? "s" : "", grfids);
 
			if (argc >= 3) {
 
				int days = std::max(atoi(argv[2]), 1);
 
				_newgrf_profile_end_date = _date + days;
 

	
 
				char datestrbuf[32]{ 0 };
 
				SetDParam(0, _newgrf_profile_end_date);
 
				GetString(datestrbuf, STR_JUST_DATE_ISO, lastof(datestrbuf));
 
				IConsolePrintF(CC_DEBUG, "Profiling will automatically stop on game date %s", datestrbuf);
 
				IConsolePrint(CC_DEBUG, "Profiling will automatically stop on game date {}.", datestrbuf);
 
			} else {
 
				_newgrf_profile_end_date = MAX_DAY;
 
			}
 
		} else if (_newgrf_profilers.empty()) {
 
			IConsolePrint(CC_ERROR, "No GRFs selected for profiling, did not start.");
 
		} else {
 
			IConsolePrint(CC_ERROR, "Did not start profiling for any GRFs, all selected GRFs are already profiling.");
 
		}
 
		return true;
 
	}
 

	
 
	/* "stop" sub-command */
 
	if (strncasecmp(argv[1], "sto", 3) == 0) {
 
		NewGRFProfiler::FinishAll();
 
		return true;
 
	}
 

	
 
	/* "abort" sub-command */
 
	if (strncasecmp(argv[1], "abo", 3) == 0) {
 
		for (NewGRFProfiler &pr : _newgrf_profilers) {
 
			pr.Abort();
 
		}
 
		_newgrf_profile_end_date = MAX_DAY;
 
		return true;
 
@@ -2144,168 +2142,168 @@ DEF_CONSOLE_CMD(ConFramerate)
 
	ConPrintFramerate();
 
	return true;
 
}
 

	
 
DEF_CONSOLE_CMD(ConFramerateWindow)
 
{
 
	extern void ShowFramerateWindow();
 

	
 
	if (argc == 0) {
 
		IConsoleHelp("Open the frame rate window");
 
		return true;
 
	}
 

	
 
	if (_network_dedicated) {
 
		IConsolePrint(CC_ERROR, "Can not open frame rate window on a dedicated server");
 
		return false;
 
	}
 

	
 
	ShowFramerateWindow();
 
	return true;
 
}
 

	
 
static void ConDumpRoadTypes()
 
{
 
	IConsolePrintF(CC_DEFAULT, "  Flags:");
 
	IConsolePrintF(CC_DEFAULT, "    c = catenary");
 
	IConsolePrintF(CC_DEFAULT, "    l = no level crossings");
 
	IConsolePrintF(CC_DEFAULT, "    X = no houses");
 
	IConsolePrintF(CC_DEFAULT, "    h = hidden");
 
	IConsolePrintF(CC_DEFAULT, "    T = buildable by towns");
 
	IConsolePrint(CC_DEFAULT, "  Flags:");
 
	IConsolePrint(CC_DEFAULT, "    c = catenary");
 
	IConsolePrint(CC_DEFAULT, "    l = no level crossings");
 
	IConsolePrint(CC_DEFAULT, "    X = no houses");
 
	IConsolePrint(CC_DEFAULT, "    h = hidden");
 
	IConsolePrint(CC_DEFAULT, "    T = buildable by towns");
 

	
 
	std::map<uint32, const GRFFile *> grfs;
 
	for (RoadType rt = ROADTYPE_BEGIN; rt < ROADTYPE_END; rt++) {
 
		const RoadTypeInfo *rti = GetRoadTypeInfo(rt);
 
		if (rti->label == 0) continue;
 
		uint32 grfid = 0;
 
		const GRFFile *grf = rti->grffile[ROTSG_GROUND];
 
		if (grf != nullptr) {
 
			grfid = grf->grfid;
 
			grfs.emplace(grfid, grf);
 
		}
 
		IConsolePrintF(CC_DEFAULT, "  %02u %s %c%c%c%c, Flags: %c%c%c%c%c, GRF: %08X, %s",
 
		IConsolePrint(CC_DEFAULT, "  {:02d} {} {:c}{:c}{:c}{:c}, Flags: {}{}{}{}{}, GRF: {:08X}, {}",
 
				(uint)rt,
 
				RoadTypeIsTram(rt) ? "Tram" : "Road",
 
				rti->label >> 24, rti->label >> 16, rti->label >> 8, rti->label,
 
				HasBit(rti->flags, ROTF_CATENARY)          ? 'c' : '-',
 
				HasBit(rti->flags, ROTF_NO_LEVEL_CROSSING) ? 'l' : '-',
 
				HasBit(rti->flags, ROTF_NO_HOUSES)         ? 'X' : '-',
 
				HasBit(rti->flags, ROTF_HIDDEN)            ? 'h' : '-',
 
				HasBit(rti->flags, ROTF_TOWN_BUILD)        ? 'T' : '-',
 
				BSWAP32(grfid),
 
				GetStringPtr(rti->strings.name)
 
		);
 
	}
 
	for (const auto &grf : grfs) {
 
		IConsolePrintF(CC_DEFAULT, "  GRF: %08X = %s", BSWAP32(grf.first), grf.second->filename);
 
		IConsolePrint(CC_DEFAULT, "  GRF: {:08X} = {}", BSWAP32(grf.first), grf.second->filename);
 
	}
 
}
 

	
 
static void ConDumpRailTypes()
 
{
 
	IConsolePrintF(CC_DEFAULT, "  Flags:");
 
	IConsolePrintF(CC_DEFAULT, "    c = catenary");
 
	IConsolePrintF(CC_DEFAULT, "    l = no level crossings");
 
	IConsolePrintF(CC_DEFAULT, "    h = hidden");
 
	IConsolePrintF(CC_DEFAULT, "    s = no sprite combine");
 
	IConsolePrintF(CC_DEFAULT, "    a = always allow 90 degree turns");
 
	IConsolePrintF(CC_DEFAULT, "    d = always disallow 90 degree turns");
 
	IConsolePrint(CC_DEFAULT, "  Flags:");
 
	IConsolePrint(CC_DEFAULT, "    c = catenary");
 
	IConsolePrint(CC_DEFAULT, "    l = no level crossings");
 
	IConsolePrint(CC_DEFAULT, "    h = hidden");
 
	IConsolePrint(CC_DEFAULT, "    s = no sprite combine");
 
	IConsolePrint(CC_DEFAULT, "    a = always allow 90 degree turns");
 
	IConsolePrint(CC_DEFAULT, "    d = always disallow 90 degree turns");
 

	
 
	std::map<uint32, const GRFFile *> grfs;
 
	for (RailType rt = RAILTYPE_BEGIN; rt < RAILTYPE_END; rt++) {
 
		const RailtypeInfo *rti = GetRailTypeInfo(rt);
 
		if (rti->label == 0) continue;
 
		uint32 grfid = 0;
 
		const GRFFile *grf = rti->grffile[RTSG_GROUND];
 
		if (grf != nullptr) {
 
			grfid = grf->grfid;
 
			grfs.emplace(grfid, grf);
 
		}
 
		IConsolePrintF(CC_DEFAULT, "  %02u %c%c%c%c, Flags: %c%c%c%c%c%c, GRF: %08X, %s",
 
		IConsolePrint(CC_DEFAULT, "  {:02d} {:c}{:c}{:c}{:c}, Flags: {}{}{}{}{}{}, GRF: {:08X}, {}",
 
				(uint)rt,
 
				rti->label >> 24, rti->label >> 16, rti->label >> 8, rti->label,
 
				HasBit(rti->flags, RTF_CATENARY)          ? 'c' : '-',
 
				HasBit(rti->flags, RTF_NO_LEVEL_CROSSING) ? 'l' : '-',
 
				HasBit(rti->flags, RTF_HIDDEN)            ? 'h' : '-',
 
				HasBit(rti->flags, RTF_NO_SPRITE_COMBINE) ? 's' : '-',
 
				HasBit(rti->flags, RTF_ALLOW_90DEG)       ? 'a' : '-',
 
				HasBit(rti->flags, RTF_DISALLOW_90DEG)    ? 'd' : '-',
 
				BSWAP32(grfid),
 
				GetStringPtr(rti->strings.name)
 
		);
 
	}
 
	for (const auto &grf : grfs) {
 
		IConsolePrintF(CC_DEFAULT, "  GRF: %08X = %s", BSWAP32(grf.first), grf.second->filename);
 
		IConsolePrint(CC_DEFAULT, "  GRF: {:08X} = {}", BSWAP32(grf.first), grf.second->filename);
 
	}
 
}
 

	
 
static void ConDumpCargoTypes()
 
{
 
	IConsolePrintF(CC_DEFAULT, "  Cargo classes:");
 
	IConsolePrintF(CC_DEFAULT, "    p = passenger");
 
	IConsolePrintF(CC_DEFAULT, "    m = mail");
 
	IConsolePrintF(CC_DEFAULT, "    x = express");
 
	IConsolePrintF(CC_DEFAULT, "    a = armoured");
 
	IConsolePrintF(CC_DEFAULT, "    b = bulk");
 
	IConsolePrintF(CC_DEFAULT, "    g = piece goods");
 
	IConsolePrintF(CC_DEFAULT, "    l = liquid");
 
	IConsolePrintF(CC_DEFAULT, "    r = refrigerated");
 
	IConsolePrintF(CC_DEFAULT, "    h = hazardous");
 
	IConsolePrintF(CC_DEFAULT, "    c = covered/sheltered");
 
	IConsolePrintF(CC_DEFAULT, "    S = special");
 
	IConsolePrint(CC_DEFAULT, "  Cargo classes:");
 
	IConsolePrint(CC_DEFAULT, "    p = passenger");
 
	IConsolePrint(CC_DEFAULT, "    m = mail");
 
	IConsolePrint(CC_DEFAULT, "    x = express");
 
	IConsolePrint(CC_DEFAULT, "    a = armoured");
 
	IConsolePrint(CC_DEFAULT, "    b = bulk");
 
	IConsolePrint(CC_DEFAULT, "    g = piece goods");
 
	IConsolePrint(CC_DEFAULT, "    l = liquid");
 
	IConsolePrint(CC_DEFAULT, "    r = refrigerated");
 
	IConsolePrint(CC_DEFAULT, "    h = hazardous");
 
	IConsolePrint(CC_DEFAULT, "    c = covered/sheltered");
 
	IConsolePrint(CC_DEFAULT, "    S = special");
 

	
 
	std::map<uint32, const GRFFile *> grfs;
 
	for (CargoID i = 0; i < NUM_CARGO; i++) {
 
		const CargoSpec *spec = CargoSpec::Get(i);
 
		if (!spec->IsValid()) continue;
 
		uint32 grfid = 0;
 
		const GRFFile *grf = spec->grffile;
 
		if (grf != nullptr) {
 
			grfid = grf->grfid;
 
			grfs.emplace(grfid, grf);
 
		}
 
		IConsolePrintF(CC_DEFAULT, "  %02u Bit: %2u, Label: %c%c%c%c, Callback mask: 0x%02X, Cargo class: %c%c%c%c%c%c%c%c%c%c%c, GRF: %08X, %s",
 
		IConsolePrint(CC_DEFAULT, "  {:02d} Bit: {:2d}, Label: {:c}{:c}{:c}{:c}, Callback mask: 0x{:02X}, Cargo class: {}{}{}{}{}{}{}{}{}{}{}, GRF: {:08X}, {}",
 
				(uint)i,
 
				spec->bitnum,
 
				spec->label >> 24, spec->label >> 16, spec->label >> 8, spec->label,
 
				spec->callback_mask,
 
				(spec->classes & CC_PASSENGERS)   != 0 ? 'p' : '-',
 
				(spec->classes & CC_MAIL)         != 0 ? 'm' : '-',
 
				(spec->classes & CC_EXPRESS)      != 0 ? 'x' : '-',
 
				(spec->classes & CC_ARMOURED)     != 0 ? 'a' : '-',
 
				(spec->classes & CC_BULK)         != 0 ? 'b' : '-',
 
				(spec->classes & CC_PIECE_GOODS)  != 0 ? 'g' : '-',
 
				(spec->classes & CC_LIQUID)       != 0 ? 'l' : '-',
 
				(spec->classes & CC_REFRIGERATED) != 0 ? 'r' : '-',
 
				(spec->classes & CC_HAZARDOUS)    != 0 ? 'h' : '-',
 
				(spec->classes & CC_COVERED)      != 0 ? 'c' : '-',
 
				(spec->classes & CC_SPECIAL)      != 0 ? 'S' : '-',
 
				BSWAP32(grfid),
 
				GetStringPtr(spec->name)
 
		);
 
	}
 
	for (const auto &grf : grfs) {
 
		IConsolePrintF(CC_DEFAULT, "  GRF: %08X = %s", BSWAP32(grf.first), grf.second->filename);
 
		IConsolePrint(CC_DEFAULT, "  GRF: {:08X} = {}", BSWAP32(grf.first), grf.second->filename);
 
	}
 
}
 

	
 

	
 
DEF_CONSOLE_CMD(ConDumpInfo)
 
{
 
	if (argc != 2) {
 
		IConsoleHelp("Dump debugging information.");
 
		IConsoleHelp("Usage: dump_info roadtypes|railtypes|cargotypes");
 
		IConsoleHelp("  Show information about road/tram types, rail types or cargo types.");
 
		return true;
 
	}
 

	
 
	if (strcasecmp(argv[1], "roadtypes") == 0) {
 
		ConDumpRoadTypes();
 
		return true;
 
	}
 

	
 
	if (strcasecmp(argv[1], "railtypes") == 0) {
 
		ConDumpRailTypes();
 
		return true;
 
	}
 

	
 
	if (strcasecmp(argv[1], "cargotypes") == 0) {
src/console_func.h
Show inline comments
 
@@ -25,32 +25,30 @@ void IConsoleClose();
 
void IConsolePrint(TextColour colour_code, const std::string &string);
 

	
 
/**
 
 * Handle the printing of text entered into the console or redirected there
 
 * by any other means. Text can be redirected to other clients in a network game
 
 * as well as to a logfile. If the network server is a dedicated server, all activities
 
 * are also logged. All lines to print are added to a temporary buffer which can be
 
 * used as a history to print them onscreen
 
 * @param colour_code The colour of the command.
 
 * @param format_string The formatting string to tell what to do with the remaining arguments.
 
 * @param first_arg The first argument to the format.
 
 * @param other_args The other arguments to the format.
 
 * @tparam T The type of formatting parameter.
 
 * @tparam A The type of the first argument.
 
 * @tparam Args The types of the other arguments.
 
 */
 
template <typename T, typename A, typename ... Args>
 
static inline void IConsolePrint(TextColour colour_code, const T &format, A first_arg, Args&&... other_args)
 
{
 
	/* The separate first_arg argument is added to aid overloading.
 
	 * Otherwise the calls that do no need formatting will still use this function. */
 
	IConsolePrint(colour_code, fmt::format(format, first_arg, other_args...));
 
}
 

	
 
void CDECL IConsolePrintF(TextColour colour_code, const char *format, ...) WARN_FORMAT(2, 3);
 

	
 
/* Parser */
 
void IConsoleCmdExec(const char *cmdstr, const uint recurse_count = 0);
 

	
 
bool IsValidConsoleColour(TextColour c);
 

	
 
#endif /* CONSOLE_FUNC_H */
src/console_gui.cpp
Show inline comments
 
@@ -266,49 +266,49 @@ struct IConsoleWindow : Window
 
			case WKC_SHIFT | WKC_PAGEDOWN:
 
				this->Scroll(-scroll_height);
 
				break;
 

	
 
			case WKC_SHIFT | WKC_PAGEUP:
 
				this->Scroll(scroll_height);
 
				break;
 

	
 
			case WKC_SHIFT | WKC_DOWN:
 
				this->Scroll(-1);
 
				break;
 

	
 
			case WKC_SHIFT | WKC_UP:
 
				this->Scroll(1);
 
				break;
 

	
 
			case WKC_BACKQUOTE:
 
				IConsoleSwitch();
 
				break;
 

	
 
			case WKC_RETURN: case WKC_NUM_ENTER: {
 
				/* We always want the ] at the left side; we always force these strings to be left
 
				 * aligned anyway. So enforce this in all cases by adding a left-to-right marker,
 
				 * otherwise it will be drawn at the wrong side with right-to-left texts. */
 
				IConsolePrintF(CC_COMMAND, LRM "] %s", _iconsole_cmdline.buf);
 
				IConsolePrint(CC_COMMAND, LRM "] {}", _iconsole_cmdline.buf);
 
				const char *cmd = IConsoleHistoryAdd(_iconsole_cmdline.buf);
 
				IConsoleClearCommand();
 

	
 
				if (cmd != nullptr) IConsoleCmdExec(cmd);
 
				break;
 
			}
 

	
 
			case WKC_CTRL | WKC_RETURN:
 
				_iconsole_mode = (_iconsole_mode == ICONSOLE_FULL) ? ICONSOLE_OPENED : ICONSOLE_FULL;
 
				IConsoleResize(this);
 
				MarkWholeScreenDirty();
 
				break;
 

	
 
			case (WKC_CTRL | 'L'):
 
				IConsoleCmdExec("clear");
 
				break;
 

	
 
			default:
 
				if (_iconsole_cmdline.HandleKeyPress(key, keycode) != HKPR_NOT_HANDLED) {
 
					IConsoleWindow::scroll = 0;
 
					IConsoleResetHistoryPos();
 
					this->SetDirty();
 
				} else {
 
					return ES_NOT_HANDLED;
 
@@ -378,52 +378,52 @@ struct IConsoleWindow : Window
 
		this->Scroll(-wheel);
 
	}
 

	
 
	void OnFocus() override
 
	{
 
		VideoDriver::GetInstance()->EditBoxGainedFocus();
 
	}
 

	
 
	void OnFocusLost() override
 
	{
 
		VideoDriver::GetInstance()->EditBoxLostFocus();
 
	}
 
};
 

	
 
int IConsoleWindow::scroll = 0;
 

	
 
void IConsoleGUIInit()
 
{
 
	IConsoleResetHistoryPos();
 
	_iconsole_mode = ICONSOLE_CLOSED;
 

	
 
	IConsoleLine::Reset();
 
	memset(_iconsole_history, 0, sizeof(_iconsole_history));
 

	
 
	IConsolePrintF(CC_WARNING, "OpenTTD Game Console Revision 7 - %s", _openttd_revision);
 
	IConsolePrint(CC_WHITE,  "------------------------------------");
 
	IConsolePrint(CC_WHITE,  "use \"help\" for more information");
 
	IConsolePrint(CC_WHITE,  "");
 
	IConsolePrint(TC_LIGHT_BLUE, "OpenTTD Game Console Revision 7 - {}", _openttd_revision);
 
	IConsolePrint(CC_WHITE, "------------------------------------");
 
	IConsolePrint(CC_WHITE, "use \"help\" for more information");
 
	IConsolePrint(CC_WHITE, "");
 
	IConsoleClearCommand();
 
}
 

	
 
void IConsoleClearBuffer()
 
{
 
	IConsoleLine::Reset();
 
}
 

	
 
void IConsoleGUIFree()
 
{
 
	IConsoleClearBuffer();
 
}
 

	
 
/** Change the size of the in-game console window after the screen size changed, or the window state changed. */
 
void IConsoleResize(Window *w)
 
{
 
	switch (_iconsole_mode) {
 
		case ICONSOLE_OPENED:
 
			w->height = _screen.height / 3;
 
			w->width = _screen.width;
 
			break;
 
		case ICONSOLE_FULL:
 
			w->height = _screen.height - ICON_BOTTOM_BORDERWIDTH;
 
			w->width = _screen.width;
src/framerate_gui.cpp
Show inline comments
 
@@ -1002,80 +1002,80 @@ static WindowDesc _frametime_graph_windo
 
);
 

	
 

	
 

	
 
/** Open the general framerate window */
 
void ShowFramerateWindow()
 
{
 
	AllocateWindowDescFront<FramerateWindow>(&_framerate_display_desc, 0);
 
}
 

	
 
/** Open a graph window for a performance element */
 
void ShowFrametimeGraphWindow(PerformanceElement elem)
 
{
 
	if (elem < PFE_FIRST || elem >= PFE_MAX) return; // maybe warn?
 
	AllocateWindowDescFront<FrametimeGraphWindow>(&_frametime_graph_window_desc, elem, true);
 
}
 

	
 
/** Print performance statistics to game console */
 
void ConPrintFramerate()
 
{
 
	const int count1 = NUM_FRAMERATE_POINTS / 8;
 
	const int count2 = NUM_FRAMERATE_POINTS / 4;
 
	const int count3 = NUM_FRAMERATE_POINTS / 1;
 

	
 
	IConsolePrintF(TC_SILVER, "Based on num. data points: %d %d %d", count1, count2, count3);
 
	IConsolePrint(TC_SILVER, "Based on num. data points: {} {} {}", count1, count2, count3);
 

	
 
	static const char *MEASUREMENT_NAMES[PFE_MAX] = {
 
		"Game loop",
 
		"  GL station ticks",
 
		"  GL train ticks",
 
		"  GL road vehicle ticks",
 
		"  GL ship ticks",
 
		"  GL aircraft ticks",
 
		"  GL landscape ticks",
 
		"  GL link graph delays",
 
		"Drawing",
 
		"  Viewport drawing",
 
		"Video output",
 
		"Sound mixing",
 
		"AI/GS scripts total",
 
		"Game script",
 
	};
 
	char ai_name_buf[128];
 

	
 
	static const PerformanceElement rate_elements[] = { PFE_GAMELOOP, PFE_DRAWING, PFE_VIDEO };
 

	
 
	bool printed_anything = false;
 

	
 
	for (const PerformanceElement *e = rate_elements; e < rate_elements + lengthof(rate_elements); e++) {
 
		auto &pf = _pf_data[*e];
 
		if (pf.num_valid == 0) continue;
 
		IConsolePrintF(TC_GREEN, "%s rate: %.2ffps  (expected: %.2ffps)",
 
		IConsolePrint(TC_GREEN, "{} rate: {:.2f}fps  (expected: {:.2f}fps)",
 
			MEASUREMENT_NAMES[*e],
 
			pf.GetRate(),
 
			pf.expected_rate);
 
		printed_anything = true;
 
	}
 

	
 
	for (PerformanceElement e = PFE_FIRST; e < PFE_MAX; e++) {
 
		auto &pf = _pf_data[e];
 
		if (pf.num_valid == 0) continue;
 
		const char *name;
 
		if (e < PFE_AI0) {
 
			name = MEASUREMENT_NAMES[e];
 
		} else {
 
			seprintf(ai_name_buf, lastof(ai_name_buf), "AI %d %s", e - PFE_AI0 + 1, GetAIName(e - PFE_AI0)),
 
			name = ai_name_buf;
 
		}
 
		IConsolePrintF(TC_LIGHT_BLUE, "%s times: %.2fms  %.2fms  %.2fms",
 
		IConsolePrint(TC_LIGHT_BLUE, "{} times: {:.2f}ms  {:.2f}ms  {:.2f}ms",
 
			name,
 
			pf.GetAverageDurationMilliseconds(count1),
 
			pf.GetAverageDurationMilliseconds(count2),
 
			pf.GetAverageDurationMilliseconds(count3));
 
		printed_anything = true;
 
	}
 

	
 
	if (!printed_anything) {
 
		IConsolePrint(CC_ERROR, "No performance measurements have been taken yet.");
 
	}
 
}
src/music/midifile.cpp
Show inline comments
 
@@ -1088,67 +1088,67 @@ std::string MidiFile::GetSMFFile(const M
 

	
 
	byte *data;
 
	size_t datalen;
 
	data = GetMusicCatEntryData(song.filename, song.cat_index, datalen);
 
	if (data == nullptr) return std::string();
 

	
 
	MidiFile midifile;
 
	if (!midifile.LoadMpsData(data, datalen)) {
 
		free(data);
 
		return std::string();
 
	}
 
	free(data);
 

	
 
	if (midifile.WriteSMF(output_filename.c_str())) {
 
		return output_filename;
 
	} else {
 
		return std::string();
 
	}
 
}
 

	
 

	
 
static bool CmdDumpSMF(byte argc, char *argv[])
 
{
 
	if (argc == 0) {
 
		IConsolePrint(CC_WARNING, "Write the current song to a Standard MIDI File. Usage: 'dumpsmf <filename>'");
 
		IConsolePrint(CC_HELP, "Write the current song to a Standard MIDI File. Usage: 'dumpsmf <filename>'.");
 
		return true;
 
	}
 
	if (argc != 2) {
 
		IConsolePrint(CC_WARNING, "You must specify a filename to write MIDI data to.");
 
		return false;
 
	}
 

	
 
	if (_midifile_instance == nullptr) {
 
		IConsolePrint(CC_ERROR, "There is no MIDI file loaded currently, make sure music is playing, and you're using a driver that works with raw MIDI.");
 
		return false;
 
	}
 

	
 
	char fnbuf[MAX_PATH] = { 0 };
 
	if (seprintf(fnbuf, lastof(fnbuf), "%s%s", FiosGetScreenshotDir(), argv[1]) >= (int)lengthof(fnbuf)) {
 
		IConsolePrint(CC_ERROR, "Filename too long.");
 
		return false;
 
	}
 
	IConsolePrintF(CC_INFO, "Dumping MIDI to: %s", fnbuf);
 
	IConsolePrint(CC_INFO, "Dumping MIDI to '{}'.", fnbuf);
 

	
 
	if (_midifile_instance->WriteSMF(fnbuf)) {
 
		IConsolePrint(CC_INFO, "File written successfully.");
 
		return true;
 
	} else {
 
		IConsolePrint(CC_ERROR, "An error occurred writing MIDI file.");
 
		return false;
 
	}
 
}
 

	
 
static void RegisterConsoleMidiCommands()
 
{
 
	static bool registered = false;
 
	if (!registered) {
 
		IConsole::CmdRegister("dumpsmf", CmdDumpSMF);
 
		registered = true;
 
	}
 
}
 

	
 
MidiFile::MidiFile()
 
{
 
	RegisterConsoleMidiCommands();
 
}
 

	
src/network/network_client.cpp
Show inline comments
 
@@ -1139,49 +1139,49 @@ NetworkRecvStatus ClientNetworkGameSocke
 
	/* Only when we're trying to join we really
 
	 * care about the server shutting down. */
 
	if (this->status >= STATUS_JOIN) {
 
		/* To throttle the reconnects a bit, every clients waits its
 
		 * Client ID modulo 16. This way reconnects should be spread
 
		 * out a bit. */
 
		_network_reconnect = _network_own_client_id % 16;
 
		ShowErrorMessage(STR_NETWORK_MESSAGE_SERVER_REBOOT, INVALID_STRING_ID, WL_CRITICAL);
 
	}
 

	
 
	if (this->status == STATUS_ACTIVE) ClientNetworkEmergencySave();
 

	
 
	return NETWORK_RECV_STATUS_SERVER_ERROR;
 
}
 

	
 
NetworkRecvStatus ClientNetworkGameSocketHandler::Receive_SERVER_RCON(Packet *p)
 
{
 
	if (this->status < STATUS_AUTHORIZED) return NETWORK_RECV_STATUS_MALFORMED_PACKET;
 

	
 
	TextColour colour_code = (TextColour)p->Recv_uint16();
 
	if (!IsValidConsoleColour(colour_code)) return NETWORK_RECV_STATUS_MALFORMED_PACKET;
 

	
 
	std::string rcon_out = p->Recv_string(NETWORK_RCONCOMMAND_LENGTH);
 

	
 
	IConsolePrint(colour_code, rcon_out.c_str());
 
	IConsolePrint(colour_code, rcon_out);
 

	
 
	return NETWORK_RECV_STATUS_OKAY;
 
}
 

	
 
NetworkRecvStatus ClientNetworkGameSocketHandler::Receive_SERVER_MOVE(Packet *p)
 
{
 
	if (this->status < STATUS_AUTHORIZED) return NETWORK_RECV_STATUS_MALFORMED_PACKET;
 

	
 
	/* Nothing more in this packet... */
 
	ClientID client_id   = (ClientID)p->Recv_uint32();
 
	CompanyID company_id = (CompanyID)p->Recv_uint8();
 

	
 
	if (client_id == 0) {
 
		/* definitely an invalid client id, debug message and do nothing. */
 
		Debug(net, 1, "Received invalid client index = 0");
 
		return NETWORK_RECV_STATUS_MALFORMED_PACKET;
 
	}
 

	
 
	const NetworkClientInfo *ci = NetworkClientInfo::GetByClientID(client_id);
 
	/* Just make sure we do not try to use a client_index that does not exist */
 
	if (ci == nullptr) return NETWORK_RECV_STATUS_OKAY;
 

	
 
	/* if not valid player, force spectator, else check player exists */
 
	if (!Company::IsValidID(company_id)) company_id = COMPANY_SPECTATOR;
src/newgrf_profiling.cpp
Show inline comments
 
@@ -74,54 +74,54 @@ void NewGRFProfiler::EndResolve(const Sp
 

	
 
	this->calls.push_back(this->cur_call);
 
}
 

	
 
/**
 
 * Capture a recursive sprite group resolution.
 
 */
 
void NewGRFProfiler::RecursiveResolve()
 
{
 
	this->cur_call.subs += 1;
 
}
 

	
 
void NewGRFProfiler::Start()
 
{
 
	this->Abort();
 
	this->active = true;
 
	this->start_tick = _tick_counter;
 
}
 

	
 
uint32 NewGRFProfiler::Finish()
 
{
 
	if (!this->active) return 0;
 

	
 
	if (this->calls.empty()) {
 
		IConsolePrintF(CC_DEBUG, "Finished profile of NewGRF [%08X], no events collected, not writing a file", BSWAP32(this->grffile->grfid));
 
		IConsolePrint(CC_DEBUG, "Finished profile of NewGRF [{:08X}], no events collected, not writing a file.", BSWAP32(this->grffile->grfid));
 
		return 0;
 
	}
 

	
 
	std::string filename = this->GetOutputFilename();
 
	IConsolePrintF(CC_DEBUG, "Finished profile of NewGRF [%08X], writing %u events to %s", BSWAP32(this->grffile->grfid), (uint)this->calls.size(), filename.c_str());
 
	IConsolePrint(CC_DEBUG, "Finished profile of NewGRF [{:08X}], writing {} events to '{}'.", BSWAP32(this->grffile->grfid), this->calls.size(), filename);
 

	
 
	FILE *f = FioFOpenFile(filename, "wt", Subdirectory::NO_DIRECTORY);
 
	FileCloser fcloser(f);
 

	
 
	uint32 total_microseconds = 0;
 

	
 
	fputs("Tick,Sprite,Feature,Item,CallbackID,Microseconds,Depth,Result\n", f);
 
	for (const Call &c : this->calls) {
 
		fprintf(f, "%u,%u,0x%X,%u,0x%X,%u,%u,%u\n", c.tick, c.root_sprite, c.feat, c.item, (uint)c.cb, c.time, c.subs, c.result);
 
		total_microseconds += c.time;
 
	}
 

	
 
	this->Abort();
 

	
 
	return total_microseconds;
 
}
 

	
 
void NewGRFProfiler::Abort()
 
{
 
	this->active = false;
 
	this->calls.clear();
 
}
 

	
 
/**
 
@@ -130,31 +130,31 @@ void NewGRFProfiler::Abort()
 
 */
 
std::string NewGRFProfiler::GetOutputFilename() const
 
{
 
	char timestamp[16] = {};
 
	LocalTime::Format(timestamp, lastof(timestamp), "%Y%m%d-%H%M");
 

	
 
	char filepath[MAX_PATH] = {};
 
	seprintf(filepath, lastof(filepath), "%sgrfprofile-%s-%08X.csv", FiosGetScreenshotDir(), timestamp, BSWAP32(this->grffile->grfid));
 

	
 
	return std::string(filepath);
 
}
 

	
 
uint32 NewGRFProfiler::FinishAll()
 
{
 
	int max_ticks = 0;
 
	uint32 total_microseconds = 0;
 
	for (NewGRFProfiler &pr : _newgrf_profilers) {
 
		if (pr.active) {
 
			total_microseconds += pr.Finish();
 
			max_ticks = std::max(max_ticks, _tick_counter - pr.start_tick);
 
		}
 
	}
 

	
 
	if (total_microseconds > 0 && max_ticks > 0) {
 
		IConsolePrintF(CC_DEBUG, "Total NewGRF callback processing: %u microseconds over %d ticks", total_microseconds, max_ticks);
 
		IConsolePrint(CC_DEBUG, "Total NewGRF callback processing: {} microseconds over {} ticks.", total_microseconds, max_ticks);
 
	}
 

	
 
	_newgrf_profile_end_date = MAX_DAY;
 

	
 
	return total_microseconds;
 
}
src/settings.cpp
Show inline comments
 
@@ -1908,127 +1908,127 @@ bool SetSettingValue(const StringSetting
 

	
 
/**
 
 * Handle changing a string value. This performs validation of the input value
 
 * and calls the appropriate callbacks, and saves it when the value is changed.
 
 * @param object The object the setting is in.
 
 * @param newval The new value for the setting.
 
 */
 
void StringSettingDesc::ChangeValue(const void *object, std::string &newval) const
 
{
 
	this->MakeValueValid(newval);
 
	if (this->pre_check != nullptr && !this->pre_check(newval)) return;
 

	
 
	this->Write(object, newval);
 
	if (this->post_callback != nullptr) this->post_callback(newval);
 

	
 
	if (_save_config) SaveToConfig();
 
}
 

	
 
/* Those 2 functions need to be here, else we have to make some stuff non-static
 
 * and besides, it is also better to keep stuff like this at the same place */
 
void IConsoleSetSetting(const char *name, const char *value, bool force_newgame)
 
{
 
	const SettingDesc *sd = GetSettingFromName(name);
 
	if (sd == nullptr) {
 
		IConsolePrintF(CC_WARNING, "'%s' is an unknown setting.", name);
 
		IConsolePrint(CC_ERROR, "'{}' is an unknown setting.", name);
 
		return;
 
	}
 

	
 
	bool success = true;
 
	if (sd->IsStringSetting()) {
 
		success = SetSettingValue(sd->AsStringSetting(), value, force_newgame);
 
	} else if (sd->IsIntSetting()) {
 
		uint32 val;
 
		extern bool GetArgumentInteger(uint32 *value, const char *arg);
 
		success = GetArgumentInteger(&val, value);
 
		if (!success) {
 
			IConsolePrintF(CC_ERROR, "'%s' is not an integer.", value);
 
			IConsolePrint(CC_ERROR, "'{}' is not an integer.", value);
 
			return;
 
		}
 

	
 
		success = SetSettingValue(sd->AsIntSetting(), val, force_newgame);
 
	}
 

	
 
	if (!success) {
 
		if (_network_server) {
 
			IConsolePrint(CC_ERROR, "This command/variable is not available during network games.");
 
		} else {
 
			IConsolePrint(CC_ERROR, "This command/variable is only available to a network server.");
 
		}
 
	}
 
}
 

	
 
void IConsoleSetSetting(const char *name, int value)
 
{
 
	const SettingDesc *sd = GetSettingFromName(name);
 
	assert(sd != nullptr);
 
	SetSettingValue(sd->AsIntSetting(), value);
 
}
 

	
 
/**
 
 * Output value of a specific setting to the console
 
 * @param name  Name of the setting to output its value
 
 * @param force_newgame force the newgame settings
 
 */
 
void IConsoleGetSetting(const char *name, bool force_newgame)
 
{
 
	const SettingDesc *sd = GetSettingFromName(name);
 
	if (sd == nullptr) {
 
		IConsolePrintF(CC_WARNING, "'%s' is an unknown setting.", name);
 
		IConsolePrint(CC_ERROR, "'{}' is an unknown setting.", name);
 
		return;
 
	}
 

	
 
	const void *object = (_game_mode == GM_MENU || force_newgame) ? &_settings_newgame : &_settings_game;
 

	
 
	if (sd->IsStringSetting()) {
 
		IConsolePrintF(CC_WARNING, "Current value for '%s' is: '%s'", sd->name.c_str(), sd->AsStringSetting()->Read(object).c_str());
 
		IConsolePrint(CC_INFO, "Current value for '{}' is '{}'.", sd->name, sd->AsStringSetting()->Read(object));
 
	} else if (sd->IsIntSetting()) {
 
		char value[20];
 
		sd->FormatValue(value, lastof(value), object);
 
		const IntSettingDesc *int_setting = sd->AsIntSetting();
 
		IConsolePrintF(CC_WARNING, "Current value for '%s' is: '%s' (min: %s%d, max: %u)",
 
		IConsolePrint(CC_INFO, "Current value for '{}' is '{}' (min: {}{}, max: {}).",
 
			sd->name.c_str(), value, (sd->flags & SF_GUI_0_IS_SPECIAL) ? "(0) " : "", int_setting->min, int_setting->max);
 
	}
 
}
 

	
 
/**
 
 * List all settings and their value to the console
 
 *
 
 * @param prefilter  If not \c nullptr, only list settings with names that begin with \a prefilter prefix
 
 */
 
void IConsoleListSettings(const char *prefilter)
 
{
 
	IConsolePrintF(CC_WARNING, "All settings with their current value:");
 
	IConsolePrint(CC_HELP, "All settings with their current value:");
 

	
 
	for (auto &sd : _settings) {
 
		if (!SlIsObjectCurrentlyValid(sd->save.version_from, sd->save.version_to)) continue;
 
		if (prefilter != nullptr && sd->name.find(prefilter) == std::string::npos) continue;
 
		char value[80];
 
		sd->FormatValue(value, lastof(value), &GetGameSettings());
 
		IConsolePrintF(CC_DEFAULT, "%s = %s", sd->name.c_str(), value);
 
		IConsolePrint(CC_DEFAULT, "{} = {}", sd->name, value);
 
	}
 

	
 
	IConsolePrintF(CC_WARNING, "Use 'setting' command to change a value");
 
	IConsolePrint(CC_HELP, "Use 'setting' command to change a value.");
 
}
 

	
 
/**
 
 * Get the SaveLoad description for the SettingTable.
 
 * @param settings SettingDesc struct containing all information.
 
 * @param is_loading True iff the SaveLoad table is for loading.
 
 * @return Vector with SaveLoad entries for the SettingTable.
 
 */
 
static std::vector<SaveLoad> GetSettingsDesc(const SettingTable &settings, bool is_loading)
 
{
 
	std::vector<SaveLoad> saveloads;
 
	for (auto &sd : settings) {
 
		if (sd->flags & SF_NOT_IN_SAVE) continue;
 

	
 
		if (is_loading && (sd->flags & SF_NO_NETWORK_SYNC) && _networking && !_network_server) {
 
			/* We don't want to read this setting, so we do need to skip over it. */
 
			saveloads.push_back({sd->save.cmd, GetVarFileType(sd->save.conv) | SLE_VAR_NULL, sd->save.length, sd->save.version_from, sd->save.version_to, 0, nullptr, 0});
 
			continue;
 
		}
 

	
 
		saveloads.push_back(sd->save);
 
	}
 

	
 
	return saveloads;
0 comments (0 inline, 0 general)