Changeset - r22443:89afba6ce61a
[Not reviewed]
master
0 16 0
alberth - 8 years ago 2016-09-04 12:57:43
alberth@openttd.org
(svn r27650) -Codechange: Replace SaveOrLoadMode by FileOperation and DetailedFileType.
16 files changed with 113 insertions and 100 deletions:
0 comments (0 inline, 0 general)
src/console_cmds.cpp
Show inline comments
 
@@ -225,193 +225,193 @@ DEF_CONSOLE_CMD(ConResetEnginePool)
 
		return true;
 
	}
 

	
 
	return true;
 
}
 

	
 
#ifdef _DEBUG
 
/**
 
 * Reset a tile to bare land in debug mode.
 
 * param tile number.
 
 * @return True when the tile is reset or the help on usage was printed (0 or two parameters).
 
 */
 
DEF_CONSOLE_CMD(ConResetTile)
 
{
 
	if (argc == 0) {
 
		IConsoleHelp("Reset a tile to bare land. Usage: 'resettile <tile>'");
 
		IConsoleHelp("Tile can be either decimal (34161) or hexadecimal (0x4a5B)");
 
		return true;
 
	}
 

	
 
	if (argc == 2) {
 
		uint32 result;
 
		if (GetArgumentInteger(&result, argv[1])) {
 
			DoClearSquare((TileIndex)result);
 
			return true;
 
		}
 
	}
 

	
 
	return false;
 
}
 
#endif /* _DEBUG */
 

	
 
/**
 
 * Scroll to a tile on the map.
 
 * @param arg1 tile tile number or tile x coordinate.
 
 * @param arg2 optionally tile y coordinate.
 
 * @note When only one argument is given it is intepreted as the tile number.
 
 *       When two arguments are given, they are interpreted as the tile's x
 
 *       and y coordinates.
 
 * @return True when either console help was shown or a proper amount of parameters given.
 
 */
 
DEF_CONSOLE_CMD(ConScrollToTile)
 
{
 
	switch (argc) {
 
		case 0:
 
			IConsoleHelp("Center the screen on a given tile.");
 
			IConsoleHelp("Usage: 'scrollto <tile>' or 'scrollto <x> <y>'");
 
			IConsoleHelp("Numbers can be either decimal (34161) or hexadecimal (0x4a5B).");
 
			return true;
 

	
 
		case 2: {
 
			uint32 result;
 
			if (GetArgumentInteger(&result, argv[1])) {
 
				if (result >= MapSize()) {
 
					IConsolePrint(CC_ERROR, "Tile does not exist");
 
					return true;
 
				}
 
				ScrollMainWindowToTile((TileIndex)result);
 
				return true;
 
			}
 
			break;
 
		}
 

	
 
		case 3: {
 
			uint32 x, y;
 
			if (GetArgumentInteger(&x, argv[1]) && GetArgumentInteger(&y, argv[2])) {
 
				if (x >= MapSizeX() || y >= MapSizeY()) {
 
					IConsolePrint(CC_ERROR, "Tile does not exist");
 
					return true;
 
				}
 
				ScrollMainWindowToTile(TileXY(x, y));
 
				return true;
 
			}
 
			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, SL_SAVE, SAVE_DIR) != SL_OK) {
 
		if (SaveOrLoad(filename, FOP_SAVE, DFT_GAME_FILE, SAVE_DIR) != SL_OK) {
 
			IConsolePrint(CC_ERROR, "Saving map failed");
 
		} else {
 
			IConsolePrintF(CC_DEFAULT, "Map successfully saved to %s", 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;
 
}
 

	
 
DEF_CONSOLE_CMD(ConLoad)
 
{
 
	if (argc == 0) {
 
		IConsoleHelp("Load a game by name or index. Usage: 'load <file | number>'");
 
		return true;
 
	}
 

	
 
	if (argc != 2) return false;
 

	
 
	const char *file = argv[1];
 
	_console_file_list.ValidateFileList();
 
	const FiosItem *item = _console_file_list.FindItem(file);
 
	if (item != NULL) {
 
		if (GetAbstractFileType(item->type) == FT_SAVEGAME) {
 
			_switch_mode = SM_LOAD_GAME;
 
			_file_to_saveload.SetMode(item->type);
 

	
 
			strecpy(_file_to_saveload.name, FiosBrowseTo(item), lastof(_file_to_saveload.name));
 
			strecpy(_file_to_saveload.title, item->title, lastof(_file_to_saveload.title));
 
		} else {
 
			IConsolePrintF(CC_ERROR, "%s: Not a savegame.", file);
 
		}
 
	} else {
 
		IConsolePrintF(CC_ERROR, "%s: No such file or directory.", file);
 
	}
 

	
 
	return true;
 
}
 

	
 

	
 
DEF_CONSOLE_CMD(ConRemove)
 
{
 
	if (argc == 0) {
 
		IConsoleHelp("Remove a savegame by name or index. Usage: 'rm <file | number>'");
 
		return true;
 
	}
 

	
 
	if (argc != 2) return false;
 

	
 
	const char *file = argv[1];
 
	_console_file_list.ValidateFileList();
 
	const FiosItem *item = _console_file_list.FindItem(file);
 
	if (item != NULL) {
 
		if (!FiosDelete(item->name)) {
 
			IConsolePrintF(CC_ERROR, "%s: Failed to delete file", file);
 
		}
 
	} else {
 
		IConsolePrintF(CC_ERROR, "%s: No such file or directory.", 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.Length(); i++) {
 
		IConsolePrintF(CC_DEFAULT, "%d) %s", i, _console_file_list[i].title);
 
	}
 

	
src/crashlog.cpp
Show inline comments
 
@@ -295,193 +295,193 @@ char *CrashLog::LogLibraries(char *buffe
 
}
 

	
 
/**
 
 * Writes the gamelog data to the buffer.
 
 * @param buffer The begin where to write at.
 
 * @param last   The last position in the buffer to write to.
 
 * @return the position of the \c '\0' character after the buffer.
 
 */
 
char *CrashLog::LogGamelog(char *buffer, const char *last) const
 
{
 
	CrashLog::gamelog_buffer = buffer;
 
	CrashLog::gamelog_last = last;
 
	GamelogPrint(&CrashLog::GamelogFillCrashLog);
 
	return CrashLog::gamelog_buffer + seprintf(CrashLog::gamelog_buffer, last, "\n");
 
}
 

	
 
/**
 
 * Fill the crash log buffer with all data of a crash log.
 
 * @param buffer The begin where to write at.
 
 * @param last   The last position in the buffer to write to.
 
 * @return the position of the \c '\0' character after the buffer.
 
 */
 
char *CrashLog::FillCrashLog(char *buffer, const char *last) const
 
{
 
	time_t cur_time = time(NULL);
 
	buffer += seprintf(buffer, last, "*** OpenTTD Crash Report ***\n\n");
 
	buffer += seprintf(buffer, last, "Crash at: %s", asctime(gmtime(&cur_time)));
 

	
 
	YearMonthDay ymd;
 
	ConvertDateToYMD(_date, &ymd);
 
	buffer += seprintf(buffer, last, "In game date: %i-%02i-%02i (%i)\n\n", ymd.year, ymd.month + 1, ymd.day, _date_fract);
 

	
 
	buffer = this->LogError(buffer, last, CrashLog::message);
 
	buffer = this->LogOpenTTDVersion(buffer, last);
 
	buffer = this->LogRegisters(buffer, last);
 
	buffer = this->LogStacktrace(buffer, last);
 
	buffer = this->LogOSVersion(buffer, last);
 
	buffer = this->LogCompiler(buffer, last);
 
	buffer = this->LogConfiguration(buffer, last);
 
	buffer = this->LogLibraries(buffer, last);
 
	buffer = this->LogModules(buffer, last);
 
	buffer = this->LogGamelog(buffer, last);
 

	
 
	buffer += seprintf(buffer, last, "*** End of OpenTTD Crash Report ***\n");
 
	return buffer;
 
}
 

	
 
/**
 
 * Write the crash log to a file.
 
 * @note On success the filename will be filled with the full path of the
 
 *       crash log file. Make sure filename is at least \c MAX_PATH big.
 
 * @param buffer The begin of the buffer to write to the disk.
 
 * @param filename      Output for the filename of the written file.
 
 * @param filename_last The last position in the filename buffer.
 
 * @return true when the crash log was successfully written.
 
 */
 
bool CrashLog::WriteCrashLog(const char *buffer, char *filename, const char *filename_last) const
 
{
 
	seprintf(filename, filename_last, "%scrash.log", _personal_dir);
 

	
 
	FILE *file = FioFOpenFile(filename, "w", NO_DIRECTORY);
 
	if (file == NULL) return false;
 

	
 
	size_t len = strlen(buffer);
 
	size_t written = fwrite(buffer, 1, len, file);
 

	
 
	FioFCloseFile(file);
 
	return len == written;
 
}
 

	
 
/* virtual */ int CrashLog::WriteCrashDump(char *filename, const char *filename_last) const
 
{
 
	/* Stub implementation; not all OSes support this. */
 
	return 0;
 
}
 

	
 
/**
 
 * Write the (crash) savegame to a file.
 
 * @note On success the filename will be filled with the full path of the
 
 *       crash save file. Make sure filename is at least \c MAX_PATH big.
 
 * @param filename      Output for the filename of the written file.
 
 * @param filename_last The last position in the filename buffer.
 
 * @return true when the crash save was successfully made.
 
 */
 
bool CrashLog::WriteSavegame(char *filename, const char *filename_last) const
 
{
 
	/* If the map array doesn't exist, saving will fail too. If the map got
 
	 * initialised, there is a big chance the rest is initialised too. */
 
	if (_m == NULL) return false;
 

	
 
	try {
 
		GamelogEmergency();
 

	
 
		seprintf(filename, filename_last, "%scrash.sav", _personal_dir);
 

	
 
		/* Don't do a threaded saveload. */
 
		return SaveOrLoad(filename, SL_SAVE, NO_DIRECTORY, false) == SL_OK;
 
		return SaveOrLoad(filename, FOP_SAVE, DFT_GAME_FILE, NO_DIRECTORY, false) == SL_OK;
 
	} catch (...) {
 
		return false;
 
	}
 
}
 

	
 
/**
 
 * Write the (crash) screenshot to a file.
 
 * @note On success the filename will be filled with the full path of the
 
 *       screenshot. Make sure filename is at least \c MAX_PATH big.
 
 * @param filename      Output for the filename of the written file.
 
 * @param filename_last The last position in the filename buffer.
 
 * @return true when the crash screenshot was successfully made.
 
 */
 
bool CrashLog::WriteScreenshot(char *filename, const char *filename_last) const
 
{
 
	/* Don't draw when we have invalid screen size */
 
	if (_screen.width < 1 || _screen.height < 1 || _screen.dst_ptr == NULL) return false;
 

	
 
	bool res = MakeScreenshot(SC_CRASHLOG, "crash");
 
	if (res) strecpy(filename, _full_screenshot_name, filename_last);
 
	return res;
 
}
 

	
 
/**
 
 * Makes the crash log, writes it to a file and then subsequently tries
 
 * to make a crash dump and crash savegame. It uses DEBUG to write
 
 * information like paths to the console.
 
 * @return true when everything is made successfully.
 
 */
 
bool CrashLog::MakeCrashLog() const
 
{
 
	/* Don't keep looping logging crashes. */
 
	static bool crashlogged = false;
 
	if (crashlogged) return false;
 
	crashlogged = true;
 

	
 
	char filename[MAX_PATH];
 
	char buffer[65536];
 
	bool ret = true;
 

	
 
	printf("Crash encountered, generating crash log...\n");
 
	this->FillCrashLog(buffer, lastof(buffer));
 
	printf("%s\n", buffer);
 
	printf("Crash log generated.\n\n");
 

	
 
	printf("Writing crash log to disk...\n");
 
	bool bret = this->WriteCrashLog(buffer, filename, lastof(filename));
 
	if (bret) {
 
		printf("Crash log written to %s. Please add this file to any bug reports.\n\n", filename);
 
	} else {
 
		printf("Writing crash log failed. Please attach the output above to any bug reports.\n\n");
 
		ret = false;
 
	}
 

	
 
	/* Don't mention writing crash dumps because not all platforms support it. */
 
	int dret = this->WriteCrashDump(filename, lastof(filename));
 
	if (dret < 0) {
 
		printf("Writing crash dump failed.\n\n");
 
		ret = false;
 
	} else if (dret > 0) {
 
		printf("Crash dump written to %s. Please add this file to any bug reports.\n\n", filename);
 
	}
 

	
 
	printf("Writing crash savegame...\n");
 
	bret = this->WriteSavegame(filename, lastof(filename));
 
	if (bret) {
 
		printf("Crash savegame written to %s. Please add this file and the last (auto)save to any bug reports.\n\n", filename);
 
	} else {
 
		ret = false;
 
		printf("Writing crash savegame failed. Please attach the last (auto)save to any bug reports.\n\n");
 
	}
 

	
 
	printf("Writing crash screenshot...\n");
 
	bret = this->WriteScreenshot(filename, lastof(filename));
 
	if (bret) {
 
		printf("Crash screenshot written to %s. Please add this file to any bug reports.\n\n", filename);
 
	} else {
 
		ret = false;
 
		printf("Writing crash screenshot failed.\n\n");
 
	}
 

	
 
	return ret;
 
}
 

	
 
/**
 
 * Sets a message for the error message handler.
 
 * @param message The error message of the error.
 
 */
 
/* static */ void CrashLog::SetErrorMessage(const char *message)
 
{
 
	CrashLog::message = message;
 
}
 

	
 
/**
 
 * Try to close the sound/video stuff so it doesn't keep lingering around
 
 * incorrect video states or so, e.g. keeping dpmi disabled.
src/fileio_type.h
Show inline comments
 
/* $Id$ */
 

	
 
/*
 
 * This file is part of OpenTTD.
 
 * OpenTTD is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, version 2.
 
 * OpenTTD is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
 
 * See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with OpenTTD. If not, see <http://www.gnu.org/licenses/>.
 
 */
 

	
 
/** @file fileio_type.h Types for Standard In/Out file operations */
 

	
 
#ifndef FILEIO_TYPE_H
 
#define FILEIO_TYPE_H
 

	
 
#include "core/enum_type.hpp"
 

	
 
/** The different abstract types of files that the system knows about. */
 
enum AbstractFileType {
 
	FT_NONE,      ///< nothing to do
 
	FT_SAVEGAME,  ///< old or new savegame
 
	FT_SCENARIO,  ///< old or new scenario
 
	FT_HEIGHTMAP, ///< heightmap file
 

	
 
	FT_INVALID = 7, ///< Invalid or unknown file type.
 
	FT_NUMBITS = 3, ///< Number of bits required for storing a #AbstractFileType value.
 
	FT_MASK = (1 << FT_NUMBITS) - 1, ///< Bitmask for extracting an abstract file type.
 
};
 

	
 
/** Kinds of files in each #AbstractFileType. */
 
enum DetailedFileType {
 
	/* Save game and scenario files. */
 
	DFT_OLD_GAME_FILE, ///< Old save game or scenario file.
 
	DFT_GAME_FILE,     ///< Save game or scenario file.
 

	
 
	/* Heightmap files. */
 
	DFT_HEIGHTMAP_BMP, ///< BMP file.
 
	DFT_HEIGHTMAP_PNG, ///< PNG file.
 

	
 
	/* fios 'files' */
 
	DFT_FIOS_DRIVE,  ///< A drive (letter) entry.
 
	DFT_FIOS_PARENT, ///< A parent directory entry.
 
	DFT_FIOS_DIR,    ///< A directory entry.
 
	DFT_FIOS_DIRECT, ///< Direct filename.
 

	
 
	DFT_INVALID = 255, ///< Unknown or invalid file.
 
};
 

	
 
/** Operation performed on the file. */
 
enum FileOperation {
 
	FOP_CHECK,   ///< Load file for checking and/or preview.
 
	FOP_LOAD, ///< File is being loaded.
 
	FOP_SAVE, ///< File is being saved.
 

	
 
	FOP_INVALID, ///< Unknown file operation.
 
};
 

	
 
/**
 
 * Construct an enum value for #FiosType as a combination of an abstract and a detailed file type.
 
 * @param abstract Abstract file type (one of #AbstractFileType).
 
 * @param detailed Detailed file type (one of #DetailedFileType).
 
 */
 
#define MAKE_FIOS_TYPE(abstract, detailed) ((abstract) | ((detailed) << FT_NUMBITS))
 

	
 
/**
 
 * Elements of a file system that are recognized.
 
 * Values are a combination of #AbstractFileType and #DetailedFileType.
 
 * @see GetAbstractFileType GetDetailedFileType
 
 */
 
enum FiosType {
 
	FIOS_TYPE_DRIVE  = MAKE_FIOS_TYPE(FT_NONE, DFT_FIOS_DRIVE),
 
	FIOS_TYPE_PARENT = MAKE_FIOS_TYPE(FT_NONE, DFT_FIOS_PARENT),
 
	FIOS_TYPE_DIR    = MAKE_FIOS_TYPE(FT_NONE, DFT_FIOS_DIR),
 
	FIOS_TYPE_DIRECT = MAKE_FIOS_TYPE(FT_NONE, DFT_FIOS_DIRECT),
 

	
 
	FIOS_TYPE_FILE         = MAKE_FIOS_TYPE(FT_SAVEGAME, DFT_GAME_FILE),
 
	FIOS_TYPE_OLDFILE      = MAKE_FIOS_TYPE(FT_SAVEGAME, DFT_OLD_GAME_FILE),
 
	FIOS_TYPE_SCENARIO     = MAKE_FIOS_TYPE(FT_SCENARIO, DFT_GAME_FILE),
 
	FIOS_TYPE_OLD_SCENARIO = MAKE_FIOS_TYPE(FT_SCENARIO, DFT_OLD_GAME_FILE),
 
	FIOS_TYPE_PNG          = MAKE_FIOS_TYPE(FT_HEIGHTMAP, DFT_HEIGHTMAP_PNG),
 
	FIOS_TYPE_BMP          = MAKE_FIOS_TYPE(FT_HEIGHTMAP, DFT_HEIGHTMAP_BMP),
 

	
 
	FIOS_TYPE_INVALID = MAKE_FIOS_TYPE(FT_INVALID, DFT_INVALID),
 
};
 

	
 
#undef MAKE_FIOS_TYPE
 

	
 
/**
 
 * Extract the abstract file type from a #FiosType.
 
 * @param fios_type Type to query.
 
 * @return The Abstract file type of the \a fios_type.
 
 */
 
inline AbstractFileType GetAbstractFileType(FiosType fios_type)
 
{
 
	return static_cast<AbstractFileType>(fios_type & FT_MASK);
 
}
 

	
 
/**
 
 * Extract the detailed file type from a #FiosType.
 
 * @param fios_type Type to query.
 
 * @return The Detailed file type of the \a fios_type.
 
 */
 
inline DetailedFileType GetDetailedFileType(FiosType fios_type)
 
{
 
	return static_cast<DetailedFileType>(fios_type >> FT_NUMBITS);
 
}
 

	
 
/**
 
 * The different kinds of subdirectories OpenTTD uses
 
 */
 
enum Subdirectory {
 
	BASE_DIR,      ///< Base directory for all subdirectories
 
	SAVE_DIR,      ///< Base directory for all savegames
 
	AUTOSAVE_DIR,  ///< Subdirectory of save for autosaves
 
	SCENARIO_DIR,  ///< Base directory for all scenarios
 
	HEIGHTMAP_DIR, ///< Subdirectory of scenario for heightmaps
 
	OLD_GM_DIR,    ///< Old subdirectory for the music
 
	OLD_DATA_DIR,  ///< Old subdirectory for the data.
 
	BASESET_DIR,   ///< Subdirectory for all base data (base sets, intro game)
 
	NEWGRF_DIR,    ///< Subdirectory for all NewGRFs
 
	LANG_DIR,      ///< Subdirectory for all translation files
 
	AI_DIR,        ///< Subdirectory for all %AI files
 
	AI_LIBRARY_DIR,///< Subdirectory for all %AI libraries
 
	GAME_DIR,      ///< Subdirectory for all game scripts
 
	GAME_LIBRARY_DIR, ///< Subdirectory for all GS libraries
 
	SCREENSHOT_DIR,   ///< Subdirectory for all screenshots
 
	NUM_SUBDIRS,   ///< Number of subdirectories
 
	NO_DIRECTORY,  ///< A path without any base directory
 
};
 

	
 
/**
 
 * Types of searchpaths OpenTTD might use
 
 */
 
enum Searchpath {
 
	SP_FIRST_DIR,
 
	SP_WORKING_DIR = SP_FIRST_DIR, ///< Search in the working directory
 
#if defined(WITH_XDG_BASEDIR) && defined(WITH_PERSONAL_DIR)
 
	SP_PERSONAL_DIR_XDG,           ///< Search in the personal directory from the XDG specification
 
#endif
 
	SP_PERSONAL_DIR,               ///< Search in the personal directory
 
	SP_SHARED_DIR,                 ///< Search in the shared directory, like 'Shared Files' under Windows
 
	SP_BINARY_DIR,                 ///< Search in the directory where the binary resides
 
	SP_INSTALLATION_DIR,           ///< Search in the installation directory
 
	SP_APPLICATION_BUNDLE_DIR,     ///< Search within the application bundle
 
	SP_AUTODOWNLOAD_DIR,           ///< Search within the autodownload directory
 
	NUM_SEARCHPATHS
 
};
 

	
 
DECLARE_POSTFIX_INCREMENT(Searchpath)
 

	
 
#endif /* FILEIO_TYPE_H */
src/fios_gui.cpp
Show inline comments
 
@@ -491,293 +491,293 @@ public:
 
				break;
 
			case WID_SL_SORT_BYNAME:
 
			case WID_SL_SORT_BYDATE: {
 
				Dimension d = GetStringBoundingBox(this->GetWidget<NWidgetCore>(widget)->widget_data);
 
				d.width += padding.width + Window::SortButtonWidth() * 2; // Doubled since the string is centred and it also looks better.
 
				d.height += padding.height;
 
				*size = maxdim(*size, d);
 
				break;
 
			}
 
		}
 
	}
 

	
 
	virtual void OnPaint()
 
	{
 
		if (_savegame_sort_dirty) {
 
			_savegame_sort_dirty = false;
 
			SortSaveGameList(this->fios_items);
 
		}
 

	
 
		this->vscroll->SetCount(this->fios_items.Length());
 
		this->DrawWidgets();
 
	}
 

	
 
	virtual void OnClick(Point pt, int widget, int click_count)
 
	{
 
		switch (widget) {
 
			case WID_SL_SORT_BYNAME: // Sort save names by name
 
				_savegame_sort_order = (_savegame_sort_order == SORT_BY_NAME) ?
 
					SORT_BY_NAME | SORT_DESCENDING : SORT_BY_NAME;
 
				_savegame_sort_dirty = true;
 
				this->SetDirty();
 
				break;
 

	
 
			case WID_SL_SORT_BYDATE: // Sort save names by date
 
				_savegame_sort_order = (_savegame_sort_order == SORT_BY_DATE) ?
 
					SORT_BY_DATE | SORT_DESCENDING : SORT_BY_DATE;
 
				_savegame_sort_dirty = true;
 
				this->SetDirty();
 
				break;
 

	
 
			case WID_SL_HOME_BUTTON: // OpenTTD 'button', jumps to OpenTTD directory
 
				FiosBrowseTo(&o_dir);
 
				this->InvalidateData();
 
				break;
 

	
 
			case WID_SL_LOAD_BUTTON:
 
				if (this->selected != NULL && !_load_check_data.HasErrors()) {
 
					const char *name = FiosBrowseTo(this->selected);
 
					_file_to_saveload.SetMode(this->selected->type);
 

	
 
					strecpy(_file_to_saveload.name, name, lastof(_file_to_saveload.name));
 
					strecpy(_file_to_saveload.title, this->selected->title, lastof(_file_to_saveload.title));
 

	
 
					if (this->abstract_filetype == FT_HEIGHTMAP) {
 
						delete this;
 
						ShowHeightmapLoad();
 

	
 
					} else if (!_load_check_data.HasNewGrfs() || _load_check_data.grf_compatibility != GLC_NOT_FOUND || _settings_client.gui.UserIsAllowedToChangeNewGRFs()) {
 
						_switch_mode = (_game_mode == GM_EDITOR) ? SM_LOAD_SCENARIO : SM_LOAD_GAME;
 
						ClearErrorMessages();
 
						delete this;
 
					}
 
				}
 
				break;
 

	
 
			case WID_SL_NEWGRF_INFO:
 
				if (_load_check_data.HasNewGrfs()) {
 
					ShowNewGRFSettings(false, false, false, &_load_check_data.grfconfig);
 
				}
 
				break;
 

	
 
			case WID_SL_MISSING_NEWGRFS:
 
				if (!_network_available) {
 
					ShowErrorMessage(STR_NETWORK_ERROR_NOTAVAILABLE, INVALID_STRING_ID, WL_ERROR);
 
				} else if (_load_check_data.HasNewGrfs()) {
 
#if defined(ENABLE_NETWORK)
 
					ShowMissingContentWindow(_load_check_data.grfconfig);
 
#endif
 
				}
 
				break;
 

	
 
			case WID_SL_DRIVES_DIRECTORIES_LIST: { // Click the listbox
 
				int y = this->vscroll->GetScrolledRowFromWidget(pt.y, this, WID_SL_DRIVES_DIRECTORIES_LIST, WD_FRAMERECT_TOP);
 
				if (y == INT_MAX) return;
 

	
 
				const FiosItem *file = this->fios_items.Get(y);
 

	
 
				const char *name = FiosBrowseTo(file);
 
				if (name != NULL) {
 
					if (click_count == 1) {
 
						if (this->selected != file) {
 
							this->selected = file;
 
							_load_check_data.Clear();
 

	
 
							if (GetDetailedFileType(file->type) == DFT_GAME_FILE) {
 
								/* Other detailed file types cannot be checked before. */
 
								SaveOrLoad(name, SL_LOAD_CHECK, NO_DIRECTORY, false);
 
								SaveOrLoad(name, FOP_CHECK, DFT_GAME_FILE, NO_DIRECTORY, false);
 
							}
 

	
 
							this->InvalidateData(1);
 
						}
 
						if (this->fop == FOP_SAVE) {
 
							/* Copy clicked name to editbox */
 
							this->filename_editbox.text.Assign(file->title);
 
							this->SetWidgetDirty(WID_SL_SAVE_OSK_TITLE);
 
						}
 
					} else if (!_load_check_data.HasErrors()) {
 
						this->selected = file;
 
						if (this->fop == FOP_LOAD) {
 
							if (this->abstract_filetype == FT_SAVEGAME || this->abstract_filetype == FT_SCENARIO) {
 
								this->OnClick(pt, WID_SL_LOAD_BUTTON, 1);
 
							} else {
 
								assert(this->abstract_filetype == FT_HEIGHTMAP);
 
								_file_to_saveload.SetMode(file->type);
 
								strecpy(_file_to_saveload.name, name, lastof(_file_to_saveload.name));
 
								strecpy(_file_to_saveload.title, file->title, lastof(_file_to_saveload.title));
 

	
 
								delete this;
 
								ShowHeightmapLoad();
 
							}
 
						}
 
					}
 
				} else {
 
					/* Changed directory, need refresh. */
 
					this->InvalidateData();
 
				}
 
				break;
 
			}
 

	
 
			case WID_SL_CONTENT_DOWNLOAD:
 
				if (!_network_available) {
 
					ShowErrorMessage(STR_NETWORK_ERROR_NOTAVAILABLE, INVALID_STRING_ID, WL_ERROR);
 
				} else {
 
#if defined(ENABLE_NETWORK)
 
					assert(this->fop == FOP_LOAD);
 
					switch (this->abstract_filetype) {
 
						default: NOT_REACHED();
 
						case FT_SCENARIO:  ShowNetworkContentListWindow(NULL, CONTENT_TYPE_SCENARIO);  break;
 
						case FT_HEIGHTMAP: ShowNetworkContentListWindow(NULL, CONTENT_TYPE_HEIGHTMAP); break;
 
					}
 
#endif
 
				}
 
				break;
 

	
 
			case WID_SL_DELETE_SELECTION: // Delete
 
				break;
 

	
 
			case WID_SL_SAVE_GAME: // Save game
 
				/* Note, this is also called via the OSK; and we need to lower the button. */
 
				this->HandleButtonClick(WID_SL_SAVE_GAME);
 
				break;
 
		}
 
	}
 

	
 
	virtual EventState OnKeyPress(WChar key, uint16 keycode)
 
	{
 
		if (keycode == WKC_ESC) {
 
			delete this;
 
			return ES_HANDLED;
 
		}
 

	
 
		return ES_NOT_HANDLED;
 
	}
 

	
 
	virtual void OnTimeout()
 
	{
 
		/* Widgets WID_SL_DELETE_SELECTION and WID_SL_SAVE_GAME only exist when saving to a file. */
 
		if (this->fop != FOP_SAVE) return;
 

	
 
		if (this->IsWidgetLowered(WID_SL_DELETE_SELECTION)) { // Delete button clicked
 
			if (!FiosDelete(this->filename_editbox.text.buf)) {
 
				ShowErrorMessage(STR_ERROR_UNABLE_TO_DELETE_FILE, INVALID_STRING_ID, WL_ERROR);
 
			} else {
 
				this->InvalidateData();
 
				/* Reset file name to current date on successful delete */
 
				if (this->abstract_filetype == FT_SAVEGAME) GenerateFileName();
 
			}
 
		} else if (this->IsWidgetLowered(WID_SL_SAVE_GAME)) { // Save button clicked
 
			if (this->abstract_filetype == FT_SAVEGAME || this->abstract_filetype == FT_SCENARIO) {
 
				_switch_mode = SM_SAVE_GAME;
 
				FiosMakeSavegameName(_file_to_saveload.name, this->filename_editbox.text.buf, lastof(_file_to_saveload.name));
 
			} else {
 
				_switch_mode = SM_SAVE_HEIGHTMAP;
 
				FiosMakeHeightmapName(_file_to_saveload.name, this->filename_editbox.text.buf, lastof(_file_to_saveload.name));
 
			}
 

	
 
			/* In the editor set up the vehicle engines correctly (date might have changed) */
 
			if (_game_mode == GM_EDITOR) StartupEngines();
 
		}
 
	}
 

	
 
	virtual void OnResize()
 
	{
 
		this->vscroll->SetCapacityFromWidget(this, WID_SL_DRIVES_DIRECTORIES_LIST);
 
	}
 

	
 
	/**
 
	 * Some data on this window has become invalid.
 
	 * @param data Information about the changed data.
 
	 * @param gui_scope Whether the call is done from GUI scope. You may not do everything when not in GUI scope. See #InvalidateWindowData() for details.
 
	 */
 
	virtual void OnInvalidateData(int data = 0, bool gui_scope = true)
 
	{
 
		switch (data) {
 
			case 0:
 
				/* Rescan files */
 
				this->selected = NULL;
 
				_load_check_data.Clear();
 
				if (!gui_scope) break;
 

	
 
				_fios_path_changed = true;
 
				this->fios_items.BuildFileList(this->abstract_filetype, this->fop);
 
				this->vscroll->SetCount(this->fios_items.Length());
 
				this->selected = NULL;
 
				_load_check_data.Clear();
 
				/* FALL THROUGH */
 
			case 1:
 
				/* Selection changes */
 
				if (!gui_scope) break;
 

	
 
				if (this->fop != FOP_LOAD) break;
 

	
 
				switch (this->abstract_filetype) {
 
					case FT_HEIGHTMAP:
 
						this->SetWidgetDisabledState(WID_SL_LOAD_BUTTON, this->selected == NULL || _load_check_data.HasErrors());
 
						break;
 

	
 
					case FT_SAVEGAME:
 
					case FT_SCENARIO: {
 
						bool disabled = this->selected == NULL || _load_check_data.HasErrors();
 
						if (!_settings_client.gui.UserIsAllowedToChangeNewGRFs()) {
 
							disabled |= _load_check_data.HasNewGrfs() && _load_check_data.grf_compatibility == GLC_NOT_FOUND;
 
						}
 
						this->SetWidgetDisabledState(WID_SL_LOAD_BUTTON, disabled);
 
						this->SetWidgetDisabledState(WID_SL_NEWGRF_INFO, !_load_check_data.HasNewGrfs());
 
						this->SetWidgetDisabledState(WID_SL_MISSING_NEWGRFS,
 
								!_load_check_data.HasNewGrfs() || _load_check_data.grf_compatibility == GLC_ALL_GOOD);
 
						break;
 
					}
 

	
 
					default:
 
						NOT_REACHED();
 
				}
 
				break;
 
		}
 
	}
 
};
 

	
 
/** Load game/scenario */
 
static WindowDesc _load_dialog_desc(
 
	WDP_CENTER, "load_game", 500, 294,
 
	WC_SAVELOAD, WC_NONE,
 
	0,
 
	_nested_load_dialog_widgets, lengthof(_nested_load_dialog_widgets)
 
);
 

	
 
/** Load heightmap */
 
static WindowDesc _load_heightmap_dialog_desc(
 
	WDP_CENTER, "load_heightmap", 257, 320,
 
	WC_SAVELOAD, WC_NONE,
 
	0,
 
	_nested_load_heightmap_dialog_widgets, lengthof(_nested_load_heightmap_dialog_widgets)
 
);
 

	
 
/** Save game/scenario */
 
static WindowDesc _save_dialog_desc(
 
	WDP_CENTER, "save_game", 500, 294,
 
	WC_SAVELOAD, WC_NONE,
 
	0,
 
	_nested_save_dialog_widgets, lengthof(_nested_save_dialog_widgets)
 
);
 

	
 
/**
 
 * Launch save/load dialog in the given mode.
 
 * @param abstract_filetype Kind of file to handle.
 
 * @param fop File operation to perform (load or save).
 
 */
 
void ShowSaveLoadDialog(AbstractFileType abstract_filetype, FileOperation fop)
 
{
 
	DeleteWindowById(WC_SAVELOAD, 0);
 

	
 
	WindowDesc *sld;
 
	if (fop == FOP_SAVE) {
 
		sld = &_save_dialog_desc;
 
	} else {
 
		/* Dialogue for loading a file. */
 
		sld = (abstract_filetype == FT_HEIGHTMAP) ? &_load_heightmap_dialog_desc : &_load_dialog_desc;
 
	}
 

	
 
	_file_to_saveload.filetype = abstract_filetype;
 
	_file_to_saveload.abstract_ftype = abstract_filetype;
 

	
 
	new SaveLoadWindow(sld, abstract_filetype, fop);
 
}
src/genworld.cpp
Show inline comments
 
@@ -111,193 +111,193 @@ static void _GenerateWorld(void *)
 
		BasePersistentStorageArray::SwitchMode(PSM_ENTER_GAMELOOP);
 

	
 
		IncreaseGeneratingWorldProgress(GWP_MAP_INIT);
 
		/* Must start economy early because of the costs. */
 
		StartupEconomy();
 

	
 
		/* Don't generate landscape items when in the scenario editor. */
 
		if (_gw.mode == GWM_EMPTY) {
 
			SetGeneratingWorldProgress(GWP_OBJECT, 1);
 

	
 
			/* Make sure the tiles at the north border are void tiles if needed. */
 
			if (_settings_game.construction.freeform_edges) {
 
				for (uint row = 0; row < MapSizeY(); row++) MakeVoid(TileXY(0, row));
 
				for (uint col = 0; col < MapSizeX(); col++) MakeVoid(TileXY(col, 0));
 
			}
 

	
 
			/* Make the map the height of the setting */
 
			if (_game_mode != GM_MENU) FlatEmptyWorld(_settings_game.game_creation.se_flat_world_height);
 

	
 
			ConvertGroundTilesIntoWaterTiles();
 
			IncreaseGeneratingWorldProgress(GWP_OBJECT);
 
		} else {
 
			GenerateLandscape(_gw.mode);
 
			GenerateClearTile();
 

	
 
			/* only generate towns, tree and industries in newgame mode. */
 
			if (_game_mode != GM_EDITOR) {
 
				if (!GenerateTowns(_settings_game.economy.town_layout)) {
 
					_cur_company.Restore();
 
					HandleGeneratingWorldAbortion();
 
					return;
 
				}
 
				GenerateIndustries();
 
				GenerateObjects();
 
				GenerateTrees();
 
			}
 
		}
 

	
 
		/* These are probably pointless when inside the scenario editor. */
 
		SetGeneratingWorldProgress(GWP_GAME_INIT, 3);
 
		StartupCompanies();
 
		IncreaseGeneratingWorldProgress(GWP_GAME_INIT);
 
		StartupEngines();
 
		IncreaseGeneratingWorldProgress(GWP_GAME_INIT);
 
		StartupDisasters();
 
		_generating_world = false;
 

	
 
		/* No need to run the tile loop in the scenario editor. */
 
		if (_gw.mode != GWM_EMPTY) {
 
			uint i;
 

	
 
			SetGeneratingWorldProgress(GWP_RUNTILELOOP, 0x500);
 
			for (i = 0; i < 0x500; i++) {
 
				RunTileLoop();
 
				_tick_counter++;
 
				IncreaseGeneratingWorldProgress(GWP_RUNTILELOOP);
 
			}
 

	
 
			if (_game_mode != GM_EDITOR) {
 
				Game::StartNew();
 

	
 
				if (Game::GetInstance() != NULL) {
 
					SetGeneratingWorldProgress(GWP_RUNSCRIPT, 2500);
 
					_generating_world = true;
 
					for (i = 0; i < 2500; i++) {
 
						Game::GameLoop();
 
						IncreaseGeneratingWorldProgress(GWP_RUNSCRIPT);
 
						if (Game::GetInstance()->IsSleeping()) break;
 
					}
 
					_generating_world = false;
 
				}
 
			}
 
		}
 

	
 
		BasePersistentStorageArray::SwitchMode(PSM_LEAVE_GAMELOOP);
 

	
 
		ResetObjectToPlace();
 
		_cur_company.Trash();
 
		_current_company = _local_company = _gw.lc;
 

	
 
		SetGeneratingWorldProgress(GWP_GAME_START, 1);
 
		/* Call any callback */
 
		if (_gw.proc != NULL) _gw.proc();
 
		IncreaseGeneratingWorldProgress(GWP_GAME_START);
 

	
 
		CleanupGeneration();
 
		_modal_progress_work_mutex->EndCritical();
 

	
 
		ShowNewGRFError();
 

	
 
		if (_network_dedicated) DEBUG(net, 1, "Map generated, starting game");
 
		DEBUG(desync, 1, "new_map: %08x", _settings_game.game_creation.generation_seed);
 

	
 
		if (_debug_desync_level > 0) {
 
			char name[MAX_PATH];
 
			seprintf(name, lastof(name), "dmp_cmds_%08x_%08x.sav", _settings_game.game_creation.generation_seed, _date);
 
			SaveOrLoad(name, SL_SAVE, AUTOSAVE_DIR, false);
 
			SaveOrLoad(name, FOP_SAVE, DFT_GAME_FILE, AUTOSAVE_DIR, false);
 
		}
 
	} catch (...) {
 
		BasePersistentStorageArray::SwitchMode(PSM_LEAVE_GAMELOOP, true);
 
		if (_cur_company.IsValid()) _cur_company.Restore();
 
		_generating_world = false;
 
		_modal_progress_work_mutex->EndCritical();
 
		throw;
 
	}
 
}
 

	
 
/**
 
 * Set here the function, if any, that you want to be called when landscape
 
 * generation is done.
 
 * @param proc callback procedure
 
 */
 
void GenerateWorldSetCallback(GWDoneProc *proc)
 
{
 
	_gw.proc = proc;
 
}
 

	
 
/**
 
 * Set here the function, if any, that you want to be called when landscape
 
 * generation is aborted.
 
 * @param proc callback procedure
 
 */
 
void GenerateWorldSetAbortCallback(GWAbortProc *proc)
 
{
 
	_gw.abortp = proc;
 
}
 

	
 
/**
 
 * This will wait for the thread to finish up his work. It will not continue
 
 * till the work is done.
 
 */
 
void WaitTillGeneratedWorld()
 
{
 
	if (_gw.thread == NULL) return;
 

	
 
	_modal_progress_work_mutex->EndCritical();
 
	_modal_progress_paint_mutex->EndCritical();
 
	_gw.quit_thread = true;
 
	_gw.thread->Join();
 
	delete _gw.thread;
 
	_gw.thread   = NULL;
 
	_gw.threaded = false;
 
	_modal_progress_work_mutex->BeginCritical();
 
	_modal_progress_paint_mutex->BeginCritical();
 
}
 

	
 
/**
 
 * Initializes the abortion process
 
 */
 
void AbortGeneratingWorld()
 
{
 
	_gw.abort = true;
 
}
 

	
 
/**
 
 * Is the generation being aborted?
 
 * @return the 'aborted' status
 
 */
 
bool IsGeneratingWorldAborted()
 
{
 
	return _gw.abort;
 
}
 

	
 
/**
 
 * Really handle the abortion, i.e. clean up some of the mess
 
 */
 
void HandleGeneratingWorldAbortion()
 
{
 
	/* Clean up - in SE create an empty map, otherwise, go to intro menu */
 
	_switch_mode = (_game_mode == GM_EDITOR) ? SM_EDITOR : SM_MENU;
 

	
 
	if (_gw.abortp != NULL) _gw.abortp();
 

	
 
	CleanupGeneration();
 

	
 
	if (_gw.thread != NULL) _gw.thread->Exit();
 

	
 
	SwitchToMode(_switch_mode);
 
}
 

	
 
/**
 
 * Generate a world.
 
 * @param mode The mode of world generation (see GenWorldMode).
 
 * @param size_x The X-size of the map.
 
 * @param size_y The Y-size of the map.
 
 * @param reset_settings Whether to reset the game configuration (used for restart)
 
 */
 
void GenerateWorld(GenWorldMode mode, uint size_x, uint size_y, bool reset_settings)
 
{
 
	if (HasModalProgress()) return;
 
	_gw.mode   = mode;
 
	_gw.size_x = size_x;
 
	_gw.size_y = size_y;
src/genworld_gui.cpp
Show inline comments
 
@@ -740,193 +740,193 @@ struct GenerateLandscapeWindow : public 
 
					ShowQueryString(STR_JUST_INT, STR_MAPGEN_NUMBER_OF_TOWNS, 5, this, CS_NUMERAL, QSF_NONE);
 
				}
 
				_settings_newgame.difficulty.number_towns = index;
 
				break;
 

	
 
			case WID_GL_INDUSTRY_PULLDOWN: _settings_newgame.difficulty.industry_density = index; break;
 
			case WID_GL_TERRAIN_PULLDOWN:  _settings_newgame.difficulty.terrain_type     = index; break;
 

	
 
			case WID_GL_WATER_PULLDOWN: {
 
				if ((uint)index == CUSTOM_SEA_LEVEL_NUMBER_DIFFICULTY) {
 
					this->widget_id = widget;
 
					SetDParam(0, _settings_newgame.game_creation.custom_sea_level);
 
					ShowQueryString(STR_JUST_INT, STR_MAPGEN_QUANTITY_OF_SEA_LAKES, 3, this, CS_NUMERAL, QSF_NONE);
 
				}
 
				_settings_newgame.difficulty.quantity_sea_lakes = index;
 
				break;
 
			}
 
		}
 
		this->InvalidateData();
 
	}
 

	
 
	virtual void OnQueryTextFinished(char *str)
 
	{
 
		/* Was 'cancel' pressed? */
 
		if (str == NULL) return;
 

	
 
		int32 value;
 
		if (!StrEmpty(str)) {
 
			value = atoi(str);
 
		} else {
 
			/* An empty string means revert to the default */
 
			switch (this->widget_id) {
 
				case WID_GL_MAX_HEIGHTLEVEL_TEXT: value = DEF_MAX_HEIGHTLEVEL; break;
 
				case WID_GL_START_DATE_TEXT: value = DEF_START_YEAR; break;
 
				case WID_GL_SNOW_LEVEL_TEXT: value = DEF_SNOWLINE_HEIGHT; break;
 
				case WID_GL_TOWN_PULLDOWN:   value = 1; break;
 
				case WID_GL_WATER_PULLDOWN:  value = CUSTOM_SEA_LEVEL_MIN_PERCENTAGE; break;
 
				default: NOT_REACHED();
 
			}
 
		}
 

	
 
		switch (this->widget_id) {
 
			case WID_GL_MAX_HEIGHTLEVEL_TEXT:
 
				this->SetWidgetDirty(WID_GL_MAX_HEIGHTLEVEL_TEXT);
 
				_settings_newgame.construction.max_heightlevel = Clamp(value, MIN_MAX_HEIGHTLEVEL, MAX_MAX_HEIGHTLEVEL);
 
				break;
 

	
 
			case WID_GL_START_DATE_TEXT:
 
				this->SetWidgetDirty(WID_GL_START_DATE_TEXT);
 
				_settings_newgame.game_creation.starting_year = Clamp(value, MIN_YEAR, MAX_YEAR);
 
				break;
 

	
 
			case WID_GL_SNOW_LEVEL_TEXT:
 
				this->SetWidgetDirty(WID_GL_SNOW_LEVEL_TEXT);
 
				_settings_newgame.game_creation.snow_line_height = Clamp(value, MIN_SNOWLINE_HEIGHT, MAX_SNOWLINE_HEIGHT);
 
				break;
 

	
 
			case WID_GL_TOWN_PULLDOWN:
 
				_settings_newgame.game_creation.custom_town_number = Clamp(value, 1, CUSTOM_TOWN_MAX_NUMBER);
 
				break;
 

	
 
			case WID_GL_WATER_PULLDOWN:
 
				_settings_newgame.game_creation.custom_sea_level = Clamp(value, CUSTOM_SEA_LEVEL_MIN_PERCENTAGE, CUSTOM_SEA_LEVEL_MAX_PERCENTAGE);
 
				break;
 
		}
 

	
 
		this->InvalidateData();
 
	}
 
};
 

	
 
static WindowDesc _generate_landscape_desc(
 
	WDP_CENTER, NULL, 0, 0,
 
	WC_GENERATE_LANDSCAPE, WC_NONE,
 
	0,
 
	_nested_generate_landscape_widgets, lengthof(_nested_generate_landscape_widgets)
 
);
 

	
 
static WindowDesc _heightmap_load_desc(
 
	WDP_CENTER, NULL, 0, 0,
 
	WC_GENERATE_LANDSCAPE, WC_NONE,
 
	0,
 
	_nested_heightmap_load_widgets, lengthof(_nested_heightmap_load_widgets)
 
);
 

	
 
static void _ShowGenerateLandscape(GenerateLandscapeWindowMode mode)
 
{
 
	uint x = 0;
 
	uint y = 0;
 

	
 
	DeleteWindowByClass(WC_GENERATE_LANDSCAPE);
 

	
 
	/* Generate a new seed when opening the window */
 
	_settings_newgame.game_creation.generation_seed = InteractiveRandom();
 

	
 
	if (mode == GLWM_HEIGHTMAP) {
 
		/* If the function returns negative, it means there was a problem loading the heightmap */
 
		if (!GetHeightmapDimensions(_file_to_saveload.name, &x, &y)) return;
 
		if (!GetHeightmapDimensions(_file_to_saveload.detail_ftype, _file_to_saveload.name, &x, &y)) return;
 
	}
 

	
 
	WindowDesc *desc = (mode == GLWM_HEIGHTMAP) ? &_heightmap_load_desc : &_generate_landscape_desc;
 
	GenerateLandscapeWindow *w = AllocateWindowDescFront<GenerateLandscapeWindow>(desc, mode, true);
 

	
 
	if (mode == GLWM_HEIGHTMAP) {
 
		w->x = x;
 
		w->y = y;
 
		strecpy(w->name, _file_to_saveload.title, lastof(w->name));
 
	}
 

	
 
	SetWindowDirty(WC_GENERATE_LANDSCAPE, mode);
 
}
 

	
 
/** Start with a normal game. */
 
void ShowGenerateLandscape()
 
{
 
	_ShowGenerateLandscape(GLWM_GENERATE);
 
}
 

	
 
/** Start with loading a heightmap. */
 
void ShowHeightmapLoad()
 
{
 
	_ShowGenerateLandscape(GLWM_HEIGHTMAP);
 
}
 

	
 
/** Start with a scenario editor. */
 
void StartScenarioEditor()
 
{
 
	StartGeneratingLandscape(GLWM_SCENARIO);
 
}
 

	
 
/**
 
 * Start a normal game without the GUI.
 
 * @param seed The seed of the new game.
 
 */
 
void StartNewGameWithoutGUI(uint seed)
 
{
 
	/* GenerateWorld takes care of the possible GENERATE_NEW_SEED value in 'seed' */
 
	_settings_newgame.game_creation.generation_seed = seed;
 

	
 
	StartGeneratingLandscape(GLWM_GENERATE);
 
}
 

	
 
struct CreateScenarioWindow : public Window
 
{
 
	uint widget_id;
 

	
 
	CreateScenarioWindow(WindowDesc *desc, WindowNumber window_number) : Window(desc)
 
	{
 
		this->InitNested(window_number);
 
		this->LowerWidget(_settings_newgame.game_creation.landscape + WID_CS_TEMPERATE);
 
	}
 

	
 
	virtual void SetStringParameters(int widget) const
 
	{
 
		switch (widget) {
 
			case WID_CS_START_DATE_TEXT:
 
				SetDParam(0, ConvertYMDToDate(_settings_newgame.game_creation.starting_year, 0, 1));
 
				break;
 

	
 
			case WID_CS_MAPSIZE_X_PULLDOWN:
 
				SetDParam(0, 1 << _settings_newgame.game_creation.map_x);
 
				break;
 

	
 
			case WID_CS_MAPSIZE_Y_PULLDOWN:
 
				SetDParam(0, 1 << _settings_newgame.game_creation.map_y);
 
				break;
 

	
 
			case WID_CS_FLAT_LAND_HEIGHT_TEXT:
 
				SetDParam(0, _settings_newgame.game_creation.se_flat_world_height);
 
				break;
 
		}
 
	}
 

	
 
	virtual void OnPaint()
 
	{
 
		this->SetWidgetDisabledState(WID_CS_START_DATE_DOWN,       _settings_newgame.game_creation.starting_year <= MIN_YEAR);
 
		this->SetWidgetDisabledState(WID_CS_START_DATE_UP,         _settings_newgame.game_creation.starting_year >= MAX_YEAR);
 
		this->SetWidgetDisabledState(WID_CS_FLAT_LAND_HEIGHT_DOWN, _settings_newgame.game_creation.se_flat_world_height <= 0);
 
		this->SetWidgetDisabledState(WID_CS_FLAT_LAND_HEIGHT_UP,   _settings_newgame.game_creation.se_flat_world_height >= MAX_TILE_HEIGHT);
 

	
 
		this->SetWidgetLoweredState(WID_CS_TEMPERATE, _settings_newgame.game_creation.landscape == LT_TEMPERATE);
 
		this->SetWidgetLoweredState(WID_CS_ARCTIC,    _settings_newgame.game_creation.landscape == LT_ARCTIC);
 
		this->SetWidgetLoweredState(WID_CS_TROPICAL,  _settings_newgame.game_creation.landscape == LT_TROPIC);
 
		this->SetWidgetLoweredState(WID_CS_TOYLAND,   _settings_newgame.game_creation.landscape == LT_TOYLAND);
 

	
 
		this->DrawWidgets();
 
	}
 

	
 
	virtual void UpdateWidgetSize(int widget, Dimension *size, const Dimension &padding, Dimension *fill, Dimension *resize)
 
	{
 
		StringID str = STR_JUST_INT;
 
		switch (widget) {
 
			case WID_CS_START_DATE_TEXT:
 
				SetDParam(0, ConvertYMDToDate(MAX_YEAR, 0, 1));
src/heightmap.cpp
Show inline comments
 
@@ -9,323 +9,323 @@
 

	
 
/** @file heightmap.cpp Creating of maps from heightmaps. */
 

	
 
#include "stdafx.h"
 
#include "heightmap.h"
 
#include "clear_map.h"
 
#include "void_map.h"
 
#include "error.h"
 
#include "saveload/saveload.h"
 
#include "bmp.h"
 
#include "gfx_func.h"
 
#include "fios.h"
 
#include "fileio_func.h"
 

	
 
#include "table/strings.h"
 

	
 
#include "safeguards.h"
 

	
 
/**
 
 * Convert RGB colours to Grayscale using 29.9% Red, 58.7% Green, 11.4% Blue
 
 *  (average luminosity formula, NTSC Colour Space)
 
 */
 
static inline byte RGBToGrayscale(byte red, byte green, byte blue)
 
{
 
	/* To avoid doubles and stuff, multiply it with a total of 65536 (16bits), then
 
	 *  divide by it to normalize the value to a byte again. */
 
	return ((red * 19595) + (green * 38470) + (blue * 7471)) / 65536;
 
}
 

	
 

	
 
#ifdef WITH_PNG
 

	
 
#include <png.h>
 

	
 
/**
 
 * The PNG Heightmap loader.
 
 */
 
static void ReadHeightmapPNGImageData(byte *map, png_structp png_ptr, png_infop info_ptr)
 
{
 
	uint x, y;
 
	byte gray_palette[256];
 
	png_bytep *row_pointers = NULL;
 
	bool has_palette = png_get_color_type(png_ptr, info_ptr) == PNG_COLOR_TYPE_PALETTE;
 
	uint channels = png_get_channels(png_ptr, info_ptr);
 

	
 
	/* Get palette and convert it to grayscale */
 
	if (has_palette) {
 
		int i;
 
		int palette_size;
 
		png_color *palette;
 
		bool all_gray = true;
 

	
 
		png_get_PLTE(png_ptr, info_ptr, &palette, &palette_size);
 
		for (i = 0; i < palette_size && (palette_size != 16 || all_gray); i++) {
 
			all_gray &= palette[i].red == palette[i].green && palette[i].red == palette[i].blue;
 
			gray_palette[i] = RGBToGrayscale(palette[i].red, palette[i].green, palette[i].blue);
 
		}
 

	
 
		/**
 
		 * For a non-gray palette of size 16 we assume that
 
		 * the order of the palette determines the height;
 
		 * the first entry is the sea (level 0), the second one
 
		 * level 1, etc.
 
		 */
 
		if (palette_size == 16 && !all_gray) {
 
			for (i = 0; i < palette_size; i++) {
 
				gray_palette[i] = 256 * i / palette_size;
 
			}
 
		}
 
	}
 

	
 
	row_pointers = png_get_rows(png_ptr, info_ptr);
 

	
 
	/* Read the raw image data and convert in 8-bit grayscale */
 
	for (x = 0; x < png_get_image_width(png_ptr, info_ptr); x++) {
 
		for (y = 0; y < png_get_image_height(png_ptr, info_ptr); y++) {
 
			byte *pixel = &map[y * png_get_image_width(png_ptr, info_ptr) + x];
 
			uint x_offset = x * channels;
 

	
 
			if (has_palette) {
 
				*pixel = gray_palette[row_pointers[y][x_offset]];
 
			} else if (channels == 3) {
 
				*pixel = RGBToGrayscale(row_pointers[y][x_offset + 0],
 
						row_pointers[y][x_offset + 1], row_pointers[y][x_offset + 2]);
 
			} else {
 
				*pixel = row_pointers[y][x_offset];
 
			}
 
		}
 
	}
 
}
 

	
 
/**
 
 * Reads the heightmap and/or size of the heightmap from a PNG file.
 
 * If map == NULL only the size of the PNG is read, otherwise a map
 
 * with grayscale pixels is allocated and assigned to *map.
 
 */
 
static bool ReadHeightmapPNG(char *filename, uint *x, uint *y, byte **map)
 
static bool ReadHeightmapPNG(const char *filename, uint *x, uint *y, byte **map)
 
{
 
	FILE *fp;
 
	png_structp png_ptr = NULL;
 
	png_infop info_ptr  = NULL;
 

	
 
	fp = FioFOpenFile(filename, "rb", HEIGHTMAP_DIR);
 
	if (fp == NULL) {
 
		ShowErrorMessage(STR_ERROR_PNGMAP, STR_ERROR_PNGMAP_FILE_NOT_FOUND, WL_ERROR);
 
		return false;
 
	}
 

	
 
	png_ptr = png_create_read_struct(PNG_LIBPNG_VER_STRING, NULL, NULL, NULL);
 
	if (png_ptr == NULL) {
 
		ShowErrorMessage(STR_ERROR_PNGMAP, STR_ERROR_PNGMAP_MISC, WL_ERROR);
 
		fclose(fp);
 
		return false;
 
	}
 

	
 
	info_ptr = png_create_info_struct(png_ptr);
 
	if (info_ptr == NULL || setjmp(png_jmpbuf(png_ptr))) {
 
		ShowErrorMessage(STR_ERROR_PNGMAP, STR_ERROR_PNGMAP_MISC, WL_ERROR);
 
		fclose(fp);
 
		png_destroy_read_struct(&png_ptr, &info_ptr, NULL);
 
		return false;
 
	}
 

	
 
	png_init_io(png_ptr, fp);
 

	
 
	/* Allocate memory and read image, without alpha or 16-bit samples
 
	 * (result is either 8-bit indexed/grayscale or 24-bit RGB) */
 
	png_set_packing(png_ptr);
 
	png_read_png(png_ptr, info_ptr, PNG_TRANSFORM_PACKING | PNG_TRANSFORM_STRIP_ALPHA | PNG_TRANSFORM_STRIP_16, NULL);
 

	
 
	/* Maps of wrong colour-depth are not used.
 
	 * (this should have been taken care of by stripping alpha and 16-bit samples on load) */
 
	if ((png_get_channels(png_ptr, info_ptr) != 1) && (png_get_channels(png_ptr, info_ptr) != 3) && (png_get_bit_depth(png_ptr, info_ptr) != 8)) {
 
		ShowErrorMessage(STR_ERROR_PNGMAP, STR_ERROR_PNGMAP_IMAGE_TYPE, WL_ERROR);
 
		fclose(fp);
 
		png_destroy_read_struct(&png_ptr, &info_ptr, NULL);
 
		return false;
 
	}
 

	
 
	uint width = png_get_image_width(png_ptr, info_ptr);
 
	uint height = png_get_image_height(png_ptr, info_ptr);
 

	
 
	/* Check if image dimensions don't overflow a size_t to avoid memory corruption. */
 
	if ((uint64)width * height >= (size_t)-1) {
 
		ShowErrorMessage(STR_ERROR_PNGMAP, STR_ERROR_HEIGHTMAP_TOO_LARGE, WL_ERROR);
 
		fclose(fp);
 
		png_destroy_read_struct(&png_ptr, &info_ptr, NULL);
 
		return false;
 
	}
 

	
 
	if (map != NULL) {
 
		*map = MallocT<byte>(width * height);
 
		ReadHeightmapPNGImageData(*map, png_ptr, info_ptr);
 
	}
 

	
 
	*x = width;
 
	*y = height;
 

	
 
	fclose(fp);
 
	png_destroy_read_struct(&png_ptr, &info_ptr, NULL);
 
	return true;
 
}
 

	
 
#endif /* WITH_PNG */
 

	
 

	
 
/**
 
 * The BMP Heightmap loader.
 
 */
 
static void ReadHeightmapBMPImageData(byte *map, BmpInfo *info, BmpData *data)
 
{
 
	uint x, y;
 
	byte gray_palette[256];
 

	
 
	if (data->palette != NULL) {
 
		uint i;
 
		bool all_gray = true;
 

	
 
		if (info->palette_size != 2) {
 
			for (i = 0; i < info->palette_size && (info->palette_size != 16 || all_gray); i++) {
 
				all_gray &= data->palette[i].r == data->palette[i].g && data->palette[i].r == data->palette[i].b;
 
				gray_palette[i] = RGBToGrayscale(data->palette[i].r, data->palette[i].g, data->palette[i].b);
 
			}
 

	
 
			/**
 
			 * For a non-gray palette of size 16 we assume that
 
			 * the order of the palette determines the height;
 
			 * the first entry is the sea (level 0), the second one
 
			 * level 1, etc.
 
			 */
 
			if (info->palette_size == 16 && !all_gray) {
 
				for (i = 0; i < info->palette_size; i++) {
 
					gray_palette[i] = 256 * i / info->palette_size;
 
				}
 
			}
 
		} else {
 
			/**
 
			 * For a palette of size 2 we assume that the order of the palette determines the height;
 
			 * the first entry is the sea (level 0), the second one is the land (level 1)
 
			 */
 
			gray_palette[0] = 0;
 
			gray_palette[1] = 16;
 
		}
 
	}
 

	
 
	/* Read the raw image data and convert in 8-bit grayscale */
 
	for (y = 0; y < info->height; y++) {
 
		byte *pixel = &map[y * info->width];
 
		byte *bitmap = &data->bitmap[y * info->width * (info->bpp == 24 ? 3 : 1)];
 

	
 
		for (x = 0; x < info->width; x++) {
 
			if (info->bpp != 24) {
 
				*pixel++ = gray_palette[*bitmap++];
 
			} else {
 
				*pixel++ = RGBToGrayscale(*bitmap, *(bitmap + 1), *(bitmap + 2));
 
				bitmap += 3;
 
			}
 
		}
 
	}
 
}
 

	
 
/**
 
 * Reads the heightmap and/or size of the heightmap from a BMP file.
 
 * If map == NULL only the size of the BMP is read, otherwise a map
 
 * with grayscale pixels is allocated and assigned to *map.
 
 */
 
static bool ReadHeightmapBMP(char *filename, uint *x, uint *y, byte **map)
 
static bool ReadHeightmapBMP(const char *filename, uint *x, uint *y, byte **map)
 
{
 
	FILE *f;
 
	BmpInfo info;
 
	BmpData data;
 
	BmpBuffer buffer;
 

	
 
	/* Init BmpData */
 
	memset(&data, 0, sizeof(data));
 

	
 
	f = FioFOpenFile(filename, "rb", HEIGHTMAP_DIR);
 
	if (f == NULL) {
 
		ShowErrorMessage(STR_ERROR_BMPMAP, STR_ERROR_PNGMAP_FILE_NOT_FOUND, WL_ERROR);
 
		return false;
 
	}
 

	
 
	BmpInitializeBuffer(&buffer, f);
 

	
 
	if (!BmpReadHeader(&buffer, &info, &data)) {
 
		ShowErrorMessage(STR_ERROR_BMPMAP, STR_ERROR_BMPMAP_IMAGE_TYPE, WL_ERROR);
 
		fclose(f);
 
		BmpDestroyData(&data);
 
		return false;
 
	}
 

	
 
	/* Check if image dimensions don't overflow a size_t to avoid memory corruption. */
 
	if ((uint64)info.width * info.height >= (size_t)-1 / (info.bpp == 24 ? 3 : 1)) {
 
		ShowErrorMessage(STR_ERROR_BMPMAP, STR_ERROR_HEIGHTMAP_TOO_LARGE, WL_ERROR);
 
		fclose(f);
 
		BmpDestroyData(&data);
 
		return false;
 
	}
 

	
 
	if (map != NULL) {
 
		if (!BmpReadBitmap(&buffer, &info, &data)) {
 
			ShowErrorMessage(STR_ERROR_BMPMAP, STR_ERROR_BMPMAP_IMAGE_TYPE, WL_ERROR);
 
			fclose(f);
 
			BmpDestroyData(&data);
 
			return false;
 
		}
 

	
 
		*map = MallocT<byte>(info.width * info.height);
 
		ReadHeightmapBMPImageData(*map, &info, &data);
 
	}
 

	
 
	BmpDestroyData(&data);
 

	
 
	*x = info.width;
 
	*y = info.height;
 

	
 
	fclose(f);
 
	return true;
 
}
 

	
 
/**
 
 * Converts a given grayscale map to something that fits in OTTD map system
 
 * and create a map of that data.
 
 * @param img_width  the with of the image in pixels/tiles
 
 * @param img_height the height of the image in pixels/tiles
 
 * @param map        the input map
 
 */
 
static void GrayscaleToMapHeights(uint img_width, uint img_height, byte *map)
 
{
 
	/* Defines the detail of the aspect ratio (to avoid doubles) */
 
	const uint num_div = 16384;
 

	
 
	uint width, height;
 
	uint row, col;
 
	uint row_pad = 0, col_pad = 0;
 
	uint img_scale;
 
	uint img_row, img_col;
 
	TileIndex tile;
 

	
 
	/* Get map size and calculate scale and padding values */
 
	switch (_settings_game.game_creation.heightmap_rotation) {
 
		default: NOT_REACHED();
 
		case HM_COUNTER_CLOCKWISE:
 
			width   = MapSizeX();
 
			height  = MapSizeY();
 
			break;
 
		case HM_CLOCKWISE:
 
			width   = MapSizeY();
 
			height  = MapSizeX();
 
			break;
 
	}
 

	
 
	if ((img_width * num_div) / img_height > ((width * num_div) / height)) {
 
		/* Image is wider than map - center vertically */
 
		img_scale = (width * num_div) / img_width;
 
		row_pad = (1 + height - ((img_height * img_scale) / num_div)) / 2;
 
	} else {
 
		/* Image is taller than map - center horizontally */
 
		img_scale = (height * num_div) / img_height;
 
		col_pad = (1 + width - ((img_width * img_scale) / num_div)) / 2;
 
	}
 

	
 
	if (_settings_game.construction.freeform_edges) {
 
@@ -351,162 +351,173 @@ static void GrayscaleToMapHeights(uint i
 
				/* Use nearest neighbour resizing to scale map data.
 
				 *  We rotate the map 45 degrees (counter)clockwise */
 
				img_row = (((row - row_pad) * num_div) / img_scale);
 
				switch (_settings_game.game_creation.heightmap_rotation) {
 
					default: NOT_REACHED();
 
					case HM_COUNTER_CLOCKWISE:
 
						img_col = (((width - 1 - col - col_pad) * num_div) / img_scale);
 
						break;
 
					case HM_CLOCKWISE:
 
						img_col = (((col - col_pad) * num_div) / img_scale);
 
						break;
 
				}
 

	
 
				assert(img_row < img_height);
 
				assert(img_col < img_width);
 

	
 
				uint heightmap_height = map[img_row * img_width + img_col];
 

	
 
				if (heightmap_height > 0) {
 
					/* 0 is sea level.
 
					 * Other grey scales are scaled evenly to the available height levels > 0.
 
					 * (The coastline is independent from the number of height levels) */
 
					heightmap_height = 1 + (heightmap_height - 1) * _settings_game.construction.max_heightlevel / 255;
 
				}
 

	
 
				SetTileHeight(tile, heightmap_height);
 
			}
 
			/* Only clear the tiles within the map area. */
 
			if (IsInnerTile(tile)) {
 
				MakeClear(tile, CLEAR_GRASS, 3);
 
			}
 
		}
 
	}
 
}
 

	
 
/**
 
 * This function takes care of the fact that land in OpenTTD can never differ
 
 * more than 1 in height
 
 */
 
void FixSlopes()
 
{
 
	uint width, height;
 
	int row, col;
 
	byte current_tile;
 

	
 
	/* Adjust height difference to maximum one horizontal/vertical change. */
 
	width   = MapSizeX();
 
	height  = MapSizeY();
 

	
 
	/* Top and left edge */
 
	for (row = 0; (uint)row < height; row++) {
 
		for (col = 0; (uint)col < width; col++) {
 
			current_tile = MAX_TILE_HEIGHT;
 
			if (col != 0) {
 
				/* Find lowest tile; either the top or left one */
 
				current_tile = TileHeight(TileXY(col - 1, row)); // top edge
 
			}
 
			if (row != 0) {
 
				if (TileHeight(TileXY(col, row - 1)) < current_tile) {
 
					current_tile = TileHeight(TileXY(col, row - 1)); // left edge
 
				}
 
			}
 

	
 
			/* Does the height differ more than one? */
 
			if (TileHeight(TileXY(col, row)) >= (uint)current_tile + 2) {
 
				/* Then change the height to be no more than one */
 
				SetTileHeight(TileXY(col, row), current_tile + 1);
 
			}
 
		}
 
	}
 

	
 
	/* Bottom and right edge */
 
	for (row = height - 1; row >= 0; row--) {
 
		for (col = width - 1; col >= 0; col--) {
 
			current_tile = MAX_TILE_HEIGHT;
 
			if ((uint)col != width - 1) {
 
				/* Find lowest tile; either the bottom and right one */
 
				current_tile = TileHeight(TileXY(col + 1, row)); // bottom edge
 
			}
 

	
 
			if ((uint)row != height - 1) {
 
				if (TileHeight(TileXY(col, row + 1)) < current_tile) {
 
					current_tile = TileHeight(TileXY(col, row + 1)); // right edge
 
				}
 
			}
 

	
 
			/* Does the height differ more than one? */
 
			if (TileHeight(TileXY(col, row)) >= (uint)current_tile + 2) {
 
				/* Then change the height to be no more than one */
 
				SetTileHeight(TileXY(col, row), current_tile + 1);
 
			}
 
		}
 
	}
 
}
 

	
 
/**
 
 * Reads the heightmap with the correct file reader
 
 * Reads the heightmap with the correct file reader.
 
 * @param dft Type of image file.
 
 * @param filename Name of the file to load.
 
 * @param [out] x Length of the image.
 
 * @param [out] y Height of the image.
 
 * @param [inout] map If not \c NULL, destination to store the loaded block of image data.
 
 * @return Whether loading was successful.
 
 */
 
static bool ReadHeightMap(char *filename, uint *x, uint *y, byte **map)
 
static bool ReadHeightMap(DetailedFileType dft, const char *filename, uint *x, uint *y, byte **map)
 
{
 
	switch (_file_to_saveload.mode) {
 
		default: NOT_REACHED();
 
	switch (dft) {
 
		default:
 
			NOT_REACHED();
 

	
 
#ifdef WITH_PNG
 
		case SL_PNG:
 
		case DFT_HEIGHTMAP_PNG:
 
			return ReadHeightmapPNG(filename, x, y, map);
 
#endif /* WITH_PNG */
 
		case SL_BMP:
 

	
 
		case DFT_HEIGHTMAP_BMP:
 
			return ReadHeightmapBMP(filename, x, y, map);
 
	}
 
}
 

	
 
/**
 
 * Get the dimensions of a heightmap.
 
 * @param dft Type of image file.
 
 * @param filename to query
 
 * @param x dimension x
 
 * @param y dimension y
 
 * @return Returns false if loading of the image failed.
 
 */
 
bool GetHeightmapDimensions(char *filename, uint *x, uint *y)
 
bool GetHeightmapDimensions(DetailedFileType dft, const char *filename, uint *x, uint *y)
 
{
 
	return ReadHeightMap(filename, x, y, NULL);
 
	return ReadHeightMap(dft, filename, x, y, NULL);
 
}
 

	
 
/**
 
 * Load a heightmap from file and change the map in his current dimensions
 
 *  to a landscape representing the heightmap.
 
 * It converts pixels to height. The brighter, the higher.
 
 * @param dft Type of image file.
 
 * @param filename of the heightmap file to be imported
 
 */
 
void LoadHeightmap(char *filename)
 
void LoadHeightmap(DetailedFileType dft, const char *filename)
 
{
 
	uint x, y;
 
	byte *map = NULL;
 

	
 
	if (!ReadHeightMap(filename, &x, &y, &map)) {
 
	if (!ReadHeightMap(dft, filename, &x, &y, &map)) {
 
		free(map);
 
		return;
 
	}
 

	
 
	GrayscaleToMapHeights(x, y, map);
 
	free(map);
 

	
 
	FixSlopes();
 
	MarkWholeScreenDirty();
 
}
 

	
 
/**
 
 * Make an empty world where all tiles are of height 'tile_height'.
 
 * @param tile_height of the desired new empty world
 
 */
 
void FlatEmptyWorld(byte tile_height)
 
{
 
	int edge_distance = _settings_game.construction.freeform_edges ? 0 : 2;
 
	for (uint row = edge_distance; row < MapSizeY() - edge_distance; row++) {
 
		for (uint col = edge_distance; col < MapSizeX() - edge_distance; col++) {
 
			SetTileHeight(TileXY(col, row), tile_height);
 
		}
 
	}
 

	
 
	FixSlopes();
 
	MarkWholeScreenDirty();
 
}
src/heightmap.h
Show inline comments
 
/* $Id$ */
 

	
 
/*
 
 * This file is part of OpenTTD.
 
 * OpenTTD is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, version 2.
 
 * OpenTTD is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
 
 * See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with OpenTTD. If not, see <http://www.gnu.org/licenses/>.
 
 */
 

	
 
/** @file heightmap.h Functions related to creating heightmaps from files. */
 

	
 
#ifndef HEIGHTMAP_H
 
#define HEIGHTMAP_H
 

	
 
#include "fileio_type.h"
 

	
 
/**
 
 * Order of these enums has to be the same as in lang/english.txt
 
 * Otherwise you will get inconsistent behaviour.
 
 */
 
enum HeightmapRotation {
 
	HM_COUNTER_CLOCKWISE, ///< Rotate the map counter clockwise 45 degrees
 
	HM_CLOCKWISE,         ///< Rotate the map clockwise 45 degrees
 
};
 

	
 
bool GetHeightmapDimensions(char *filename, uint *x, uint *y);
 
void LoadHeightmap(char *filename);
 
bool GetHeightmapDimensions(DetailedFileType dft, const char *filename, uint *x, uint *y);
 
void LoadHeightmap(DetailedFileType dft, const char *filename);
 
void FlatEmptyWorld(byte tile_height);
 
void FixSlopes();
 

	
 
#endif /* HEIGHTMAP_H */
src/landscape.cpp
Show inline comments
 
@@ -1129,186 +1129,186 @@ static bool FlowRiver(TileIndex spring, 
 

	
 
		uint height2 = TileHeight(end);
 
		if (IsTileFlat(end) && (height2 < height || (height2 == height && IsWaterTile(end)))) {
 
			found = true;
 
			break;
 
		}
 

	
 
		for (DiagDirection d = DIAGDIR_BEGIN; d < DIAGDIR_END; d++) {
 
			TileIndex t2 = end + TileOffsByDiagDir(d);
 
			if (IsValidTile(t2) && !IS_MARKED(t2) && FlowsDown(end, t2)) {
 
				SET_MARK(t2);
 
				count++;
 
				queue.push_back(t2);
 
			}
 
		}
 
	} while (!queue.empty());
 

	
 
	if (found) {
 
		/* Flow further down hill. */
 
		found = FlowRiver(spring, end);
 
	} else if (count > 32) {
 
		/* Maybe we can make a lake. Find the Nth of the considered tiles. */
 
		TileIndex lakeCenter = 0;
 
		int i = RandomRange(count - 1) + 1;
 
		std::set<TileIndex>::const_iterator cit = marks.begin();
 
		while (--i) cit++;
 
		lakeCenter = *cit;
 

	
 
		if (IsValidTile(lakeCenter) &&
 
				/* A river, or lake, can only be built on flat slopes. */
 
				IsTileFlat(lakeCenter) &&
 
				/* We want the lake to be built at the height of the river. */
 
				TileHeight(begin) == TileHeight(lakeCenter) &&
 
				/* We don't want the lake at the entry of the valley. */
 
				lakeCenter != begin &&
 
				/* We don't want lakes in the desert. */
 
				(_settings_game.game_creation.landscape != LT_TROPIC || GetTropicZone(lakeCenter) != TROPICZONE_DESERT) &&
 
				/* We only want a lake if the river is long enough. */
 
				DistanceManhattan(spring, lakeCenter) > _settings_game.game_creation.min_river_length) {
 
			end = lakeCenter;
 
			MakeRiver(lakeCenter, Random());
 
			uint range = RandomRange(8) + 3;
 
			CircularTileSearch(&lakeCenter, range, MakeLake, &height);
 
			/* Call the search a second time so artefacts from going circular in one direction get (mostly) hidden. */
 
			lakeCenter = end;
 
			CircularTileSearch(&lakeCenter, range, MakeLake, &height);
 
			found = true;
 
		}
 
	}
 

	
 
	marks.clear();
 
	if (found) BuildRiver(begin, end);
 
	return found;
 
}
 

	
 
/**
 
 * Actually (try to) create some rivers.
 
 */
 
static void CreateRivers()
 
{
 
	int amount = _settings_game.game_creation.amount_of_rivers;
 
	if (amount == 0) return;
 

	
 
	uint wells = ScaleByMapSize(4 << _settings_game.game_creation.amount_of_rivers);
 
	SetGeneratingWorldProgress(GWP_RIVER, wells + 256 / 64); // Include the tile loop calls below.
 

	
 
	for (; wells != 0; wells--) {
 
		IncreaseGeneratingWorldProgress(GWP_RIVER);
 
		for (int tries = 0; tries < 128; tries++) {
 
			TileIndex t = RandomTile();
 
			if (!CircularTileSearch(&t, 8, FindSpring, NULL)) continue;
 
			if (FlowRiver(t, t)) break;
 
		}
 
	}
 

	
 
	/* Run tile loop to update the ground density. */
 
	for (uint i = 0; i != 256; i++) {
 
		if (i % 64 == 0) IncreaseGeneratingWorldProgress(GWP_RIVER);
 
		RunTileLoop();
 
	}
 
}
 

	
 
void GenerateLandscape(byte mode)
 
{
 
	/** Number of steps of landscape generation */
 
	enum GenLandscapeSteps {
 
		GLS_HEIGHTMAP    =  3, ///< Loading a heightmap
 
		GLS_TERRAGENESIS =  5, ///< Terragenesis generator
 
		GLS_ORIGINAL     =  2, ///< Original generator
 
		GLS_TROPIC       = 12, ///< Extra steps needed for tropic landscape
 
		GLS_OTHER        =  0, ///< Extra steps for other landscapes
 
	};
 
	uint steps = (_settings_game.game_creation.landscape == LT_TROPIC) ? GLS_TROPIC : GLS_OTHER;
 

	
 
	if (mode == GWM_HEIGHTMAP) {
 
		SetGeneratingWorldProgress(GWP_LANDSCAPE, steps + GLS_HEIGHTMAP);
 
		LoadHeightmap(_file_to_saveload.name);
 
		LoadHeightmap(_file_to_saveload.detail_ftype, _file_to_saveload.name);
 
		IncreaseGeneratingWorldProgress(GWP_LANDSCAPE);
 
	} else if (_settings_game.game_creation.land_generator == LG_TERRAGENESIS) {
 
		SetGeneratingWorldProgress(GWP_LANDSCAPE, steps + GLS_TERRAGENESIS);
 
		GenerateTerrainPerlin();
 
	} else {
 
		SetGeneratingWorldProgress(GWP_LANDSCAPE, steps + GLS_ORIGINAL);
 
		if (_settings_game.construction.freeform_edges) {
 
			for (uint x = 0; x < MapSizeX(); x++) MakeVoid(TileXY(x, 0));
 
			for (uint y = 0; y < MapSizeY(); y++) MakeVoid(TileXY(0, y));
 
		}
 
		switch (_settings_game.game_creation.landscape) {
 
			case LT_ARCTIC: {
 
				uint32 r = Random();
 

	
 
				for (uint i = ScaleByMapSize(GB(r, 0, 7) + 950); i != 0; --i) {
 
					GenerateTerrain(2, 0);
 
				}
 

	
 
				uint flag = GB(r, 7, 2) | 4;
 
				for (uint i = ScaleByMapSize(GB(r, 9, 7) + 450); i != 0; --i) {
 
					GenerateTerrain(4, flag);
 
				}
 
				break;
 
			}
 

	
 
			case LT_TROPIC: {
 
				uint32 r = Random();
 

	
 
				for (uint i = ScaleByMapSize(GB(r, 0, 7) + 170); i != 0; --i) {
 
					GenerateTerrain(0, 0);
 
				}
 

	
 
				uint flag = GB(r, 7, 2) | 4;
 
				for (uint i = ScaleByMapSize(GB(r, 9, 8) + 1700); i != 0; --i) {
 
					GenerateTerrain(0, flag);
 
				}
 

	
 
				flag ^= 2;
 

	
 
				for (uint i = ScaleByMapSize(GB(r, 17, 7) + 410); i != 0; --i) {
 
					GenerateTerrain(3, flag);
 
				}
 
				break;
 
			}
 

	
 
			default: {
 
				uint32 r = Random();
 

	
 
				assert(_settings_game.difficulty.quantity_sea_lakes != CUSTOM_SEA_LEVEL_NUMBER_DIFFICULTY);
 
				uint i = ScaleByMapSize(GB(r, 0, 7) + (3 - _settings_game.difficulty.quantity_sea_lakes) * 256 + 100);
 
				for (; i != 0; --i) {
 
					/* Make sure we do not overflow. */
 
					GenerateTerrain(Clamp(_settings_game.difficulty.terrain_type, 0, 3), 0);
 
				}
 
				break;
 
			}
 
		}
 
	}
 

	
 
	/* Do not call IncreaseGeneratingWorldProgress() before FixSlopes(),
 
	 * it allows screen redraw. Drawing of broken slopes crashes the game */
 
	FixSlopes();
 
	IncreaseGeneratingWorldProgress(GWP_LANDSCAPE);
 
	ConvertGroundTilesIntoWaterTiles();
 
	IncreaseGeneratingWorldProgress(GWP_LANDSCAPE);
 

	
 
	if (_settings_game.game_creation.landscape == LT_TROPIC) CreateDesertOrRainForest();
 

	
 
	CreateRivers();
 
}
 

	
 
void OnTick_Town();
 
void OnTick_Trees();
 
void OnTick_Station();
 
void OnTick_Industry();
 

	
 
void OnTick_Companies();
 
void OnTick_LinkGraph();
 

	
 
void CallLandscapeTick()
 
{
 
	OnTick_Town();
 
	OnTick_Trees();
 
	OnTick_Station();
 
	OnTick_Industry();
 

	
 
	OnTick_Companies();
 
	OnTick_LinkGraph();
 
}
src/network/network_client.cpp
Show inline comments
 
@@ -427,193 +427,193 @@ NetworkRecvStatus ClientNetworkGameSocke
 
	p->Send_uint64(data);
 

	
 
	my_client->SendPacket(p);
 
	return NETWORK_RECV_STATUS_OKAY;
 
}
 

	
 
/** Send an error-packet over the network */
 
NetworkRecvStatus ClientNetworkGameSocketHandler::SendError(NetworkErrorCode errorno)
 
{
 
	Packet *p = new Packet(PACKET_CLIENT_ERROR);
 

	
 
	p->Send_uint8(errorno);
 
	my_client->SendPacket(p);
 
	return NETWORK_RECV_STATUS_OKAY;
 
}
 

	
 
/**
 
 * Tell the server that we like to change the password of the company.
 
 * @param password The new password.
 
 */
 
NetworkRecvStatus ClientNetworkGameSocketHandler::SendSetPassword(const char *password)
 
{
 
	Packet *p = new Packet(PACKET_CLIENT_SET_PASSWORD);
 

	
 
	p->Send_string(GenerateCompanyPasswordHash(password, _password_server_id, _password_game_seed));
 
	my_client->SendPacket(p);
 
	return NETWORK_RECV_STATUS_OKAY;
 
}
 

	
 
/**
 
 * Tell the server that we like to change the name of the client.
 
 * @param name The new name.
 
 */
 
NetworkRecvStatus ClientNetworkGameSocketHandler::SendSetName(const char *name)
 
{
 
	Packet *p = new Packet(PACKET_CLIENT_SET_NAME);
 

	
 
	p->Send_string(name);
 
	my_client->SendPacket(p);
 
	return NETWORK_RECV_STATUS_OKAY;
 
}
 

	
 
/**
 
 * Tell the server we would like to quit.
 
 */
 
NetworkRecvStatus ClientNetworkGameSocketHandler::SendQuit()
 
{
 
	Packet *p = new Packet(PACKET_CLIENT_QUIT);
 

	
 
	my_client->SendPacket(p);
 
	return NETWORK_RECV_STATUS_OKAY;
 
}
 

	
 
/**
 
 * Send a console command.
 
 * @param pass The password for the remote command.
 
 * @param command The actual command.
 
 */
 
NetworkRecvStatus ClientNetworkGameSocketHandler::SendRCon(const char *pass, const char *command)
 
{
 
	Packet *p = new Packet(PACKET_CLIENT_RCON);
 
	p->Send_string(pass);
 
	p->Send_string(command);
 
	my_client->SendPacket(p);
 
	return NETWORK_RECV_STATUS_OKAY;
 
}
 

	
 
/**
 
 * Ask the server to move us.
 
 * @param company The company to move to.
 
 * @param password The password of the company to move to.
 
 */
 
NetworkRecvStatus ClientNetworkGameSocketHandler::SendMove(CompanyID company, const char *password)
 
{
 
	Packet *p = new Packet(PACKET_CLIENT_MOVE);
 
	p->Send_uint8(company);
 
	p->Send_string(GenerateCompanyPasswordHash(password, _password_server_id, _password_game_seed));
 
	my_client->SendPacket(p);
 
	return NETWORK_RECV_STATUS_OKAY;
 
}
 

	
 
/**
 
 * Check whether the client is actually connected (and in the game).
 
 * @return True when the client is connected.
 
 */
 
bool ClientNetworkGameSocketHandler::IsConnected()
 
{
 
	return my_client != NULL && my_client->status == STATUS_ACTIVE;
 
}
 

	
 

	
 
/***********
 
 * Receiving functions
 
 *   DEF_CLIENT_RECEIVE_COMMAND has parameter: Packet *p
 
 ************/
 

	
 
extern bool SafeLoad(const char *filename, int mode, GameMode newgm, Subdirectory subdir, struct LoadFilter *lf = NULL);
 
extern bool SafeLoad(const char *filename, FileOperation fop, DetailedFileType dft, GameMode newgm, Subdirectory subdir, struct LoadFilter *lf = NULL);
 

	
 
NetworkRecvStatus ClientNetworkGameSocketHandler::Receive_SERVER_FULL(Packet *p)
 
{
 
	/* We try to join a server which is full */
 
	ShowErrorMessage(STR_NETWORK_ERROR_SERVER_FULL, INVALID_STRING_ID, WL_CRITICAL);
 
	DeleteWindowById(WC_NETWORK_STATUS_WINDOW, WN_NETWORK_STATUS_WINDOW_JOIN);
 

	
 
	return NETWORK_RECV_STATUS_SERVER_FULL;
 
}
 

	
 
NetworkRecvStatus ClientNetworkGameSocketHandler::Receive_SERVER_BANNED(Packet *p)
 
{
 
	/* We try to join a server where we are banned */
 
	ShowErrorMessage(STR_NETWORK_ERROR_SERVER_BANNED, INVALID_STRING_ID, WL_CRITICAL);
 
	DeleteWindowById(WC_NETWORK_STATUS_WINDOW, WN_NETWORK_STATUS_WINDOW_JOIN);
 

	
 
	return NETWORK_RECV_STATUS_SERVER_BANNED;
 
}
 

	
 
NetworkRecvStatus ClientNetworkGameSocketHandler::Receive_SERVER_COMPANY_INFO(Packet *p)
 
{
 
	if (this->status != STATUS_COMPANY_INFO) return NETWORK_RECV_STATUS_MALFORMED_PACKET;
 

	
 
	byte company_info_version = p->Recv_uint8();
 

	
 
	if (!this->HasClientQuit() && company_info_version == NETWORK_COMPANY_INFO_VERSION) {
 
		/* We have received all data... (there are no more packets coming) */
 
		if (!p->Recv_bool()) return NETWORK_RECV_STATUS_CLOSE_QUERY;
 

	
 
		CompanyID current = (Owner)p->Recv_uint8();
 
		if (current >= MAX_COMPANIES) return NETWORK_RECV_STATUS_CLOSE_QUERY;
 

	
 
		NetworkCompanyInfo *company_info = GetLobbyCompanyInfo(current);
 
		if (company_info == NULL) return NETWORK_RECV_STATUS_CLOSE_QUERY;
 

	
 
		p->Recv_string(company_info->company_name, sizeof(company_info->company_name));
 
		company_info->inaugurated_year = p->Recv_uint32();
 
		company_info->company_value    = p->Recv_uint64();
 
		company_info->money            = p->Recv_uint64();
 
		company_info->income           = p->Recv_uint64();
 
		company_info->performance      = p->Recv_uint16();
 
		company_info->use_password     = p->Recv_bool();
 
		for (uint i = 0; i < NETWORK_VEH_END; i++) {
 
			company_info->num_vehicle[i] = p->Recv_uint16();
 
		}
 
		for (uint i = 0; i < NETWORK_VEH_END; i++) {
 
			company_info->num_station[i] = p->Recv_uint16();
 
		}
 
		company_info->ai               = p->Recv_bool();
 

	
 
		p->Recv_string(company_info->clients, sizeof(company_info->clients));
 

	
 
		SetWindowDirty(WC_NETWORK_WINDOW, WN_NETWORK_WINDOW_LOBBY);
 

	
 
		return NETWORK_RECV_STATUS_OKAY;
 
	}
 

	
 
	return NETWORK_RECV_STATUS_CLOSE_QUERY;
 
}
 

	
 
/* This packet contains info about the client (playas and name)
 
 *  as client we save this in NetworkClientInfo, linked via 'client_id'
 
 *  which is always an unique number on a server. */
 
NetworkRecvStatus ClientNetworkGameSocketHandler::Receive_SERVER_CLIENT_INFO(Packet *p)
 
{
 
	NetworkClientInfo *ci;
 
	ClientID client_id = (ClientID)p->Recv_uint32();
 
	CompanyID playas = (CompanyID)p->Recv_uint8();
 
	char name[NETWORK_NAME_LENGTH];
 

	
 
	p->Recv_string(name, sizeof(name));
 

	
 
	if (this->status < STATUS_AUTHORIZED) return NETWORK_RECV_STATUS_MALFORMED_PACKET;
 
	if (this->HasClientQuit()) return NETWORK_RECV_STATUS_CONN_LOST;
 

	
 
	ci = NetworkClientInfo::GetByClientID(client_id);
 
	if (ci != NULL) {
 
		if (playas == ci->client_playas && strcmp(name, ci->client_name) != 0) {
 
			/* Client name changed, display the change */
 
			NetworkTextMessage(NETWORK_ACTION_NAME_CHANGE, CC_DEFAULT, false, ci->client_name, name);
 
		} else if (playas != ci->client_playas) {
 
			/* The client changed from client-player..
 
			 * Do not display that for now */
 
		}
 

	
 
		/* Make sure we're in the company the server tells us to be in,
 
		 * for the rare case that we get moved while joining. */
 
		if (client_id == _network_own_client_id) SetLocalCompany(!Company::IsValidID(playas) ? COMPANY_SPECTATOR : playas);
 

	
 
		ci->client_playas = playas;
 
		strecpy(ci->client_name, name, lastof(ci->client_name));
 

	
 
		SetWindowDirty(WC_CLIENT_LIST, 0);
 

	
 
		return NETWORK_RECV_STATUS_OKAY;
 
	}
 
@@ -743,193 +743,193 @@ NetworkRecvStatus ClientNetworkGameSocke
 
}
 

	
 
NetworkRecvStatus ClientNetworkGameSocketHandler::Receive_SERVER_WELCOME(Packet *p)
 
{
 
	if (this->status < STATUS_JOIN || this->status >= STATUS_AUTHORIZED) return NETWORK_RECV_STATUS_MALFORMED_PACKET;
 
	this->status = STATUS_AUTHORIZED;
 

	
 
	_network_own_client_id = (ClientID)p->Recv_uint32();
 

	
 
	/* Initialize the password hash salting variables, even if they were previously. */
 
	_password_game_seed = p->Recv_uint32();
 
	p->Recv_string(_password_server_id, sizeof(_password_server_id));
 

	
 
	/* Start receiving the map */
 
	return SendGetMap();
 
}
 

	
 
NetworkRecvStatus ClientNetworkGameSocketHandler::Receive_SERVER_WAIT(Packet *p)
 
{
 
	/* We set the internal wait state when requesting the map. */
 
	if (this->status != STATUS_MAP_WAIT) return NETWORK_RECV_STATUS_MALFORMED_PACKET;
 

	
 
	/* But... only now we set the join status to waiting, instead of requesting. */
 
	_network_join_status = NETWORK_JOIN_STATUS_WAITING;
 
	_network_join_waiting = p->Recv_uint8();
 
	SetWindowDirty(WC_NETWORK_STATUS_WINDOW, WN_NETWORK_STATUS_WINDOW_JOIN);
 

	
 
	return NETWORK_RECV_STATUS_OKAY;
 
}
 

	
 
NetworkRecvStatus ClientNetworkGameSocketHandler::Receive_SERVER_MAP_BEGIN(Packet *p)
 
{
 
	if (this->status < STATUS_AUTHORIZED || this->status >= STATUS_MAP) return NETWORK_RECV_STATUS_MALFORMED_PACKET;
 
	this->status = STATUS_MAP;
 

	
 
	if (this->savegame != NULL) return NETWORK_RECV_STATUS_MALFORMED_PACKET;
 

	
 
	this->savegame = new PacketReader();
 

	
 
	_frame_counter = _frame_counter_server = _frame_counter_max = p->Recv_uint32();
 

	
 
	_network_join_bytes = 0;
 
	_network_join_bytes_total = 0;
 

	
 
	_network_join_status = NETWORK_JOIN_STATUS_DOWNLOADING;
 
	SetWindowDirty(WC_NETWORK_STATUS_WINDOW, WN_NETWORK_STATUS_WINDOW_JOIN);
 

	
 
	return NETWORK_RECV_STATUS_OKAY;
 
}
 

	
 
NetworkRecvStatus ClientNetworkGameSocketHandler::Receive_SERVER_MAP_SIZE(Packet *p)
 
{
 
	if (this->status != STATUS_MAP) return NETWORK_RECV_STATUS_MALFORMED_PACKET;
 
	if (this->savegame == NULL) return NETWORK_RECV_STATUS_MALFORMED_PACKET;
 

	
 
	_network_join_bytes_total = p->Recv_uint32();
 
	SetWindowDirty(WC_NETWORK_STATUS_WINDOW, WN_NETWORK_STATUS_WINDOW_JOIN);
 

	
 
	return NETWORK_RECV_STATUS_OKAY;
 
}
 

	
 
NetworkRecvStatus ClientNetworkGameSocketHandler::Receive_SERVER_MAP_DATA(Packet *p)
 
{
 
	if (this->status != STATUS_MAP) return NETWORK_RECV_STATUS_MALFORMED_PACKET;
 
	if (this->savegame == NULL) return NETWORK_RECV_STATUS_MALFORMED_PACKET;
 

	
 
	/* We are still receiving data, put it to the file */
 
	this->savegame->AddPacket(p);
 

	
 
	_network_join_bytes = (uint32)this->savegame->written_bytes;
 
	SetWindowDirty(WC_NETWORK_STATUS_WINDOW, WN_NETWORK_STATUS_WINDOW_JOIN);
 

	
 
	return NETWORK_RECV_STATUS_OKAY;
 
}
 

	
 
NetworkRecvStatus ClientNetworkGameSocketHandler::Receive_SERVER_MAP_DONE(Packet *p)
 
{
 
	if (this->status != STATUS_MAP) return NETWORK_RECV_STATUS_MALFORMED_PACKET;
 
	if (this->savegame == NULL) return NETWORK_RECV_STATUS_MALFORMED_PACKET;
 

	
 
	_network_join_status = NETWORK_JOIN_STATUS_PROCESSING;
 
	SetWindowDirty(WC_NETWORK_STATUS_WINDOW, WN_NETWORK_STATUS_WINDOW_JOIN);
 

	
 
	/*
 
	 * Make sure everything is set for reading.
 
	 *
 
	 * We need the local copy and reset this->savegame because when
 
	 * loading fails the network gets reset upon loading the intro
 
	 * game, which would cause us to free this->savegame twice.
 
	 */
 
	LoadFilter *lf = this->savegame;
 
	this->savegame = NULL;
 
	lf->Reset();
 

	
 
	/* The map is done downloading, load it */
 
	ClearErrorMessages();
 
	bool load_success = SafeLoad(NULL, SL_LOAD, GM_NORMAL, NO_DIRECTORY, lf);
 
	bool load_success = SafeLoad(NULL, FOP_LOAD, DFT_GAME_FILE, GM_NORMAL, NO_DIRECTORY, lf);
 

	
 
	/* Long savegame loads shouldn't affect the lag calculation! */
 
	this->last_packet = _realtime_tick;
 

	
 
	if (!load_success) {
 
		DeleteWindowById(WC_NETWORK_STATUS_WINDOW, WN_NETWORK_STATUS_WINDOW_JOIN);
 
		ShowErrorMessage(STR_NETWORK_ERROR_SAVEGAMEERROR, INVALID_STRING_ID, WL_CRITICAL);
 
		return NETWORK_RECV_STATUS_SAVEGAME;
 
	}
 
	/* If the savegame has successfully loaded, ALL windows have been removed,
 
	 * only toolbar/statusbar and gamefield are visible */
 

	
 
	/* Say we received the map and loaded it correctly! */
 
	SendMapOk();
 

	
 
	/* New company/spectator (invalid company) or company we want to join is not active
 
	 * Switch local company to spectator and await the server's judgement */
 
	if (_network_join_as == COMPANY_NEW_COMPANY || !Company::IsValidID(_network_join_as)) {
 
		SetLocalCompany(COMPANY_SPECTATOR);
 

	
 
		if (_network_join_as != COMPANY_SPECTATOR) {
 
			/* We have arrived and ready to start playing; send a command to make a new company;
 
			 * the server will give us a client-id and let us in */
 
			_network_join_status = NETWORK_JOIN_STATUS_REGISTERING;
 
			ShowJoinStatusWindow();
 
			NetworkSendCommand(0, 0, 0, CMD_COMPANY_CTRL, NULL, NULL, _local_company);
 
		}
 
	} else {
 
		/* take control over an existing company */
 
		SetLocalCompany(_network_join_as);
 
	}
 

	
 
	return NETWORK_RECV_STATUS_OKAY;
 
}
 

	
 
NetworkRecvStatus ClientNetworkGameSocketHandler::Receive_SERVER_FRAME(Packet *p)
 
{
 
	if (this->status != STATUS_ACTIVE) return NETWORK_RECV_STATUS_MALFORMED_PACKET;
 

	
 
	_frame_counter_server = p->Recv_uint32();
 
	_frame_counter_max = p->Recv_uint32();
 
#ifdef ENABLE_NETWORK_SYNC_EVERY_FRAME
 
	/* Test if the server supports this option
 
	 *  and if we are at the frame the server is */
 
	if (p->pos + 1 < p->size) {
 
		_sync_frame = _frame_counter_server;
 
		_sync_seed_1 = p->Recv_uint32();
 
#ifdef NETWORK_SEND_DOUBLE_SEED
 
		_sync_seed_2 = p->Recv_uint32();
 
#endif
 
	}
 
#endif
 
	/* Receive the token. */
 
	if (p->pos != p->size) this->token = p->Recv_uint8();
 

	
 
	DEBUG(net, 5, "Received FRAME %d", _frame_counter_server);
 

	
 
	/* Let the server know that we received this frame correctly
 
	 *  We do this only once per day, to save some bandwidth ;) */
 
	if (!_network_first_time && last_ack_frame < _frame_counter) {
 
		last_ack_frame = _frame_counter + DAY_TICKS;
 
		DEBUG(net, 4, "Sent ACK at %d", _frame_counter);
 
		SendAck();
 
	}
 

	
 
	return NETWORK_RECV_STATUS_OKAY;
 
}
 

	
 
NetworkRecvStatus ClientNetworkGameSocketHandler::Receive_SERVER_SYNC(Packet *p)
 
{
 
	if (this->status != STATUS_ACTIVE) return NETWORK_RECV_STATUS_MALFORMED_PACKET;
 

	
 
	_sync_frame = p->Recv_uint32();
 
	_sync_seed_1 = p->Recv_uint32();
 
#ifdef NETWORK_SEND_DOUBLE_SEED
 
	_sync_seed_2 = p->Recv_uint32();
 
#endif
 

	
 
	return NETWORK_RECV_STATUS_OKAY;
 
}
 

	
 
NetworkRecvStatus ClientNetworkGameSocketHandler::Receive_SERVER_COMMAND(Packet *p)
 
{
 
	if (this->status != STATUS_ACTIVE) return NETWORK_RECV_STATUS_MALFORMED_PACKET;
 

	
 
	CommandPacket cp;
 
	const char *err = this->ReceiveCommand(p, &cp);
 
	cp.frame    = p->Recv_uint32();
 
	cp.my_cmd   = p->Recv_bool();
 

	
 
	if (err != NULL) {
 
		IConsolePrintF(CC_ERROR, "WARNING: %s from server, dropping...", err);
 
		return NETWORK_RECV_STATUS_MALFORMED_PACKET;
 
	}
 

	
 
	this->incoming_queue.Append(&cp);
src/openttd.cpp
Show inline comments
 
@@ -235,193 +235,193 @@ static void WriteSavegameInfo(const char
 
	p += seprintf(p, lastof(buf), "Modified:     %d\n", ever_modified);
 

	
 
	if (removed_newgrfs) {
 
		p += seprintf(p, lastof(buf), "NewGRFs have been removed\n");
 
	}
 

	
 
	p = strecpy(p, "NewGRFs:\n", lastof(buf));
 
	if (_load_check_data.HasNewGrfs()) {
 
		for (GRFConfig *c = _load_check_data.grfconfig; c != NULL; c = c->next) {
 
			char md5sum[33];
 
			md5sumToString(md5sum, lastof(md5sum), HasBit(c->flags, GCF_COMPATIBLE) ? c->original_md5sum : c->ident.md5sum);
 
			p += seprintf(p, lastof(buf), "%08X %s %s\n", c->ident.grfid, md5sum, c->filename);
 
		}
 
	}
 

	
 
	/* ShowInfo put output to stderr, but version information should go
 
	 * to stdout; this is the only exception */
 
#if !defined(WIN32) && !defined(WIN64)
 
	printf("%s\n", buf);
 
#else
 
	ShowInfo(buf);
 
#endif
 
}
 

	
 

	
 
/**
 
 * Extract the resolution from the given string and store
 
 * it in the 'res' parameter.
 
 * @param res variable to store the resolution in.
 
 * @param s   the string to decompose.
 
 */
 
static void ParseResolution(Dimension *res, const char *s)
 
{
 
	const char *t = strchr(s, 'x');
 
	if (t == NULL) {
 
		ShowInfoF("Invalid resolution '%s'", s);
 
		return;
 
	}
 

	
 
	res->width  = max(strtoul(s, NULL, 0), 64UL);
 
	res->height = max(strtoul(t + 1, NULL, 0), 64UL);
 
}
 

	
 

	
 
/**
 
 * Unitializes drivers, frees allocated memory, cleans pools, ...
 
 * Generally, prepares the game for shutting down
 
 */
 
static void ShutdownGame()
 
{
 
	IConsoleFree();
 

	
 
	if (_network_available) NetworkShutDown(); // Shut down the network and close any open connections
 

	
 
	DriverFactoryBase::ShutdownDrivers();
 

	
 
	UnInitWindowSystem();
 

	
 
	/* stop the scripts */
 
	AI::Uninitialize(false);
 
	Game::Uninitialize(false);
 

	
 
	/* Uninitialize variables that are allocated dynamically */
 
	GamelogReset();
 

	
 
#ifdef ENABLE_NETWORK
 
	free(_config_file);
 
#endif
 

	
 
	LinkGraphSchedule::Clear();
 
	PoolBase::Clean(PT_ALL);
 

	
 
	/* No NewGRFs were loaded when it was still bootstrapping. */
 
	if (_game_mode != GM_BOOTSTRAP) ResetNewGRFData();
 

	
 
	/* Close all and any open filehandles */
 
	FioCloseAll();
 

	
 
	UninitFreeType();
 
}
 

	
 
/**
 
 * Load the introduction game.
 
 * @param load_newgrfs Whether to load the NewGRFs or not.
 
 */
 
static void LoadIntroGame(bool load_newgrfs = true)
 
{
 
	_game_mode = GM_MENU;
 

	
 
	if (load_newgrfs) ResetGRFConfig(false);
 

	
 
	/* Setup main window */
 
	ResetWindowSystem();
 
	SetupColoursAndInitialWindow();
 

	
 
	/* Load the default opening screen savegame */
 
	if (SaveOrLoad("opntitle.dat", SL_LOAD, BASESET_DIR) != SL_OK) {
 
	if (SaveOrLoad("opntitle.dat", FOP_LOAD, DFT_GAME_FILE, BASESET_DIR) != SL_OK) {
 
		GenerateWorld(GWM_EMPTY, 64, 64); // if failed loading, make empty world.
 
		WaitTillGeneratedWorld();
 
		SetLocalCompany(COMPANY_SPECTATOR);
 
	} else {
 
		SetLocalCompany(COMPANY_FIRST);
 
	}
 

	
 
	_pause_mode = PM_UNPAUSED;
 
	_cursor.fix_at = false;
 

	
 
	if (load_newgrfs) CheckForMissingSprites();
 
	CheckForMissingGlyphs();
 

	
 
	/* Play main theme */
 
	if (MusicDriver::GetInstance()->IsSongPlaying()) ResetMusic();
 
}
 

	
 
void MakeNewgameSettingsLive()
 
{
 
	for (CompanyID c = COMPANY_FIRST; c < MAX_COMPANIES; c++) {
 
		if (_settings_game.ai_config[c] != NULL) {
 
			delete _settings_game.ai_config[c];
 
		}
 
	}
 
	if (_settings_game.game_config != NULL) {
 
		delete _settings_game.game_config;
 
	}
 

	
 
	/* Copy newgame settings to active settings.
 
	 * Also initialise old settings needed for savegame conversion. */
 
	_settings_game = _settings_newgame;
 
	_old_vds = _settings_client.company.vehicle;
 

	
 
	for (CompanyID c = COMPANY_FIRST; c < MAX_COMPANIES; c++) {
 
		_settings_game.ai_config[c] = NULL;
 
		if (_settings_newgame.ai_config[c] != NULL) {
 
			_settings_game.ai_config[c] = new AIConfig(_settings_newgame.ai_config[c]);
 
		}
 
	}
 
	_settings_game.game_config = NULL;
 
	if (_settings_newgame.game_config != NULL) {
 
		_settings_game.game_config = new GameConfig(_settings_newgame.game_config);
 
	}
 
}
 

	
 
void OpenBrowser(const char *url)
 
{
 
	/* Make sure we only accept urls that are sure to open a browser. */
 
	if (strstr(url, "http://") != url && strstr(url, "https://") != url) return;
 

	
 
	extern void OSOpenBrowser(const char *url);
 
	OSOpenBrowser(url);
 
}
 

	
 
/** Callback structure of statements to be executed after the NewGRF scan. */
 
struct AfterNewGRFScan : NewGRFScanCallback {
 
	Year startyear;                    ///< The start year.
 
	uint generation_seed;              ///< Seed for the new game.
 
	char *dedicated_host;              ///< Hostname for the dedicated server.
 
	uint16 dedicated_port;             ///< Port for the dedicated server.
 
	char *network_conn;                ///< Information about the server to connect to, or NULL.
 
	const char *join_server_password;  ///< The password to join the server with.
 
	const char *join_company_password; ///< The password to join the company with.
 
	bool *save_config_ptr;             ///< The pointer to the save config setting.
 
	bool save_config;                  ///< The save config setting.
 

	
 
	/**
 
	 * Create a new callback.
 
	 * @param save_config_ptr Pointer to the save_config local variable which
 
	 *                        decides whether to save of exit or not.
 
	 */
 
	AfterNewGRFScan(bool *save_config_ptr) :
 
			startyear(INVALID_YEAR), generation_seed(GENERATE_NEW_SEED),
 
			dedicated_host(NULL), dedicated_port(0), network_conn(NULL),
 
			join_server_password(NULL), join_company_password(NULL),
 
			save_config_ptr(save_config_ptr), save_config(true)
 
	{
 
	}
 

	
 
	virtual void OnNewGRFsScanned()
 
	{
 
		ResetGRFConfig(false);
 

	
 
		TarScanner::DoScan(TarScanner::SCENARIO);
 

	
 
		AI::Initialize();
 
		Game::Initialize();
 

	
 
		/* We want the new (correct) NewGRF count to survive the loading. */
 
		uint last_newgrf_count = _settings_client.gui.last_newgrf_count;
 
		LoadFromConfig();
 
		_settings_client.gui.last_newgrf_count = last_newgrf_count;
 
		/* Since the default for the palette might have changed due to
 
		 * reading the configuration file, recalculate that now. */
 
		UpdateNewGRFConfigPalette();
 

	
 
@@ -526,224 +526,225 @@ static const OptionData _options[] = {
 
	GETOPT_END()
 
};
 

	
 
/**
 
 * Main entry point for this lovely game.
 
 * @param argc The number of arguments passed to this game.
 
 * @param argv The values of the arguments.
 
 * @return 0 when there is no error.
 
 */
 
int openttd_main(int argc, char *argv[])
 
{
 
	char *musicdriver = NULL;
 
	char *sounddriver = NULL;
 
	char *videodriver = NULL;
 
	char *blitter = NULL;
 
	char *graphics_set = NULL;
 
	char *sounds_set = NULL;
 
	char *music_set = NULL;
 
	Dimension resolution = {0, 0};
 
	/* AfterNewGRFScan sets save_config to true after scanning completed. */
 
	bool save_config = false;
 
	AfterNewGRFScan *scanner = new AfterNewGRFScan(&save_config);
 
#if defined(ENABLE_NETWORK)
 
	bool dedicated = false;
 
	char *debuglog_conn = NULL;
 

	
 
	extern bool _dedicated_forks;
 
	_dedicated_forks = false;
 
#endif /* ENABLE_NETWORK */
 

	
 
	_game_mode = GM_MENU;
 
	_switch_mode = SM_MENU;
 
	_config_file = NULL;
 

	
 
	GetOptData mgo(argc - 1, argv + 1, _options);
 
	int ret = 0;
 

	
 
	int i;
 
	while ((i = mgo.GetOpt()) != -1) {
 
		switch (i) {
 
		case 'I': free(graphics_set); graphics_set = stredup(mgo.opt); break;
 
		case 'S': free(sounds_set); sounds_set = stredup(mgo.opt); break;
 
		case 'M': free(music_set); music_set = stredup(mgo.opt); break;
 
		case 'm': free(musicdriver); musicdriver = stredup(mgo.opt); break;
 
		case 's': free(sounddriver); sounddriver = stredup(mgo.opt); break;
 
		case 'v': free(videodriver); videodriver = stredup(mgo.opt); break;
 
		case 'b': free(blitter); blitter = stredup(mgo.opt); break;
 
#if defined(ENABLE_NETWORK)
 
		case 'D':
 
			free(musicdriver);
 
			free(sounddriver);
 
			free(videodriver);
 
			free(blitter);
 
			musicdriver = stredup("null");
 
			sounddriver = stredup("null");
 
			videodriver = stredup("dedicated");
 
			blitter = stredup("null");
 
			dedicated = true;
 
			SetDebugString("net=6");
 
			if (mgo.opt != NULL) {
 
				/* Use the existing method for parsing (openttd -n).
 
				 * However, we do ignore the #company part. */
 
				const char *temp = NULL;
 
				const char *port = NULL;
 
				ParseConnectionString(&temp, &port, mgo.opt);
 
				if (!StrEmpty(mgo.opt)) scanner->dedicated_host = mgo.opt;
 
				if (port != NULL) scanner->dedicated_port = atoi(port);
 
			}
 
			break;
 
		case 'f': _dedicated_forks = true; break;
 
		case 'n':
 
			scanner->network_conn = mgo.opt; // optional IP parameter, NULL if unset
 
			break;
 
		case 'l':
 
			debuglog_conn = mgo.opt;
 
			break;
 
		case 'p':
 
			scanner->join_server_password = mgo.opt;
 
			break;
 
		case 'P':
 
			scanner->join_company_password = mgo.opt;
 
			break;
 
#endif /* ENABLE_NETWORK */
 
		case 'r': ParseResolution(&resolution, mgo.opt); break;
 
		case 't': scanner->startyear = atoi(mgo.opt); break;
 
		case 'd': {
 
#if defined(WIN32)
 
				CreateConsole();
 
#endif
 
				if (mgo.opt != NULL) SetDebugString(mgo.opt);
 
				break;
 
			}
 
		case 'e': _switch_mode = (_switch_mode == SM_LOAD_GAME || _switch_mode == SM_LOAD_SCENARIO ? SM_LOAD_SCENARIO : SM_EDITOR); break;
 
		case 'g':
 
			if (mgo.opt != NULL) {
 
				strecpy(_file_to_saveload.name, mgo.opt, lastof(_file_to_saveload.name));
 
				_switch_mode = (_switch_mode == SM_EDITOR || _switch_mode == SM_LOAD_SCENARIO ? SM_LOAD_SCENARIO : SM_LOAD_GAME);
 
				_file_to_saveload.mode = SL_LOAD;
 
				bool is_scenario = _switch_mode == SM_EDITOR || _switch_mode == SM_LOAD_SCENARIO;
 
				_switch_mode = is_scenario ? SM_LOAD_SCENARIO : SM_LOAD_GAME;
 
				_file_to_saveload.SetMode(FOP_LOAD, is_scenario ? FT_SCENARIO : FT_SAVEGAME, DFT_GAME_FILE);
 

	
 
				/* if the file doesn't exist or it is not a valid savegame, let the saveload code show an error */
 
				const char *t = strrchr(_file_to_saveload.name, '.');
 
				if (t != NULL) {
 
					FiosType ft = FiosGetSavegameListCallback(FOP_LOAD, _file_to_saveload.name, t, NULL, NULL);
 
					if (ft != FIOS_TYPE_INVALID) _file_to_saveload.SetMode(ft);
 
				}
 

	
 
				break;
 
			}
 

	
 
			_switch_mode = SM_NEWGAME;
 
			/* Give a random map if no seed has been given */
 
			if (scanner->generation_seed == GENERATE_NEW_SEED) {
 
				scanner->generation_seed = InteractiveRandom();
 
			}
 
			break;
 
		case 'q': {
 
			DeterminePaths(argv[0]);
 
			if (StrEmpty(mgo.opt)) {
 
				ret = 1;
 
				goto exit_noshutdown;
 
			}
 

	
 
			char title[80];
 
			title[0] = '\0';
 
			FiosGetSavegameListCallback(FOP_LOAD, mgo.opt, strrchr(mgo.opt, '.'), title, lastof(title));
 

	
 
			_load_check_data.Clear();
 
			SaveOrLoadResult res = SaveOrLoad(mgo.opt, SL_LOAD_CHECK, SAVE_DIR, false);
 
			SaveOrLoadResult res = SaveOrLoad(mgo.opt, FOP_CHECK, DFT_GAME_FILE, SAVE_DIR, false);
 
			if (res != SL_OK || _load_check_data.HasErrors()) {
 
				fprintf(stderr, "Failed to open savegame\n");
 
				if (_load_check_data.HasErrors()) {
 
					char buf[256];
 
					SetDParamStr(0, _load_check_data.error_data);
 
					GetString(buf, _load_check_data.error, lastof(buf));
 
					fprintf(stderr, "%s\n", buf);
 
				}
 
				goto exit_noshutdown;
 
			}
 

	
 
			WriteSavegameInfo(title);
 

	
 
			goto exit_noshutdown;
 
		}
 
		case 'G': scanner->generation_seed = atoi(mgo.opt); break;
 
		case 'c': free(_config_file); _config_file = stredup(mgo.opt); break;
 
		case 'x': scanner->save_config = false; break;
 
		case 'h':
 
			i = -2; // Force printing of help.
 
			break;
 
		}
 
		if (i == -2) break;
 
	}
 

	
 
	if (i == -2 || mgo.numleft > 0) {
 
		/* Either the user typed '-h', he made an error, or he added unrecognized command line arguments.
 
		 * In all cases, print the help, and exit.
 
		 *
 
		 * The next two functions are needed to list the graphics sets. We can't do them earlier
 
		 * because then we cannot show it on the debug console as that hasn't been configured yet. */
 
		DeterminePaths(argv[0]);
 
		TarScanner::DoScan(TarScanner::BASESET);
 
		BaseGraphics::FindSets();
 
		BaseSounds::FindSets();
 
		BaseMusic::FindSets();
 
		ShowHelp();
 

	
 
		goto exit_noshutdown;
 
	}
 

	
 
#if defined(WINCE) && defined(_DEBUG)
 
	/* Switch on debug lvl 4 for WinCE if Debug release, as you can't give params, and you most likely do want this information */
 
	SetDebugString("4");
 
#endif
 

	
 
	DeterminePaths(argv[0]);
 
	TarScanner::DoScan(TarScanner::BASESET);
 

	
 
#if defined(ENABLE_NETWORK)
 
	if (dedicated) DEBUG(net, 0, "Starting dedicated version %s", _openttd_revision);
 
	if (_dedicated_forks && !dedicated) _dedicated_forks = false;
 

	
 
#if defined(UNIX) && !defined(__MORPHOS__)
 
	/* We must fork here, or we'll end up without some resources we need (like sockets) */
 
	if (_dedicated_forks) DedicatedFork();
 
#endif
 
#endif
 

	
 
	LoadFromConfig(true);
 

	
 
	if (resolution.width != 0) _cur_resolution = resolution;
 

	
 
	/*
 
	 * The width and height must be at least 1 pixel and width times
 
	 * height times bytes per pixel must still fit within a 32 bits
 
	 * integer, even for 32 bpp video modes. This way all internal
 
	 * drawing routines work correctly.
 
	 */
 
	_cur_resolution.width  = ClampU(_cur_resolution.width,  1, UINT16_MAX / 2);
 
	_cur_resolution.height = ClampU(_cur_resolution.height, 1, UINT16_MAX / 2);
 

	
 
	/* Assume the cursor starts within the game as not all video drivers
 
	 * get an event that the cursor is within the window when it is opened.
 
	 * Saying the cursor is there makes no visible difference as it would
 
	 * just be out of the bounds of the window. */
 
	_cursor.in_window = true;
 

	
 
	/* enumerate language files */
 
	InitializeLanguagePacks();
 

	
 
	/* Initialize the regular font for FreeType */
 
	InitFreeType(false);
 

	
 
	/* This must be done early, since functions use the SetWindowDirty* calls */
 
	InitWindowSystem();
 

	
 
	BaseGraphics::FindSets();
 
	if (graphics_set == NULL && BaseGraphics::ini_set != NULL) graphics_set = stredup(BaseGraphics::ini_set);
 
	if (!BaseGraphics::SetSet(graphics_set)) {
 
		if (!StrEmpty(graphics_set)) {
 
			BaseGraphics::SetSet(NULL);
 

	
 
			ErrorMessageData msg(STR_CONFIG_ERROR, STR_CONFIG_ERROR_INVALID_BASE_GRAPHICS_NOT_FOUND);
 
			msg.SetDParamStr(0, graphics_set);
 
			ScheduleErrorMessage(msg);
 
@@ -904,352 +905,353 @@ exit_normal:
 
#ifdef ENABLE_NETWORK
 
	extern FILE *_log_fd;
 
	if (_log_fd != NULL) {
 
		fclose(_log_fd);
 
	}
 
#endif /* ENABLE_NETWORK */
 

	
 
	return ret;
 
}
 

	
 
void HandleExitGameRequest()
 
{
 
	if (_game_mode == GM_MENU || _game_mode == GM_BOOTSTRAP) { // do not ask to quit on the main screen
 
		_exit_game = true;
 
	} else if (_settings_client.gui.autosave_on_exit) {
 
		DoExitSave();
 
		_exit_game = true;
 
	} else {
 
		AskExitGame();
 
	}
 
}
 

	
 
static void MakeNewGameDone()
 
{
 
	SettingsDisableElrail(_settings_game.vehicle.disable_elrails);
 

	
 
	/* In a dedicated server, the server does not play */
 
	if (!VideoDriver::GetInstance()->HasGUI()) {
 
		SetLocalCompany(COMPANY_SPECTATOR);
 
		if (_settings_client.gui.pause_on_newgame) DoCommandP(0, PM_PAUSED_NORMAL, 1, CMD_PAUSE);
 
		IConsoleCmdExec("exec scripts/game_start.scr 0");
 
		return;
 
	}
 

	
 
	/* Create a single company */
 
	DoStartupNewCompany(false);
 

	
 
	Company *c = Company::Get(COMPANY_FIRST);
 
	c->settings = _settings_client.company;
 

	
 
	IConsoleCmdExec("exec scripts/game_start.scr 0");
 

	
 
	SetLocalCompany(COMPANY_FIRST);
 

	
 
	InitializeRailGUI();
 

	
 
#ifdef ENABLE_NETWORK
 
	/* We are the server, we start a new company (not dedicated),
 
	 * so set the default password *if* needed. */
 
	if (_network_server && !StrEmpty(_settings_client.network.default_company_pass)) {
 
		NetworkChangeCompanyPassword(_local_company, _settings_client.network.default_company_pass);
 
	}
 
#endif /* ENABLE_NETWORK */
 

	
 
	if (_settings_client.gui.pause_on_newgame) DoCommandP(0, PM_PAUSED_NORMAL, 1, CMD_PAUSE);
 

	
 
	CheckEngines();
 
	CheckIndustries();
 
	MarkWholeScreenDirty();
 
}
 

	
 
static void MakeNewGame(bool from_heightmap, bool reset_settings)
 
{
 
	_game_mode = GM_NORMAL;
 

	
 
	ResetGRFConfig(true);
 

	
 
	GenerateWorldSetCallback(&MakeNewGameDone);
 
	GenerateWorld(from_heightmap ? GWM_HEIGHTMAP : GWM_NEWGAME, 1 << _settings_game.game_creation.map_x, 1 << _settings_game.game_creation.map_y, reset_settings);
 
}
 

	
 
static void MakeNewEditorWorldDone()
 
{
 
	SetLocalCompany(OWNER_NONE);
 
}
 

	
 
static void MakeNewEditorWorld()
 
{
 
	_game_mode = GM_EDITOR;
 

	
 
	ResetGRFConfig(true);
 

	
 
	GenerateWorldSetCallback(&MakeNewEditorWorldDone);
 
	GenerateWorld(GWM_EMPTY, 1 << _settings_game.game_creation.map_x, 1 << _settings_game.game_creation.map_y);
 
}
 

	
 
/**
 
 * Load the specified savegame but on error do different things.
 
 * If loading fails due to corrupt savegame, bad version, etc. go back to
 
 * a previous correct state. In the menu for example load the intro game again.
 
 * @param mode mode of loading, either SL_LOAD or SL_OLD_LOAD
 
 * @param newgm switch to this mode of loading fails due to some unknown error
 
 * @param filename file to be loaded
 
 * @param subdir default directory to look for filename, set to 0 if not needed
 
 * @param lf Load filter to use, if NULL: use filename + subdir.
 
 */
 
bool SafeLoad(const char *filename, int mode, GameMode newgm, Subdirectory subdir, struct LoadFilter *lf = NULL)
 
bool SafeLoad(const char *filename, FileOperation fop, DetailedFileType dft, GameMode newgm, Subdirectory subdir, struct LoadFilter *lf = NULL)
 
{
 
	assert(mode == SL_LOAD || (lf == NULL && mode == SL_OLD_LOAD));
 
	assert(fop == FOP_LOAD);
 
	assert(dft == DFT_GAME_FILE || (lf == NULL && dft == DFT_OLD_GAME_FILE));
 
	GameMode ogm = _game_mode;
 

	
 
	_game_mode = newgm;
 

	
 
	switch (lf == NULL ? SaveOrLoad(filename, mode, subdir) : LoadWithFilter(lf)) {
 
	switch (lf == NULL ? SaveOrLoad(filename, fop, dft, subdir) : LoadWithFilter(lf)) {
 
		case SL_OK: return true;
 

	
 
		case SL_REINIT:
 
#ifdef ENABLE_NETWORK
 
			if (_network_dedicated) {
 
				/*
 
				 * We need to reinit a network map...
 
				 * We can't simply load the intro game here as that game has many
 
				 * special cases which make clients desync immediately. So we fall
 
				 * back to just generating a new game with the current settings.
 
				 */
 
				DEBUG(net, 0, "Loading game failed, so a new (random) game will be started!");
 
				MakeNewGame(false, true);
 
				return false;
 
			}
 
			if (_network_server) {
 
				/* We can't load the intro game as server, so disconnect first. */
 
				NetworkDisconnect();
 
			}
 
#endif /* ENABLE_NETWORK */
 

	
 
			switch (ogm) {
 
				default:
 
				case GM_MENU:   LoadIntroGame();      break;
 
				case GM_EDITOR: MakeNewEditorWorld(); break;
 
			}
 
			return false;
 

	
 
		default:
 
			_game_mode = ogm;
 
			return false;
 
	}
 
}
 

	
 
void SwitchToMode(SwitchMode new_mode)
 
{
 
#ifdef ENABLE_NETWORK
 
	/* If we are saving something, the network stays in his current state */
 
	if (new_mode != SM_SAVE_GAME) {
 
		/* If the network is active, make it not-active */
 
		if (_networking) {
 
			if (_network_server && (new_mode == SM_LOAD_GAME || new_mode == SM_NEWGAME || new_mode == SM_RESTARTGAME)) {
 
				NetworkReboot();
 
			} else {
 
				NetworkDisconnect();
 
			}
 
		}
 

	
 
		/* If we are a server, we restart the server */
 
		if (_is_network_server) {
 
			/* But not if we are going to the menu */
 
			if (new_mode != SM_MENU) {
 
				/* check if we should reload the config */
 
				if (_settings_client.network.reload_cfg) {
 
					LoadFromConfig();
 
					MakeNewgameSettingsLive();
 
					ResetGRFConfig(false);
 
				}
 
				NetworkServerStart();
 
			} else {
 
				/* This client no longer wants to be a network-server */
 
				_is_network_server = false;
 
			}
 
		}
 
	}
 
#endif /* ENABLE_NETWORK */
 
	/* Make sure all AI controllers are gone at quitting game */
 
	if (new_mode != SM_SAVE_GAME) AI::KillAll();
 

	
 
	switch (new_mode) {
 
		case SM_EDITOR: // Switch to scenario editor
 
			MakeNewEditorWorld();
 
			break;
 

	
 
		case SM_RESTARTGAME: // Restart --> 'Random game' with current settings
 
		case SM_NEWGAME: // New Game --> 'Random game'
 
#ifdef ENABLE_NETWORK
 
			if (_network_server) {
 
				seprintf(_network_game_info.map_name, lastof(_network_game_info.map_name), "Random Map");
 
			}
 
#endif /* ENABLE_NETWORK */
 
			MakeNewGame(false, new_mode == SM_NEWGAME);
 
			break;
 

	
 
		case SM_LOAD_GAME: { // Load game, Play Scenario
 
			ResetGRFConfig(true);
 
			ResetWindowSystem();
 

	
 
			if (!SafeLoad(_file_to_saveload.name, _file_to_saveload.mode, GM_NORMAL, NO_DIRECTORY)) {
 
			if (!SafeLoad(_file_to_saveload.name, _file_to_saveload.file_op, _file_to_saveload.detail_ftype, GM_NORMAL, NO_DIRECTORY)) {
 
				SetDParamStr(0, GetSaveLoadErrorString());
 
				ShowErrorMessage(STR_JUST_RAW_STRING, INVALID_STRING_ID, WL_ERROR);
 
			} else {
 
				if (_file_to_saveload.filetype == FT_SCENARIO) {
 
				if (_file_to_saveload.abstract_ftype == FT_SCENARIO) {
 
					/* Reset engine pool to simplify changing engine NewGRFs in scenario editor. */
 
					EngineOverrideManager::ResetToCurrentNewGRFConfig();
 
				}
 
				/* Update the local company for a loaded game. It is either always
 
				 * company #1 (eg 0) or in the case of a dedicated server a spectator */
 
				SetLocalCompany(_network_dedicated ? COMPANY_SPECTATOR : COMPANY_FIRST);
 
				/* Execute the game-start script */
 
				IConsoleCmdExec("exec scripts/game_start.scr 0");
 
				/* Decrease pause counter (was increased from opening load dialog) */
 
				DoCommandP(0, PM_PAUSED_SAVELOAD, 0, CMD_PAUSE);
 
#ifdef ENABLE_NETWORK
 
				if (_network_server) {
 
					seprintf(_network_game_info.map_name, lastof(_network_game_info.map_name), "%s (Loaded game)", _file_to_saveload.title);
 
				}
 
#endif /* ENABLE_NETWORK */
 
			}
 
			break;
 
		}
 

	
 
		case SM_START_HEIGHTMAP: // Load a heightmap and start a new game from it
 
#ifdef ENABLE_NETWORK
 
			if (_network_server) {
 
				seprintf(_network_game_info.map_name, lastof(_network_game_info.map_name), "%s (Heightmap)", _file_to_saveload.title);
 
			}
 
#endif /* ENABLE_NETWORK */
 
			MakeNewGame(true, true);
 
			break;
 

	
 
		case SM_LOAD_HEIGHTMAP: // Load heightmap from scenario editor
 
			SetLocalCompany(OWNER_NONE);
 

	
 
			GenerateWorld(GWM_HEIGHTMAP, 1 << _settings_game.game_creation.map_x, 1 << _settings_game.game_creation.map_y);
 
			MarkWholeScreenDirty();
 
			break;
 

	
 
		case SM_LOAD_SCENARIO: { // Load scenario from scenario editor
 
			if (SafeLoad(_file_to_saveload.name, _file_to_saveload.mode, GM_EDITOR, NO_DIRECTORY)) {
 
			if (SafeLoad(_file_to_saveload.name, _file_to_saveload.file_op, _file_to_saveload.detail_ftype, GM_EDITOR, NO_DIRECTORY)) {
 
				SetLocalCompany(OWNER_NONE);
 
				_settings_newgame.game_creation.starting_year = _cur_year;
 
				/* Cancel the saveload pausing */
 
				DoCommandP(0, PM_PAUSED_SAVELOAD, 0, CMD_PAUSE);
 
			} else {
 
				SetDParamStr(0, GetSaveLoadErrorString());
 
				ShowErrorMessage(STR_JUST_RAW_STRING, INVALID_STRING_ID, WL_ERROR);
 
			}
 
			break;
 
		}
 

	
 
		case SM_MENU: // Switch to game intro menu
 
			LoadIntroGame();
 
			if (BaseSounds::ini_set == NULL && BaseSounds::GetUsedSet()->fallback) {
 
				ShowErrorMessage(STR_WARNING_FALLBACK_SOUNDSET, INVALID_STRING_ID, WL_CRITICAL);
 
				BaseSounds::ini_set = stredup(BaseSounds::GetUsedSet()->name);
 
			}
 
			break;
 

	
 
		case SM_SAVE_GAME: // Save game.
 
			/* Make network saved games on pause compatible to singleplayer */
 
			if (SaveOrLoad(_file_to_saveload.name, SL_SAVE, NO_DIRECTORY) != SL_OK) {
 
			if (SaveOrLoad(_file_to_saveload.name, FOP_SAVE, DFT_GAME_FILE, NO_DIRECTORY) != SL_OK) {
 
				SetDParamStr(0, GetSaveLoadErrorString());
 
				ShowErrorMessage(STR_JUST_RAW_STRING, INVALID_STRING_ID, WL_ERROR);
 
			} else {
 
				DeleteWindowById(WC_SAVELOAD, 0);
 
			}
 
			break;
 

	
 
		case SM_SAVE_HEIGHTMAP: // Save heightmap.
 
			MakeHeightmapScreenshot(_file_to_saveload.name);
 
			DeleteWindowById(WC_SAVELOAD, 0);
 
			break;
 

	
 
		case SM_GENRANDLAND: // Generate random land within scenario editor
 
			SetLocalCompany(OWNER_NONE);
 
			GenerateWorld(GWM_RANDOM, 1 << _settings_game.game_creation.map_x, 1 << _settings_game.game_creation.map_y);
 
			/* XXX: set date */
 
			MarkWholeScreenDirty();
 
			break;
 

	
 
		default: NOT_REACHED();
 
	}
 
}
 

	
 

	
 
/**
 
 * Check the validity of some of the caches.
 
 * Especially in the sense of desyncs between
 
 * the cached value and what the value would
 
 * be when calculated from the 'base' data.
 
 */
 
static void CheckCaches()
 
{
 
	/* Return here so it is easy to add checks that are run
 
	 * always to aid testing of caches. */
 
	if (_debug_desync_level <= 1) return;
 

	
 
	/* Check the town caches. */
 
	SmallVector<TownCache, 4> old_town_caches;
 
	Town *t;
 
	FOR_ALL_TOWNS(t) {
 
		MemCpyT(old_town_caches.Append(), &t->cache);
 
	}
 

	
 
	extern void RebuildTownCaches();
 
	RebuildTownCaches();
 
	RebuildSubsidisedSourceAndDestinationCache();
 

	
 
	uint i = 0;
 
	FOR_ALL_TOWNS(t) {
 
		if (MemCmpT(old_town_caches.Get(i), &t->cache) != 0) {
 
			DEBUG(desync, 2, "town cache mismatch: town %i", (int)t->index);
 
		}
 
		i++;
 
	}
 

	
 
	/* Check company infrastructure cache. */
 
	SmallVector<CompanyInfrastructure, 4> old_infrastructure;
 
	Company *c;
 
	FOR_ALL_COMPANIES(c) MemCpyT(old_infrastructure.Append(), &c->infrastructure);
 

	
 
	extern void AfterLoadCompanyStats();
 
	AfterLoadCompanyStats();
 

	
 
	i = 0;
 
	FOR_ALL_COMPANIES(c) {
 
		if (MemCmpT(old_infrastructure.Get(i), &c->infrastructure) != 0) {
 
			DEBUG(desync, 2, "infrastructure cache mismatch: company %i", (int)c->index);
 
		}
 
		i++;
 
	}
 

	
 
	/* Strict checking of the road stop cache entries */
 
	const RoadStop *rs;
 
	FOR_ALL_ROADSTOPS(rs) {
 
		if (IsStandardRoadStopTile(rs->xy)) continue;
 

	
 
		assert(rs->GetEntry(DIAGDIR_NE) != rs->GetEntry(DIAGDIR_NW));
 
		rs->GetEntry(DIAGDIR_NE)->CheckIntegrity(rs);
 
		rs->GetEntry(DIAGDIR_NW)->CheckIntegrity(rs);
 
	}
 

	
 
	Vehicle *v;
 
	FOR_ALL_VEHICLES(v) {
 
		extern void FillNewGRFVehicleCache(const Vehicle *v);
 
		if (v != v->First() || v->vehstatus & VS_CRASHED || !v->IsPrimaryVehicle()) continue;
 

	
 
		uint length = 0;
 
		for (const Vehicle *u = v; u != NULL; u = u->Next()) length++;
 

	
 
		NewGRFCache        *grf_cache = CallocT<NewGRFCache>(length);
 
		VehicleCache       *veh_cache = CallocT<VehicleCache>(length);
 
		GroundVehicleCache *gro_cache = CallocT<GroundVehicleCache>(length);
 
		TrainCache         *tra_cache = CallocT<TrainCache>(length);
 

	
 
		length = 0;
 
		for (const Vehicle *u = v; u != NULL; u = u->Next()) {
 
@@ -1274,227 +1276,227 @@ static void CheckCaches()
 
			case VEH_TRAIN:    Train::From(v)->ConsistChanged(CCF_TRACK); break;
 
			case VEH_ROAD:     RoadVehUpdateCache(RoadVehicle::From(v)); break;
 
			case VEH_AIRCRAFT: UpdateAircraftCache(Aircraft::From(v));   break;
 
			case VEH_SHIP:     Ship::From(v)->UpdateCache();             break;
 
			default: break;
 
		}
 

	
 
		length = 0;
 
		for (const Vehicle *u = v; u != NULL; u = u->Next()) {
 
			FillNewGRFVehicleCache(u);
 
			if (memcmp(&grf_cache[length], &u->grf_cache, sizeof(NewGRFCache)) != 0) {
 
				DEBUG(desync, 2, "newgrf cache mismatch: type %i, vehicle %i, company %i, unit number %i, wagon %i", (int)v->type, v->index, (int)v->owner, v->unitnumber, length);
 
			}
 
			if (memcmp(&veh_cache[length], &u->vcache, sizeof(VehicleCache)) != 0) {
 
				DEBUG(desync, 2, "vehicle cache mismatch: type %i, vehicle %i, company %i, unit number %i, wagon %i", (int)v->type, v->index, (int)v->owner, v->unitnumber, length);
 
			}
 
			switch (u->type) {
 
				case VEH_TRAIN:
 
					if (memcmp(&gro_cache[length], &Train::From(u)->gcache, sizeof(GroundVehicleCache)) != 0) {
 
						DEBUG(desync, 2, "train ground vehicle cache mismatch: vehicle %i, company %i, unit number %i, wagon %i", v->index, (int)v->owner, v->unitnumber, length);
 
					}
 
					if (memcmp(&tra_cache[length], &Train::From(u)->tcache, sizeof(TrainCache)) != 0) {
 
						DEBUG(desync, 2, "train cache mismatch: vehicle %i, company %i, unit number %i, wagon %i", v->index, (int)v->owner, v->unitnumber, length);
 
					}
 
					break;
 
				case VEH_ROAD:
 
					if (memcmp(&gro_cache[length], &RoadVehicle::From(u)->gcache, sizeof(GroundVehicleCache)) != 0) {
 
						DEBUG(desync, 2, "road vehicle ground vehicle cache mismatch: vehicle %i, company %i, unit number %i, wagon %i", v->index, (int)v->owner, v->unitnumber, length);
 
					}
 
					break;
 
				default:
 
					break;
 
			}
 
			length++;
 
		}
 

	
 
		free(grf_cache);
 
		free(veh_cache);
 
		free(gro_cache);
 
		free(tra_cache);
 
	}
 

	
 
	/* Check whether the caches are still valid */
 
	FOR_ALL_VEHICLES(v) {
 
		byte buff[sizeof(VehicleCargoList)];
 
		memcpy(buff, &v->cargo, sizeof(VehicleCargoList));
 
		v->cargo.InvalidateCache();
 
		assert(memcmp(&v->cargo, buff, sizeof(VehicleCargoList)) == 0);
 
	}
 

	
 
	Station *st;
 
	FOR_ALL_STATIONS(st) {
 
		for (CargoID c = 0; c < NUM_CARGO; c++) {
 
			byte buff[sizeof(StationCargoList)];
 
			memcpy(buff, &st->goods[c].cargo, sizeof(StationCargoList));
 
			st->goods[c].cargo.InvalidateCache();
 
			assert(memcmp(&st->goods[c].cargo, buff, sizeof(StationCargoList)) == 0);
 
		}
 
	}
 
}
 

	
 
/**
 
 * State controlling game loop.
 
 * The state must not be changed from anywhere but here.
 
 * That check is enforced in DoCommand.
 
 */
 
void StateGameLoop()
 
{
 
	/* don't execute the state loop during pause */
 
	if (_pause_mode != PM_UNPAUSED) {
 
		UpdateLandscapingLimits();
 
#ifndef DEBUG_DUMP_COMMANDS
 
		Game::GameLoop();
 
#endif
 
		CallWindowTickEvent();
 
		return;
 
	}
 
	if (HasModalProgress()) return;
 

	
 
	Layouter::ReduceLineCache();
 

	
 
	if (_game_mode == GM_EDITOR) {
 
		BasePersistentStorageArray::SwitchMode(PSM_ENTER_GAMELOOP);
 
		RunTileLoop();
 
		CallVehicleTicks();
 
		CallLandscapeTick();
 
		BasePersistentStorageArray::SwitchMode(PSM_LEAVE_GAMELOOP);
 
		UpdateLandscapingLimits();
 

	
 
		CallWindowTickEvent();
 
		NewsLoop();
 
	} else {
 
		if (_debug_desync_level > 2 && _date_fract == 0 && (_date & 0x1F) == 0) {
 
			/* Save the desync savegame if needed. */
 
			char name[MAX_PATH];
 
			seprintf(name, lastof(name), "dmp_cmds_%08x_%08x.sav", _settings_game.game_creation.generation_seed, _date);
 
			SaveOrLoad(name, SL_SAVE, AUTOSAVE_DIR, false);
 
			SaveOrLoad(name, FOP_SAVE, DFT_GAME_FILE, AUTOSAVE_DIR, false);
 
		}
 

	
 
		CheckCaches();
 

	
 
		/* All these actions has to be done from OWNER_NONE
 
		 *  for multiplayer compatibility */
 
		Backup<CompanyByte> cur_company(_current_company, OWNER_NONE, FILE_LINE);
 

	
 
		BasePersistentStorageArray::SwitchMode(PSM_ENTER_GAMELOOP);
 
		AnimateAnimatedTiles();
 
		IncreaseDate();
 
		RunTileLoop();
 
		CallVehicleTicks();
 
		CallLandscapeTick();
 
		BasePersistentStorageArray::SwitchMode(PSM_LEAVE_GAMELOOP);
 

	
 
#ifndef DEBUG_DUMP_COMMANDS
 
		AI::GameLoop();
 
		Game::GameLoop();
 
#endif
 
		UpdateLandscapingLimits();
 

	
 
		CallWindowTickEvent();
 
		NewsLoop();
 
		cur_company.Restore();
 
	}
 

	
 
	assert(IsLocalCompany());
 
}
 

	
 
/**
 
 * Create an autosave. The default name is "autosave#.sav". However with
 
 * the setting 'keep_all_autosave' the name defaults to company-name + date
 
 */
 
static void DoAutosave()
 
{
 
	char buf[MAX_PATH];
 

	
 
#if defined(PSP)
 
	/* Autosaving in networking is too time expensive for the PSP */
 
	if (_networking) return;
 
#endif /* PSP */
 

	
 
	if (_settings_client.gui.keep_all_autosave) {
 
		GenerateDefaultSaveName(buf, lastof(buf));
 
		strecat(buf, ".sav", lastof(buf));
 
	} else {
 
		static int _autosave_ctr = 0;
 

	
 
		/* generate a savegame name and number according to _settings_client.gui.max_num_autosaves */
 
		seprintf(buf, lastof(buf), "autosave%d.sav", _autosave_ctr);
 

	
 
		if (++_autosave_ctr >= _settings_client.gui.max_num_autosaves) _autosave_ctr = 0;
 
	}
 

	
 
	DEBUG(sl, 2, "Autosaving to '%s'", buf);
 
	if (SaveOrLoad(buf, SL_SAVE, AUTOSAVE_DIR) != SL_OK) {
 
	if (SaveOrLoad(buf, FOP_SAVE, DFT_GAME_FILE, AUTOSAVE_DIR) != SL_OK) {
 
		ShowErrorMessage(STR_ERROR_AUTOSAVE_FAILED, INVALID_STRING_ID, WL_ERROR);
 
	}
 
}
 

	
 
void GameLoop()
 
{
 
	if (_game_mode == GM_BOOTSTRAP) {
 
#ifdef ENABLE_NETWORK
 
		/* Check for UDP stuff */
 
		if (_network_available) NetworkBackgroundLoop();
 
#endif
 
		InputLoop();
 
		return;
 
	}
 

	
 
	ProcessAsyncSaveFinish();
 

	
 
	/* autosave game? */
 
	if (_do_autosave) {
 
		DoAutosave();
 
		_do_autosave = false;
 
		SetWindowDirty(WC_STATUS_BAR, 0);
 
	}
 

	
 
	/* switch game mode? */
 
	if (_switch_mode != SM_NONE && !HasModalProgress()) {
 
		SwitchToMode(_switch_mode);
 
		_switch_mode = SM_NONE;
 
	}
 

	
 
	IncreaseSpriteLRU();
 
	InteractiveRandom();
 

	
 
	extern int _caret_timer;
 
	_caret_timer += 3;
 
	CursorTick();
 

	
 
#ifdef ENABLE_NETWORK
 
	/* Check for UDP stuff */
 
	if (_network_available) NetworkBackgroundLoop();
 

	
 
	if (_networking && !HasModalProgress()) {
 
		/* Multiplayer */
 
		NetworkGameLoop();
 
	} else {
 
		if (_network_reconnect > 0 && --_network_reconnect == 0) {
 
			/* This means that we want to reconnect to the last host
 
			 * We do this here, because it means that the network is really closed */
 
			NetworkClientConnectGame(NetworkAddress(_settings_client.network.last_host, _settings_client.network.last_port), COMPANY_SPECTATOR);
 
		}
 
		/* Singleplayer */
 
		StateGameLoop();
 
	}
 

	
 
	/* Check chat messages roughly once a second. */
 
	static uint check_message = 0;
 
	if (++check_message > 1000 / MILLISECONDS_PER_TICK) {
 
		check_message = 0;
 
		NetworkChatMessageLoop();
 
	}
 
#else
 
	StateGameLoop();
 
#endif /* ENABLE_NETWORK */
 

	
 
	if (!_pause_mode && HasBit(_display_opt, DO_FULL_ANIMATION)) DoPaletteAnimations();
 

	
 
	if (!_pause_mode || _game_mode == GM_EDITOR || _settings_game.construction.command_pause_level > CMDPL_NO_CONSTRUCTION) MoveAllTextEffects();
 

	
 
	InputLoop();
 

	
 
	SoundDriver::GetInstance()->MainLoop();
 
	MusicLoop();
 
}
src/saveload/afterload.cpp
Show inline comments
 
@@ -155,193 +155,193 @@ static void ConvertTownOwner()
 
					_m[tile].m3 = OWNER_TOWN;
 
				}
 
				/* FALL THROUGH */
 

	
 
			case MP_TUNNELBRIDGE:
 
				if (_m[tile].m1 & 0x80) SetTileOwner(tile, OWNER_TOWN);
 
				break;
 

	
 
			default: break;
 
		}
 
	}
 
}
 

	
 
/* since savegame version 4.1, exclusive transport rights are stored at towns */
 
static void UpdateExclusiveRights()
 
{
 
	Town *t;
 

	
 
	FOR_ALL_TOWNS(t) {
 
		t->exclusivity = INVALID_COMPANY;
 
	}
 

	
 
	/* FIXME old exclusive rights status is not being imported (stored in s->blocked_months_obsolete)
 
	 *   could be implemented this way:
 
	 * 1.) Go through all stations
 
	 *     Build an array town_blocked[ town_id ][ company_id ]
 
	 *     that stores if at least one station in that town is blocked for a company
 
	 * 2.) Go through that array, if you find a town that is not blocked for
 
	 *     one company, but for all others, then give him exclusivity.
 
	 */
 
}
 

	
 
static const byte convert_currency[] = {
 
	 0,  1, 12,  8,  3,
 
	10, 14, 19,  4,  5,
 
	 9, 11, 13,  6, 17,
 
	16, 22, 21,  7, 15,
 
	18,  2, 20,
 
};
 

	
 
/* since savegame version 4.2 the currencies are arranged differently */
 
static void UpdateCurrencies()
 
{
 
	_settings_game.locale.currency = convert_currency[_settings_game.locale.currency];
 
}
 

	
 
/* Up to revision 1413 the invisible tiles at the southern border have not been
 
 * MP_VOID, even though they should have. This is fixed by this function
 
 */
 
static void UpdateVoidTiles()
 
{
 
	uint i;
 

	
 
	for (i = 0; i < MapMaxY(); ++i) MakeVoid(i * MapSizeX() + MapMaxX());
 
	for (i = 0; i < MapSizeX(); ++i) MakeVoid(MapSizeX() * MapMaxY() + i);
 
}
 

	
 
static inline RailType UpdateRailType(RailType rt, RailType min)
 
{
 
	return rt >= min ? (RailType)(rt + 1): rt;
 
}
 

	
 
/**
 
 * Update the viewport coordinates of all signs.
 
 */
 
void UpdateAllVirtCoords()
 
{
 
	UpdateAllStationVirtCoords();
 
	UpdateAllSignVirtCoords();
 
	UpdateAllTownVirtCoords();
 
}
 

	
 
/**
 
 * Initialization of the windows and several kinds of caches.
 
 * This is not done directly in AfterLoadGame because these
 
 * functions require that all saveload conversions have been
 
 * done. As people tend to add savegame conversion stuff after
 
 * the intialization of the windows and caches quite some bugs
 
 * had been made.
 
 * Moving this out of there is both cleaner and less bug-prone.
 
 */
 
static void InitializeWindowsAndCaches()
 
{
 
	/* Initialize windows */
 
	ResetWindowSystem();
 
	SetupColoursAndInitialWindow();
 

	
 
	/* Update coordinates of the signs. */
 
	UpdateAllVirtCoords();
 
	ResetViewportAfterLoadGame();
 

	
 
	Company *c;
 
	FOR_ALL_COMPANIES(c) {
 
		/* For each company, verify (while loading a scenario) that the inauguration date is the current year and set it
 
		 * accordingly if it is not the case.  No need to set it on companies that are not been used already,
 
		 * thus the MIN_YEAR (which is really nothing more than Zero, initialized value) test */
 
		if (_file_to_saveload.filetype == FT_SCENARIO && c->inaugurated_year != MIN_YEAR) {
 
		if (_file_to_saveload.abstract_ftype == FT_SCENARIO && c->inaugurated_year != MIN_YEAR) {
 
			c->inaugurated_year = _cur_year;
 
		}
 
	}
 

	
 
	/* Count number of objects per type */
 
	Object *o;
 
	FOR_ALL_OBJECTS(o) {
 
		Object::IncTypeCount(o->type);
 
	}
 

	
 
	/* Identify owners of persistent storage arrays */
 
	Industry *i;
 
	FOR_ALL_INDUSTRIES(i) {
 
		if (i->psa != NULL) {
 
			i->psa->feature = GSF_INDUSTRIES;
 
			i->psa->tile = i->location.tile;
 
		}
 
	}
 
	Station *s;
 
	FOR_ALL_STATIONS(s) {
 
		if (s->airport.psa != NULL) {
 
			s->airport.psa->feature = GSF_AIRPORTS;
 
			s->airport.psa->tile = s->airport.tile;
 
		}
 
	}
 
	Town *t;
 
	FOR_ALL_TOWNS(t) {
 
		for (std::list<PersistentStorage *>::iterator it = t->psa_list.begin(); it != t->psa_list.end(); ++it) {
 
			(*it)->feature = GSF_FAKE_TOWNS;
 
			(*it)->tile = t->xy;
 
		}
 
	}
 

	
 
	RecomputePrices();
 

	
 
	GroupStatistics::UpdateAfterLoad();
 

	
 
	Station::RecomputeIndustriesNearForAll();
 
	RebuildSubsidisedSourceAndDestinationCache();
 

	
 
	/* Towns have a noise controlled number of airports system
 
	 * So each airport's noise value must be added to the town->noise_reached value
 
	 * Reset each town's noise_reached value to '0' before. */
 
	UpdateAirportsNoise();
 

	
 
	CheckTrainsLengths();
 
	ShowNewGRFError();
 
	ShowAIDebugWindowIfAIError();
 

	
 
	/* Rebuild the smallmap list of owners. */
 
	BuildOwnerLegend();
 
}
 

	
 
typedef void (CDECL *SignalHandlerPointer)(int);
 
static SignalHandlerPointer _prev_segfault = NULL;
 
static SignalHandlerPointer _prev_abort    = NULL;
 
static SignalHandlerPointer _prev_fpe      = NULL;
 

	
 
static void CDECL HandleSavegameLoadCrash(int signum);
 

	
 
/**
 
 * Replaces signal handlers of SIGSEGV and SIGABRT
 
 * and stores pointers to original handlers in memory.
 
 */
 
static void SetSignalHandlers()
 
{
 
	_prev_segfault = signal(SIGSEGV, HandleSavegameLoadCrash);
 
	_prev_abort    = signal(SIGABRT, HandleSavegameLoadCrash);
 
	_prev_fpe      = signal(SIGFPE,  HandleSavegameLoadCrash);
 
}
 

	
 
/**
 
 * Resets signal handlers back to original handlers.
 
 */
 
static void ResetSignalHandlers()
 
{
 
	signal(SIGSEGV, _prev_segfault);
 
	signal(SIGABRT, _prev_abort);
 
	signal(SIGFPE,  _prev_fpe);
 
}
 

	
 
/**
 
 * Try to find the overridden GRF identifier of the given GRF.
 
 * @param c the GRF to get the 'previous' version of.
 
 * @return the GRF identifier or \a c if none could be found.
 
 */
 
static const GRFIdentifier *GetOverriddenIdentifier(const GRFConfig *c)
 
{
 
	const LoggedAction *la = &_gamelog_action[_gamelog_actions - 1];
 
	if (la->at != GLAT_LOAD) return &c->ident;
 

	
 
	const LoggedChange *lcend = &la->change[la->changes];
 
	for (const LoggedChange *lc = la->change; lc != lcend; lc++) {
 
		if (lc->ct == GLCT_GRFCOMPAT && lc->grfcompat.grfid == c->ident.grfid) return &lc->grfcompat;
 
	}
 

	
src/saveload/saveload.cpp
Show inline comments
 
@@ -2685,278 +2685,282 @@ static SaveOrLoadResult DoLoad(LoadFilte
 
	_sl.lf = fmt->init_load(_sl.lf);
 
	_sl.reader = new ReadBuffer(_sl.lf);
 
	_next_offs = 0;
 

	
 
	if (!load_check) {
 
		/* Old maps were hardcoded to 256x256 and thus did not contain
 
		 * any mapsize information. Pre-initialize to 256x256 to not to
 
		 * confuse old games */
 
		InitializeGame(256, 256, true, true);
 

	
 
		GamelogReset();
 

	
 
		if (IsSavegameVersionBefore(4)) {
 
			/*
 
			 * NewGRFs were introduced between 0.3,4 and 0.3.5, which both
 
			 * shared savegame version 4. Anything before that 'obviously'
 
			 * does not have any NewGRFs. Between the introduction and
 
			 * savegame version 41 (just before 0.5) the NewGRF settings
 
			 * were not stored in the savegame and they were loaded by
 
			 * using the settings from the main menu.
 
			 * So, to recap:
 
			 * - savegame version  <  4:  do not load any NewGRFs.
 
			 * - savegame version >= 41:  load NewGRFs from savegame, which is
 
			 *                            already done at this stage by
 
			 *                            overwriting the main menu settings.
 
			 * - other savegame versions: use main menu settings.
 
			 *
 
			 * This means that users *can* crash savegame version 4..40
 
			 * savegames if they set incompatible NewGRFs in the main menu,
 
			 * but can't crash anymore for savegame version < 4 savegames.
 
			 *
 
			 * Note: this is done here because AfterLoadGame is also called
 
			 * for TTO/TTD/TTDP savegames which have their own NewGRF logic.
 
			 */
 
			ClearGRFConfigList(&_grfconfig);
 
		}
 
	}
 

	
 
	if (load_check) {
 
		/* Load chunks into _load_check_data.
 
		 * No pools are loaded. References are not possible, and thus do not need resolving. */
 
		SlLoadCheckChunks();
 
	} else {
 
		/* Load chunks and resolve references */
 
		SlLoadChunks();
 
		SlFixPointers();
 
	}
 

	
 
	ClearSaveLoadState();
 

	
 
	_savegame_type = SGT_OTTD;
 

	
 
	if (load_check) {
 
		/* The only part from AfterLoadGame() we need */
 
		_load_check_data.grf_compatibility = IsGoodGRFConfigList(_load_check_data.grfconfig);
 
	} else {
 
		GamelogStartAction(GLAT_LOAD);
 

	
 
		/* After loading fix up savegame for any internal changes that
 
		 * might have occurred since then. If it fails, load back the old game. */
 
		if (!AfterLoadGame()) {
 
			GamelogStopAction();
 
			return SL_REINIT;
 
		}
 

	
 
		GamelogStopAction();
 
	}
 

	
 
	return SL_OK;
 
}
 

	
 
/**
 
 * Load the game using a (reader) filter.
 
 * @param reader   The filter to read the savegame from.
 
 * @return Return the result of the action. #SL_OK or #SL_REINIT ("unload" the game)
 
 */
 
SaveOrLoadResult LoadWithFilter(LoadFilter *reader)
 
{
 
	try {
 
		_sl.action = SLA_LOAD;
 
		return DoLoad(reader, false);
 
	} catch (...) {
 
		ClearSaveLoadState();
 
		return SL_REINIT;
 
	}
 
}
 

	
 
/**
 
 * Main Save or Load function where the high-level saveload functions are
 
 * handled. It opens the savegame, selects format and checks versions
 
 * @param filename The name of the savegame being created/loaded
 
 * @param mode Save or load mode. Load can also be a TTD(Patch) game. Use #SL_LOAD, #SL_OLD_LOAD, #SL_LOAD_CHECK, or #SL_SAVE.
 
 * @param sb The sub directory to save the savegame in
 
 * @param threaded True when threaded saving is allowed
 
 * @return Return the result of the action. #SL_OK, #SL_ERROR, or #SL_REINIT ("unload" the game)
 
 */
 
SaveOrLoadResult SaveOrLoad(const char *filename, int mode, Subdirectory sb, bool threaded)
 
SaveOrLoadResult SaveOrLoad(const char *filename, FileOperation fop, DetailedFileType dft, Subdirectory sb, bool threaded)
 
{
 
	/* An instance of saving is already active, so don't go saving again */
 
	if (_sl.saveinprogress && mode == SL_SAVE && threaded) {
 
	if (_sl.saveinprogress && fop == FOP_SAVE && dft == DFT_GAME_FILE && threaded) {
 
		/* if not an autosave, but a user action, show error message */
 
		if (!_do_autosave) ShowErrorMessage(STR_ERROR_SAVE_STILL_IN_PROGRESS, INVALID_STRING_ID, WL_ERROR);
 
		return SL_OK;
 
	}
 
	WaitTillSaved();
 

	
 
	try {
 
		/* Load a TTDLX or TTDPatch game */
 
		if (mode == SL_OLD_LOAD) {
 
		if (fop == FOP_LOAD && dft == DFT_OLD_GAME_FILE) {
 
			InitializeGame(256, 256, true, true); // set a mapsize of 256x256 for TTDPatch games or it might get confused
 

	
 
			/* TTD/TTO savegames have no NewGRFs, TTDP savegame have them
 
			 * and if so a new NewGRF list will be made in LoadOldSaveGame.
 
			 * Note: this is done here because AfterLoadGame is also called
 
			 * for OTTD savegames which have their own NewGRF logic. */
 
			ClearGRFConfigList(&_grfconfig);
 
			GamelogReset();
 
			if (!LoadOldSaveGame(filename)) return SL_REINIT;
 
			_sl_version = 0;
 
			_sl_minor_version = 0;
 
			GamelogStartAction(GLAT_LOAD);
 
			if (!AfterLoadGame()) {
 
				GamelogStopAction();
 
				return SL_REINIT;
 
			}
 
			GamelogStopAction();
 
			return SL_OK;
 
		}
 

	
 
		switch (mode) {
 
			case SL_LOAD_CHECK: _sl.action = SLA_LOAD_CHECK; break;
 
			case SL_LOAD: _sl.action = SLA_LOAD; break;
 
			case SL_SAVE: _sl.action = SLA_SAVE; break;
 
		assert(dft == DFT_GAME_FILE);
 
		switch (dft) {
 
			case FOP_CHECK:
 
				_sl.action = SLA_LOAD_CHECK;
 
				break;
 

	
 
			case FOP_LOAD:
 
				_sl.action = SLA_LOAD;
 
				break;
 

	
 
			case FOP_SAVE:
 
				_sl.action = SLA_SAVE;
 
				break;
 

	
 
			default: NOT_REACHED();
 
		}
 

	
 
		FILE *fh = (mode == SL_SAVE) ? FioFOpenFile(filename, "wb", sb) : FioFOpenFile(filename, "rb", sb);
 
		FILE *fh = (fop == FOP_SAVE) ? FioFOpenFile(filename, "wb", sb) : FioFOpenFile(filename, "rb", sb);
 

	
 
		/* Make it a little easier to load savegames from the console */
 
		if (fh == NULL && mode != SL_SAVE) fh = FioFOpenFile(filename, "rb", SAVE_DIR);
 
		if (fh == NULL && mode != SL_SAVE) fh = FioFOpenFile(filename, "rb", BASE_DIR);
 
		if (fh == NULL && mode != SL_SAVE) fh = FioFOpenFile(filename, "rb", SCENARIO_DIR);
 
		if (fh == NULL && fop != FOP_SAVE) fh = FioFOpenFile(filename, "rb", SAVE_DIR);
 
		if (fh == NULL && fop != FOP_SAVE) fh = FioFOpenFile(filename, "rb", BASE_DIR);
 
		if (fh == NULL && fop != FOP_SAVE) fh = FioFOpenFile(filename, "rb", SCENARIO_DIR);
 

	
 
		if (fh == NULL) {
 
			SlError(mode == SL_SAVE ? STR_GAME_SAVELOAD_ERROR_FILE_NOT_WRITEABLE : STR_GAME_SAVELOAD_ERROR_FILE_NOT_READABLE);
 
			SlError(fop == FOP_SAVE ? STR_GAME_SAVELOAD_ERROR_FILE_NOT_WRITEABLE : STR_GAME_SAVELOAD_ERROR_FILE_NOT_READABLE);
 
		}
 

	
 
		if (mode == SL_SAVE) { // SAVE game
 
		if (fop == FOP_SAVE) { // SAVE game
 
			DEBUG(desync, 1, "save: %08x; %02x; %s", _date, _date_fract, filename);
 
			if (_network_server || !_settings_client.gui.threaded_saves) threaded = false;
 

	
 
			return DoSave(new FileWriter(fh), threaded);
 
		}
 

	
 
		/* LOAD game */
 
		assert(mode == SL_LOAD || mode == SL_LOAD_CHECK);
 
		assert(fop == FOP_LOAD || fop == FOP_CHECK);
 
		DEBUG(desync, 1, "load: %s", filename);
 
		return DoLoad(new FileReader(fh), mode == SL_LOAD_CHECK);
 
		return DoLoad(new FileReader(fh), fop == FOP_CHECK);
 
	} catch (...) {
 
		/* This code may be executed both for old and new save games. */
 
		ClearSaveLoadState();
 

	
 
		/* Skip the "colour" character */
 
		if (mode != SL_LOAD_CHECK) DEBUG(sl, 0, "%s", GetSaveLoadErrorString() + 3);
 
		if (fop != FOP_CHECK) DEBUG(sl, 0, "%s", GetSaveLoadErrorString() + 3);
 

	
 
		/* A saver/loader exception!! reinitialize all variables to prevent crash! */
 
		return (mode == SL_LOAD || mode == SL_OLD_LOAD) ? SL_REINIT : SL_ERROR;
 
		return (fop == FOP_LOAD) ? SL_REINIT : SL_ERROR;
 
	}
 
}
 

	
 
/** Do a save when exiting the game (_settings_client.gui.autosave_on_exit) */
 
void DoExitSave()
 
{
 
	SaveOrLoad("exit.sav", SL_SAVE, AUTOSAVE_DIR);
 
	SaveOrLoad("exit.sav", FOP_SAVE, DFT_GAME_FILE, AUTOSAVE_DIR);
 
}
 

	
 
/**
 
 * Fill the buffer with the default name for a savegame *or* screenshot.
 
 * @param buf the buffer to write to.
 
 * @param last the last element in the buffer.
 
 */
 
void GenerateDefaultSaveName(char *buf, const char *last)
 
{
 
	/* Check if we have a name for this map, which is the name of the first
 
	 * available company. When there's no company available we'll use
 
	 * 'Spectator' as "company" name. */
 
	CompanyID cid = _local_company;
 
	if (!Company::IsValidID(cid)) {
 
		const Company *c;
 
		FOR_ALL_COMPANIES(c) {
 
			cid = c->index;
 
			break;
 
		}
 
	}
 

	
 
	SetDParam(0, cid);
 

	
 
	/* Insert current date */
 
	switch (_settings_client.gui.date_format_in_default_names) {
 
		case 0: SetDParam(1, STR_JUST_DATE_LONG); break;
 
		case 1: SetDParam(1, STR_JUST_DATE_TINY); break;
 
		case 2: SetDParam(1, STR_JUST_DATE_ISO); break;
 
		default: NOT_REACHED();
 
	}
 
	SetDParam(2, _date);
 

	
 
	/* Get the correct string (special string for when there's not company) */
 
	GetString(buf, !Company::IsValidID(cid) ? STR_SAVEGAME_NAME_SPECTATOR : STR_SAVEGAME_NAME_DEFAULT, last);
 
	SanitizeFilename(buf);
 
}
 

	
 
/**
 
 * Set the mode and file type of the file to save or load based on the type of file entry at the file system.
 
 * @param ft Type of file entry of the file system.
 
 */
 
void FileToSaveLoad::SetMode(FiosType ft)
 
{
 
	switch (ft) {
 
		case FIOS_TYPE_FILE:
 
		case FIOS_TYPE_SCENARIO:
 
			this->mode = SL_LOAD;
 
			break;
 

	
 
		case FIOS_TYPE_OLDFILE:
 
		case FIOS_TYPE_OLD_SCENARIO:
 
			this->mode = SL_OLD_LOAD;
 
			break;
 

	
 
#ifdef WITH_PNG
 
		case FIOS_TYPE_PNG:
 
			this->mode = SL_PNG;
 
			break;
 
#endif /* WITH_PNG */
 

	
 
		case FIOS_TYPE_BMP:
 
			this->mode = SL_BMP;
 
			break;
 

	
 
		default:
 
			this->mode = SL_INVALID;
 
			break;
 
	this->SetMode(FOP_LOAD, GetAbstractFileType(ft), GetDetailedFileType(ft));
 
	}
 

	
 
	this->filetype = GetAbstractFileType(ft);
 
	if (this->filetype == FT_NONE) this->filetype = FT_INVALID;
 
/**
 
 * Set the mode and file type of the file to save or load.
 
 * @param fop File operation being performed.
 
 * @param aft Abstract file type.
 
 * @param dft Detailed file type.
 
 */
 
void FileToSaveLoad::SetMode(FileOperation fop, AbstractFileType aft, DetailedFileType dft)
 
{
 
	if (aft == FT_INVALID || aft == FT_NONE) {
 
		this->file_op = FOP_INVALID;
 
		this->detail_ftype = DFT_INVALID;
 
		this->abstract_ftype = FT_INVALID;
 
		return;
 
	}
 

	
 
	this->file_op = fop;
 
	this->detail_ftype = dft;
 
	this->abstract_ftype = aft;
 
}
 

	
 
#if 0
 
/**
 
 * Function to get the type of the savegame by looking at the file header.
 
 * NOTICE: Not used right now, but could be used if extensions of savegames are garbled
 
 * @param file Savegame to be checked
 
 * @return SL_OLD_LOAD or SL_LOAD of the file
 
 */
 
int GetSavegameType(char *file)
 
{
 
	const SaveLoadFormat *fmt;
 
	uint32 hdr;
 
	FILE *f;
 
	int mode = SL_OLD_LOAD;
 

	
 
	f = fopen(file, "rb");
 
	if (fread(&hdr, sizeof(hdr), 1, f) != 1) {
 
		DEBUG(sl, 0, "Savegame is obsolete or invalid format");
 
		mode = SL_LOAD; // don't try to get filename, just show name as it is written
 
	} else {
 
		/* see if we have any loader for this type. */
 
		for (fmt = _saveload_formats; fmt != endof(_saveload_formats); fmt++) {
 
			if (fmt->tag == hdr) {
 
				mode = SL_LOAD; // new type of savegame
 
				break;
 
			}
 
		}
 
	}
 

	
 
	fclose(f);
 
	return mode;
 
}
 
#endif
src/saveload/saveload.h
Show inline comments
 
/* $Id$ */
 

	
 
/*
 
 * This file is part of OpenTTD.
 
 * OpenTTD is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, version 2.
 
 * OpenTTD is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
 
 * See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with OpenTTD. If not, see <http://www.gnu.org/licenses/>.
 
 */
 

	
 
/** @file saveload.h Functions/types related to saving and loading games. */
 

	
 
#ifndef SAVELOAD_H
 
#define SAVELOAD_H
 

	
 
#include "../fileio_type.h"
 
#include "../strings_type.h"
 

	
 
/** Save or load result codes. */
 
enum SaveOrLoadResult {
 
	SL_OK     = 0, ///< completed successfully
 
	SL_ERROR  = 1, ///< error that was caught before internal structures were modified
 
	SL_REINIT = 2, ///< error that was caught in the middle of updating game state, need to clear it. (can only happen during load)
 
};
 

	
 
/** Save or load mode. @see SaveOrLoad */
 
enum SaveOrLoadMode {
 
	SL_INVALID    = -1, ///< Invalid mode.
 
	SL_LOAD       =  0, ///< Load game.
 
	SL_SAVE       =  1, ///< Save game.
 
	SL_OLD_LOAD   =  2, ///< Load old game.
 
	SL_PNG        =  3, ///< Load PNG file (height map).
 
	SL_BMP        =  4, ///< Load BMP file (height map).
 
	SL_LOAD_CHECK =  5, ///< Load for game preview.
 
};
 

	
 
/** Deals with the type of the savegame, independent of extension */
 
struct FileToSaveLoad {
 
	SaveOrLoadMode mode;       ///< savegame/scenario type (old, new)
 
	AbstractFileType filetype; ///< what type of file are we dealing with
 
	char name[MAX_PATH];       ///< name
 
	char title[255];           ///< internal name of the game
 
	FileOperation file_op;           ///< File operation to perform.
 
	DetailedFileType detail_ftype;   ///< Concrete file type (PNG, BMP, old save, etc).
 
	AbstractFileType abstract_ftype; ///< Abstract type of file (scenario, heightmap, etc).
 
	char name[MAX_PATH];             ///< Name of the file.
 
	char title[255];                 ///< Internal name of the game.
 

	
 
	void SetMode(FiosType ft);
 
	void SetMode(FileOperation fop, AbstractFileType aft, DetailedFileType dft);
 
};
 

	
 
/** Types of save games. */
 
enum SavegameType {
 
	SGT_TTD,    ///< TTD  savegame (can be detected incorrectly)
 
	SGT_TTDP1,  ///< TTDP savegame ( -//- ) (data at NW border)
 
	SGT_TTDP2,  ///< TTDP savegame in new format (data at SE border)
 
	SGT_OTTD,   ///< OTTD savegame
 
	SGT_TTO,    ///< TTO savegame
 
	SGT_INVALID = 0xFF, ///< broken savegame (used internally)
 
};
 

	
 
extern FileToSaveLoad _file_to_saveload;
 

	
 
void GenerateDefaultSaveName(char *buf, const char *last);
 
void SetSaveLoadError(uint16 str);
 
const char *GetSaveLoadErrorString();
 
SaveOrLoadResult SaveOrLoad(const char *filename, int mode, Subdirectory sb, bool threaded = true);
 
SaveOrLoadResult SaveOrLoad(const char *filename, FileOperation fop, DetailedFileType dft, Subdirectory sb, bool threaded = true);
 
void WaitTillSaved();
 
void ProcessAsyncSaveFinish();
 
void DoExitSave();
 

	
 
SaveOrLoadResult SaveWithFilter(struct SaveFilter *writer, bool threaded);
 
SaveOrLoadResult LoadWithFilter(struct LoadFilter *reader);
 

	
 
typedef void ChunkSaveLoadProc();
 
typedef void AutolengthProc(void *arg);
 

	
 
/** Handlers and description of chunk. */
 
struct ChunkHandler {
 
	uint32 id;                          ///< Unique ID (4 letters).
 
	ChunkSaveLoadProc *save_proc;       ///< Save procedure of the chunk.
 
	ChunkSaveLoadProc *load_proc;       ///< Load procedure of the chunk.
 
	ChunkSaveLoadProc *ptrs_proc;       ///< Manipulate pointers in the chunk.
 
	ChunkSaveLoadProc *load_check_proc; ///< Load procedure for game preview.
 
	uint32 flags;                       ///< Flags of the chunk. @see ChunkType
 
};
 

	
 
struct NullStruct {
 
	byte null;
 
};
 

	
 
/** Type of reference (#SLE_REF, #SLE_CONDREF). */
 
enum SLRefType {
 
	REF_ORDER          =  0, ///< Load/save a reference to an order.
 
	REF_VEHICLE        =  1, ///< Load/save a reference to a vehicle.
 
	REF_STATION        =  2, ///< Load/save a reference to a station.
 
	REF_TOWN           =  3, ///< Load/save a reference to a town.
 
	REF_VEHICLE_OLD    =  4, ///< Load/save an old-style reference to a vehicle (for pre-4.4 savegames).
 
	REF_ROADSTOPS      =  5, ///< Load/save a reference to a bus/truck stop.
 
	REF_ENGINE_RENEWS  =  6, ///< Load/save a reference to an engine renewal (autoreplace).
 
	REF_CARGO_PACKET   =  7, ///< Load/save a reference to a cargo packet.
 
	REF_ORDERLIST      =  8, ///< Load/save a reference to an orderlist.
 
	REF_STORAGE        =  9, ///< Load/save a reference to a persistent storage.
 
	REF_LINK_GRAPH     = 10, ///< Load/save a reference to a link graph.
 
	REF_LINK_GRAPH_JOB = 11, ///< Load/save a reference to a link graph job.
 
};
 

	
 
/** Highest possible savegame version. */
 
#define SL_MAX_VERSION UINT16_MAX
 

	
 
/** Flags of a chunk. */
 
enum ChunkType {
 
	CH_RIFF         =  0,
 
	CH_ARRAY        =  1,
 
	CH_SPARSE_ARRAY =  2,
 
	CH_TYPE_MASK    =  3,
 
	CH_LAST         =  8, ///< Last chunk in this array.
 
	CH_AUTO_LENGTH  = 16,
 
};
 

	
 
/**
 
 * VarTypes is the general bitmasked magic type that tells us
 
 * certain characteristics about the variable it refers to. For example
 
 * SLE_FILE_* gives the size(type) as it would be in the savegame and
 
 * SLE_VAR_* the size(type) as it is in memory during runtime. These are
 
 * the first 8 bits (0-3 SLE_FILE, 4-7 SLE_VAR).
 
 * Bits 8-15 are reserved for various flags as explained below
 
 */
 
enum VarTypes {
 
	/* 4 bits allocated a maximum of 16 types for NumberType */
 
	SLE_FILE_I8       = 0,
 
	SLE_FILE_U8       = 1,
 
	SLE_FILE_I16      = 2,
 
	SLE_FILE_U16      = 3,
 
	SLE_FILE_I32      = 4,
 
	SLE_FILE_U32      = 5,
 
	SLE_FILE_I64      = 6,
 
	SLE_FILE_U64      = 7,
 
	SLE_FILE_STRINGID = 8, ///< StringID offset into strings-array
 
	SLE_FILE_STRING   = 9,
 
	/* 6 more possible file-primitives */
 

	
 
	/* 4 bits allocated a maximum of 16 types for NumberType */
 
	SLE_VAR_BL    =  0 << 4,
 
	SLE_VAR_I8    =  1 << 4,
 
	SLE_VAR_U8    =  2 << 4,
 
	SLE_VAR_I16   =  3 << 4,
 
	SLE_VAR_U16   =  4 << 4,
 
	SLE_VAR_I32   =  5 << 4,
 
	SLE_VAR_U32   =  6 << 4,
 
	SLE_VAR_I64   =  7 << 4,
 
	SLE_VAR_U64   =  8 << 4,
 
	SLE_VAR_NULL  =  9 << 4, ///< useful to write zeros in savegame.
 
	SLE_VAR_STRB  = 10 << 4, ///< string (with pre-allocated buffer)
 
	SLE_VAR_STRBQ = 11 << 4, ///< string enclosed in quotes (with pre-allocated buffer)
 
	SLE_VAR_STR   = 12 << 4, ///< string pointer
 
	SLE_VAR_STRQ  = 13 << 4, ///< string pointer enclosed in quotes
 
	SLE_VAR_NAME  = 14 << 4, ///< old custom name to be converted to a char pointer
 
	/* 1 more possible memory-primitives */
 

	
 
	/* Shortcut values */
 
	SLE_VAR_CHAR = SLE_VAR_I8,
 

	
src/saveload/signs_sl.cpp
Show inline comments
 
/* $Id$ */
 

	
 
/*
 
 * This file is part of OpenTTD.
 
 * OpenTTD is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, version 2.
 
 * OpenTTD is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
 
 * See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with OpenTTD. If not, see <http://www.gnu.org/licenses/>.
 
 */
 

	
 
/** @file signs_sl.cpp Code handling saving and loading of economy data */
 

	
 
#include "../stdafx.h"
 
#include "../signs_base.h"
 
#include "../fios.h"
 

	
 
#include "saveload.h"
 

	
 
#include "../safeguards.h"
 

	
 
/** Description of a sign within the savegame. */
 
static const SaveLoad _sign_desc[] = {
 
	SLE_CONDVAR(Sign, name,  SLE_NAME,                   0, 83),
 
	SLE_CONDSTR(Sign, name,  SLE_STR | SLF_ALLOW_CONTROL, 0, 84, SL_MAX_VERSION),
 
	SLE_CONDVAR(Sign, x,     SLE_FILE_I16 | SLE_VAR_I32, 0, 4),
 
	SLE_CONDVAR(Sign, y,     SLE_FILE_I16 | SLE_VAR_I32, 0, 4),
 
	SLE_CONDVAR(Sign, x,     SLE_INT32,                  5, SL_MAX_VERSION),
 
	SLE_CONDVAR(Sign, y,     SLE_INT32,                  5, SL_MAX_VERSION),
 
	SLE_CONDVAR(Sign, owner, SLE_UINT8,                  6, SL_MAX_VERSION),
 
	SLE_CONDVAR(Sign, z,     SLE_FILE_U8  | SLE_VAR_I32, 0, 163),
 
	SLE_CONDVAR(Sign, z,     SLE_INT32,                164, SL_MAX_VERSION),
 
	SLE_END()
 
};
 

	
 
/** Save all signs */
 
static void Save_SIGN()
 
{
 
	Sign *si;
 

	
 
	FOR_ALL_SIGNS(si) {
 
		SlSetArrayIndex(si->index);
 
		SlObject(si, _sign_desc);
 
	}
 
}
 

	
 
/** Load all signs */
 
static void Load_SIGN()
 
{
 
	int index;
 
	while ((index = SlIterateArray()) != -1) {
 
		Sign *si = new (index) Sign();
 
		SlObject(si, _sign_desc);
 
		/* Before version 6.1, signs didn't have owner.
 
		 * Before version 83, invalid signs were determined by si->str == 0.
 
		 * Before version 103, owner could be a bankrupted company.
 
		 *  - we can't use IsValidCompany() now, so this is fixed in AfterLoadGame()
 
		 * All signs that were saved are valid (including those with just 'Sign' and INVALID_OWNER).
 
		 *  - so set owner to OWNER_NONE if needed (signs from pre-version 6.1 would be lost) */
 
		if (IsSavegameVersionBefore(6, 1) || (IsSavegameVersionBefore(83) && si->owner == INVALID_OWNER)) {
 
			si->owner = OWNER_NONE;
 
		}
 

	
 
		/* Signs placed in scenario editor shall now be OWNER_DEITY */
 
		if (IsSavegameVersionBefore(171) && si->owner == OWNER_NONE && _file_to_saveload.filetype == FT_SCENARIO) {
 
		if (IsSavegameVersionBefore(171) && si->owner == OWNER_NONE && _file_to_saveload.abstract_ftype == FT_SCENARIO) {
 
			si->owner = OWNER_DEITY;
 
		}
 
	}
 
}
 

	
 
/** Chunk handlers related to signs. */
 
extern const ChunkHandler _sign_chunk_handlers[] = {
 
	{ 'SIGN', Save_SIGN, Load_SIGN, NULL, NULL, CH_ARRAY | CH_LAST},
 
};
src/video/dedicated_v.cpp
Show inline comments
 
@@ -49,286 +49,286 @@ static void OS2_SwitchToConsoleMode()
 
{
 
	PPIB pib;
 
	PTIB tib;
 

	
 
	DosGetInfoBlocks(&tib, &pib);
 

	
 
	/* Change flag from PM to VIO */
 
	pib->pib_ultype = 3;
 
}
 
#endif
 

	
 
#if defined(UNIX) || defined(PSP)
 
#	include <sys/time.h> /* gettimeofday */
 
#	include <sys/types.h>
 
#	include <unistd.h>
 
#	include <signal.h>
 
#	define STDIN 0  /* file descriptor for standard input */
 
#	if defined(PSP)
 
#		include <sys/fd_set.h>
 
#		include <sys/select.h>
 
#	endif /* PSP */
 

	
 
/* Signal handlers */
 
static void DedicatedSignalHandler(int sig)
 
{
 
	if (_game_mode == GM_NORMAL && _settings_client.gui.autosave_on_exit) DoExitSave();
 
	_exit_game = true;
 
	signal(sig, DedicatedSignalHandler);
 
}
 
#endif
 

	
 
#if defined(WIN32)
 
# include <windows.h> /* GetTickCount */
 
# if !defined(WINCE)
 
#  include <conio.h>
 
# endif
 
# include <time.h>
 
# include <tchar.h>
 
static HANDLE _hInputReady, _hWaitForInputHandling;
 
static HANDLE _hThread; // Thread to close
 
static char _win_console_thread_buffer[200];
 

	
 
/* Windows Console thread. Just loop and signal when input has been received */
 
static void WINAPI CheckForConsoleInput()
 
{
 
#if defined(WINCE)
 
	/* WinCE doesn't support console stuff */
 
	return;
 
#else
 
	DWORD nb;
 
	HANDLE hStdin = GetStdHandle(STD_INPUT_HANDLE);
 
	for (;;) {
 
		ReadFile(hStdin, _win_console_thread_buffer, lengthof(_win_console_thread_buffer), &nb, NULL);
 
		if (nb >= lengthof(_win_console_thread_buffer)) nb = lengthof(_win_console_thread_buffer) - 1;
 
		_win_console_thread_buffer[nb] = '\0';
 

	
 
		/* Signal input waiting that input is read and wait for it being handled
 
		 * SignalObjectAndWait() should be used here, but it's unsupported in Win98< */
 
		SetEvent(_hInputReady);
 
		WaitForSingleObject(_hWaitForInputHandling, INFINITE);
 
	}
 
#endif
 
}
 

	
 
static void CreateWindowsConsoleThread()
 
{
 
	DWORD dwThreadId;
 
	/* Create event to signal when console input is ready */
 
	_hInputReady = CreateEvent(NULL, false, false, NULL);
 
	_hWaitForInputHandling = CreateEvent(NULL, false, false, NULL);
 
	if (_hInputReady == NULL || _hWaitForInputHandling == NULL) usererror("Cannot create console event!");
 

	
 
	_hThread = CreateThread(NULL, 0, (LPTHREAD_START_ROUTINE)CheckForConsoleInput, NULL, 0, &dwThreadId);
 
	if (_hThread == NULL) usererror("Cannot create console thread!");
 

	
 
	DEBUG(driver, 2, "Windows console thread started");
 
}
 

	
 
static void CloseWindowsConsoleThread()
 
{
 
	CloseHandle(_hThread);
 
	CloseHandle(_hInputReady);
 
	CloseHandle(_hWaitForInputHandling);
 
	DEBUG(driver, 2, "Windows console thread shut down");
 
}
 

	
 
#endif
 

	
 
#include "../safeguards.h"
 

	
 

	
 
static void *_dedicated_video_mem;
 

	
 
/* Whether a fork has been done. */
 
bool _dedicated_forks;
 

	
 
extern bool SafeLoad(const char *filename, int mode, GameMode newgm, Subdirectory subdir, struct LoadFilter *lf = NULL);
 
extern bool SafeLoad(const char *filename, FileOperation fop, DetailedFileType dft, GameMode newgm, Subdirectory subdir, struct LoadFilter *lf = NULL);
 

	
 
static FVideoDriver_Dedicated iFVideoDriver_Dedicated;
 

	
 

	
 
const char *VideoDriver_Dedicated::Start(const char * const *parm)
 
{
 
	int bpp = BlitterFactory::GetCurrentBlitter()->GetScreenDepth();
 
	_dedicated_video_mem = (bpp == 0) ? NULL : MallocT<byte>(_cur_resolution.width * _cur_resolution.height * (bpp / 8));
 

	
 
	_screen.width  = _screen.pitch = _cur_resolution.width;
 
	_screen.height = _cur_resolution.height;
 
	_screen.dst_ptr = _dedicated_video_mem;
 
	ScreenSizeChanged();
 
	BlitterFactory::GetCurrentBlitter()->PostResize();
 

	
 
#if defined(WINCE)
 
	/* WinCE doesn't support console stuff */
 
#elif defined(WIN32)
 
	/* For win32 we need to allocate a console (debug mode does the same) */
 
	CreateConsole();
 
	CreateWindowsConsoleThread();
 
	SetConsoleTitle(_T("OpenTTD Dedicated Server"));
 
#endif
 

	
 
#ifdef _MSC_VER
 
	/* Disable the MSVC assertion message box. */
 
	_set_error_mode(_OUT_TO_STDERR);
 
#endif
 

	
 
#ifdef __OS2__
 
	/* For OS/2 we also need to switch to console mode instead of PM mode */
 
	OS2_SwitchToConsoleMode();
 
#endif
 

	
 
	DEBUG(driver, 1, "Loading dedicated server");
 
	return NULL;
 
}
 

	
 
void VideoDriver_Dedicated::Stop()
 
{
 
#ifdef WIN32
 
	CloseWindowsConsoleThread();
 
#endif
 
	free(_dedicated_video_mem);
 
}
 

	
 
void VideoDriver_Dedicated::MakeDirty(int left, int top, int width, int height) {}
 
bool VideoDriver_Dedicated::ChangeResolution(int w, int h) { return false; }
 
bool VideoDriver_Dedicated::ToggleFullscreen(bool fs) { return false; }
 

	
 
#if defined(UNIX) || defined(__OS2__) || defined(PSP)
 
static bool InputWaiting()
 
{
 
	struct timeval tv;
 
	fd_set readfds;
 

	
 
	tv.tv_sec = 0;
 
	tv.tv_usec = 1;
 

	
 
	FD_ZERO(&readfds);
 
	FD_SET(STDIN, &readfds);
 

	
 
	/* don't care about writefds and exceptfds: */
 
	return select(STDIN + 1, &readfds, NULL, NULL, &tv) > 0;
 
}
 

	
 
static uint32 GetTime()
 
{
 
	struct timeval tim;
 

	
 
	gettimeofday(&tim, NULL);
 
	return tim.tv_usec / 1000 + tim.tv_sec * 1000;
 
}
 

	
 
#else
 

	
 
static bool InputWaiting()
 
{
 
	return WaitForSingleObject(_hInputReady, 1) == WAIT_OBJECT_0;
 
}
 

	
 
static uint32 GetTime()
 
{
 
	return GetTickCount();
 
}
 

	
 
#endif
 

	
 
static void DedicatedHandleKeyInput()
 
{
 
	static char input_line[1024] = "";
 

	
 
	if (!InputWaiting()) return;
 

	
 
	if (_exit_game) return;
 

	
 
#if defined(UNIX) || defined(__OS2__) || defined(PSP)
 
	if (fgets(input_line, lengthof(input_line), stdin) == NULL) return;
 
#else
 
	/* Handle console input, and signal console thread, it can accept input again */
 
	assert_compile(lengthof(_win_console_thread_buffer) <= lengthof(input_line));
 
	strecpy(input_line, _win_console_thread_buffer, lastof(input_line));
 
	SetEvent(_hWaitForInputHandling);
 
#endif
 

	
 
	/* Remove trailing \r or \n */
 
	for (char *c = input_line; *c != '\0'; c++) {
 
		if (*c == '\n' || *c == '\r' || c == lastof(input_line)) {
 
			*c = '\0';
 
			break;
 
		}
 
	}
 
	str_validate(input_line, lastof(input_line));
 

	
 
	IConsoleCmdExec(input_line); // execute command
 
}
 

	
 
void VideoDriver_Dedicated::MainLoop()
 
{
 
	uint32 cur_ticks = GetTime();
 
	uint32 next_tick = cur_ticks + MILLISECONDS_PER_TICK;
 

	
 
	/* Signal handlers */
 
#if defined(UNIX) || defined(PSP)
 
	signal(SIGTERM, DedicatedSignalHandler);
 
	signal(SIGINT, DedicatedSignalHandler);
 
	signal(SIGQUIT, DedicatedSignalHandler);
 
#endif
 

	
 
	/* Load the dedicated server stuff */
 
	_is_network_server = true;
 
	_network_dedicated = true;
 
	_current_company = _local_company = COMPANY_SPECTATOR;
 

	
 
	/* If SwitchMode is SM_LOAD_GAME, it means that the user used the '-g' options */
 
	if (_switch_mode != SM_LOAD_GAME) {
 
		StartNewGameWithoutGUI(GENERATE_NEW_SEED);
 
		SwitchToMode(_switch_mode);
 
		_switch_mode = SM_NONE;
 
	} else {
 
		_switch_mode = SM_NONE;
 
		/* First we need to test if the savegame can be loaded, else we will end up playing the
 
		 *  intro game... */
 
		if (!SafeLoad(_file_to_saveload.name, _file_to_saveload.mode, GM_NORMAL, BASE_DIR)) {
 
		if (!SafeLoad(_file_to_saveload.name, _file_to_saveload.file_op, _file_to_saveload.detail_ftype, GM_NORMAL, BASE_DIR)) {
 
			/* Loading failed, pop out.. */
 
			DEBUG(net, 0, "Loading requested map failed, aborting");
 
			_networking = false;
 
		} else {
 
			/* We can load this game, so go ahead */
 
			SwitchToMode(SM_LOAD_GAME);
 
		}
 
	}
 

	
 
	/* Done loading, start game! */
 

	
 
	if (!_networking) {
 
		DEBUG(net, 0, "Dedicated server could not be started, aborting");
 
		return;
 
	}
 

	
 
	while (!_exit_game) {
 
		uint32 prev_cur_ticks = cur_ticks; // to check for wrapping
 
		InteractiveRandom(); // randomness
 

	
 
		if (!_dedicated_forks) DedicatedHandleKeyInput();
 

	
 
		cur_ticks = GetTime();
 
		_realtime_tick += cur_ticks - prev_cur_ticks;
 
		if (cur_ticks >= next_tick || cur_ticks < prev_cur_ticks || _ddc_fastforward) {
 
			next_tick = cur_ticks + MILLISECONDS_PER_TICK;
 

	
 
			GameLoop();
 
			UpdateWindows();
 
		}
 

	
 
		/* Don't sleep when fast forwarding (for desync debugging) */
 
		if (!_ddc_fastforward) {
 
			/* Sleep longer on a dedicated server, if the game is paused and no clients connected.
 
			 * That can allow the CPU to better use deep sleep states. */
 
			if (_pause_mode != 0 && !HasClients()) {
 
				CSleep(100);
 
			} else {
 
				CSleep(1);
 
			}
 
		}
 
	}
 
}
 

	
 
#endif /* ENABLE_NETWORK */
0 comments (0 inline, 0 general)