Changeset - r26194:f7347205838e
[Not reviewed]
Ruby Dennington (Theleruby) - 2 years ago 2022-03-20 13:57:20
theleruby@gmail.com
Ginever.net OpenTTD Patchpack 6.0.0
69 files changed:
Changeset was too big and was cut off... Show full diff anyway
0 comments (0 inline, 0 general)
.gitignore
Show inline comments
 
@@ -2,6 +2,9 @@
 
/build*
 
CMakeSettings.json
 
docs/aidocs/*
 
docs/gamedocs/*
 
docs/source/*
 
/out
 
/.ottdrev-gpp
 
/media/baseset/openttd.grf
 
/media/baseset/orig_extra.grf
 
\ No newline at end of file
cmake/scripts/FindVersion.cmake
Show inline comments
 
@@ -100,12 +100,26 @@ if(GIT_FOUND AND EXISTS "${CMAKE_SOURCE_
 
        set(REV_ISTAG 0)
 
        set(REV_ISSTABLETAG 0)
 
    endif()
 

	
 
    # Restore LC_ALL
 
    set(ENV{LC_ALL} "${SAVED_LC_ALL}")
 
elseif(EXISTS "${CMAKE_SOURCE_DIR}/versiondump.py")
 
    execute_process(COMMAND py -3 versiondump.py
 
                    WORKING_DIRECTORY ${CMAKE_SOURCE_DIR}
 
    )
 
    file(READ "${CMAKE_SOURCE_DIR}/.ottdrev-gpp" OTTDREV)
 
    string(REPLACE "\n" "" OTTDREV "${OTTDREV}")
 
    string(REPLACE "\t" ";" OTTDREV "${OTTDREV}")
 
    list(GET OTTDREV 0 REV_VERSION)
 
    list(GET OTTDREV 1 REV_ISODATE)
 
    list(GET OTTDREV 2 REV_MODIFIED)
 
    list(GET OTTDREV 3 REV_HASH)
 
    list(GET OTTDREV 4 REV_ISTAG)
 
    list(GET OTTDREV 5 REV_ISSTABLETAG)
 
    list(GET OTTDREV 6 REV_YEAR)
 
elseif(EXISTS "${CMAKE_SOURCE_DIR}/.ottdrev")
 
    file(READ "${CMAKE_SOURCE_DIR}/.ottdrev" OTTDREV)
 
    string(REPLACE "\n" "" OTTDREV "${OTTDREV}")
 
    string(REPLACE "\t" ";" OTTDREV "${OTTDREV}")
 
    list(GET OTTDREV 0 REV_VERSION)
 
    list(GET OTTDREV 1 REV_ISODATE)
media/baseset/openttd.grf
Show inline comments
 
deleted file
 
binary diff not shown
media/baseset/orig_extra.grf
Show inline comments
 
deleted file
 
binary diff not shown
src/CMakeLists.txt
Show inline comments
 
@@ -404,12 +404,13 @@ add_files(
 
    story_gui.cpp
 
    story_type.h
 
    strgen/strgen.h
 
    string.cpp
 
    string_base.h
 
    string_func.h
 
    string_func_extra.h
 
    string_type.h
 
    stringfilter.cpp
 
    stringfilter_type.h
 
    strings.cpp
 
    strings_func.h
 
    strings_type.h
src/aircraft.h
Show inline comments
 
@@ -54,12 +54,13 @@ void GetAircraftSpriteSize(EngineID engi
 
void UpdateAirplanesOnNewStation(const Station *st);
 
void UpdateAircraftCache(Aircraft *v, bool update_range = false);
 

	
 
void AircraftLeaveHangar(Aircraft *v, Direction exit_dir);
 
void AircraftNextAirportPos_and_Order(Aircraft *v);
 
void SetAircraftPosition(Aircraft *v, int x, int y, int z);
 
void FindBreakdownDestination(Aircraft *v);
 

	
 
void GetAircraftFlightLevelBounds(const Vehicle *v, int *min, int *max);
 
template <class T>
 
int GetAircraftFlightLevel(T *v, bool takeoff = false);
 

	
 
/** Variables that are cached to improve performance and such. */
src/aircraft_cmd.cpp
Show inline comments
 
@@ -146,14 +146,14 @@ static StationID FindNearestHangar(const
 

	
 
		const AirportFTAClass *afc = st->airport.GetFTA();
 

	
 
		/* don't crash the plane if we know it can't land at the airport */
 
		if ((afc->flags & AirportFTAClass::SHORT_STRIP) && (avi->subtype & AIR_FAST) && !_cheats.no_jetcrash.value) continue;
 

	
 
		/* the plane won't land at any helicopter station */
 
		if (!(afc->flags & AirportFTAClass::AIRPLANES) && (avi->subtype & AIR_CTOL)) continue;
 
		/* the plane won't land at any helicopter station and vice versa*/
 
		if (!CanVehicleUseStation(v, st)) continue;
 

	
 
		/* Check if our last and next destinations can be reached from the depot airport. */
 
		if (max_range != 0) {
 
			uint last_dist = (last_dest != nullptr && last_dest->airport.tile != INVALID_TILE) ? DistanceSquare(st->airport.tile, last_dest->airport.tile) : 0;
 
			uint next_dist = (next_dest != nullptr && next_dest->airport.tile != INVALID_TILE) ? DistanceSquare(st->airport.tile, next_dest->airport.tile) : 0;
 
			if (last_dist > max_range || next_dist > max_range) continue;
 
@@ -323,12 +323,15 @@ CommandCost CmdBuildAircraft(DoCommandFl
 

	
 
		u->subtype = AIR_SHADOW;
 
		u->UpdateDeltaXY();
 

	
 
		v->reliability = e->reliability;
 
		v->reliability_spd_dec = e->reliability_spd_dec;
 
		/* higher speed means higher breakdown chance */
 
		/* to somewhat compensate for the fact that fast aircraft spend less time in the air */
 
		v->breakdown_chance = Clamp(64 + (AircraftVehInfo(v->engine_type)->max_speed >> 3), 0, 255);
 
		v->max_age = e->GetLifeLengthInDays();
 

	
 
		v->pos = GetVehiclePosOnBuild(tile);
 

	
 
		v->state = HANGAR;
 
		v->previous_pos = v->pos;
 
@@ -434,12 +437,13 @@ static void CheckIfAircraftNeedsService(
 
}
 

	
 
Money Aircraft::GetRunningCost() const
 
{
 
	const Engine *e = this->GetEngine();
 
	uint cost_factor = GetVehicleProperty(this, PROP_AIRCRAFT_RUNNING_COST_FACTOR, e->u.air.running_cost);
 
	cost_factor *= _settings_game.economy.running_cost_multiplier_air;
 
	return GetPrice(PR_RUNNING_AIRCRAFT, cost_factor, e->GetGRF());
 
}
 

	
 
void Aircraft::OnNewDay()
 
{
 
	if (!this->IsNormalAircraft()) return;
 
@@ -604,13 +608,13 @@ void UpdateAircraftCache(Aircraft *v, bo
 
	v->vcache.cached_cargo_age_period = GetVehicleProperty(v, PROP_AIRCRAFT_CARGO_AGE_PERIOD, EngInfo(v->engine_type)->cargo_age_period);
 
	Aircraft *u = v->Next(); // Shadow for mail
 
	u->vcache.cached_cargo_age_period = GetVehicleProperty(u, PROP_AIRCRAFT_CARGO_AGE_PERIOD, EngInfo(u->engine_type)->cargo_age_period);
 

	
 
	/* Update aircraft range. */
 
	if (update_range) {
 
		v->acache.cached_max_range = GetVehicleProperty(v, PROP_AIRCRAFT_RANGE, AircraftVehInfo(v->engine_type)->max_range);
 
		v->acache.cached_max_range = (GetVehicleProperty(v, PROP_AIRCRAFT_RANGE, AircraftVehInfo(v->engine_type)->max_range) * _settings_game.vehicle.plane_range_multiplier);
 
		/* Squared it now so we don't have to do it later all the time. */
 
		v->acache.cached_max_range_sqr = v->acache.cached_max_range * v->acache.cached_max_range;
 
	}
 
}
 

	
 

	
 
@@ -646,15 +650,23 @@ static int UpdateAircraftSpeed(Aircraft 
 

	
 
	/* Adjust speed limits by plane speed factor to prevent taxiing
 
	 * and take-off speeds being too low. */
 
	speed_limit *= _settings_game.vehicle.plane_speed;
 

	
 
	/* adjust speed for broken vehicles */
 
	if (v->vehstatus & VS_AIRCRAFT_BROKEN) {
 
		if (SPEED_LIMIT_BROKEN < speed_limit) hard_limit = false;
 
		speed_limit = std::min<uint>(speed_limit, SPEED_LIMIT_BROKEN);
 
	if (v->breakdown_ctr == 1 && v->breakdown_type == BREAKDOWN_AIRCRAFT_SPEED) {
 
		if (v->breakdown_severity < 10)
 
		{
 
			// stupid workaround for fixing old saves with severity 0, also FGSP
 
			v->breakdown_severity = 10;
 
		}
 
		uint percentage_speed_limit = v->breakdown_severity;
 
		uint cached_max_speed = v->vcache.cached_max_speed;
 
		uint broken_down_max_speed = ((cached_max_speed * percentage_speed_limit) + 99) / 100;
 
		if (broken_down_max_speed < speed_limit) hard_limit = false;
 
		speed_limit = std::min<uint>(speed_limit, broken_down_max_speed);
 
	}
 

	
 
	if (v->vcache.cached_max_speed < speed_limit) {
 
		if (v->cur_speed < speed_limit) hard_limit = false;
 
		speed_limit = v->vcache.cached_max_speed;
 
	}
 
@@ -1153,12 +1165,45 @@ static bool AircraftController(Aircraft 
 
		SetAircraftPosition(v, gp.x, gp.y, z);
 
	} while (--count != 0);
 
	return false;
 
}
 

	
 
/**
 
 * Send a broken plane that needs to visit a depot to the correct location.
 
 * @param v The airplane in question
 
 */
 
void FindBreakdownDestination(Aircraft *v)
 
{
 
	assert(v->type == VEH_AIRCRAFT && v->breakdown_ctr == 1);
 

	
 
	DestinationID destination = INVALID_STATION;
 
	if (v->breakdown_type == BREAKDOWN_AIRCRAFT_DEPOT) {
 
		/* Go to a hangar, if possible at our current destination */
 
		v->FindClosestDepot(NULL, &destination, NULL);
 
	} else if (v->breakdown_type == BREAKDOWN_AIRCRAFT_EM_LANDING) {
 
		/* Go to the nearest airport with a hangar */
 
		destination = FindNearestHangar(v);
 
	} else {
 
		NOT_REACHED();
 
	}
 

	
 
	if(destination != INVALID_STATION) {
 
		if(destination != v->current_order.GetDestination()) {
 
			v->current_order.MakeGoToDepot(destination, ODTFB_BREAKDOWN);
 
			AircraftNextAirportPos_and_Order(v);
 
		} else {
 
			v->current_order.MakeGoToDepot(destination, ODTFB_BREAKDOWN);
 
		}
 
	} else {
 
		/* If no hangar was found, crash */
 
		v->targetairport = INVALID_STATION;
 
		CrashAirplane(v);
 
	}
 
}
 

	
 
/**
 
 * Handle crashed aircraft \a v.
 
 * @param v Crashed aircraft.
 
 */
 
static bool HandleCrashedAircraft(Aircraft *v)
 
{
 
	v->crashed_counter += 3;
 
@@ -1232,14 +1277,15 @@ static void HandleAircraftSmoke(Aircraft
 
		{ -5,  5 },
 
		{  0,  6 }
 
	};
 

	
 
	if (!(v->vehstatus & VS_AIRCRAFT_BROKEN)) return;
 

	
 
	/* Stop smoking when landed */
 
	if (v->cur_speed < 10) {
 
	/* breakdown-related speed limits are lifted when we are on the ground */
 
	if(v->state != FLYING && v->state != LANDING && v->breakdown_type == BREAKDOWN_AIRCRAFT_SPEED) {
 
		/* Stop smoking when landed */
 
		v->vehstatus &= ~VS_AIRCRAFT_BROKEN;
 
		v->breakdown_ctr = 0;
 
		return;
 
	}
 

	
 
	/* Spawn effect et most once per Tick, i.e. !mode */
 
@@ -1362,14 +1408,18 @@ static void MaybeCrashAirplane(Aircraft 
 

	
 
	uint32 prob;
 
	if ((st->airport.GetFTA()->flags & AirportFTAClass::SHORT_STRIP) &&
 
			(AircraftVehInfo(v->engine_type)->subtype & AIR_FAST) &&
 
			!_cheats.no_jetcrash.value) {
 
		prob = 3276;
 
	} else if (_settings_game.vehicle.plane_crashes == 0) {
 
		return;
 
	} else if (_settings_game.vehicle.improved_breakdowns && v->breakdown_ctr == 1 && v->breakdown_type == BREAKDOWN_AIRCRAFT_EM_LANDING) {
 
		/* Airplanes that are attempting an emergency landing have a 2% chance to crash */
 
		prob = 0x10000 / 50;
 
	} else {
 
		if (_settings_game.vehicle.plane_crashes == 0) return;
 
		prob = (0x4000 << _settings_game.vehicle.plane_crashes) / 1500;
 
	}
 

	
 
	if (GB(Random(), 0, 22) > prob) return;
 

	
 
	/* Crash the airplane. Remove all goods stored at the station. */
src/clear_cmd.cpp
Show inline comments
 
@@ -31,13 +31,13 @@ static CommandCost ClearTile_Clear(TileI
 
		PR_CLEAR_ROUGH,
 
		PR_CLEAR_ROCKS,
 
		PR_CLEAR_FIELDS,
 
		PR_CLEAR_ROUGH,
 
		PR_CLEAR_ROUGH,
 
	};
 
	CommandCost price(EXPENSES_CONSTRUCTION);
 
	CommandCost price(EXPENSES_T_DEMOLITION);
 

	
 
	if (!IsClearGround(tile, CLEAR_GRASS) || GetClearDensity(tile) != 0) {
 
		price.AddCost(_price[clear_price_table[GetClearGround(tile)]]);
 
	}
 

	
 
	if (flags & DC_EXEC) DoClearSquare(tile);
src/command_type.h
Show inline comments
 
@@ -19,13 +19,13 @@ struct GRFFile;
 

	
 
/**
 
 * Common return value for all commands. Wraps the cost and
 
 * a possible error message/state together.
 
 */
 
class CommandCost {
 
	ExpensesType expense_type; ///< the type of expence as shown on the finances view
 
public: ExpensesType expense_type; ///< the type of expence as shown on the finances view
 
	Money cost;       ///< The cost of this action
 
	StringID message; ///< Warning message for when success is unset
 
	bool success;     ///< Whether the comment went fine up to this moment
 
	const GRFFile *textref_stack_grffile; ///< NewGRF providing the #TextRefStack content.
 
	uint textref_stack_size;   ///< Number of uint32 values to put on the #TextRefStack for the error message.
 

	
src/company_cmd.cpp
Show inline comments
 
@@ -577,12 +577,14 @@ Company *DoStartupNewCompany(bool is_ai,
 

	
 
	if (is_ai && (!_networking || _network_server)) AI::StartNew(c->index);
 

	
 
	AI::BroadcastNewEvent(new ScriptEventCompanyNew(c->index), c->index);
 
	Game::NewEvent(new ScriptEventCompanyNew(c->index));
 

	
 
	if (!is_ai) UpdateAllTownVirtCoords();
 

	
 
	return c;
 
}
 

	
 
/** Start the next competitor now. */
 
void StartupCompanies()
 
{
src/company_gui.cpp
Show inline comments
 
@@ -67,30 +67,50 @@ static ExpensesType _expenses_list_1[] =
 
	EXPENSES_TRAIN_INC,
 
	EXPENSES_ROADVEH_INC,
 
	EXPENSES_AIRCRAFT_INC,
 
	EXPENSES_SHIP_INC,
 
	EXPENSES_LOAN_INT,
 
	EXPENSES_OTHER,
 
	EXPENSES_T_TRAIN_CON,
 
	EXPENSES_T_ROAD_CON,
 
	EXPENSES_T_AIRCRAFT_CON,
 
	EXPENSES_T_SHIP_CON,
 
	EXPENSES_T_TREES_CON,
 
	EXPENSES_T_SCENERY_CON,
 
	EXPENSES_T_LANDSCAPING,
 
	EXPENSES_T_DEMOLITION,
 
	EXPENSES_T_REWARD_INC,
 
	EXPENSES_T_DILAPIDATION,
 
};
 

	
 
/** Grouped list of expenses. */
 
static ExpensesType _expenses_list_2[] = {
 
	EXPENSES_TRAIN_INC,
 
	EXPENSES_ROADVEH_INC,
 
	EXPENSES_AIRCRAFT_INC,
 
	EXPENSES_SHIP_INC,
 
	EXPENSES_T_REWARD_INC,
 
	INVALID_EXPENSES,
 
	EXPENSES_TRAIN_RUN,
 
	EXPENSES_ROADVEH_RUN,
 
	EXPENSES_AIRCRAFT_RUN,
 
	EXPENSES_SHIP_RUN,
 
	EXPENSES_PROPERTY,
 
	EXPENSES_T_DILAPIDATION,
 
	EXPENSES_LOAN_INT,
 
	INVALID_EXPENSES,
 
	EXPENSES_T_TRAIN_CON,
 
	EXPENSES_T_ROAD_CON,
 
	EXPENSES_T_AIRCRAFT_CON,
 
	EXPENSES_T_SHIP_CON,
 
	EXPENSES_T_TREES_CON,
 
	EXPENSES_T_SCENERY_CON,
 
	EXPENSES_CONSTRUCTION,
 
	EXPENSES_NEW_VEHICLES,
 
	EXPENSES_T_LANDSCAPING,
 
	EXPENSES_T_DEMOLITION,
 
	EXPENSES_OTHER,
 
	INVALID_EXPENSES,
 
};
 

	
 
/** Expense list container. */
 
struct ExpensesList {
src/company_type.h
Show inline comments
 
@@ -33,13 +33,13 @@ enum Owner : byte {
 
	COMPANY_INACTIVE_CLIENT = 253, ///< The client is joining
 
	COMPANY_NEW_COMPANY     = 254, ///< The client wants a new company
 
	COMPANY_SPECTATOR       = 255, ///< The client is spectating
 
};
 
DECLARE_POSTFIX_INCREMENT(Owner)
 

	
 
static const uint MAX_LENGTH_PRESIDENT_NAME_CHARS = 32; ///< The maximum length of a president name in characters including '\0'
 
static const uint MAX_LENGTH_PRESIDENT_NAME_CHARS = 48; ///< The maximum length of a president name in characters including '\0'
 
static const uint MAX_LENGTH_COMPANY_NAME_CHARS   = 32; ///< The maximum length of a company name in characters including '\0'
 

	
 
static const uint MAX_HISTORY_QUARTERS            = 24; ///< The maximum number of quarters kept as performance's history
 
static const uint MAX_COMPANY_SHARE_OWNERS        =  4; ///< The maximum number of shares of a company that can be owned by another company.
 

	
 
/** Define basic enum properties */
src/core/bitmath_func.hpp
Show inline comments
 
@@ -372,20 +372,39 @@ private:
 

	
 
#if defined(__APPLE__)
 
	/* Make endian swapping use Apple's macros to increase speed
 
	 * (since it will use hardware swapping if available).
 
	 * Even though they should return uint16 and uint32, we get
 
	 * warnings if we don't cast those (why?) */
 
#	define BSWAP64(x) (static_cast<uint64>(CFSwapInt64(x)))
 
#	define BSWAP32(x) (static_cast<uint32>(CFSwapInt32(x)))
 
#	define BSWAP16(x) (static_cast<uint16>(CFSwapInt16(x)))
 
#elif defined(_MSC_VER)
 
	/* MSVC has intrinsics for swapping, resulting in faster code */
 
#	define BSWAP64(x) (_byteswap_uint64(x))
 
#	define BSWAP32(x) (_byteswap_ulong(x))
 
#	define BSWAP16(x) (_byteswap_ushort(x))
 
#else
 
	/**
 
	 * Perform a 64 bits endianness bitswap on x.
 
	 * @param x the variable to bitswap
 
	 * @return the bitswapped value.
 
	 */
 
	static inline uint64 BSWAP64(uint64 x)
 
	{
 
#if !defined(__ICC) && (defined(__GNUC__) || defined(__clang__))
 
		/* GCC >= 4.3 provides a builtin, resulting in faster code */
 
		return (uint64)__builtin_bswap64((uint64)x);
 
#else
 
		return ((x >> 56) & 0xFFULL) | ((x >> 40) & 0xFF00ULL) | ((x >> 24) & 0xFF0000ULL) | ((x >> 8) & 0xFF000000ULL) |
 
				((x << 8) & 0xFF00000000ULL) | ((x << 24) & 0xFF0000000000ULL) | ((x << 40) & 0xFF000000000000ULL) | ((x << 56) & 0xFF00000000000000ULL);
 
				;
 
#endif /* __GNUC__ || __clang__ */
 
	}
 

	
 
	/**
 
	 * Perform a 32 bits endianness bitswap on x.
 
	 * @param x the variable to bitswap
 
	 * @return the bitswapped value.
 
	 */
 
	static inline uint32 BSWAP32(uint32 x)
 
	{
src/core/endian_func.hpp
Show inline comments
 
@@ -14,31 +14,39 @@
 
#include "bitmath_func.hpp"
 

	
 
/* Setup alignment and conversion macros */
 
#if TTD_ENDIAN == TTD_BIG_ENDIAN
 
#	define FROM_BE16(x) (x)
 
#	define FROM_BE32(x) (x)
 
#	define FROM_BE64(x) (x)
 
#	define TO_BE16(x)   (x)
 
#	define TO_BE32(x)   (x)
 
#	define TO_BE32X(x)  (x)
 
#	define TO_BE64(x)   (x)
 
#	define FROM_LE16(x) BSWAP16(x)
 
#	define FROM_LE32(x) BSWAP32(x)
 
#	define FROM_LE64(x) BSWAP64(x)
 
#	define TO_LE16(x)   BSWAP16(x)
 
#	define TO_LE32(x)   BSWAP32(x)
 
#	define TO_LE32X(x)  BSWAP32(x)
 
#	define TO_LE64(x)   BSWAP64(x)
 
#else
 
#	define FROM_BE16(x) BSWAP16(x)
 
#	define FROM_BE32(x) BSWAP32(x)
 
#	define FROM_BE64(x) BSWAP64(x)
 
#	define TO_BE16(x)   BSWAP16(x)
 
#	define TO_BE32(x)   BSWAP32(x)
 
#	define TO_BE32X(x)  BSWAP32(x)
 
#	define TO_BE64(x)   BSWAP64(x)
 
#	define FROM_LE16(x) (x)
 
#	define FROM_LE32(x) (x)
 
#	define FROM_LE64(x) (x)
 
#	define TO_LE16(x)   (x)
 
#	define TO_LE32(x)   (x)
 
#	define TO_LE32X(x)  (x)
 
#	define TO_LE64(x)   (x)
 
#endif /* TTD_ENDIAN == TTD_BIG_ENDIAN */
 

	
 
static inline uint16 ReadLE16Aligned(const void *x)
 
{
 
	return FROM_LE16(*(const uint16*)x);
 
}
src/date.cpp
Show inline comments
 
@@ -33,13 +33,14 @@ uint16 _tick_counter;  ///< Ever increme
 
 * Set the date.
 
 * @param date  New date
 
 * @param fract The number of ticks that have passed on this date.
 
 */
 
void SetDate(Date date, DateFract fract)
 
{
 
	assert(fract < DAY_TICKS);
 
	//Get rid of this insane assert
 
	//assert(fract < DAY_TICKS);
 

	
 
	YearMonthDay ymd;
 

	
 
	_date = date;
 
	_date_fract = fract;
 
	ConvertDateToYMD(date, &ymd);
src/date_type.h
Show inline comments
 
@@ -22,25 +22,32 @@ typedef uint8  Day;   ///< Type for the 
 
/**
 
 * 1 day is 74 ticks; _date_fract used to be uint16 and incremented by 885. On
 
 *                    an overflow the new day begun and 65535 / 885 = 74.
 
 * 1 tick is approximately 30 ms.
 
 * 1 day is thus about 2 seconds (74 * 30 = 2220) on a machine that can run OpenTTD normally
 
 */
 
static const int DAY_TICKS         =  74; ///< ticks per day
 
static const int ORIG_DAY_TICKS    =  74; ///< unscaled ticks per day
 
static const int DAYS_IN_YEAR      = 365; ///< days per year
 
static const int DAYS_IN_LEAP_YEAR = 366; ///< sometimes, you need one day more...
 
static const int MONTHS_IN_YEAR    =  12; ///< months per year
 

	
 
/**
 
 * Day length in ticks, calculated using a configurable scale factor.
 
 */
 
#define DAY_TICKS (ORIG_DAY_TICKS * _settings_game.economy.daylength_multiplier)
 

	
 
static const int STATION_RATING_TICKS     = 185; ///< cycle duration for updating station rating
 
static const int STATION_ACCEPTANCE_TICKS = 250; ///< cycle duration for updating station acceptance
 
static const int STATION_LINKGRAPH_TICKS  = 504; ///< cycle duration for cleaning dead links
 
static const int CARGO_AGING_TICKS        = 185; ///< cycle duration for aging cargo
 
static const int INDUSTRY_PRODUCE_TICKS   = 256; ///< cycle duration for industry production
 
static const int TOWN_GROWTH_TICKS        = 70;  ///< cycle duration for towns trying to grow. (this originates from the size of the town array in TTD
 
static const int ORIG_TOWN_GROWTH_TICKS   = 70;  ///< cycle duration for towns trying to grow. (this originates from the size of the town array in TTD)
 
static const int INDUSTRY_CUT_TREE_TICKS  = INDUSTRY_PRODUCE_TICKS * 2; ///< cycle duration for lumber mill's extra action
 

	
 
#define TOWN_GROWTH_TICKS (ORIG_TOWN_GROWTH_TICKS * _settings_game.economy.town_growth_multiplier)
 

	
 

	
 
/*
 
 * ORIGINAL_BASE_YEAR, ORIGINAL_MAX_YEAR and DAYS_TILL_ORIGINAL_BASE_YEAR are
 
 * primarily used for loading newgrf and savegame data and returning some
 
 * newgrf (callback) functions that were in the original (TTD) inherited
 
 * format, where '_date == 0' meant that it was 1920-01-01.
 
@@ -108,7 +115,10 @@ struct YearMonthDay {
 
};
 

	
 
static const Year  INVALID_YEAR  = -1; ///< Representation of an invalid year
 
static const Date  INVALID_DATE  = -1; ///< Representation of an invalid date
 
static const Ticks INVALID_TICKS = -1; ///< Representation of an invalid number of ticks
 

	
 
// daylength patch requirement
 
#include "settings_type.h"
 

	
 
#endif /* DATE_TYPE_H */
src/depot_type.h
Show inline comments
 
@@ -10,9 +10,9 @@
 
#ifndef DEPOT_TYPE_H
 
#define DEPOT_TYPE_H
 

	
 
typedef uint16 DepotID; ///< Type for the unique identifier of depots.
 
struct Depot;
 

	
 
static const uint MAX_LENGTH_DEPOT_NAME_CHARS = 32; ///< The maximum length of a depot name in characters including '\0'
 
static const uint MAX_LENGTH_DEPOT_NAME_CHARS = 128; ///< The maximum length of a depot name in characters including '\0'
 

	
 
#endif /* DEPOT_TYPE_H */
src/disaster_vehicle.cpp
Show inline comments
 
@@ -359,12 +359,13 @@ static bool DisasterTick_Ufo(DisasterVeh
 
		RoadVehicle *u = RoadVehicle::Get(v->dest_tile);
 
		assert(u != nullptr && u->type == VEH_ROAD && u->IsFrontEngine());
 

	
 
		uint dist = Delta(v->x_pos, u->x_pos) + Delta(v->y_pos, u->y_pos);
 

	
 
		if (dist < TILE_SIZE && !(u->vehstatus & VS_HIDDEN) && u->breakdown_ctr == 0) {
 
			u->breakdown_type = BREAKDOWN_CRITICAL;
 
			u->breakdown_ctr = 3;
 
			u->breakdown_delay = 140;
 
		}
 

	
 
		v->direction = GetDirectionTowards(v, u->x_pos, u->y_pos);
 
		GetNewVehiclePosResult gp = GetNewVehiclePos(v);
 
@@ -544,12 +545,13 @@ static bool DisasterTick_Big_Ufo(Disaste
 

	
 
		for (Vehicle *target : Vehicle::Iterate()) {
 
			if (target->IsGroundVehicle()) {
 
				if (Delta(target->x_pos, v->x_pos) + Delta(target->y_pos, v->y_pos) <= 12 * (int)TILE_SIZE) {
 
					target->breakdown_ctr = 5;
 
					target->breakdown_delay = 0xF0;
 
					target->breakdown_type = BREAKDOWN_CRITICAL;
 
				}
 
			}
 
		}
 

	
 
		Town *t = ClosestTownFromTile(v->dest_tile, UINT_MAX);
 
		SetDParam(0, t->index);
src/economy.cpp
Show inline comments
 
@@ -744,13 +744,38 @@ void RecomputePrices()
 
{
 
	/* Setup maximum loan */
 
	_economy.max_loan = ((uint64)_settings_game.difficulty.max_loan * _economy.inflation_prices >> 16) / 50000 * 50000;
 

	
 
	/* Setup price bases */
 
	for (Price i = PR_BEGIN; i < PR_END; i++) {
 
		Money price = _price_base_specs[i].start_price;
 
		Money price;
 
		
 
		if (i == PR_INFRASTRUCTURE_RAIL)
 
		{
 
			price = _settings_game.economy.infrastructure_base_cost_rail;
 
		}
 
		else if (i == PR_INFRASTRUCTURE_ROAD)
 
		{
 
			price = _settings_game.economy.infrastructure_base_cost_road;
 
		}
 
		else if (i == PR_INFRASTRUCTURE_WATER)
 
		{
 
			price = _settings_game.economy.infrastructure_base_cost_water;
 
		}
 
		else if (i == PR_INFRASTRUCTURE_AIRPORT)
 
		{
 
			price = _settings_game.economy.infrastructure_base_cost_air;
 
		}
 
		else if (i == PR_INFRASTRUCTURE_STATION)
 
		{
 
			price = _settings_game.economy.infrastructure_base_cost_station;
 
		}
 
		else
 
		{
 
			price = _price_base_specs[i].start_price;
 
		}
 

	
 
		/* Apply difficulty settings */
 
		uint mod = 1;
 
		switch (_price_base_specs[i].category) {
 
			case PCAT_RUNNING:
 
				mod = _settings_game.difficulty.vehicle_costs;
src/economy_type.h
Show inline comments
 
@@ -152,25 +152,35 @@ DECLARE_POSTFIX_INCREMENT(Price)
 

	
 
typedef Money Prices[PR_END]; ///< Prices of everything. @see Price
 
typedef int8 PriceMultipliers[PR_END];
 

	
 
/** Types of expenses. */
 
enum ExpensesType : byte {
 
	EXPENSES_CONSTRUCTION =  0,   ///< Construction costs.
 
	EXPENSES_CONSTRUCTION =  0,   ///< Construction costs. [miscellaneous/unsorted]
 
	EXPENSES_NEW_VEHICLES,        ///< New vehicles.
 
	EXPENSES_TRAIN_RUN,           ///< Running costs trains.
 
	EXPENSES_ROADVEH_RUN,         ///< Running costs road vehicles.
 
	EXPENSES_AIRCRAFT_RUN,        ///< Running costs aircraft.
 
	EXPENSES_SHIP_RUN,            ///< Running costs ships.
 
	EXPENSES_PROPERTY,            ///< Property costs.
 
	EXPENSES_TRAIN_INC,           ///< Income from trains.
 
	EXPENSES_ROADVEH_INC,         ///< Income from road vehicles.
 
	EXPENSES_AIRCRAFT_INC,        ///< Income from aircraft.
 
	EXPENSES_SHIP_INC,            ///< Income from ships.
 
	EXPENSES_LOAN_INT,            ///< Interest payments over the loan.
 
	EXPENSES_OTHER,               ///< Other expenses.
 
	EXPENSES_T_TRAIN_CON,
 
	EXPENSES_T_ROAD_CON,
 
	EXPENSES_T_AIRCRAFT_CON,
 
	EXPENSES_T_SHIP_CON,
 
	EXPENSES_T_TREES_CON,
 
	EXPENSES_T_SCENERY_CON,
 
	EXPENSES_T_LANDSCAPING,
 
	EXPENSES_T_DEMOLITION,
 
	EXPENSES_T_REWARD_INC,
 
	EXPENSES_T_DILAPIDATION,
 
	EXPENSES_END,                 ///< Number of expense types.
 
	INVALID_EXPENSES      = 0xFF, ///< Invalid expense type.
 
};
 

	
 
/** Define basic enum properties for ExpensesType */
 
template <> struct EnumPropsT<ExpensesType> : MakeEnumPropsT<ExpensesType, byte, EXPENSES_CONSTRUCTION, EXPENSES_END, INVALID_EXPENSES, 8> {};
src/engine.cpp
Show inline comments
 
@@ -273,28 +273,32 @@ Money Engine::GetRunningCost() const
 
	uint cost_factor;
 
	switch (this->type) {
 
		case VEH_ROAD:
 
			base_price = this->u.road.running_cost_class;
 
			if (base_price == INVALID_PRICE) return 0;
 
			cost_factor = GetEngineProperty(this->index, PROP_ROADVEH_RUNNING_COST_FACTOR, this->u.road.running_cost);
 
			cost_factor *= _settings_game.economy.running_cost_multiplier_road;
 
			break;
 

	
 
		case VEH_TRAIN:
 
			base_price = this->u.rail.running_cost_class;
 
			if (base_price == INVALID_PRICE) return 0;
 
			cost_factor = GetEngineProperty(this->index, PROP_TRAIN_RUNNING_COST_FACTOR, this->u.rail.running_cost);
 
			cost_factor *= _settings_game.economy.running_cost_multiplier_rail;
 
			break;
 

	
 
		case VEH_SHIP:
 
			base_price = PR_RUNNING_SHIP;
 
			cost_factor = GetEngineProperty(this->index, PROP_SHIP_RUNNING_COST_FACTOR, this->u.ship.running_cost);
 
			cost_factor *= _settings_game.economy.running_cost_multiplier_water;
 
			break;
 

	
 
		case VEH_AIRCRAFT:
 
			base_price = PR_RUNNING_AIRCRAFT;
 
			cost_factor = GetEngineProperty(this->index, PROP_AIRCRAFT_RUNNING_COST_FACTOR, this->u.air.running_cost);
 
			cost_factor *= _settings_game.economy.running_cost_multiplier_air;
 
			break;
 

	
 
		default: NOT_REACHED();
 
	}
 

	
 
	return GetPrice(base_price, cost_factor, this->GetGRF(), -8);
 
@@ -440,13 +444,13 @@ Date Engine::GetLifeLengthInDays() const
 
 * @return Range of the aircraft type in tiles or 0 if unlimited range.
 
 */
 
uint16 Engine::GetRange() const
 
{
 
	switch (this->type) {
 
		case VEH_AIRCRAFT:
 
			return GetEngineProperty(this->index, PROP_AIRCRAFT_RANGE, this->u.air.max_range);
 
			return (GetEngineProperty(this->index, PROP_AIRCRAFT_RANGE, this->u.air.max_range) * _settings_game.vehicle.plane_range_multiplier);
 

	
 
		default: NOT_REACHED();
 
	}
 
}
 

	
 
/**
src/engine_type.h
Show inline comments
 
@@ -167,11 +167,11 @@ enum EngineMiscFlags {
 
 */
 
enum EngineFlags {
 
	ENGINE_AVAILABLE         = 1, ///< This vehicle is available to everyone.
 
	ENGINE_EXCLUSIVE_PREVIEW = 2, ///< This vehicle is in the exclusive preview stage, either being used or being offered to a company.
 
};
 

	
 
static const uint MAX_LENGTH_ENGINE_NAME_CHARS = 32; ///< The maximum length of an engine name in characters including '\0'
 
static const uint MAX_LENGTH_ENGINE_NAME_CHARS = 128; ///< The maximum length of an engine name in characters including '\0'
 

	
 
static const EngineID INVALID_ENGINE = 0xFFFF; ///< Constant denoting an invalid engine.
 

	
 
#endif /* ENGINE_TYPE_H */
src/fios.h
Show inline comments
 
@@ -20,12 +20,18 @@
 
enum SaveLoadInvalidateWindowData {
 
	SLIWD_RESCAN_FILES,          ///< Rescan all files (when changed directory, ...)
 
	SLIWD_SELECTION_CHANGES,     ///< File selection has changed (user click, ...)
 
	SLIWD_FILTER_CHANGES,        ///< The filename filter has changed (via the editbox)
 
};
 

	
 
enum PatchpackSaveExtensionType {
 
	PSXT_NONE,
 
	PSXT_MODIFIED,
 
	PSXT_EXTENDED
 
};
 

	
 
typedef SmallMap<uint, CompanyProperties *> CompanyPropertiesMap;
 

	
 
/**
 
 * Container for loading in mode SL_LOAD_CHECK.
 
 */
 
struct LoadCheckData {
 
@@ -42,12 +48,15 @@ struct LoadCheckData {
 

	
 
	GRFConfig *grfconfig;                         ///< NewGrf configuration from save.
 
	GRFListCompatibility grf_compatibility;       ///< Summary state of NewGrfs, whether missing files or only compatible found.
 

	
 
	struct LoggedAction *gamelog_action;          ///< Gamelog actions
 
	uint gamelog_actions;                         ///< Number of gamelog actions
 
	uint16 save_version;                          ///< Save version
 
	PatchpackSaveExtensionType save_ext_type;     ///< Extension type
 
	std::string save_version_label;               ///< Save version label
 

	
 
	LoadCheckData() : error_data(nullptr), grfconfig(nullptr),
 
			grf_compatibility(GLC_NOT_FOUND), gamelog_action(nullptr), gamelog_actions(0)
 
	{
 
		this->Clear();
 
	}
src/fios_gui.cpp
Show inline comments
 
@@ -51,12 +51,15 @@ void LoadCheckData::Clear()
 
	this->error = INVALID_STRING_ID;
 
	free(this->error_data);
 
	this->error_data = nullptr;
 

	
 
	this->map_size_x = this->map_size_y = 256; // Default for old savegames which do not store mapsize.
 
	this->current_date = 0;
 
	this->save_version = SL_MIN_VERSION;
 
	this->save_ext_type = PSXT_NONE;
 
	this->save_version_label = "";
 
	this->settings = {};
 

	
 
	for (auto &pair : this->companies) {
 
		delete pair.second;
 
	}
 
	companies.clear();
 
@@ -468,12 +471,25 @@ public:
 
				if (this->selected == nullptr) break;
 

	
 
				uint y = r.top + FONT_HEIGHT_NORMAL * 2 + WD_PAR_VSEP_NORMAL + WD_FRAMERECT_TOP + WD_FRAMERECT_BOTTOM;
 
				uint y_max = r.bottom - FONT_HEIGHT_NORMAL - WD_FRAMERECT_BOTTOM;
 

	
 
				if (y > y_max) break;
 
				if (_load_check_data.checkable) {
 
					SetDParam(0, _load_check_data.save_version);
 
					SetDParamStr(1, _load_check_data.save_ext_type == PSXT_EXTENDED ? "X" : _load_check_data.save_ext_type == PSXT_MODIFIED ? "M"  : "");
 
					DrawString(r.left + WD_FRAMERECT_LEFT, r.right - WD_FRAMERECT_RIGHT, y, STR_SAVELOAD_VERSION_WITH_EXTENSION);
 
					y += FONT_HEIGHT_NORMAL;
 
					if (_load_check_data.save_version_label.length() > 0)
 
					{
 
						SetDParamStr(0, _load_check_data.save_version_label.c_str());
 
						DrawString(r.left + WD_FRAMERECT_LEFT, r.right - WD_FRAMERECT_RIGHT, y, STR_SAVELOAD_VERSION_LABEL);
 
						y += FONT_HEIGHT_NORMAL;
 
					}
 
					y += FONT_HEIGHT_NORMAL;
 
				}
 
				if (!_load_check_data.checkable) {
 
					/* Old savegame, no information available */
 
					DrawString(r.left + WD_FRAMERECT_LEFT, r.right - WD_FRAMERECT_RIGHT, y, STR_SAVELOAD_DETAIL_NOT_AVAILABLE);
 
					y += FONT_HEIGHT_NORMAL;
 
				} else if (_load_check_data.error != INVALID_STRING_ID) {
 
					/* Incompatible / broken savegame */
src/gamelog.cpp
Show inline comments
 
@@ -48,14 +48,14 @@ static const char * GetGamelogRevisionSt
 
	static_assert(lengthof(gamelog_revision) > GAMELOG_REVISION_LENGTH);
 

	
 
	if (IsReleasedVersion()) {
 
		return _openttd_revision;
 
	} else if (gamelog_revision[0] == 0) {
 
		/* Prefix character indication revision status */
 
		assert(_openttd_revision_modified < 3);
 
		gamelog_revision[0] = "gum"[_openttd_revision_modified]; // g = "git", u = "unknown", m = "modified"
 
		assert(_openttd_revision_modified < 5);
 
		gamelog_revision[0] = "gumhh"[_openttd_revision_modified]; // g = "git", u = "unknown", m = "modified", "h" = "hg"
 
		/* Append the revision hash */
 
		strecat(gamelog_revision, _openttd_revision_hash, lastof(gamelog_revision));
 
		/* Truncate string to GAMELOG_REVISION_LENGTH bytes */
 
		gamelog_revision[GAMELOG_REVISION_LENGTH - 1] = '\0';
 
	}
 
	return gamelog_revision;
src/ground_vehicle.cpp
Show inline comments
 
@@ -25,18 +25,15 @@ void GroundVehicle<T, Type>::PowerChange
 

	
 
	uint32 total_power = 0;
 
	uint32 max_te = 0;
 
	uint32 number_of_parts = 0;
 
	uint16 max_track_speed = this->vcache.cached_max_speed; // Max track speed in internal units.
 

	
 
	this->CalculatePower(total_power, max_te, false);
 
	
 
	for (const T *u = v; u != nullptr; u = u->Next()) {
 
		uint32 current_power = u->GetPower() + u->GetPoweredPartPower(u);
 
		total_power += current_power;
 

	
 
		/* Only powered parts add tractive effort. */
 
		if (current_power > 0) max_te += u->GetWeight() * u->GetTractiveEffort();
 
		number_of_parts++;
 

	
 
		/* Get minimum max speed for this track. */
 
		uint16 track_speed = u->GetMaxTrackSpeed();
 
		if (track_speed > 0) max_track_speed = std::min(max_track_speed, track_speed);
 
	}
 
@@ -53,14 +50,12 @@ void GroundVehicle<T, Type>::PowerChange
 
		/* According to the specs, a value of 0x01 in the air drag property means "no air drag". */
 
		air_drag = (air_drag_value == 1) ? 0 : air_drag_value;
 
	}
 

	
 
	this->gcache.cached_air_drag = air_drag + 3 * air_drag * number_of_parts / 20;
 

	
 
	max_te *= GROUND_ACCELERATION; // Tractive effort in (tonnes * 1000 * 9.8 =) N.
 
	max_te /= 256;  // Tractive effort is a [0-255] coefficient.
 
	if (this->gcache.cached_power != total_power || this->gcache.cached_max_te != max_te) {
 
		/* Stop the vehicle if it has no power. */
 
		if (total_power == 0) this->vehstatus |= VS_STOPPED;
 

	
 
		this->gcache.cached_power = total_power;
 
		this->gcache.cached_max_te = max_te;
 
@@ -68,12 +63,36 @@ void GroundVehicle<T, Type>::PowerChange
 
		SetWindowWidgetDirty(WC_VEHICLE_VIEW, this->index, WID_VV_START_STOP);
 
	}
 

	
 
	this->gcache.cached_max_track_speed = max_track_speed;
 
}
 

	
 
template <class T, VehicleType Type>
 
void GroundVehicle<T, Type>::CalculatePower(uint32& total_power, uint32& max_te, bool breakdowns) const {
 

	
 
	total_power = 0;
 
	max_te = 0;
 
    
 
	const T *v = T::From(this);
 

	
 
	for (const T *u = v; u != NULL; u = u->Next()) {
 
		uint32 current_power = u->GetPower() + u->GetPoweredPartPower(u);
 
		total_power += current_power;
 

	
 
		/* Only powered parts add tractive effort. */
 
		if (current_power > 0) max_te += u->GetWeight() * u->GetTractiveEffort();
 
		
 
		if (breakdowns && u->breakdown_ctr == 1 && u->breakdown_type == BREAKDOWN_LOW_POWER) {
 
			total_power = total_power * u->breakdown_severity / 256;
 
                }
 
	}
 

	
 
	max_te *= GROUND_ACCELERATION; // Tractive effort in (tonnes * 1000 * 9.8 =) N.
 
	max_te /= 256;   // Tractive effort is a [0-255] coefficient.
 
}
 

	
 
/**
 
 * Recalculates the cached weight of a vehicle and its parts. Should be called each time the cargo on
 
 * the consist changes.
 
 */
 
template <class T, VehicleType Type>
 
void GroundVehicle<T, Type>::CargoChanged()
 
@@ -99,13 +118,13 @@ void GroundVehicle<T, Type>::CargoChange
 

	
 
/**
 
 * Calculates the acceleration of the vehicle under its current conditions.
 
 * @return Current acceleration of the vehicle.
 
 */
 
template <class T, VehicleType Type>
 
int GroundVehicle<T, Type>::GetAcceleration() const
 
int GroundVehicle<T, Type>::GetAcceleration()
 
{
 
	/* Templated class used for function calls for performance reasons. */
 
	const T *v = T::From(this);
 
	/* Speed is used squared later on, so U16 * U16, and then multiplied by other values. */
 
	int64 speed = v->GetCurrentSpeed(); // [km/h-ish]
 

	
 
@@ -114,12 +133,13 @@ int GroundVehicle<T, Type>::GetAccelerat
 

	
 
	/* Power is stored in HP, we need it in watts.
 
	 * Each vehicle can have U16 power, 128 vehicles, HP -> watt
 
	 * and km/h to m/s conversion below result in a maximum of
 
	 * about 1.1E11, way more than 4.3E9 of int32. */
 
	int64 power = this->gcache.cached_power * 746ll;
 
	uint32 max_te = this->gcache.cached_max_te; // [N]
 

	
 
	/* This is constructed from:
 
	 *  - axle resistance:  U16 power * 10 for 128 vehicles.
 
	 *     * 8.3E7
 
	 *  - rolling friction: U16 power * 144 for 128 vehicles.
 
	 *     * 1.2E9
 
@@ -145,41 +165,98 @@ int GroundVehicle<T, Type>::GetAccelerat
 

	
 
	resistance += this->GetSlopeResistance();
 

	
 
	/* This value allows to know if the vehicle is accelerating or braking. */
 
	AccelStatus mode = v->GetAccelerationStatus();
 

	
 
	const int max_te = this->gcache.cached_max_te; // [N]
 
	/* handle breakdown power reduction */
 
	//TODO
 
	if(  Type == VEH_TRAIN  && mode == AS_ACCEL && HasBit(Train::From(this)->flags, VRF_BREAKDOWN_POWER)) {
 
		/* We'd like to cache this, but changing cached_power has too many unwanted side-effects */
 
		uint32 power_temp;
 
		this->CalculatePower(power_temp, max_te, true);
 
		power = power_temp * 74611;
 
	}
 
	
 
		
 
	/* Constructued from power, with need to multiply by 18 and assuming
 
	 * low speed, it needs to be a 64 bit integer too. */
 
	int64 force;
 
	if (speed > 0) {
 
		if (!maglev) {
 
			/* Conversion factor from km/h to m/s is 5/18 to get [N] in the end. */
 
			force = power * 18 / (speed * 5);
 
			if (mode == AS_ACCEL && force > max_te) force = max_te;
 
			if (mode == AS_ACCEL && force > (int)max_te) force = max_te;
 
		} else {
 
			force = power / 25;
 
		}
 
	} else {
 
		/* "Kickoff" acceleration. */
 
		force = (mode == AS_ACCEL && !maglev) ? std::min<int>(max_te, power) : power;
 
		force = std::max(force, (mass * 8) + resistance);
 
	}
 

	
 
	/* If power is 0 because of a breakdown, we make the force 0 if accelerating */
 
	if ( Type == VEH_TRAIN && mode == AS_ACCEL && HasBit(Train::From(this)->flags, VRF_BREAKDOWN_POWER) && power == 0) {
 
		force = 0;
 
	}
 

	
 
	/* Calculate the breakdown chance */
 
	if (_settings_game.vehicle.improved_breakdowns) {
 
		assert(this->gcache.cached_max_track_speed > 0);
 
		/** First, calculate (resistance / force * current speed / max speed) << 16.
 
		 * This yields a number x on a 0-1 scale, but shifted 16 bits to the left.
 
		 * We then calculate 64 + 128x, clamped to 0-255, but still shifted 16 bits to the left.
 
		 * Then we apply a correction for multiengine trains, and in the end we shift it 16 bits to the right to get a 0-255 number.
 
		 * @note A seperate correction for multiheaded engines is done in CheckVehicleBreakdown. We can't do that here because it would affect the whole consist.
 
		 */
 
		uint64 breakdown_factor = (uint64)abs(resistance) * (uint64)(this->cur_speed << 16);
 
		breakdown_factor /= (std::max<uint64>(force, (int64)100) * this->gcache.cached_max_track_speed);
 
		breakdown_factor = std::min<uint64>((64 << 16) + (breakdown_factor * 128), 255 << 16);
 
		if ( Type == VEH_TRAIN && Train::From(this)->tcache.cached_num_engines > 1) {
 
			/* For multiengine trains, breakdown chance is multiplied by 3 / (num_engines + 2) */
 
			breakdown_factor *= 3;
 
			breakdown_factor /= (Train::From(this)->tcache.cached_num_engines + 2);
 
		}
 
		/* breakdown_chance is at least 5 (5 / 128 = ~4% of the normal chance) */
 
		this->breakdown_chance = (uint8) std::max<uint64>(breakdown_factor >> 16, (uint64)5);
 
	} else {
 
		this->breakdown_chance = 128;
 
	}
 

	
 
	if (mode == AS_ACCEL) {
 
		/* Easy way out when there is no acceleration. */
 
		if (force == resistance) return 0;
 

	
 
		/* When we accelerate, make sure we always keep doing that, even when
 
		 * the excess force is more than the mass. Otherwise a vehicle going
 
		 * down hill will never slow down enough, and a vehicle that came up
 
		 * a hill will never speed up enough to (eventually) get back to the
 
		 * same (maximum) speed. */
 
		int accel = ClampToI32((force - resistance) / (mass * 4));
 
		return force < resistance ? std::min(-1, accel) : std::max(1, accel);
 
		accel = force < resistance ? std::min(-1, accel) : std::max(1, accel);
 
		if (this->type == VEH_TRAIN ) {
 
			if(_settings_game.vehicle.train_acceleration_model == AM_ORIGINAL &&
 
				HasBit(Train::From(this)->flags, VRF_BREAKDOWN_POWER)) {
 
				/* We need to apply the power reducation for non-realistic acceleration here */
 
				uint32 power;
 
				CalculatePower(power, max_te, true);
 
				accel = accel * power / this->gcache.cached_power;
 
				accel -= this->acceleration >> 1;
 
			}
 

	
 

	
 
			if ( this->IsFrontEngine() && !(this->current_order_time & 0x1FF) &&
 
				!(this->current_order.IsType(OT_LOADING)) &&
 
				!(Train::From(this)->flags & (VRF_IS_BROKEN | (1 << VRF_TRAIN_STUCK))) &&
 
				this->cur_speed < 3 && accel < 5) {
 
				SetBit(Train::From(this)->flags, VRF_TO_HEAVY);
 
			}
 
		}
 

	
 
		return accel;
 
	} else {
 
		return ClampToI32(std::min<int64>(-force - resistance, -10000) / mass);
 
	}
 
}
 

	
 
/**
src/ground_vehicle.hpp
Show inline comments
 
@@ -86,15 +86,17 @@ struct GroundVehicle : public Specialize
 
	 * The constructor at SpecializedVehicle must be called.
 
	 */
 
	GroundVehicle() : SpecializedVehicle<T, Type>() {}
 

	
 
	void PowerChanged();
 
	void CargoChanged();
 
	int GetAcceleration() const;
 
	int GetAcceleration();
 
	bool IsChainInDepot() const override;
 

	
 
	void CalculatePower(uint32& power, uint32& max_te, bool breakdowns) const;
 

	
 
	/**
 
	 * Common code executed for crashed ground vehicles
 
	 * @param flooded was this vehicle flooded?
 
	 * @return number of victims
 
	 */
 
	uint Crash(bool flooded) override
 
@@ -364,13 +366,28 @@ protected:
 
	{
 
		uint spd = this->subspeed + accel;
 
		this->subspeed = (byte)spd;
 

	
 
		/* When we are going faster than the maximum speed, reduce the speed
 
		 * somewhat gradually. But never lower than the maximum speed. */
 
		int tempmax = max_speed;
 
		int tempmax = ((this->breakdown_ctr == 1) ? this->cur_speed : max_speed);
 
		
 
		if (this->breakdown_ctr == 1) {
 
			if (this->breakdown_type == BREAKDOWN_LOW_POWER) {
 
				if((this->tick_counter & 0x7) == 0) {
 
					if(this->cur_speed > (this->breakdown_severity * max_speed) >> 8) {
 
						tempmax = this->cur_speed - (this->cur_speed / 10) - 1;
 
					} else {
 
						tempmax = (this->breakdown_severity * max_speed) >> 8;
 
					}
 
				}
 
			}
 
			if(this->breakdown_type == BREAKDOWN_LOW_SPEED)
 
				tempmax = std::min<int>(max_speed, this->breakdown_severity);
 
		}
 

	
 
		if (this->cur_speed > max_speed) {
 
			tempmax = std::max(this->cur_speed - (this->cur_speed / 10) - 1, max_speed);
 
		}
 

	
 
		/* Enforce a maximum and minimum speed. Normally we would use something like
 
		 * Clamp for this, but in this case min_speed might be below the maximum speed
src/group_gui.cpp
Show inline comments
 
@@ -204,13 +204,13 @@ private:
 
	uint ComputeGroupInfoSize()
 
	{
 
		this->column_size[VGC_FOLD] = maxdim(GetSpriteSize(SPR_CIRCLE_FOLDED), GetSpriteSize(SPR_CIRCLE_UNFOLDED));
 
		this->tiny_step_height = this->column_size[VGC_FOLD].height;
 

	
 
		this->column_size[VGC_NAME] = maxdim(GetStringBoundingBox(STR_GROUP_DEFAULT_TRAINS + this->vli.vtype), GetStringBoundingBox(STR_GROUP_ALL_TRAINS + this->vli.vtype));
 
		this->column_size[VGC_NAME].width = std::max(170u, this->column_size[VGC_NAME].width);
 
		this->column_size[VGC_NAME].width = std::max(450u, this->column_size[VGC_NAME].width);
 
		this->tiny_step_height = std::max(this->tiny_step_height, this->column_size[VGC_NAME].height);
 

	
 
		this->column_size[VGC_PROTECT] = GetSpriteSize(SPR_GROUP_REPLACE_PROTECT);
 
		this->tiny_step_height = std::max(this->tiny_step_height, this->column_size[VGC_PROTECT].height);
 

	
 
		this->column_size[VGC_AUTOREPLACE] = GetSpriteSize(SPR_GROUP_REPLACE_ACTIVE);
 
@@ -1081,20 +1081,20 @@ public:
 
	}
 

	
 
};
 

	
 

	
 
static WindowDesc _other_group_desc(
 
	WDP_AUTO, "list_groups", 460, 246,
 
	WDP_AUTO, "list_groups", 940, 246,
 
	WC_INVALID, WC_NONE,
 
	0,
 
	_nested_group_widgets, lengthof(_nested_group_widgets)
 
);
 

	
 
static WindowDesc _train_group_desc(
 
	WDP_AUTO, "list_groups_train", 525, 246,
 
	WDP_AUTO, "list_groups_train", 1005, 246,
 
	WC_TRAINS_LIST, WC_NONE,
 
	0,
 
	_nested_group_widgets, lengthof(_nested_group_widgets)
 
);
 

	
 
/**
src/group_type.h
Show inline comments
 
@@ -14,11 +14,11 @@ typedef uint16 GroupID; ///< Type for al
 

	
 
static const GroupID NEW_GROUP     = 0xFFFC; ///< Sentinel for a to-be-created group.
 
static const GroupID ALL_GROUP     = 0xFFFD; ///< All vehicles are in this group.
 
static const GroupID DEFAULT_GROUP = 0xFFFE; ///< Ungrouped vehicles are in this group.
 
static const GroupID INVALID_GROUP = 0xFFFF; ///< Sentinel for invalid groups.
 

	
 
static const uint MAX_LENGTH_GROUP_NAME_CHARS = 32; ///< The maximum length of a group name in characters including '\0'
 
static const uint MAX_LENGTH_GROUP_NAME_CHARS = 128; ///< The maximum length of a group name in characters including '\0'
 

	
 
struct Group;
 

	
 
#endif /* GROUP_TYPE_H */
src/industry_cmd.cpp
Show inline comments
 
@@ -517,13 +517,13 @@ static CommandCost ClearTile_Industry(Ti
 

	
 
	if (flags & DC_EXEC) {
 
		AI::BroadcastNewEvent(new ScriptEventIndustryClose(i->index));
 
		Game::NewEvent(new ScriptEventIndustryClose(i->index));
 
		delete i;
 
	}
 
	return CommandCost(EXPENSES_CONSTRUCTION, indspec->GetRemovalCost());
 
	return CommandCost(EXPENSES_T_DEMOLITION, indspec->GetRemovalCost());
 
}
 

	
 
/**
 
 * Move produced cargo from industry to nearby stations.
 
 * @param tile Industry tile
 
 * @return true if any cargo was moved.
 
@@ -2051,13 +2051,13 @@ CommandCost CmdBuildIndustry(DoCommandFl
 
	}
 

	
 
	if ((flags & DC_EXEC) && ind != nullptr && _game_mode != GM_EDITOR) {
 
		AdvertiseIndustryOpening(ind);
 
	}
 

	
 
	return CommandCost(EXPENSES_OTHER, indspec->GetConstructionCost());
 
	return CommandCost(EXPENSES_CONSTRUCTION, indspec->GetConstructionCost());
 
}
 

	
 
/**
 
 * Change industry properties
 
 * @param flags Type of operation.
 
 * @param ind_id IndustryID
src/landscape.cpp
Show inline comments
 
@@ -687,13 +687,13 @@ void ClearSnowLine()
 
 * @param flags of operation to conduct
 
 * @param tile tile to clear
 
 * @return the cost of this operation or an error
 
 */
 
CommandCost CmdLandscapeClear(DoCommandFlag flags, TileIndex tile)
 
{
 
	CommandCost cost(EXPENSES_CONSTRUCTION);
 
	CommandCost cost(EXPENSES_T_DEMOLITION);
 
	bool do_clear = false;
 
	/* Test for stuff which results in water when cleared. Then add the cost to also clear the water. */
 
	if ((flags & DC_FORCE_CLEAR_TILE) && HasTileWaterClass(tile) && IsTileOnWater(tile) && !IsWaterTile(tile) && !IsCoastTile(tile)) {
 
		if ((flags & DC_AUTO) && GetWaterClass(tile) == WATER_CLASS_CANAL) return_cmd_error(STR_ERROR_MUST_DEMOLISH_CANAL_FIRST);
 
		do_clear = true;
 
		cost.AddCost(GetWaterClass(tile) == WATER_CLASS_CANAL ? _price[PR_CLEAR_CANAL] : _price[PR_CLEAR_WATER]);
 
@@ -738,13 +738,13 @@ CommandCost CmdLandscapeClear(DoCommandF
 
 */
 
std::tuple<CommandCost, Money> CmdClearArea(DoCommandFlag flags, TileIndex tile, TileIndex start_tile, bool diagonal)
 
{
 
	if (start_tile >= MapSize()) return { CMD_ERROR, 0 };
 

	
 
	Money money = GetAvailableMoneyForCommand();
 
	CommandCost cost(EXPENSES_CONSTRUCTION);
 
	CommandCost cost(EXPENSES_T_DEMOLITION);
 
	CommandCost last_error = CMD_ERROR;
 
	bool had_success = false;
 

	
 
	const Company *c = (flags & (DC_AUTO | DC_BANKRUPT)) ? nullptr : Company::GetIfValid(_current_company);
 
	int limit = (c == nullptr ? INT32_MAX : GB(c->clear_limit, 16, 16));
 

	
src/lang/english.txt
Show inline comments
 
@@ -301,12 +301,13 @@ STR_SORT_BY_LIFE_TIME                   
 
STR_SORT_BY_TIMETABLE_DELAY                                     :Timetable delay
 
STR_SORT_BY_FACILITY                                            :Station type
 
STR_SORT_BY_WAITING_TOTAL                                       :Total waiting cargo
 
STR_SORT_BY_WAITING_AVAILABLE                                   :Available waiting cargo
 
STR_SORT_BY_RATING_MAX                                          :Highest cargo rating
 
STR_SORT_BY_RATING_MIN                                          :Lowest cargo rating
 
STR_SORT_BY_DILAPIDATION                                        :Dilapidation
 
STR_SORT_BY_ENGINE_ID                                           :EngineID (classic sort)
 
STR_SORT_BY_COST                                                :Cost
 
STR_SORT_BY_POWER                                               :Power
 
STR_SORT_BY_TRACTIVE_EFFORT                                     :Tractive effort
 
STR_SORT_BY_INTRO_DATE                                          :Introduction date
 
STR_SORT_BY_RUNNING_COST                                        :Running cost
 
@@ -1230,12 +1231,15 @@ STR_CONFIG_SETTING_RUNNING_COSTS_HELPTEX
 
STR_CONFIG_SETTING_CONSTRUCTION_SPEED                           :Construction speed: {STRING2}
 
STR_CONFIG_SETTING_CONSTRUCTION_SPEED_HELPTEXT                  :Limit the amount of construction actions for AIs
 

	
 
STR_CONFIG_SETTING_VEHICLE_BREAKDOWNS                           :Vehicle breakdowns: {STRING2}
 
STR_CONFIG_SETTING_VEHICLE_BREAKDOWNS_HELPTEXT                  :Control how often inadequately serviced vehicles may break down
 

	
 
STR_CONFIG_SETTING_IMPROVED_BREAKDOWNS                          :Enable improved breakdowns: {STRING}
 
STR_CONFIG_SETTING_IMPROVED_BREAKDOWNS_HELPTEXT                 :If enabled, different type of breakdowns can occur: emergency stop, temporary speed reduction, temporary power loss or a classic breakdown. Aircrafts may travel with reduced speed or have a mandatory service at the destination airport or have an emergency landing at the closest airport. Breakdown possibility is similar to classic breakdowns, but the impact to vehicles and the network is reduced.
 

	
 
STR_CONFIG_SETTING_SUBSIDY_MULTIPLIER                           :Subsidy multiplier: {STRING2}
 
STR_CONFIG_SETTING_SUBSIDY_MULTIPLIER_HELPTEXT                  :Set how much is paid for subsidised connections
 

	
 
STR_CONFIG_SETTING_SUBSIDY_DURATION                             :Subsidy duration: {STRING2}
 
STR_CONFIG_SETTING_SUBSIDY_DURATION_HELPTEXT                    :Set the number of years for which a subsidy is awarded
 

	
 
@@ -1356,12 +1360,15 @@ STR_CONFIG_SETTING_AUTOSCROLL_MAIN_VIEWP
 
STR_CONFIG_SETTING_AUTOSCROLL_MAIN_VIEWPORT                     :Main viewport
 
STR_CONFIG_SETTING_AUTOSCROLL_EVERY_VIEWPORT                    :Every viewport
 

	
 
STR_CONFIG_SETTING_BRIBE                                        :Allow bribing of the local authority: {STRING2}
 
STR_CONFIG_SETTING_BRIBE_HELPTEXT                               :Allow companies to try bribing the local town authority. If the bribe is noticed by an inspector, the company will not be able to act in the town for six months
 

	
 
STR_CONFIG_SETTING_BRIBE_RISKY                                  :Bribing can be caught by an inspector: {STRING2}
 
STR_CONFIG_SETTING_BRIBE_RISKY_HELPTEXT                         :Whether or not bribing towns can be noticed by an inspector
 

	
 
STR_CONFIG_SETTING_ALLOW_EXCLUSIVE                              :Allow buying exclusive transport rights: {STRING2}
 
STR_CONFIG_SETTING_ALLOW_EXCLUSIVE_HELPTEXT                     :If a company buys exclusive transport rights for a town, opponents' stations (passenger and cargo) won't receive any cargo for a whole year
 

	
 
STR_CONFIG_SETTING_ALLOW_FUND_BUILDINGS                         :Allow funding buildings: {STRING2}
 
STR_CONFIG_SETTING_ALLOW_FUND_BUILDINGS_HELPTEXT                :Allow companies to give money to towns for funding new houses
 

	
 
@@ -1438,12 +1445,15 @@ STR_CONFIG_SETTING_HOVER_DELAY_VALUE    
 
###setting-zero-is-special
 
STR_CONFIG_SETTING_HOVER_DELAY_DISABLED                         :Right click
 

	
 
STR_CONFIG_SETTING_POPULATION_IN_LABEL                          :Show town population in the town name label: {STRING2}
 
STR_CONFIG_SETTING_POPULATION_IN_LABEL_HELPTEXT                 :Display the population of towns in their label on the map
 

	
 
STR_CONFIG_SETTING_RATING_COLOUR_IN_LABEL                       :Colour town names based on company rating: {STRING2}
 
STR_CONFIG_SETTING_RATING_COLOUR_IN_LABEL_HELPTEXT              :Colour towns based on company rating in their label on the map
 

	
 
STR_CONFIG_SETTING_GRAPH_LINE_THICKNESS                         :Thickness of lines in graphs: {STRING2}
 
STR_CONFIG_SETTING_GRAPH_LINE_THICKNESS_HELPTEXT                :Width of the line in the graphs. A thin line is more precisely readable, a thicker line is easier to see and colours are easier to distinguish
 

	
 
STR_CONFIG_SETTING_SHOW_NEWGRF_NAME                             :Show the NewGRF's name in the build vehicle window: {STRING2}
 
STR_CONFIG_SETTING_SHOW_NEWGRF_NAME_HELPTEXT                    :Add a line to the build vehicle window, showing which NewGRF the selected vehicle comes from.
 

	
 
@@ -1859,12 +1869,69 @@ STR_CONFIG_SETTING_TOWN_FOUNDING_ALLOWED
 

	
 
STR_CONFIG_SETTING_TOWN_CARGOGENMODE                            :Town cargo generation: {STRING2}
 
STR_CONFIG_SETTING_TOWN_CARGOGENMODE_HELPTEXT                   :How much cargo is produced by houses in towns, relative to the overall population of the town.{}Quadratic growth: A town twice the size generates four times as many passengers.{}Linear growth: A town twice the size generates twice the amount of passengers.
 
###length 2
 
STR_CONFIG_SETTING_TOWN_CARGOGENMODE_ORIGINAL                   :Quadratic (original)
 
STR_CONFIG_SETTING_TOWN_CARGOGENMODE_BITCOUNT                   :Linear
 
STR_CONFIG_SETTING_DAYLENGTH_FACTOR                             :Day length multiplier: {STRING2}
 
STR_CONFIG_SETTING_DAYLENGTH_FACTOR_HELPTEXT                    :Adjusts the length of a day in OpenTTD. A multiplier of 1 is equivalent to classic TTD (each day lasts 74 ticks or 2.22 seconds)
 
STR_CONFIG_SETTING_TOWN_GROWTH_FACTOR                           :Town growth interval multiplier: {STRING2}
 
STR_CONFIG_SETTING_TOWN_GROWTH_FACTOR_HELPTEXT                  :Adjusts the length of time between town growth ticks, intended to be used alongside day length multiplier. Larger values will result in town growth happening more slowly
 
STR_CONFIG_SETTING_PLANE_RANGE_MULTIPLIER                       :Aircraft range multiplier: {STRING2}
 
STR_CONFIG_SETTING_PLANE_RANGE_MULTIPLIER_HELPTEXT              :Adjusts the maximum range of all aircraft
 
STR_CONFIG_SETTING_NAME_STATIONS_BY_INDUSTRIES                  :Station naming convention: {STRING2}
 
STR_CONFIG_SETTING_NAME_STATIONS_BY_INDUSTRIES_HELPTEXT         :Determines how stations will be named when placed; affects all players
 
STR_CONFIG_SETTING_NAME_STATIONS_BY_INDUSTRIES_SELECTION_0      :Use classic station names only
 
STR_CONFIG_SETTING_NAME_STATIONS_BY_INDUSTRIES_SELECTION_1      :Use NewGRF station names where appropriate
 
STR_CONFIG_SETTING_NAME_STATIONS_BY_INDUSTRIES_SELECTION_2      :Name stations based on nearby industries
 
STR_CONFIG_SETTING_INFRASTRUCTURE_BASE_COST_RAIL                :Rail infrastructure base cost: {STRING2}
 
STR_CONFIG_SETTING_INFRASTRUCTURE_BASE_COST_RAIL_HELPTEXT       :Adjusts the base infrastructure maintenance cost of rail tiles
 
STR_CONFIG_SETTING_INFRASTRUCTURE_BASE_COST_ROAD                :Road infrastructure base cost: {STRING2}
 
STR_CONFIG_SETTING_INFRASTRUCTURE_BASE_COST_ROAD_HELPTEXT       :Adjusts the base infrastructure maintenance cost of road tiles
 
STR_CONFIG_SETTING_INFRASTRUCTURE_BASE_COST_WATER               :Water infrastructure base cost: {STRING2}
 
STR_CONFIG_SETTING_INFRASTRUCTURE_BASE_COST_WATER_HELPTEXT      :Adjusts the base infrastructure maintenance cost of water tiles
 
STR_CONFIG_SETTING_INFRASTRUCTURE_BASE_COST_AIR                 :Air infrastructure base cost: {STRING2}
 
STR_CONFIG_SETTING_INFRASTRUCTURE_BASE_COST_AIR_HELPTEXT        :Adjusts the base infrastructure maintenance cost of airports
 
STR_CONFIG_SETTING_INFRASTRUCTURE_BASE_COST_STATION             :Station infrastructure base cost: {STRING2}
 
STR_CONFIG_SETTING_INFRASTRUCTURE_BASE_COST_STATION_HELPTEXT    :Adjusts the base infrastructure maintenance cost of station tiles
 
STR_CONFIG_SETTING_RUNNING_COST_MULTIPLIER_RAIL                 :Rail running cost multiplier: {STRING2}
 
STR_CONFIG_SETTING_RUNNING_COST_MULTIPLIER_RAIL_HELPTEXT        :Adjusts the running cost of rail vehicles, intended to be used alongside day length multiplier
 
STR_CONFIG_SETTING_RUNNING_COST_MULTIPLIER_ROAD                 :Road running cost multiplier: {STRING2}
 
STR_CONFIG_SETTING_RUNNING_COST_MULTIPLIER_ROAD_HELPTEXT        :Adjusts the running cost of road vehicles, intended to be used alongside day length multiplier
 
STR_CONFIG_SETTING_RUNNING_COST_MULTIPLIER_WATER                :Water running cost multiplier: {STRING2}
 
STR_CONFIG_SETTING_RUNNING_COST_MULTIPLIER_WATER_HELPTEXT       :Adjusts the running cost of water vehicles, intended to be used alongside day length multiplier
 
STR_CONFIG_SETTING_RUNNING_COST_MULTIPLIER_AIR                  :Air running cost multiplier: {STRING2}
 
STR_CONFIG_SETTING_RUNNING_COST_MULTIPLIER_AIR_HELPTEXT         :Adjusts the running cost of air vehicles, intended to be used alongside day length multiplier
 
STR_CONFIG_SETTING_VEHICLE_BREAKDOWN_SCALER                     :Vehicle breakdown scaler: {STRING2}
 
STR_CONFIG_SETTING_VEHICLE_BREAKDOWN_SCALER_HELPTEXT            :This is an additional breakdown chance scaler. Whenever a vehicle would normally break down in OTTD, a random 24-bit number will instead be generated (between 0 and 16777215). If it is greater than this scaler, the breakdown will be cancelled. (Setting to maximum value will behave like this scaler didn't exist)
 

	
 
STR_CONFIG_SETTING_DILAPIDATION_MAX_AMOUNT                      :Maximum station dilapidation: {STRING2}
 
STR_CONFIG_SETTING_DILAPIDATION_MAX_AMOUNT_HELPTEXT             :Every time the rating is updated (around once every 2.5 days) the amount of dilapidation at the station will be adjusted, being increased if there are excessive numbers of waiting passengers and being decreased otherwise. The more a station is dilapidated at the start of each month, the higher the amount your company will be fined. This setting controls the maximum amount of dilapidation that can be at a station. Setting to zero will disable this feature
 
STR_CONFIG_SETTING_DILAPIDATION_INCREASE                        :Dilapidation increase: {STRING2}
 
STR_CONFIG_SETTING_DILAPIDATION_INCREASE_HELPTEXT               :This is the amount by which station dilapidation will be increased if there are excessive numbers of waiting passengers
 
STR_CONFIG_SETTING_DILAPIDATION_DECREASE                        :Dilapidation decrease: {STRING2}
 
STR_CONFIG_SETTING_DILAPIDATION_DECREASE_HELPTEXT               :This is the amount by which station dilapidation will be decreased if there are not excessive numbers of waiting passengers
 
STR_CONFIG_SETTING_DILAPIDATION_POP_RAIL                        :Train station excessive passenger count: {STRING2}
 
STR_CONFIG_SETTING_DILAPIDATION_POP_RAIL_HELPTEXT               :Train stations are allowed to have this many passengers waiting before the number is considered excessive. For multi-type stations the highest value is used
 
STR_CONFIG_SETTING_DILAPIDATION_POP_ROAD                        :Road stop excessive passenger count: {STRING2}
 
STR_CONFIG_SETTING_DILAPIDATION_POP_ROAD_HELPTEXT               :Road stops are allowed to have this many passengers waiting before the number is considered excessive. For multi-type stations the highest value is used
 
STR_CONFIG_SETTING_DILAPIDATION_POP_WATER                       :Dock excessive passenger count: {STRING2}
 
STR_CONFIG_SETTING_DILAPIDATION_POP_WATER_HELPTEXT              :Docks are allowed to have this many passengers waiting before the number is considered excessive. For multi-type stations the highest value is used
 
STR_CONFIG_SETTING_DILAPIDATION_POP_AIR                         :Airport/helipad excessive passenger count: {STRING2}
 
STR_CONFIG_SETTING_DILAPIDATION_POP_AIR_HELPTEXT                :Airports and helipads are allowed to have this many passengers waiting before the number is considered excessive. For multi-type stations the highest value is used
 
STR_CONFIG_SETTING_DILAPIDATION_FINE_RAIL                       :Fine per train station dilapidation point: {STRING2}
 
STR_CONFIG_SETTING_DILAPIDATION_FINE_RAIL_HELPTEXT              :You will be fined this amount of money each month for each dilapidation point at each train station. For multi-type stations the highest value is used
 
STR_CONFIG_SETTING_DILAPIDATION_FINE_ROAD                       :Fine per road stop dilapidation point: {STRING2}
 
STR_CONFIG_SETTING_DILAPIDATION_FINE_ROAD_HELPTEXT              :You will be fined this amount of money each month for each dilapidation point at each road stop. For multi-type stations the highest value is used
 
STR_CONFIG_SETTING_DILAPIDATION_FINE_WATER                      :Fine per dock dilapidation point: {STRING2}
 
STR_CONFIG_SETTING_DILAPIDATION_FINE_WATER_HELPTEXT             :You will be fined this amount of money each month for each dilapidation point at each docks. For multi-type stations the highest value is used
 
STR_CONFIG_SETTING_DILAPIDATION_FINE_AIR                        :Fine per airport/helipad dilapidation point: {STRING2}
 
STR_CONFIG_SETTING_DILAPIDATION_FINE_AIR_HELPTEXT               :You will be fined this amount of money each month for each dilapidation point at each airport or helipad. For multi-type stations the highest value is used
 

	
 
STR_CONFIG_SETTING_LOAD_LEGACY_PATCHPACK_DATA                   :Attempt to load legacy patch data: {STRING2}
 
STR_CONFIG_SETTING_LOAD_LEGACY_PATCHPACK_DATA_HELPTEXT          :Causes the save loader to try to load legacy patch data for save files that might be from patchpack 5.x or earlier. Generally safe to leave enabled. Not all old saves are supported yet
 

	
 
STR_CONFIG_SETTING_EXTRA_TREE_PLACEMENT                         :In game placement of trees: {STRING2}
 
STR_CONFIG_SETTING_EXTRA_TREE_PLACEMENT_HELPTEXT                :Control random appearance of trees during the game. This might affect industries which rely on tree growth, for example lumber mills
 
###length 4
 
STR_CONFIG_SETTING_EXTRA_TREE_PLACEMENT_NO_SPREAD               :Grow but don't spread {RED}(breaks lumber mill)
 
STR_CONFIG_SETTING_EXTRA_TREE_PLACEMENT_SPREAD_RAINFOREST       :Grow but only spread in rain forests
 
@@ -2015,12 +2082,15 @@ STR_CONFIG_SETTING_ENVIRONMENT_AUTHORITI
 
STR_CONFIG_SETTING_ENVIRONMENT_TOWNS                            :{ORANGE}Towns
 
STR_CONFIG_SETTING_ENVIRONMENT_INDUSTRIES                       :{ORANGE}Industries
 
STR_CONFIG_SETTING_ENVIRONMENT_CARGODIST                        :{ORANGE}Cargo distribution
 
STR_CONFIG_SETTING_AI                                           :{ORANGE}Competitors
 
STR_CONFIG_SETTING_AI_NPC                                       :{ORANGE}Computer players
 
STR_CONFIG_SETTING_NETWORK                                      :{ORANGE}Network
 
STR_CONFIG_SETTING_PATCHPACK                                    :{ORANGE}Patchpack configuration
 
STR_CONFIG_SETTING_PATCHPACK_DAYLENGTH                          :{ORANGE}Economy multipliers
 
STR_CONFIG_SETTING_PATCHPACK_DILAPIDATION                       :{ORANGE}Station dilapidation
 

	
 
STR_CONFIG_SETTING_PATHFINDER_FOR_TRAINS                        :Pathfinder for trains: {STRING2}
 
STR_CONFIG_SETTING_PATHFINDER_FOR_TRAINS_HELPTEXT               :Path finder to use for trains
 
STR_CONFIG_SETTING_PATHFINDER_FOR_ROAD_VEHICLES                 :Pathfinder for road vehicles: {STRING2}
 
STR_CONFIG_SETTING_PATHFINDER_FOR_ROAD_VEHICLES_HELPTEXT        :Path finder to use for road vehicles
 
STR_CONFIG_SETTING_PATHFINDER_FOR_SHIPS                         :Pathfinder for ships: {STRING2}
 
@@ -2055,13 +2125,13 @@ STR_CONFIG_ERROR_SPRITECACHE_TOO_BIG    
 

	
 
# Video initalization errors
 
STR_VIDEO_DRIVER_ERROR                                          :{WHITE}Error with video settings...
 
STR_VIDEO_DRIVER_ERROR_NO_HARDWARE_ACCELERATION                 :{WHITE}... no compatible GPU found. Hardware acceleration disabled
 

	
 
# Intro window
 
STR_INTRO_CAPTION                                               :{WHITE}OpenTTD {REV}
 
STR_INTRO_CAPTION                                               :{WHITE}Ginever.net OpenTTD Patchpack :: {REV}
 

	
 
STR_INTRO_NEW_GAME                                              :{BLACK}New Game
 
STR_INTRO_LOAD_GAME                                             :{BLACK}Load Game
 
STR_INTRO_PLAY_SCENARIO                                         :{BLACK}Play Scenario
 
STR_INTRO_PLAY_HEIGHTMAP                                        :{BLACK}Play Heightmap
 
STR_INTRO_SCENARIO_EDITOR                                       :{BLACK}Scenario Editor
 
@@ -3030,13 +3100,13 @@ STR_LAI_OBJECT_DESCRIPTION_LIGHTHOUSE   
 
STR_LAI_OBJECT_DESCRIPTION_COMPANY_HEADQUARTERS                 :Company headquarters
 
STR_LAI_OBJECT_DESCRIPTION_COMPANY_OWNED_LAND                   :Company-owned land
 

	
 
# About OpenTTD window
 
STR_ABOUT_OPENTTD                                               :{WHITE}About OpenTTD
 
STR_ABOUT_ORIGINAL_COPYRIGHT                                    :{BLACK}Original copyright {COPYRIGHT} 1995 Chris Sawyer, All rights reserved
 
STR_ABOUT_VERSION                                               :{BLACK}OpenTTD version {REV}
 
STR_ABOUT_VERSION                                               :{BLACK}Ginever.net OpenTTD Patchpack :: {REV}
 
STR_ABOUT_COPYRIGHT_OPENTTD                                     :{BLACK}OpenTTD {COPYRIGHT} 2002-{RAW_STRING} The OpenTTD team
 

	
 
# Framerate display window
 
STR_FRAMERATE_CAPTION                                           :{WHITE}Frame rate
 
STR_FRAMERATE_CAPTION_SMALL                                     :{STRING2}{WHITE} ({DECIMAL}x)
 
STR_FRAMERATE_RATE_GAMELOOP                                     :{BLACK}Simulation rate: {STRING2}
 
@@ -3121,12 +3191,15 @@ STR_SAVELOAD_OVERWRITE_TITLE            
 
STR_SAVELOAD_OVERWRITE_WARNING                                  :{YELLOW}Are you sure you want to overwrite the existing file?
 
STR_SAVELOAD_DIRECTORY                                          :{RAW_STRING} (Directory)
 
STR_SAVELOAD_PARENT_DIRECTORY                                   :{RAW_STRING} (Parent directory)
 

	
 
STR_SAVELOAD_OSKTITLE                                           :{BLACK}Enter a name for the savegame
 

	
 
STR_SAVELOAD_VERSION_WITH_EXTENSION                             :{SILVER}Save format version: {WHITE}{NUM}{RAW_STRING}
 
STR_SAVELOAD_VERSION_LABEL                                      :{SILVER}Saved by: {WHITE}{RAW_STRING}
 

	
 
# World generation
 
STR_MAPGEN_WORLD_GENERATION_CAPTION                             :{WHITE}World Generation
 
STR_MAPGEN_MAPSIZE                                              :{BLACK}Map size:
 
STR_MAPGEN_MAPSIZE_TOOLTIP                                      :{BLACK}Select the size of the map in tiles. The number of available tiles will be slightly smaller
 
STR_MAPGEN_BY                                                   :{BLACK}*
 
STR_MAPGEN_NUMBER_OF_TOWNS                                      :{BLACK}No. of towns:
 
@@ -3526,12 +3599,13 @@ STR_STORY_BOOK_INVALID_GOAL_REF         
 

	
 
# Station list window
 
STR_STATION_LIST_TOOLTIP                                        :{BLACK}Station names - click on name to centre main view on station. Ctrl+Click opens a new viewport on station location
 
STR_STATION_LIST_USE_CTRL_TO_SELECT_MORE                        :{BLACK}Hold Ctrl to select more than one item
 
STR_STATION_LIST_CAPTION                                        :{WHITE}{COMPANY} - {COMMA} Station{P "" s}
 
STR_STATION_LIST_STATION                                        :{YELLOW}{STATION} {STATION_FEATURES}
 
STR_STATION_LIST_STATION_WITH_DILAPIDATION                      :{YELLOW}{STATION} {STATION_FEATURES} {WHITE}({NUM})
 
STR_STATION_LIST_WAYPOINT                                       :{YELLOW}{WAYPOINT}
 
STR_STATION_LIST_NONE                                           :{YELLOW}- None -
 
STR_STATION_LIST_SELECT_ALL_FACILITIES                          :{BLACK}Select all facilities
 
STR_STATION_LIST_SELECT_ALL_TYPES                               :{BLACK}Select all cargo types (including no waiting cargo)
 
STR_STATION_LIST_NO_WAITING_CARGO                               :{BLACK}No cargo of any type is waiting
 

	
 
@@ -3549,12 +3623,14 @@ STR_STATION_VIEW_EXCLUSIVE_RIGHTS_COMPAN
 

	
 
STR_STATION_VIEW_RATINGS_BUTTON                                 :{BLACK}Ratings
 
STR_STATION_VIEW_RATINGS_TOOLTIP                                :{BLACK}Show station ratings
 
STR_STATION_VIEW_SUPPLY_RATINGS_TITLE                           :{BLACK}Monthly supply and local rating:
 
STR_STATION_VIEW_CARGO_SUPPLY_RATING                            :{WHITE}{STRING}: {YELLOW}{COMMA} / {STRING} ({COMMA}%)
 

	
 
STR_STATION_VIEW_DILAPIDATION                                   :{BLACK}Station dilapidation: {YELLOW}{NUM} / {NUM}
 

	
 
STR_STATION_VIEW_GROUP                                          :{BLACK}Group by
 
STR_STATION_VIEW_WAITING_STATION                                :Station: Waiting
 
STR_STATION_VIEW_WAITING_AMOUNT                                 :Amount: Waiting
 
STR_STATION_VIEW_PLANNED_STATION                                :Station: Planned
 
STR_STATION_VIEW_PLANNED_AMOUNT                                 :Amount: Planned
 
STR_STATION_VIEW_FROM                                           :{YELLOW}{CARGO_SHORT} from {STATION}
 
@@ -3609,26 +3685,36 @@ STR_EDIT_WAYPOINT_NAME                  
 

	
 
# Finances window
 
STR_FINANCES_CAPTION                                            :{WHITE}{COMPANY} Finances {BLACK}{COMPANY_NUM}
 
STR_FINANCES_EXPENDITURE_INCOME_TITLE                           :{WHITE}Expenditure/Income
 
STR_FINANCES_YEAR                                               :{WHITE}{NUM}
 

	
 
###length 13
 
STR_FINANCES_SECTION_CONSTRUCTION                               :{GOLD}Construction
 
###length 23
 
STR_FINANCES_SECTION_CONSTRUCTION                               :{GOLD}Misc Construction
 
STR_FINANCES_SECTION_NEW_VEHICLES                               :{GOLD}New Vehicles
 
STR_FINANCES_SECTION_TRAIN_RUNNING_COSTS                        :{GOLD}Train Running Costs
 
STR_FINANCES_SECTION_ROAD_VEHICLE_RUNNING_COSTS                 :{GOLD}Road Vehicle Running Costs
 
STR_FINANCES_SECTION_AIRCRAFT_RUNNING_COSTS                     :{GOLD}Aircraft Running Costs
 
STR_FINANCES_SECTION_SHIP_RUNNING_COSTS                         :{GOLD}Ship Running Costs
 
STR_FINANCES_SECTION_PROPERTY_MAINTENANCE                       :{GOLD}Property Maintenance
 
STR_FINANCES_SECTION_TRAIN_INCOME                               :{GOLD}Train Income
 
STR_FINANCES_SECTION_ROAD_VEHICLE_INCOME                        :{GOLD}Road Vehicle Income
 
STR_FINANCES_SECTION_AIRCRAFT_INCOME                            :{GOLD}Aircraft Income
 
STR_FINANCES_SECTION_SHIP_INCOME                                :{GOLD}Ship Income
 
STR_FINANCES_SECTION_LOAN_INTEREST                              :{GOLD}Loan Interest
 
STR_FINANCES_SECTION_OTHER                                      :{GOLD}Other
 
STR_FINANCES_SECTION_T_TRAIN_CON                                :{GOLD}Railway Construction
 
STR_FINANCES_SECTION_T_ROAD_CON                                 :{GOLD}Road Construction
 
STR_FINANCES_SECTION_T_AIRCRAFT_CON                             :{GOLD}Airport Construction
 
STR_FINANCES_SECTION_T_SHIP_CON                                 :{GOLD}Waterways Construction
 
STR_FINANCES_SECTION_T_TREES_CON                                :{GOLD}Tree Construction
 
STR_FINANCES_SECTION_T_SCENERY_CON                              :{GOLD}Object Construction
 
STR_FINANCES_SECTION_T_LANDSCAPING                              :{GOLD}Landscaping
 
STR_FINANCES_SECTION_T_DEMOLITION                               :{GOLD}Demolition
 
STR_FINANCES_SECTION_T_REWARD_INC                               :{GOLD}Reward Income
 
STR_FINANCES_SECTION_T_DILAPIDATION                             :{GOLD}Station Dilapidation
 

	
 
STR_FINANCES_NEGATIVE_INCOME                                    :{BLACK}-{CURRENCY_LONG}
 
STR_FINANCES_POSITIVE_INCOME                                    :{BLACK}+{CURRENCY_LONG}
 
STR_FINANCES_TOTAL_CAPTION                                      :{WHITE}Total:
 
STR_FINANCES_BANK_BALANCE_TITLE                                 :{WHITE}Bank Balance
 
STR_FINANCES_LOAN_TITLE                                         :{WHITE}Loan
 
@@ -4134,17 +4220,26 @@ STR_VEHICLE_VIEW_AIRCRAFT_STATUS_START_S
 
# Messages in the start stop button in the vehicle view
 
STR_VEHICLE_STATUS_LOADING_UNLOADING                            :{LTBLUE}Loading / Unloading
 
STR_VEHICLE_STATUS_LEAVING                                      :{LTBLUE}Leaving
 
STR_VEHICLE_STATUS_CRASHED                                      :{RED}Crashed!
 
STR_VEHICLE_STATUS_BROKEN_DOWN                                  :{RED}Broken down
 
STR_VEHICLE_STATUS_STOPPED                                      :{RED}Stopped
 
STR_VEHICLE_STATUS_BROKEN_DOWN_VEL                              :{RED}Broken down - {STRING1}, {LTBLUE} {VELOCITY}
 
STR_VEHICLE_STATUS_TRAIN_STOPPING_VEL                           :{RED}Stopping, {VELOCITY}
 
STR_VEHICLE_STATUS_TRAIN_NO_POWER                               :{RED}No power
 
STR_VEHICLE_STATUS_TRAIN_STUCK                                  :{ORANGE}Waiting for free path
 
STR_VEHICLE_STATUS_AIRCRAFT_TOO_FAR                             :{ORANGE}Too far to next destination
 

	
 
STR_BREAKDOWN_TYPE_CRITICAL                                     :Mechanical failure
 
STR_BREAKDOWN_TYPE_EM_STOP                                      :Emergency stop
 
STR_BREAKDOWN_TYPE_LOW_SPEED                                    :Limited to {VELOCITY}
 
STR_BREAKDOWN_TYPE_LOW_POWER                                    :{COMMA}% Power
 
STR_BREAKDOWN_TYPE_DEPOT                                        :Heading to {STATION} Hangar for repairs
 
STR_BREAKDOWN_TYPE_LANDING                                      :Heading to {STATION} for emergency landing
 
STR_ERROR_TRAIN_TOO_HEAVY                                       :{WHITE}{VEHICLE} is too heavy
 

	
 
STR_VEHICLE_STATUS_HEADING_FOR_STATION_VEL                      :{LTBLUE}Heading for {STATION}, {VELOCITY}
 
STR_VEHICLE_STATUS_NO_ORDERS_VEL                                :{LTBLUE}No orders, {VELOCITY}
 
STR_VEHICLE_STATUS_HEADING_FOR_WAYPOINT_VEL                     :{LTBLUE}Heading for {WAYPOINT}, {VELOCITY}
 
STR_VEHICLE_STATUS_HEADING_FOR_DEPOT_VEL                        :{ORANGE}Heading for {DEPOT}, {VELOCITY}
 
STR_VEHICLE_STATUS_HEADING_FOR_DEPOT_SERVICE_VEL                :{LTBLUE}Service at {DEPOT}, {VELOCITY}
 

	
 
@@ -4169,12 +4264,17 @@ STR_VEHICLE_DETAILS_CAPTION             
 
STR_VEHICLE_DETAILS_TRAIN_RENAME                                :{BLACK}Name train
 
STR_VEHICLE_DETAILS_ROAD_VEHICLE_RENAME                         :{BLACK}Name road vehicle
 
STR_VEHICLE_DETAILS_SHIP_RENAME                                 :{BLACK}Name ship
 
STR_VEHICLE_DETAILS_AIRCRAFT_RENAME                             :{BLACK}Name aircraft
 

	
 
STR_VEHICLE_INFO_AGE_RUNNING_COST_YR                            :{BLACK}Age: {LTBLUE}{STRING2}{BLACK}   Running Cost: {LTBLUE}{CURRENCY_LONG}/yr
 

	
 
STR_RUNNING                                                     :{LTBLUE}Running
 
STR_NEED_REPAIR                                                 :{ORANGE}Train needs repair - max speed reduced to {VELOCITY}
 
STR_CURRENT_STATUS                                              :{BLACK}Current status: {STRING2}
 

	
 
STR_VEHICLE_INFO_AGE                                            :{COMMA} year{P "" s} ({COMMA})
 
STR_VEHICLE_INFO_AGE_RED                                        :{RED}{COMMA} year{P "" s} ({COMMA})
 

	
 
STR_VEHICLE_INFO_MAX_SPEED                                      :{BLACK}Max. speed: {LTBLUE}{VELOCITY}
 
STR_VEHICLE_INFO_MAX_SPEED_TYPE                                 :{BLACK}Max. speed: {LTBLUE}{VELOCITY} {BLACK}Aircraft type: {LTBLUE}{STRING}
 
STR_VEHICLE_INFO_MAX_SPEED_TYPE_RANGE                           :{BLACK}Max. speed: {LTBLUE}{VELOCITY} {BLACK}Aircraft type: {LTBLUE}{STRING} {BLACK}Range: {LTBLUE}{COMMA} tiles
 
@@ -4207,13 +4307,15 @@ STR_QUERY_RENAME_TRAIN_CAPTION          
 
STR_QUERY_RENAME_ROAD_VEHICLE_CAPTION                           :{WHITE}Name road vehicle
 
STR_QUERY_RENAME_SHIP_CAPTION                                   :{WHITE}Name ship
 
STR_QUERY_RENAME_AIRCRAFT_CAPTION                               :{WHITE}Name aircraft
 

	
 
# Extra buttons for train details windows
 
STR_VEHICLE_DETAILS_TRAIN_ENGINE_BUILT_AND_VALUE                :{LTBLUE}{ENGINE}{BLACK}   Built: {LTBLUE}{NUM}{BLACK} Value: {LTBLUE}{CURRENCY_LONG}
 
STR_VEHICLE_DETAILS_TRAIN_ENGINE_BUILT_AND_VALUE_AND_SPEED      :{LTBLUE}{ENGINE}{BLACK}   Built: {LTBLUE}{NUM}{BLACK} Value: {LTBLUE}{CURRENCY_LONG} {BLACK}Max. speed: {LTBLUE}{VELOCITY}
 
STR_VEHICLE_DETAILS_TRAIN_WAGON_VALUE                           :{LTBLUE}{ENGINE}{BLACK}   Value: {LTBLUE}{CURRENCY_LONG}
 
STR_VEHICLE_DETAILS_TRAIN_WAGON_VALUE_AND_SPEED                 :{LTBLUE}{ENGINE}{BLACK}   Value: {LTBLUE}{CURRENCY_LONG} {BLACK}Max. speed: {LTBLUE}{VELOCITY}
 

	
 
STR_VEHICLE_DETAILS_TRAIN_TOTAL_CAPACITY_TEXT                   :{BLACK}Total cargo capacity of this train:
 
STR_VEHICLE_DETAILS_TRAIN_TOTAL_CAPACITY                        :{LTBLUE}- {CARGO_LONG} ({CARGO_SHORT})
 
STR_VEHICLE_DETAILS_TRAIN_TOTAL_CAPACITY_MULT                   :{LTBLUE}- {CARGO_LONG} ({CARGO_SHORT}) (x{NUM})
 

	
 
STR_VEHICLE_DETAILS_CARGO_EMPTY                                 :{LTBLUE}Empty
 
@@ -4612,18 +4714,19 @@ STR_ERROR_SAVE_STILL_IN_PROGRESS        
 
STR_ERROR_AUTOSAVE_FAILED                                       :{WHITE}Autosave failed
 
STR_ERROR_UNABLE_TO_READ_DRIVE                                  :{BLACK}Unable to read drive
 
STR_ERROR_GAME_SAVE_FAILED                                      :{WHITE}Game Save Failed{}{STRING1}
 
STR_ERROR_UNABLE_TO_DELETE_FILE                                 :{WHITE}Unable to delete file
 
STR_ERROR_GAME_LOAD_FAILED                                      :{WHITE}Game Load Failed{}{STRING1}
 
STR_GAME_SAVELOAD_ERROR_BROKEN_INTERNAL_ERROR                   :Internal error: {RAW_STRING}
 
STR_GAME_SAVELOAD_ERROR_BROKEN_SAVEGAME                         :Broken savegame - {RAW_STRING}
 
STR_GAME_SAVELOAD_ERROR_TOO_NEW_SAVEGAME                        :Savegame is made with newer version
 
STR_GAME_SAVELOAD_ERROR_BROKEN_SAVEGAME                         :Save file contains incompatible patch data ({RAW_STRING})
 
STR_GAME_SAVELOAD_ERROR_TOO_NEW_SAVEGAME                        :Save file contains incompatible patch data
 
STR_GAME_SAVELOAD_ERROR_FILE_NOT_READABLE                       :File not readable
 
STR_GAME_SAVELOAD_ERROR_FILE_NOT_WRITEABLE                      :File not writeable
 
STR_GAME_SAVELOAD_ERROR_DATA_INTEGRITY_CHECK_FAILED             :Data integrity check failed
 
STR_GAME_SAVELOAD_ERROR_PATCHPACK                               :Savegame is made with a modified version
 
STR_GAME_SAVELOAD_ERROR_UNSUPPORTED_GPP                         :The legacy save loader doesn't currently support loading saves from this version
 
STR_GAME_SAVELOAD_NOT_AVAILABLE                                 :<not available>
 
STR_WARNING_LOADGAME_REMOVED_TRAMS                              :{WHITE}Game was saved in version without tram support. All trams have been removed
 

	
 
# Map generation messages
 
STR_ERROR_COULD_NOT_CREATE_TOWN                                 :{WHITE}Map generation aborted...{}... no suitable town locations
 
STR_ERROR_NO_TOWN_IN_SCENARIO                                   :{WHITE}... there is no town in this scenario
 
@@ -5464,15 +5567,30 @@ STR_DEFAULT_SIGN_NAME                   
 
STR_COMPANY_SOMEONE                                             :someone
 

	
 
STR_SAVEGAME_NAME_DEFAULT                                       :{COMPANY}, {STRING1}
 
STR_SAVEGAME_NAME_SPECTATOR                                     :Spectator, {1:STRING1}
 

	
 
# Viewport strings
 
STR_VIEWPORT_TOWN_POP_VERY_POOR_RATING                          :{RED}{TOWN} ({COMMA})
 
STR_VIEWPORT_TOWN_POP_MEDIOCRE_RATING                           :{ORANGE}{TOWN} ({COMMA})
 
STR_VIEWPORT_TOWN_POP_GOOD_RATING                               :{YELLOW}{TOWN} ({COMMA})
 
STR_VIEWPORT_TOWN_POP_NORMAL_RATING                             :{GREEN}{TOWN} ({COMMA})
 
STR_VIEWPORT_TOWN_POP_EXCELLENT_RATING                          :{LTBLUE}{TOWN} ({COMMA})
 
STR_VIEWPORT_TOWN_POP                                           :{WHITE}{TOWN} ({COMMA})
 
STR_VIEWPORT_TOWN_VERY_POOR_RATING                              :{RED}{TOWN}
 
STR_VIEWPORT_TOWN_MEDIOCRE_RATING                               :{ORANGE}{TOWN}
 
STR_VIEWPORT_TOWN_GOOD_RATING                                   :{YELLOW}{TOWN}
 
STR_VIEWPORT_TOWN_NORMAL_RATING                                 :{GREEN}{TOWN}
 
STR_VIEWPORT_TOWN_EXCELLENT_RATING                              :{LTBLUE}{TOWN}
 
STR_VIEWPORT_TOWN                                               :{WHITE}{TOWN}
 
STR_VIEWPORT_TOWN_TINY_BLACK                                    :{TINY_FONT}{BLACK}{TOWN}
 
STR_VIEWPORT_TOWN_TINY_VERY_POOR_RATING                         :{TINY_FONT}{RED}{TOWN}
 
STR_VIEWPORT_TOWN_TINY_MEDIOCRE_RATING                          :{TINY_FONT}{ORANGE}{TOWN}
 
STR_VIEWPORT_TOWN_TINY_GOOD_RATING                              :{TINY_FONT}{YELLOW}{TOWN}
 
STR_VIEWPORT_TOWN_TINY_NORMAL_RATING                            :{TINY_FONT}{GREEN}{TOWN}
 
STR_VIEWPORT_TOWN_TINY_EXCELLENT_RATING                         :{TINY_FONT}{LTBLUE}{TOWN}
 
STR_VIEWPORT_TOWN_TINY_WHITE                                    :{TINY_FONT}{WHITE}{TOWN}
 

	
 
STR_VIEWPORT_SIGN_SMALL_BLACK                                   :{TINY_FONT}{BLACK}{SIGN}
 
STR_VIEWPORT_SIGN_SMALL_WHITE                                   :{TINY_FONT}{WHITE}{SIGN}
 

	
 
STR_VIEWPORT_STATION                                            :{STATION} {STATION_FEATURES}
src/lang/english_AU.txt
Show inline comments
 
@@ -3244,13 +3244,13 @@ STR_EDIT_WAYPOINT_NAME                  
 
# Finances window
 
STR_FINANCES_CAPTION                                            :{WHITE}{COMPANY} Finances {BLACK}{COMPANY_NUM}
 
STR_FINANCES_EXPENDITURE_INCOME_TITLE                           :{WHITE}Expenditure/Income
 
STR_FINANCES_YEAR                                               :{WHITE}{NUM}
 

	
 
###length 13
 
STR_FINANCES_SECTION_CONSTRUCTION                               :{GOLD}Construction
 
STR_FINANCES_SECTION_CONSTRUCTION                               :{GOLD}Misc Construction
 
STR_FINANCES_SECTION_NEW_VEHICLES                               :{GOLD}New Vehicles
 
STR_FINANCES_SECTION_TRAIN_RUNNING_COSTS                        :{GOLD}Train Running Costs
 
STR_FINANCES_SECTION_ROAD_VEHICLE_RUNNING_COSTS                 :{GOLD}Road Vehicle Running Costs
 
STR_FINANCES_SECTION_AIRCRAFT_RUNNING_COSTS                     :{GOLD}Aircraft Running Costs
 
STR_FINANCES_SECTION_SHIP_RUNNING_COSTS                         :{GOLD}Ship Running Costs
 
STR_FINANCES_SECTION_PROPERTY_MAINTENANCE                       :{GOLD}Property Maintenance
src/lang/english_US.txt
Show inline comments
 
@@ -3610,13 +3610,13 @@ STR_EDIT_WAYPOINT_NAME                  
 
# Finances window
 
STR_FINANCES_CAPTION                                            :{WHITE}{COMPANY} Finances {BLACK}{COMPANY_NUM}
 
STR_FINANCES_EXPENDITURE_INCOME_TITLE                           :{WHITE}Expenditure/Income
 
STR_FINANCES_YEAR                                               :{WHITE}{NUM}
 

	
 
###length 13
 
STR_FINANCES_SECTION_CONSTRUCTION                               :{GOLD}Construction
 
STR_FINANCES_SECTION_CONSTRUCTION                               :{GOLD}Misc Construction
 
STR_FINANCES_SECTION_NEW_VEHICLES                               :{GOLD}New Vehicles
 
STR_FINANCES_SECTION_TRAIN_RUNNING_COSTS                        :{GOLD}Train Running Costs
 
STR_FINANCES_SECTION_ROAD_VEHICLE_RUNNING_COSTS                 :{GOLD}Road Vehicle Running Costs
 
STR_FINANCES_SECTION_AIRCRAFT_RUNNING_COSTS                     :{GOLD}Aircraft Running Costs
 
STR_FINANCES_SECTION_SHIP_RUNNING_COSTS                         :{GOLD}Ship Running Costs
 
STR_FINANCES_SECTION_PROPERTY_MAINTENANCE                       :{GOLD}Property Maintenance
src/misc_gui.cpp
Show inline comments
 
@@ -405,12 +405,21 @@ static WindowDesc _about_desc(
 
);
 

	
 
static const char * const _credits[] = {
 
	u8"Original design by Chris Sawyer",
 
	u8"Original graphics by Simon Foster",
 
	u8"",
 
	u8"Ginever.net OpenTTD Patchpack team:",
 
	u8"  Ruby Dennington (Theleruby) - Patch implementation",
 
	u8"  Paul Dennington (Paulicus25) - Lead tester",
 
	u8"  Assorted testers from Ginever Entertainment",
 
	u8"",
 
	u8"The latest version of this patchpack can be obtained from:",
 
	u8"  https://hg.ginever.net/public/openttd  (source)",
 
	u8"  https://hg.ginever.net/public/openttd-binaries  (Windows binaries)",
 
	u8"",
 
	u8"The OpenTTD team (in alphabetical order):",
 
	u8"  Grzegorz Duczy\u0144ski (adf88) - General coding (since 1.7.2)",
 
	u8"  Albert Hofkamp (Alberth) - GUI expert (since 0.7)",
 
	u8"  Matthijs Kooijman (blathijs) - Pathfinder-guru, Debian port (since 0.3)",
 
	u8"  Ulf Hermann (fonsinchen) - Cargo Distribution (since 1.3)",
 
	u8"  Christoph Elsenhans (frosch) - General coding (since 0.6)",
src/network/core/game_info.cpp
Show inline comments
 
@@ -43,13 +43,13 @@ NetworkServerGameInfo _network_game_info
 
std::string_view GetNetworkRevisionString()
 
{
 
	static std::string network_revision;
 

	
 
	if (network_revision.empty()) {
 
		network_revision = _openttd_revision;
 
		if (_openttd_revision_tagged) {
 
		if (_openttd_revision_tagged || _openttd_revision_modified == 3 || _openttd_revision_modified == 4) {
 
			/* Tagged; do not mangle further, though ensure it's not too long. */
 
			if (network_revision.size() >= NETWORK_REVISION_LENGTH) network_revision.resize(NETWORK_REVISION_LENGTH - 1);
 
		} else {
 
			/* Not tagged; add the githash suffix while ensuring the string does not become too long. */
 
			assert(_openttd_revision_modified < 3);
 
			std::string githash_suffix = fmt::format("-{}{}", "gum"[_openttd_revision_modified], _openttd_revision_hash);
src/network/network_func.h
Show inline comments
 
@@ -71,12 +71,14 @@ void NetworkServerSendConfigUpdate();
 
void NetworkServerUpdateGameInfo();
 
void NetworkServerShowStatusToConsole();
 
bool NetworkServerStart();
 
void NetworkServerNewCompany(const Company *company, NetworkClientInfo *ci);
 
bool NetworkServerChangeClientName(ClientID client_id, const std::string &new_name);
 

	
 
void NetworkSavePassword();
 
void NetworkLoadPassword();
 

	
 
void NetworkServerDoMove(ClientID client_id, CompanyID company_id);
 
void NetworkServerSendRcon(ClientID client_id, TextColour colour_code, const std::string &string);
 
void NetworkServerSendChat(NetworkAction action, DestType type, int dest, const std::string &msg, ClientID from_id, int64 data = 0, bool from_admin = false);
 
void NetworkServerSendExternalChat(const std::string &source, TextColour colour, const std::string &user, const std::string &msg);
 

	
src/network/network_server.cpp
Show inline comments
 
@@ -31,12 +31,13 @@
 
#include "../core/random_func.hpp"
 
#include "../company_cmd.h"
 
#include "../rev.h"
 
#include <mutex>
 
#include <condition_variable>
 

	
 
#include "../fileio_func.h"
 
#include "../safeguards.h"
 

	
 

	
 
/* This file handles all the server-commands */
 

	
 
DECLARE_POSTFIX_INCREMENT(ClientID)
 
@@ -1563,12 +1564,15 @@ static void NetworkAutoCleanCompanies()
 
			if (_settings_client.network.autoclean_protected != 0 && _network_company_states[c->index].months_empty > _settings_client.network.autoclean_protected && !_network_company_states[c->index].password.empty()) {
 
				/* Unprotect the company */
 
				_network_company_states[c->index].password.clear();
 
				IConsolePrint(CC_INFO, "Auto-removed protection from company #{}.", c->index + 1);
 
				_network_company_states[c->index].months_empty = 0;
 
				NetworkServerUpdateCompanyPassworded(c->index, false);
 
				if (_settings_client.network.save_password) {
 
					NetworkSavePassword();
 
				}
 
			}
 
			/* Is the company empty for autoclean_novehicles-months, and has no vehicles? */
 
			if (_settings_client.network.autoclean_novehicles != 0 && _network_company_states[c->index].months_empty > _settings_client.network.autoclean_novehicles && vehicles_in_company[c->index] == 0) {
 
				/* Shut the company down */
 
				Command<CMD_COMPANY_CTRL>::Post(CCA_DELETE, c->index, CRR_AUTOCLEAN, INVALID_CLIENT_ID);
 
				IConsolePrint(CC_INFO, "Auto-cleaned company #{} with no vehicles.", c->index + 1);
 
@@ -1656,12 +1660,16 @@ void NetworkServerSetCompanyPassword(Com
 
		_network_company_states[company_id].password = password;
 
	} else {
 
		_network_company_states[company_id].password = GenerateCompanyPasswordHash(password, _settings_client.network.network_id, _settings_game.game_creation.generation_seed);
 
	}
 

	
 
	NetworkServerUpdateCompanyPassworded(company_id, !_network_company_states[company_id].password.empty());
 

	
 
	if (_settings_client.network.save_password) {
 
		NetworkSavePassword();
 
	}
 
}
 

	
 
/**
 
 * Handle the command-queue of a socket.
 
 * @param cs The socket to handle the queue for.
 
 */
 
@@ -2073,12 +2081,15 @@ void NetworkServerNewCompany(const Compa
 

	
 
	if (!_network_server) return;
 

	
 
	_network_company_states[c->index].months_empty = 0;
 
	_network_company_states[c->index].password.clear();
 
	NetworkServerUpdateCompanyPassworded(c->index, false);
 
	if (_settings_client.network.save_password) {
 
		NetworkSavePassword();
 
	}
 

	
 
	if (ci != nullptr) {
 
		/* ci is nullptr when replaying, or for AIs. In neither case there is a client. */
 
		ci->client_playas = c->index;
 
		NetworkUpdateClientInfo(ci->client_id);
 
		Command<CMD_RENAME_PRESIDENT>::SendNet(STR_NULL, c->index, ci->client_name);
 
@@ -2091,6 +2102,44 @@ void NetworkServerNewCompany(const Compa
 
		/* ci is nullptr when replaying, or for AIs. In neither case there is a client.
 
		   We need to send Admin port update here so that they first know about the new company
 
		   and then learn about a possibly joining client (see FS#6025) */
 
		NetworkServerSendChat(NETWORK_ACTION_COMPANY_NEW, DESTTYPE_BROADCAST, 0, "", ci->client_id, c->index + 1);
 
	}
 
}
 

	
 
void NetworkSavePassword()
 
{
 
	std::string password_file_name = std::to_string(_settings_game.game_creation.generation_seed) + ".pwd";
 
	Debug(net, 0, "Saving company passwords to %s", password_file_name);
 
	FILE* file_pointer = FioFOpenFile(password_file_name, "wb", SAVE_DIR);
 
	if (file_pointer != nullptr) {
 
		for (CompanyID l_company = (CompanyID)0; l_company < MAX_COMPANIES; l_company++) {
 
			if (NetworkCompanyIsPassworded(l_company)) {
 
				const char* password = _network_company_states[l_company].password.c_str();
 
				fwrite(password, strlen(password), 1, file_pointer);
 
			}
 
			fwrite("\n", 1, 1, file_pointer);
 
		}
 
		fclose(file_pointer);
 
	}
 
}
 

	
 
void NetworkLoadPassword()
 
{
 
	std::string password_file_name = std::to_string(_settings_game.game_creation.generation_seed) + ".pwd";
 
	FILE* file_pointer = FioFOpenFile(password_file_name, "rb", SAVE_DIR);
 
	if (file_pointer != nullptr) {
 
		Debug(net, 0, "Loading company passwords from %s", password_file_name);
 
		for (CompanyID l_company = (CompanyID)0; l_company < MAX_COMPANIES; l_company++) {
 
			char password[NETWORK_PASSWORD_LENGTH] = { '\0' };
 
			fgets(password, NETWORK_PASSWORD_LENGTH, file_pointer);
 
			if (strlen(password) > 1) {
 
				_network_company_states[l_company].password = password;
 
				NetworkServerUpdateCompanyPassworded(l_company, !_network_company_states[l_company].password.empty());
 
				fseek(file_pointer, 1L, SEEK_CUR);
 
			}
 
		}
 
	}
 
	else {
 
		Debug(net, 0, "Password file %s not found", password_file_name);
 
	}
 
}
src/object_cmd.cpp
Show inline comments
 
@@ -202,13 +202,13 @@ static CommandCost ClearTile_Object(Tile
 
 * @param type the object type to build
 
 * @param view the view for the object
 
 * @return the cost of this operation or an error
 
 */
 
CommandCost CmdBuildObject(DoCommandFlag flags, TileIndex tile, ObjectType type, uint8 view)
 
{
 
	CommandCost cost(EXPENSES_CONSTRUCTION);
 
	CommandCost cost(EXPENSES_T_SCENERY_CON);
 

	
 
	if (type >= NUM_OBJECTS) return CMD_ERROR;
 
	const ObjectSpec *spec = ObjectSpec::Get(type);
 
	if (_game_mode == GM_NORMAL && !spec->IsAvailable() && !_generating_world) return CMD_ERROR;
 
	if ((_game_mode == GM_EDITOR || _generating_world) && !spec->WasEverAvailable()) return CMD_ERROR;
 

	
 
@@ -475,13 +475,13 @@ static CommandCost ClearTile_Object(Tile
 
	Object *o = Object::GetByTile(tile);
 
	TileArea ta = o->location;
 

	
 
	ObjectType type = o->type;
 
	const ObjectSpec *spec = ObjectSpec::Get(type);
 

	
 
	CommandCost cost(EXPENSES_CONSTRUCTION, spec->GetClearCost() * ta.w * ta.h / 5);
 
	CommandCost cost(EXPENSES_T_DEMOLITION, spec->GetClearCost() * ta.w * ta.h / 5);
 
	if (spec->flags & OBJECT_FLAG_CLEAR_INCOME) cost.MultiplyCost(-1); // They get an income!
 

	
 
	/* Towns can't remove any objects. */
 
	if (_current_company == OWNER_TOWN) return CMD_ERROR;
 

	
 
	/* Water can remove everything! */
 
@@ -522,13 +522,13 @@ static CommandCost ClearTile_Object(Tile
 
				c->location_of_HQ = INVALID_TILE; // reset HQ position
 
				SetWindowDirty(WC_COMPANY, c->index);
 
				CargoPacket::InvalidateAllFrom(ST_HEADQUARTERS, c->index);
 
			}
 

	
 
			/* cost of relocating company is 1% of company value */
 
			cost = CommandCost(EXPENSES_CONSTRUCTION, CalculateCompanyValue(c) / 100);
 
			cost = CommandCost(EXPENSES_T_SCENERY_CON, CalculateCompanyValue(c) / 100);
 
			break;
 
		}
 

	
 
		case OBJECT_STATUE:
 
			if (flags & DC_EXEC) {
 
				Town *town = o->town;
src/openttd.cpp
Show inline comments
 
@@ -251,13 +251,26 @@ static void WriteSavegameInfo(const char
 

	
 
	GamelogInfo(_load_check_data.gamelog_action, _load_check_data.gamelog_actions, &last_ottd_rev, &ever_modified, &removed_newgrfs);
 

	
 
	char buf[8192];
 
	char *p = buf;
 
	p += seprintf(p, lastof(buf), "Name:         %s\n", name);
 
	p += seprintf(p, lastof(buf), "Savegame ver: %d\n", _sl_version);
 
	const char *type = "";
 
	extern SaveLoadVersion _sl_is_faked_ext;
 
	extern bool _sl_is_ext_version;
 
	if (_sl_is_faked_ext != SL_MIN_VERSION) {
 
		type = " (fake extended)";
 
	} else if (_sl_is_ext_version) {
 
		type = " (extended)";
 
	}
 
	p += seprintf(p, lastof(buf), "Savegame ver: %d%s\n", _sl_version, type);
 
	for (size_t i = 0; i < XSLFI_SIZE; i++) {
 
		if (_sl_xv_feature_versions[i] > 0) {
 
			p += seprintf(p, lastof(buf), "    Feature: %s = %d\n", SlXvGetFeatureName((SlXvFeatureIndex) i), _sl_xv_feature_versions[i]);
 
		}
 
	}
 
	p += seprintf(p, lastof(buf), "NewGRF ver:   0x%08X\n", last_ottd_rev);
 
	p += seprintf(p, lastof(buf), "Modified:     %d\n", ever_modified);
 

	
 
	if (removed_newgrfs) {
 
		p += seprintf(p, lastof(buf), "NewGRFs have been removed\n");
 
	}
 
@@ -1044,12 +1057,15 @@ void SwitchToMode(SwitchMode new_mode)
 
					/* Reset engine pool to simplify changing engine NewGRFs in scenario editor. */
 
					EngineOverrideManager::ResetToCurrentNewGRFConfig();
 
				}
 
				OnStartGame(_network_dedicated);
 
				/* Decrease pause counter (was increased from opening load dialog) */
 
				Command<CMD_PAUSE>::Post(PM_PAUSED_SAVELOAD, false);
 
				if (_is_network_server && _settings_client.network.save_password) {
 
					NetworkLoadPassword();
 
				}
 
			}
 
			break;
 
		}
 

	
 
		case SM_RESTART_HEIGHTMAP: // Load a heightmap and start a new game from it with current settings
 
		case SM_START_HEIGHTMAP: // Load a heightmap and start a new game from it
src/order_type.h
Show inline comments
 
@@ -91,12 +91,13 @@ enum OrderStopLocation {
 
 * Reasons that could cause us to go to the depot.
 
 */
 
enum OrderDepotTypeFlags {
 
	ODTF_MANUAL          = 0,      ///< Manually initiated order.
 
	ODTFB_SERVICE        = 1 << 0, ///< This depot order is because of the servicing limit.
 
	ODTFB_PART_OF_ORDERS = 1 << 1, ///< This depot order is because of a regular order.
 
	ODTFB_BREAKDOWN      = 1 << 2, ///< This depot order is because of a breakdown.
 
};
 

	
 
/**
 
 * Actions that can be performed when the vehicle enters the depot.
 
 */
 
enum OrderDepotActionFlags {
src/rail_cmd.cpp
Show inline comments
 
@@ -412,13 +412,13 @@ static CommandCost CheckRailSlope(Slope 
 
	if ((f_new == FOUNDATION_INVALID) ||
 
			((f_new != FOUNDATION_NONE) && (!_settings_game.construction.build_on_slopes))) {
 
		return_cmd_error(STR_ERROR_LAND_SLOPED_IN_WRONG_DIRECTION);
 
	}
 

	
 
	Foundation f_old = GetRailFoundation(tileh, existing);
 
	return CommandCost(EXPENSES_CONSTRUCTION, f_new != f_old ? _price[PR_BUILD_FOUNDATION] : (Money)0);
 
	return CommandCost(EXPENSES_T_TRAIN_CON, f_new != f_old ? _price[PR_BUILD_FOUNDATION] : (Money)0);
 
}
 

	
 
/* Validate functions for rail building */
 
static inline bool ValParamTrackOrientation(Track track)
 
{
 
	return IsValidTrack(track);
 
@@ -432,13 +432,13 @@ static inline bool ValParamTrackOrientat
 
 * @param track track-orientation
 
 * @param auto_remove_signals false = error on signal in the way, true = auto remove signals when in the way
 
 * @return the cost of this operation or an error
 
 */
 
CommandCost CmdBuildSingleRail(DoCommandFlag flags, TileIndex tile, RailType railtype, Track track, bool auto_remove_signals)
 
{
 
	CommandCost cost(EXPENSES_CONSTRUCTION);
 
	CommandCost cost(EXPENSES_T_TRAIN_CON);
 

	
 
	if (!ValParamRailtype(railtype) || !ValParamTrackOrientation(track)) return CMD_ERROR;
 

	
 
	Slope tileh = GetTileSlope(tile);
 
	TrackBits trackbit = TrackToTrackBits(track);
 

	
 
@@ -615,13 +615,13 @@ CommandCost CmdBuildSingleRail(DoCommand
 
 * @param tile tile to remove track from
 
 * @param track rail orientation
 
 * @return the cost of this operation or an error
 
 */
 
CommandCost CmdRemoveSingleRail(DoCommandFlag flags, TileIndex tile, Track track)
 
{
 
	CommandCost cost(EXPENSES_CONSTRUCTION);
 
	CommandCost cost(EXPENSES_T_TRAIN_CON);
 
	bool crossing = false;
 

	
 
	if (!ValParamTrackOrientation(track)) return CMD_ERROR;
 
	TrackBits trackbit = TrackToTrackBits(track);
 

	
 
	/* Need to read tile owner now because it may change when the rail is removed
 
@@ -874,13 +874,13 @@ static CommandCost ValidateAutoDrag(Trac
 
 * @param auto_remove_signals false = build up to an obstacle, true = fail if an obstacle is found (used for AIs), only used for building
 
 * @param fail_on_obstacle false = error on signal in the way, true = auto remove signals when in the way, only used for building
 
 * @return the cost of this operation or an error
 
 */
 
static CommandCost CmdRailTrackHelper(DoCommandFlag flags, TileIndex tile, TileIndex end_tile, RailType railtype, Track track, bool remove, bool auto_remove_signals, bool fail_on_obstacle)
 
{
 
	CommandCost total_cost(EXPENSES_CONSTRUCTION);
 
	CommandCost total_cost(EXPENSES_T_TRAIN_CON);
 

	
 
	if ((!remove && !ValParamRailtype(railtype)) || !ValParamTrackOrientation(track)) return CMD_ERROR;
 
	if (end_tile >= MapSize()) return CMD_ERROR;
 

	
 
	Trackdir trackdir = TrackToTrackdir(track);
 

	
 
@@ -1058,23 +1058,23 @@ CommandCost CmdBuildSingleSignal(DoComma
 
	/* you can not convert a signal if no signal is on track */
 
	if (convert_signal && !HasSignalOnTrack(tile, track)) return_cmd_error(STR_ERROR_THERE_ARE_NO_SIGNALS);
 

	
 
	CommandCost cost;
 
	if (!HasSignalOnTrack(tile, track)) {
 
		/* build new signals */
 
		cost = CommandCost(EXPENSES_CONSTRUCTION, _price[PR_BUILD_SIGNALS]);
 
		cost = CommandCost(EXPENSES_T_TRAIN_CON, _price[PR_BUILD_SIGNALS]);
 
	} else {
 
		if (signals_copy != 0 && sigvar != GetSignalVariant(tile, track)) {
 
			/* convert signals <-> semaphores */
 
			cost = CommandCost(EXPENSES_CONSTRUCTION, _price[PR_BUILD_SIGNALS] + _price[PR_CLEAR_SIGNALS]);
 
			cost = CommandCost(EXPENSES_T_TRAIN_CON, _price[PR_BUILD_SIGNALS] + _price[PR_CLEAR_SIGNALS]);
 

	
 
		} else if (convert_signal) {
 
			/* convert button pressed */
 
			if (ctrl_pressed || GetSignalVariant(tile, track) != sigvar) {
 
				/* convert electric <-> semaphore */
 
				cost = CommandCost(EXPENSES_CONSTRUCTION, _price[PR_BUILD_SIGNALS] + _price[PR_CLEAR_SIGNALS]);
 
				cost = CommandCost(EXPENSES_T_TRAIN_CON, _price[PR_BUILD_SIGNALS] + _price[PR_CLEAR_SIGNALS]);
 
			} else {
 
				/* it is free to change signal type: normal-pre-exit-combo */
 
				cost = CommandCost();
 
			}
 

	
 
		} else {
 
@@ -1236,13 +1236,13 @@ static bool AdvanceSignalAutoFill(TileIn
 
 * @param minimise_gaps false = keep fixed distance, true = minimise gaps between signals
 
 * @param signal_density user defined signals_density
 
 * @return the cost of this operation or an error
 
 */
 
static CommandCost CmdSignalTrackHelper(DoCommandFlag flags, TileIndex tile, TileIndex end_tile, Track track, SignalType sigtype, SignalVariant sigvar, bool mode, bool remove, bool autofill, bool minimise_gaps, int signal_density)
 
{
 
	CommandCost total_cost(EXPENSES_CONSTRUCTION);
 
	CommandCost total_cost(EXPENSES_T_TRAIN_CON);
 

	
 
	if (end_tile >= MapSize() || !ValParamTrackOrientation(track)) return CMD_ERROR;
 
	if (signal_density == 0 || signal_density > 20) return CMD_ERROR;
 
	if (!remove && (sigtype > SIGTYPE_LAST || sigvar > SIG_SEMAPHORE)) return CMD_ERROR;
 

	
 
	if (!IsPlainRailTile(tile)) return_cmd_error(STR_ERROR_THERE_IS_NO_RAILROAD_TRACK);
 
@@ -1489,13 +1489,13 @@ CommandCost CmdRemoveSingleSignal(DoComm
 
		YapfNotifyTrackLayoutChange(tile, track);
 
		if (v != nullptr) TryPathReserve(v, false);
 

	
 
		MarkTileDirtyByTile(tile);
 
	}
 

	
 
	return CommandCost(EXPENSES_CONSTRUCTION, _price[PR_CLEAR_SIGNALS]);
 
	return CommandCost(EXPENSES_T_TRAIN_CON, _price[PR_CLEAR_SIGNALS]);
 
}
 

	
 
/**
 
 * Remove signals on a stretch of track.
 
 * Stub for the unified signal builder/remover
 
 * @param flags operation to perform
 
@@ -1538,13 +1538,13 @@ CommandCost CmdConvertRail(DoCommandFlag
 

	
 
	if (!ValParamRailtype(totype)) return CMD_ERROR;
 
	if (area_start >= MapSize()) return CMD_ERROR;
 

	
 
	TrainList affected_trains;
 

	
 
	CommandCost cost(EXPENSES_CONSTRUCTION);
 
	CommandCost cost(EXPENSES_T_TRAIN_CON);
 
	CommandCost error = CommandCost(STR_ERROR_NO_SUITABLE_RAILROAD_TRACK); // by default, there is no track to convert.
 
	bool found_convertible_track = false; // whether we actually did convert some track (see bug #7633)
 

	
 
	TileIterator *iter = diagonal ? (TileIterator *)new DiagonalTileIterator(area_start, area_end) : new OrthogonalTileIterator(area_start, area_end);
 
	for (; (tile = *iter) != INVALID_TILE; ++(*iter)) {
 
		TileType tt = GetTileType(tile);
 
@@ -1774,18 +1774,18 @@ static CommandCost RemoveTrainDepot(Tile
 
		DoClearSquare(tile);
 
		AddSideToSignalBuffer(tile, dir, owner);
 
		YapfNotifyTrackLayoutChange(tile, DiagDirToDiagTrack(dir));
 
		if (v != nullptr) TryPathReserve(v, true);
 
	}
 

	
 
	return CommandCost(EXPENSES_CONSTRUCTION, _price[PR_CLEAR_DEPOT_TRAIN]);
 
	return CommandCost(EXPENSES_T_TRAIN_CON, _price[PR_CLEAR_DEPOT_TRAIN]);
 
}
 

	
 
static CommandCost ClearTile_Track(TileIndex tile, DoCommandFlag flags)
 
{
 
	CommandCost cost(EXPENSES_CONSTRUCTION);
 
	CommandCost cost(EXPENSES_T_DEMOLITION);
 

	
 
	if (flags & DC_AUTO) {
 
		if (!IsTileOwner(tile, _current_company)) {
 
			return_cmd_error(STR_ERROR_AREA_IS_OWNED_BY_ANOTHER);
 
		}
 

	
 
@@ -2994,21 +2994,21 @@ static CommandCost TestAutoslopeOnRailTi
 
		case TRACK_BIT_RIGHT: track_corner = CORNER_E; break;
 
		case TRACK_BIT_UPPER: track_corner = CORNER_N; break;
 

	
 
		/* Surface slope must not be changed */
 
		default:
 
			if (z_old != z_new || tileh_old != tileh_new) return_cmd_error(STR_ERROR_MUST_REMOVE_RAILROAD_TRACK);
 
			return CommandCost(EXPENSES_CONSTRUCTION, _price[PR_BUILD_FOUNDATION]);
 
			return CommandCost(EXPENSES_T_TRAIN_CON, _price[PR_BUILD_FOUNDATION]);
 
	}
 

	
 
	/* The height of the track_corner must not be changed. The rest ensures GetRailFoundation() already. */
 
	z_old += GetSlopeZInCorner(RemoveHalftileSlope(tileh_old), track_corner);
 
	z_new += GetSlopeZInCorner(RemoveHalftileSlope(tileh_new), track_corner);
 
	if (z_old != z_new) return_cmd_error(STR_ERROR_MUST_REMOVE_RAILROAD_TRACK);
 

	
 
	CommandCost cost = CommandCost(EXPENSES_CONSTRUCTION, _price[PR_BUILD_FOUNDATION]);
 
	CommandCost cost = CommandCost(EXPENSES_T_TRAIN_CON, _price[PR_BUILD_FOUNDATION]);
 
	/* Make the ground dirty, if surface slope has changed */
 
	if (tileh_old != tileh_new) {
 
		/* If there is flat water on the lower halftile add the cost for clearing it */
 
		if (GetRailGroundType(tile) == RAIL_GROUND_WATER && IsSlopeWithOneCornerRaised(tileh_old)) cost.AddCost(_price[PR_CLEAR_WATER]);
 
		if ((flags & DC_EXEC) != 0) SetRailGroundType(tile, RAIL_GROUND_BARREN);
 
	}
 
@@ -3060,16 +3060,16 @@ static CommandCost TerraformTile_Track(T
 
		}
 

	
 
		/* Make the ground dirty */
 
		if ((flags & DC_EXEC) != 0) SetRailGroundType(tile, RAIL_GROUND_BARREN);
 

	
 
		/* allow terraforming */
 
		return CommandCost(EXPENSES_CONSTRUCTION, was_water ? _price[PR_CLEAR_WATER] : (Money)0);
 
		return CommandCost(EXPENSES_T_TRAIN_CON, was_water ? _price[PR_CLEAR_WATER] : (Money)0);
 
	} else if (_settings_game.construction.build_on_slopes && AutoslopeEnabled() &&
 
			AutoslopeCheckForEntranceEdge(tile, z_new, tileh_new, GetRailDepotDirection(tile))) {
 
		return CommandCost(EXPENSES_CONSTRUCTION, _price[PR_BUILD_FOUNDATION]);
 
		return CommandCost(EXPENSES_T_TRAIN_CON, _price[PR_BUILD_FOUNDATION]);
 
	}
 
	return Command<CMD_LANDSCAPE_CLEAR>::Do(flags, tile);
 
}
 

	
 

	
 
extern const TileTypeProcs _tile_type_rail_procs = {
src/rev.cpp.in
Show inline comments
 
@@ -35,13 +35,21 @@ bool IsReleasedVersion()
 
 *
 
 * <modified> shows a "M", if the binary is made from modified source code.
 
 */
 
const char _openttd_revision[] = "${REV_VERSION}";
 

	
 
/**
 
 * The text version of OpenTTD's build date.
 
 * The text version of OpenTTD's build date in short format (without the time).
 
 * Updating the build date in this file is the safest as it generally gets
 
 * updated for each revision in contrary to most other files that only see
 
 * updates when they are actually changed themselves.
 
 */
 
const char _openttd_build_date_short[] = __DATE__;
 

	
 
/**
 
 * The text version of OpenTTD's build date and time.
 
 * Updating the build date in this file is the safest as it generally gets
 
 * updated for each revision in contrary to most other files that only see
 
 * updates when they are actually changed themselves.
 
 */
 
const char _openttd_build_date[] = __DATE__ " " __TIME__;
 

	
 
@@ -59,12 +67,14 @@ const char _openttd_revision_year[] = "$
 
 * Let us know if current build was modified. This detection
 
 * works even in the case when revision string is overridden by
 
 * --revision argument.
 
 * Value 0 means no modification, 1 is for unknown state
 
 * (compiling from sources without any version control software)
 
 * and 2 is for modified revision.
 
 *
 
 * Ginever patchpack uses value 3 for hg unmodified and 4 for hg modified
 
 */
 
const byte _openttd_revision_modified = ${REV_MODIFIED};
 

	
 
/**
 
 * Indicate whether this is a tagged version.
 
 * If this is non-0, then _openttd_revision is the name of the tag,
src/rev.h
Show inline comments
 
@@ -9,12 +9,13 @@
 

	
 
#ifndef REV_H
 
#define REV_H
 

	
 
extern const char _openttd_revision[];
 
extern const char _openttd_build_date[];
 
extern const char _openttd_build_date_short[];
 
extern const char _openttd_revision_hash[];
 
extern const char _openttd_revision_year[];
 
extern const byte _openttd_revision_modified;
 
extern const byte _openttd_revision_tagged;
 
extern const char _openttd_content_version[];
 
extern const uint32 _openttd_newgrf_version;
src/road_cmd.cpp
Show inline comments
 
@@ -367,13 +367,13 @@ static CommandCost RemoveRoad(TileIndex 
 
	if (ret.Failed()) return ret;
 

	
 
	if (!IsTileType(tile, MP_ROAD)) {
 
		/* If it's the last roadtype, just clear the whole tile */
 
		if (GetRoadType(tile, OtherRoadTramType(rtt)) == INVALID_ROADTYPE) return Command<CMD_LANDSCAPE_CLEAR>::Do(flags, tile);
 

	
 
		CommandCost cost(EXPENSES_CONSTRUCTION);
 
		CommandCost cost(EXPENSES_T_ROAD_CON);
 
		if (IsTileType(tile, MP_TUNNELBRIDGE)) {
 
			/* Removing any roadbit in the bridge axis removes the roadtype (that's the behaviour remove-long-roads needs) */
 
			if ((AxisToRoadBits(DiagDirToAxis(GetTunnelBridgeDirection(tile))) & pieces) == ROAD_NONE) return_cmd_error((rtt == RTT_TRAM) ? STR_ERROR_THERE_IS_NO_TRAMWAY : STR_ERROR_THERE_IS_NO_ROAD);
 

	
 
			TileIndex other_end = GetOtherTunnelBridgeEnd(tile);
 
			/* Pay for *every* tile of the bridge or tunnel */
 
@@ -486,13 +486,13 @@ static CommandCost RemoveRoad(TileIndex 
 
					if (rtt == RTT_ROAD) SetDisallowedRoadDirections(tile, DRD_NONE);
 
					SetRoadBits(tile, present, rtt);
 
					MarkTileDirtyByTile(tile);
 
				}
 
			}
 

	
 
			CommandCost cost(EXPENSES_CONSTRUCTION, CountBits(pieces) * RoadClearCost(existing_rt));
 
			CommandCost cost(EXPENSES_T_ROAD_CON, CountBits(pieces) * RoadClearCost(existing_rt));
 
			/* If we build a foundation we have to pay for it. */
 
			if (f == FOUNDATION_NONE && GetRoadFoundation(tileh, present) != FOUNDATION_NONE) cost.AddCost(_price[PR_BUILD_FOUNDATION]);
 

	
 
			return cost;
 
		}
 

	
 
@@ -522,13 +522,13 @@ static CommandCost RemoveRoad(TileIndex 
 
				} else {
 
					SetRoadType(tile, rtt, INVALID_ROADTYPE);
 
				}
 
				MarkTileDirtyByTile(tile);
 
				YapfNotifyTrackLayoutChange(tile, railtrack);
 
			}
 
			return CommandCost(EXPENSES_CONSTRUCTION, RoadClearCost(existing_rt) * 2);
 
			return CommandCost(EXPENSES_T_ROAD_CON, RoadClearCost(existing_rt) * 2);
 
		}
 

	
 
		default:
 
		case ROAD_TILE_DEPOT:
 
			return CMD_ERROR;
 
	}
 
@@ -566,13 +566,13 @@ static CommandCost CheckRoadSlope(Slope 
 
	RoadBits type_bits = existing | *pieces;
 

	
 
	/* Roads on slopes */
 
	if (_settings_game.construction.build_on_slopes && (_invalid_tileh_slopes_road[0][tileh] & (other | type_bits)) == ROAD_NONE) {
 

	
 
		/* If we add leveling we've got to pay for it */
 
		if ((other | existing) == ROAD_NONE) return CommandCost(EXPENSES_CONSTRUCTION, _price[PR_BUILD_FOUNDATION]);
 
		if ((other | existing) == ROAD_NONE) return CommandCost(EXPENSES_T_ROAD_CON, _price[PR_BUILD_FOUNDATION]);
 

	
 
		return CommandCost();
 
	}
 

	
 
	/* Autocomplete uphill roads */
 
	*pieces |= MirrorRoadBits(*pieces);
 
@@ -586,18 +586,18 @@ static CommandCost CheckRoadSlope(Slope 
 
		if (IsSlopeWithOneCornerRaised(tileh)) {
 

	
 
			/* Prevent build on slopes if it isn't allowed */
 
			if (_settings_game.construction.build_on_slopes) {
 

	
 
				/* If we add foundation we've got to pay for it */
 
				if ((other | existing) == ROAD_NONE) return CommandCost(EXPENSES_CONSTRUCTION, _price[PR_BUILD_FOUNDATION]);
 
				if ((other | existing) == ROAD_NONE) return CommandCost(EXPENSES_T_ROAD_CON, _price[PR_BUILD_FOUNDATION]);
 

	
 
				return CommandCost();
 
			}
 
		} else {
 
			if (HasExactlyOneBit(existing) && GetRoadFoundation(tileh, existing) == FOUNDATION_NONE) return CommandCost(EXPENSES_CONSTRUCTION, _price[PR_BUILD_FOUNDATION]);
 
			if (HasExactlyOneBit(existing) && GetRoadFoundation(tileh, existing) == FOUNDATION_NONE) return CommandCost(EXPENSES_T_ROAD_CON, _price[PR_BUILD_FOUNDATION]);
 
			return CommandCost();
 
		}
 
	}
 
	return CMD_ERROR;
 
}
 

	
 
@@ -611,13 +611,13 @@ static CommandCost CheckRoadSlope(Slope 
 
 * @param town_id the town that is building the road (0 if not applicable)
 
 * @return the cost of this operation or an error
 
 */
 
CommandCost CmdBuildRoad(DoCommandFlag flags, TileIndex tile, RoadBits pieces, RoadType rt, DisallowedRoadDirections toggle_drd, TownID town_id)
 
{
 
	CompanyID company = _current_company;
 
	CommandCost cost(EXPENSES_CONSTRUCTION);
 
	CommandCost cost(EXPENSES_T_ROAD_CON);
 

	
 
	RoadBits existing = ROAD_NONE;
 
	RoadBits other_bits = ROAD_NONE;
 

	
 
	/* Road pieces are max 4 bitset values (NE, NW, SE, SW) and town can only be non-zero
 
	 * if a non-company is building the road */
 
@@ -779,13 +779,13 @@ CommandCost CmdBuildRoad(DoCommandFlag f
 
				bool reserved = HasBit(GetRailReservationTrackBits(tile), railtrack);
 
				MakeRoadCrossing(tile, company, company, GetTileOwner(tile), roaddir, GetRailType(tile), rtt == RTT_ROAD ? rt : INVALID_ROADTYPE, (rtt == RTT_TRAM) ? rt : INVALID_ROADTYPE, town_id);
 
				SetCrossingReservation(tile, reserved);
 
				UpdateLevelCrossing(tile, false);
 
				MarkTileDirtyByTile(tile);
 
			}
 
			return CommandCost(EXPENSES_CONSTRUCTION, 2 * RoadBuildCost(rt));
 
			return CommandCost(EXPENSES_T_ROAD_CON, 2 * RoadBuildCost(rt));
 
		}
 

	
 
		case MP_STATION: {
 
			if ((GetAnyRoadBits(tile, rtt) & pieces) == pieces) return_cmd_error(STR_ERROR_ALREADY_BUILT);
 
			if (!IsDriveThroughStopTile(tile)) goto do_clear;
 

	
 
@@ -996,13 +996,13 @@ CommandCost CmdBuildLongRoad(DoCommandFl
 

	
 
	/* On the X-axis, we have to swap the initial bits, so they
 
	 * will be interpreted correctly in the GTTS. Furthermore
 
	 * when you just 'click' on one tile to build them. */
 
	if ((drd == DRD_NORTHBOUND || drd == DRD_SOUTHBOUND) && (axis == AXIS_Y) == (start_tile == end_tile && start_half == end_half)) drd ^= DRD_BOTH;
 

	
 
	CommandCost cost(EXPENSES_CONSTRUCTION);
 
	CommandCost cost(EXPENSES_T_ROAD_CON);
 
	CommandCost last_error = CMD_ERROR;
 
	TileIndex tile = start_tile;
 
	bool had_bridge = false;
 
	bool had_tunnel = false;
 
	bool had_success = false;
 

	
 
@@ -1069,13 +1069,13 @@ CommandCost CmdBuildLongRoad(DoCommandFl
 
 * @param start_half start tile starts in the 2nd half of tile
 
 * @param end_half end tile starts in the 2nd half of tile (p2 & 2)
 
 * @return the cost of this operation or an error
 
 */
 
std::tuple<CommandCost, Money> CmdRemoveLongRoad(DoCommandFlag flags, TileIndex start_tile, TileIndex end_tile, RoadType rt, Axis axis, bool start_half, bool end_half)
 
{
 
	CommandCost cost(EXPENSES_CONSTRUCTION);
 
	CommandCost cost(EXPENSES_T_ROAD_CON);
 

	
 
	if (end_tile >= MapSize()) return { CMD_ERROR, 0 };
 
	if (!ValParamRoadType(rt) || !IsValidAxis(axis)) return { CMD_ERROR, 0 };
 

	
 
	/* Only drag in X or Y direction dictated by the direction variable */
 
	if (axis == AXIS_X && TileY(start_tile) != TileY(end_tile)) return { CMD_ERROR, 0 }; // x-axis
 
@@ -1201,24 +1201,24 @@ static CommandCost RemoveRoadDepot(TileI
 
		}
 

	
 
		delete Depot::GetByTile(tile);
 
		DoClearSquare(tile);
 
	}
 

	
 
	return CommandCost(EXPENSES_CONSTRUCTION, _price[PR_CLEAR_DEPOT_ROAD]);
 
	return CommandCost(EXPENSES_T_ROAD_CON, _price[PR_CLEAR_DEPOT_ROAD]);
 
}
 

	
 
static CommandCost ClearTile_Road(TileIndex tile, DoCommandFlag flags)
 
{
 
	switch (GetRoadTileType(tile)) {
 
		case ROAD_TILE_NORMAL: {
 
			RoadBits b = GetAllRoadBits(tile);
 

	
 
			/* Clear the road if only one piece is on the tile OR we are not using the DC_AUTO flag */
 
			if ((HasExactlyOneBit(b) && GetRoadBits(tile, RTT_TRAM) == ROAD_NONE) || !(flags & DC_AUTO)) {
 
				CommandCost ret(EXPENSES_CONSTRUCTION);
 
				CommandCost ret(EXPENSES_T_DEMOLITION);
 
				for (RoadTramType rtt : _roadtramtypes) {
 
					if (!MayHaveRoad(tile) || GetRoadType(tile, rtt) == INVALID_ROADTYPE) continue;
 

	
 
					CommandCost tmp_ret = RemoveRoad(tile, flags, GetRoadBits(tile, rtt), rtt, true);
 
					if (tmp_ret.Failed()) return tmp_ret;
 
					ret.AddCost(tmp_ret);
 
@@ -1226,13 +1226,13 @@ static CommandCost ClearTile_Road(TileIn
 
				return ret;
 
			}
 
			return_cmd_error(STR_ERROR_MUST_REMOVE_ROAD_FIRST);
 
		}
 

	
 
		case ROAD_TILE_CROSSING: {
 
			CommandCost ret(EXPENSES_CONSTRUCTION);
 
			CommandCost ret(EXPENSES_T_DEMOLITION);
 

	
 
			if (flags & DC_AUTO) return_cmd_error(STR_ERROR_MUST_REMOVE_ROAD_FIRST);
 

	
 
			/* Must iterate over the roadtypes in a reverse manner because
 
			 * tram tracks must be removed before the road bits. */
 
			for (RoadTramType rtt : { RTT_TRAM, RTT_ROAD }) {
 
@@ -1647,13 +1647,12 @@ static void DrawTile_Road(TileInfo *ti)
 
			break;
 

	
 
		case ROAD_TILE_CROSSING: {
 
			if (ti->tileh != SLOPE_FLAT) DrawFoundation(ti, FOUNDATION_LEVELED);
 

	
 
			Axis axis = GetCrossingRailAxis(ti->tile);
 

	
 
			const RailtypeInfo *rti = GetRailTypeInfo(GetRailType(ti->tile));
 

	
 
			RoadType road_rt = GetRoadTypeRoad(ti->tile);
 
			RoadType tram_rt = GetRoadTypeTram(ti->tile);
 
			const RoadTypeInfo *road_rti = road_rt == INVALID_ROADTYPE ? nullptr : GetRoadTypeInfo(road_rt);
 
			const RoadTypeInfo *tram_rti = tram_rt == INVALID_ROADTYPE ? nullptr : GetRoadTypeInfo(tram_rt);
 
@@ -2223,17 +2222,17 @@ static void ChangeTileOwner_Road(TileInd
 

	
 
static CommandCost TerraformTile_Road(TileIndex tile, DoCommandFlag flags, int z_new, Slope tileh_new)
 
{
 
	if (_settings_game.construction.build_on_slopes && AutoslopeEnabled()) {
 
		switch (GetRoadTileType(tile)) {
 
			case ROAD_TILE_CROSSING:
 
				if (!IsSteepSlope(tileh_new) && (GetTileMaxZ(tile) == z_new + GetSlopeMaxZ(tileh_new)) && HasBit(VALID_LEVEL_CROSSING_SLOPES, tileh_new)) return CommandCost(EXPENSES_CONSTRUCTION, _price[PR_BUILD_FOUNDATION]);
 
				if (!IsSteepSlope(tileh_new) && (GetTileMaxZ(tile) == z_new + GetSlopeMaxZ(tileh_new)) && HasBit(VALID_LEVEL_CROSSING_SLOPES, tileh_new)) return CommandCost(EXPENSES_T_ROAD_CON, _price[PR_BUILD_FOUNDATION]);
 
				break;
 

	
 
			case ROAD_TILE_DEPOT:
 
				if (AutoslopeCheckForEntranceEdge(tile, z_new, tileh_new, GetRoadDepotDirection(tile))) return CommandCost(EXPENSES_CONSTRUCTION, _price[PR_BUILD_FOUNDATION]);
 
				if (AutoslopeCheckForEntranceEdge(tile, z_new, tileh_new, GetRoadDepotDirection(tile))) return CommandCost(EXPENSES_T_ROAD_CON, _price[PR_BUILD_FOUNDATION]);
 
				break;
 

	
 
			case ROAD_TILE_NORMAL: {
 
				RoadBits bits = GetAllRoadBits(tile);
 
				RoadBits bits_copy = bits;
 
				/* Check if the slope-road_bits combination is valid at all, i.e. it is safe to call GetRoadFoundation(). */
 
@@ -2245,13 +2244,13 @@ static CommandCost TerraformTile_Road(Ti
 

	
 
						/* Get the slope on top of the foundation */
 
						z_old += ApplyFoundationToSlope(GetRoadFoundation(tileh_old, bits), &tileh_old);
 
						z_new += ApplyFoundationToSlope(GetRoadFoundation(tileh_new, bits), &tileh_new);
 

	
 
						/* The surface slope must not be changed */
 
						if ((z_old == z_new) && (tileh_old == tileh_new)) return CommandCost(EXPENSES_CONSTRUCTION, _price[PR_BUILD_FOUNDATION]);
 
						if ((z_old == z_new) && (tileh_old == tileh_new)) return CommandCost(EXPENSES_T_ROAD_CON, _price[PR_BUILD_FOUNDATION]);
 
					}
 
				}
 
				break;
 
			}
 

	
 
			default: NOT_REACHED();
src/roadveh.h
Show inline comments
 
@@ -181,12 +181,14 @@ protected: // These functions should not
 
		uint16 weight = (CargoSpec::Get(this->cargo_type)->weight * this->cargo.StoredCount()) / 16;
 

	
 
		/* Vehicle weight is not added for articulated parts. */
 
		if (!this->IsArticulatedPart()) {
 
			/* Road vehicle weight is in units of 1/4 t. */
 
			weight += GetVehicleProperty(this, PROP_ROADVEH_WEIGHT, RoadVehInfo(this->engine_type)->weight) / 4;
 
			//DIRTY HACK
 
			if ( !weight ) weight = 1; //at least 1 for realistic accelerate
 
		}
 

	
 
		return weight;
 
	}
 

	
 
	/**
src/roadveh_cmd.cpp
Show inline comments
 
@@ -291,12 +291,13 @@ CommandCost CmdBuildRoadVehicle(DoComman
 
		v->last_loading_station = INVALID_STATION;
 
		v->engine_type = e->index;
 
		v->gcache.first_engine = INVALID_ENGINE; // needs to be set before first callback
 

	
 
		v->reliability = e->reliability;
 
		v->reliability_spd_dec = e->reliability_spd_dec;
 
		v->breakdown_chance = 128;
 
		v->max_age = e->GetLifeLengthInDays();
 

	
 
		v->SetServiceInterval(Company::Get(v->owner)->settings.vehicle.servint_roadveh);
 

	
 
		v->date_of_last_service = _date;
 
		v->build_year = _cur_year;
 
@@ -372,13 +373,12 @@ CommandCost CmdTurnRoadVeh(DoCommandFlag
 

	
 
	CommandCost ret = CheckOwnership(v->owner);
 
	if (ret.Failed()) return ret;
 

	
 
	if ((v->vehstatus & VS_STOPPED) ||
 
			(v->vehstatus & VS_CRASHED) ||
 
			v->breakdown_ctr != 0 ||
 
			v->overtaking != 0 ||
 
			v->state == RVSB_WORMHOLE ||
 
			v->IsInDepot() ||
 
			v->current_order.IsType(OT_LOADING)) {
 
		return CMD_ERROR;
 
	}
 
@@ -809,12 +809,15 @@ static void RoadVehCheckOvertake(RoadVeh
 
	/* Don't overtake in stations */
 
	if (IsTileType(v->tile, MP_STATION) || IsTileType(u->tile, MP_STATION)) return;
 

	
 
	/* For now, articulated road vehicles can't overtake anything. */
 
	if (v->HasArticulatedPart()) return;
 

	
 
	/* Don't overtake if the vehicle is broken or about to break down */
 
	if (v->breakdown_ctr != 0) return;
 

	
 
	/* Vehicles are not driving in same direction || direction is not a diagonal direction */
 
	if (v->direction != u->direction || !(v->direction & 1)) return;
 

	
 
	/* Check if vehicle is in a road stop, depot, tunnel or bridge or not on a straight road */
 
	if (v->state >= RVSB_IN_ROAD_STOP || !IsStraightRoadTrackdir((Trackdir)(v->state & RVSB_TRACKDIR_MASK))) return;
 

	
 
@@ -1570,14 +1573,13 @@ static bool RoadVehController(RoadVehicl
 
	/* handle crashed */
 
	if (v->vehstatus & VS_CRASHED || RoadVehCheckTrainCrash(v)) {
 
		return RoadVehIsCrashed(v);
 
	}
 

	
 
	/* road vehicle has broken down? */
 
	if (v->HandleBreakdown()) return true;
 
	if (v->vehstatus & VS_STOPPED) {
 
	if (v->HandleBreakdown() || v->vehstatus & VS_STOPPED) {
 
		v->SetLastSpeed();
 
		return true;
 
	}
 

	
 
	ProcessOrders(v);
 
	v->HandleLoading();
 
@@ -1631,12 +1633,13 @@ static bool RoadVehController(RoadVehicl
 
Money RoadVehicle::GetRunningCost() const
 
{
 
	const Engine *e = this->GetEngine();
 
	if (e->u.road.running_cost_class == INVALID_PRICE) return 0;
 

	
 
	uint cost_factor = GetVehicleProperty(this, PROP_ROADVEH_RUNNING_COST_FACTOR, e->u.road.running_cost);
 
	cost_factor *= _settings_game.economy.running_cost_multiplier_road;
 
	if (cost_factor == 0) return 0;
 

	
 
	return GetPrice(e->u.road.running_cost_class, cost_factor, e->GetGRF());
 
}
 

	
 
bool RoadVehicle::Tick()
src/saveload/CMakeLists.txt
Show inline comments
 
@@ -10,12 +10,14 @@ add_files(
 
    cargopacket_sl.cpp
 
    cheat_sl.cpp
 
    company_sl.cpp
 
    depot_sl.cpp
 
    economy_sl.cpp
 
    engine_sl.cpp
 
    extended_ver_sl.cpp
 
    extended_ver_sl.h
 
    game_sl.cpp
 
    gamelog_sl.cpp
 
    goal_sl.cpp
 
    group_sl.cpp
 
    industry_sl.cpp
 
    labelmaps_sl.cpp
 
@@ -28,12 +30,13 @@ add_files(
 
    oldloader.cpp
 
    oldloader.h
 
    oldloader_sl.cpp
 
    order_sl.cpp
 
    saveload.cpp
 
    saveload.h
 
    saveload_buffer.h
 
    saveload_filter.h
 
    saveload_internal.h
 
    settings_sl.cpp
 
    signs_sl.cpp
 
    station_sl.cpp
 
    storage_sl.cpp
src/saveload/afterload.cpp
Show inline comments
 
@@ -56,12 +56,13 @@
 
#include "../disaster_vehicle.h"
 
#include "../ship.h"
 
#include "../water.h"
 

	
 

	
 
#include "saveload_internal.h"
 
#include "extended_ver_sl.h"
 

	
 
#include <signal.h>
 

	
 
#include "../safeguards.h"
 

	
 
extern Company *DoStartupNewCompany(bool is_ai, CompanyID company = INVALID_COMPANY);
 
@@ -747,19 +748,25 @@ bool AfterLoadGame()
 
	if (IsSavegameVersionBefore(SLV_143))  _settings_game.economy.allow_town_level_crossings = true;
 
	if (IsSavegameVersionBefore(SLV_159)) {
 
		_settings_game.vehicle.max_train_length = 50;
 
		_settings_game.construction.max_bridge_length = 64;
 
		_settings_game.construction.max_tunnel_length = 64;
 
	}
 
	if (SlXvIsFeatureMissing(XSLFI_DAYLENGTH)) {
 
		_settings_game.economy.daylength_multiplier = 1;
 
	}
 
	if (IsSavegameVersionBefore(SLV_166))  _settings_game.economy.infrastructure_maintenance = false;
 
	if (IsSavegameVersionBefore(SLV_183)) {
 
		_settings_game.linkgraph.distribution_pax = DT_MANUAL;
 
		_settings_game.linkgraph.distribution_mail = DT_MANUAL;
 
		_settings_game.linkgraph.distribution_armoured = DT_MANUAL;
 
		_settings_game.linkgraph.distribution_default = DT_MANUAL;
 
	}
 
	if (SlXvIsFeatureMissing(XSLFI_TOWN_IMPROVEMENTS)) {
 
		_settings_game.economy.town_growth_multiplier = _settings_game.economy.daylength_multiplier;
 
	}
 

	
 
	if (IsSavegameVersionBefore(SLV_ENDING_YEAR)) {
 
		_settings_game.game_creation.ending_year = DEF_END_YEAR;
 
	}
 

	
 
	/* Load the sprites */
 
@@ -2416,12 +2423,29 @@ bool AfterLoadGame()
 
	}
 

	
 
	if (IsSavegameVersionBefore(SLV_142)) {
 
		for (Depot *d : Depot::Iterate()) d->build_date = _date;
 
	}
 

	
 
	if (!SlXvIsFeaturePresent(XSLFI_EXTRA_EXPENSES, 2, 2))
 
	{
 
		// expenses size isn't currently 23. fix the array which now contains broken data
 

	
 
		bool first_extension_present = SlXvIsFeaturePresent(XSLFI_EXTRA_EXPENSES, 1, 1);
 
		int old_expenses_size = first_extension_present ? 22 : 13;
 
		int new_expenses_size = 23;
 
		int second_expenses_offset = old_expenses_size - (new_expenses_size - old_expenses_size);
 

	
 
		for (Company* c : Company::Iterate()) {
 
			std::copy_backward(&c->yearly_expenses[1][second_expenses_offset], &c->yearly_expenses[1][second_expenses_offset] + old_expenses_size, &c->yearly_expenses[2][0] + old_expenses_size);
 
			std::copy_backward(&c->yearly_expenses[0][old_expenses_size], &c->yearly_expenses[0][old_expenses_size] + old_expenses_size, &c->yearly_expenses[1][0] + old_expenses_size);
 
			std::fill_n(&c->yearly_expenses[0][old_expenses_size], new_expenses_size - old_expenses_size, 0);
 
			std::fill_n(&c->yearly_expenses[1][old_expenses_size], new_expenses_size - old_expenses_size, 0);
 
		}
 
	}
 

	
 
	/* In old versions it was possible to remove an airport while a plane was
 
	 * taking off or landing. This gives all kind of problems when building
 
	 * another airport in the same station so we don't allow that anymore.
 
	 * For old savegames with such aircraft we just throw them in the air and
 
	 * treat the aircraft like they were flying already. */
 
	if (IsSavegameVersionBefore(SLV_146)) {
 
@@ -2819,12 +2843,43 @@ bool AfterLoadGame()
 

	
 
	/* When any NewGRF has been changed the availability of some vehicles might
 
	 * have been changed too. e->company_avail must be set to 0 in that case
 
	 * which is done by StartupEngines(). */
 
	if (gcf_res != GLC_ALL_GOOD) StartupEngines();
 

	
 
	/* Set some breakdown-related variables to the correct values. */
 
	if (SlXvIsFeatureMissing(XSLFI_IMPROVED_BREAKDOWNS)) {
 

	
 
		for (Vehicle *v : Vehicle::Iterate()) {
 
			switch(v->type) {
 
				case VEH_TRAIN: {
 
					if (Train::From(v)->IsFrontEngine()) {
 
						if (v->breakdown_ctr == 1) SetBit(Train::From(v)->flags, VRF_BREAKDOWN_STOPPED);
 
					} else if (Train::From(v)->IsEngine() || Train::From(v)->IsMultiheaded()) {
 
						/** Non-front engines could have a reliability of 0.
 
						 * Set it to the reliability of the front engine or the maximum, whichever is lower. */
 
						const Engine *e = Engine::Get(v->engine_type);
 
						v->reliability_spd_dec = e->reliability_spd_dec;
 
						v->reliability = std::min(v->First()->reliability, e->reliability);
 
					}
 
				}
 
				case VEH_ROAD:
 
					v->breakdown_chance = 128;
 
					break;
 
				case VEH_SHIP:
 
					v->breakdown_chance = 64;
 
					break;
 
				case VEH_AIRCRAFT:
 
					v->breakdown_chance = Clamp(64 + (AircraftVehInfo(v->engine_type)->max_speed >> 3), 0, 255);
 
					v->breakdown_severity = 40;
 
					break;
 
				default: break;
 
			}
 
		}
 
	}
 

	
 
	/* The road owner of standard road stops was not properly accounted for. */
 
	if (IsSavegameVersionBefore(SLV_172)) {
 
		for (TileIndex t = 0; t < map_size; t++) {
 
			if (!IsStandardRoadStopTile(t)) continue;
 
			Owner o = GetTileOwner(t);
 
			SetRoadOwner(t, RTT_ROAD, o);
src/saveload/company_sl.cpp
Show inline comments
 
@@ -16,12 +16,13 @@
 
#include "../company_manager_face.h"
 
#include "../fios.h"
 
#include "../tunnelbridge_map.h"
 
#include "../tunnelbridge.h"
 
#include "../station_base.h"
 
#include "../strings_func.h"
 
#include "../settings_func.h"
 

	
 
#include "table/strings.h"
 

	
 
#include "../safeguards.h"
 

	
 
/**
 
@@ -478,15 +479,17 @@ static const SaveLoad _company_desc[] = 
 
	SLE_CONDVAR(CompanyProperties, bankrupt_asked,        SLE_FILE_U8  | SLE_VAR_U16,  SL_MIN_VERSION, SLV_104),
 
	SLE_CONDVAR(CompanyProperties, bankrupt_asked,        SLE_UINT16,                SLV_104, SL_MAX_VERSION),
 
	    SLE_VAR(CompanyProperties, bankrupt_timeout,      SLE_INT16),
 
	SLE_CONDVAR(CompanyProperties, bankrupt_value,        SLE_VAR_I64 | SLE_FILE_I32,  SL_MIN_VERSION, SLV_65),
 
	SLE_CONDVAR(CompanyProperties, bankrupt_value,        SLE_INT64,                  SLV_65, SL_MAX_VERSION),
 

	
 
	/* yearly expenses was changed to 64-bit in savegame version 2. */
 
	// please check afterload for XSLFI_EXTRA_EXPENSES before editing this again
 
	SLE_CONDARR(CompanyProperties, yearly_expenses,       SLE_FILE_I32 | SLE_VAR_I64, 3 * 13, SL_MIN_VERSION, SLV_2),
 
	SLE_CONDARR(CompanyProperties, yearly_expenses,       SLE_INT64, 3 * 13,                  SLV_2, SL_MAX_VERSION),
 
	SLE_CONDARR_X(CompanyProperties, yearly_expenses,       SLE_INT64, 3 * 13,                  SLV_2, SL_MAX_VERSION, SlXvFeatureTest(XSLFTO_AND, XSLFI_EXTRA_EXPENSES, 0, 0)),
 
	SLE_CONDARR_X(CompanyProperties, yearly_expenses,       SLE_INT64, 3 * 22,                  SLV_2, SL_MAX_VERSION, SlXvFeatureTest(XSLFTO_AND, XSLFI_EXTRA_EXPENSES, 1, 1)),
 
	SLE_CONDARR_X(CompanyProperties, yearly_expenses,       SLE_INT64, 3 * 23,                  SLV_2, SL_MAX_VERSION, SlXvFeatureTest(XSLFTO_AND, XSLFI_EXTRA_EXPENSES, 2, 2)),
 

	
 
	SLE_CONDVAR(CompanyProperties, is_ai,                 SLE_BOOL,                    SLV_2, SL_MAX_VERSION),
 

	
 
	SLE_CONDVAR(CompanyProperties, terraform_limit,       SLE_UINT32,                SLV_156, SL_MAX_VERSION),
 
	SLE_CONDVAR(CompanyProperties, clear_limit,           SLE_UINT32,                SLV_156, SL_MAX_VERSION),
 
	SLE_CONDVAR(CompanyProperties, tree_limit,            SLE_UINT32,                SLV_175, SL_MAX_VERSION),
src/saveload/compat/industry_sl_compat.h
Show inline comments
 
@@ -46,13 +46,13 @@ const SaveLoadCompat _industry_sl_compat
 
	SLC_VAR("last_cargo_accepted_at"),
 
	SLC_VAR("selected_layout"),
 
	SLC_VAR("exclusive_supplier"),
 
	SLC_VAR("exclusive_consumer"),
 
	SLC_VAR("storage"),
 
	SLC_VAR("psa"),
 
	SLC_NULL(1, SLV_82, SLV_197),
 
	SLC_NULL_X(1, SLV_82, SLV_197, SlXvFeatureTest(XSLFTO_AND, XSLFI_EXTRA_EXPENSES, 0, 0)),
 
	SLC_VAR("random"),
 
	SLC_VAR("text"),
 
	SLC_NULL(32, SLV_2, SLV_144),
 
};
 

	
 
/** Original field order for _industry_builder_desc. */
src/saveload/compat/settings_sl_compat.h
Show inline comments
 
@@ -35,12 +35,13 @@ const SaveLoadCompat _settings_sl_compat
 
	SLC_VAR("difficulty.max_loan"),
 
	SLC_VAR("difficulty.initial_interest"),
 
	SLC_VAR("difficulty.vehicle_costs"),
 
	SLC_VAR("difficulty.competitor_speed"),
 
	SLC_NULL(1, SLV_97, SLV_110),
 
	SLC_VAR("difficulty.vehicle_breakdowns"),
 
	SLC_VAR("difficulty.vehicle_breakdown_scaler"),
 
	SLC_VAR("difficulty.subsidy_multiplier"),
 
	SLC_VAR("difficulty.subsidy_duration"),
 
	SLC_VAR("difficulty.construction_cost"),
 
	SLC_VAR("difficulty.terrain_type"),
 
	SLC_VAR("difficulty.quantity_sea_lakes"),
 
	SLC_VAR("difficulty.economy"),
 
@@ -66,12 +67,13 @@ const SaveLoadCompat _settings_sl_compat
 
	SLC_VAR("construction.extra_dynamite"),
 
	SLC_VAR("construction.max_bridge_length"),
 
	SLC_VAR("construction.max_bridge_height"),
 
	SLC_VAR("construction.max_tunnel_length"),
 
	SLC_NULL(1, SL_MIN_VERSION, SLV_159),
 
	SLC_VAR("construction.train_signal_side"),
 
	SLC_VAR("construction.name_stations_based_on_industries"),
 
	SLC_VAR("station.never_expire_airports"),
 
	SLC_VAR("economy.town_layout"),
 
	SLC_VAR("economy.allow_town_roads"),
 
	SLC_VAR("economy.found_town"),
 
	SLC_VAR("economy.allow_town_level_crossings"),
 
	SLC_VAR("economy.town_cargogen_mode"),
 
@@ -115,14 +117,16 @@ const SaveLoadCompat _settings_sl_compat
 
	SLC_VAR("order.no_servicing_if_no_breakdowns"),
 
	SLC_VAR("vehicle.wagon_speed_limits"),
 
	SLC_VAR("vehicle.disable_elrails"),
 
	SLC_VAR("vehicle.freight_trains"),
 
	SLC_NULL(1, SLV_67, SLV_159),
 
	SLC_VAR("vehicle.plane_speed"),
 
	SLC_VAR("vehicle.plane_range_multiplier"),
 
	SLC_VAR("vehicle.dynamic_engines"),
 
	SLC_VAR("vehicle.plane_crashes"),
 
	SLC_VAR("vehicle.improved_breakdowns"),
 
	SLC_NULL(1, SL_MIN_VERSION, SLV_159),
 
	SLC_VAR("gui.sg_full_load_any"),
 
	SLC_VAR("order.improved_load"),
 
	SLC_VAR("order.selectgoods"),
 
	SLC_VAR("gui.sg_new_nonstop"),
 
	SLC_NULL(1, SL_MIN_VERSION, SLV_159),
 
@@ -139,24 +143,27 @@ const SaveLoadCompat _settings_sl_compat
 
	SLC_VAR("economy.inflation"),
 
	SLC_VAR("construction.raw_industry_construction"),
 
	SLC_VAR("construction.industry_platform"),
 
	SLC_VAR("economy.multiple_industry_per_town"),
 
	SLC_NULL(1, SL_MIN_VERSION, SLV_141),
 
	SLC_VAR("economy.bribe"),
 
	SLC_VAR("economy.bribe_risky"),
 
	SLC_VAR("economy.exclusive_rights"),
 
	SLC_VAR("economy.fund_buildings"),
 
	SLC_VAR("economy.fund_roads"),
 
	SLC_VAR("economy.give_money"),
 
	SLC_VAR("game_creation.snow_line_height"),
 
	SLC_VAR("game_creation.snow_coverage"),
 
	SLC_VAR("game_creation.desert_coverage"),
 
	SLC_NULL(4, SL_MIN_VERSION, SLV_144),
 
	SLC_VAR("game_creation.starting_year"),
 
	SLC_NULL(4, SL_MIN_VERSION, SLV_105),
 
	SLC_VAR("game_creation.ending_year"),
 
	SLC_VAR("economy.type"),
 
	SLC_VAR("economy.daylength_multiplier"),
 
	SLC_VAR("economy.town_growth_multiplier"),
 
	SLC_VAR("economy.allow_shares"),
 
	SLC_VAR("economy.min_years_for_shares"),
 
	SLC_VAR("economy.feeder_payment_share"),
 
	SLC_VAR("economy.town_growth_rate"),
 
	SLC_VAR("economy.larger_towns"),
 
	SLC_VAR("economy.initial_city_size"),
 
@@ -176,12 +183,32 @@ const SaveLoadCompat _settings_sl_compat
 
	SLC_VAR("pf.wait_oneway_signal"),
 
	SLC_VAR("pf.wait_twoway_signal"),
 
	SLC_VAR("economy.town_noise_population[0]"),
 
	SLC_VAR("economy.town_noise_population[1]"),
 
	SLC_VAR("economy.town_noise_population[2]"),
 
	SLC_VAR("economy.infrastructure_maintenance"),
 
	SLC_VAR("economy.infrastructure_base_cost_rail"),
 
	SLC_VAR("economy.infrastructure_base_cost_road"),
 
	SLC_VAR("economy.infrastructure_base_cost_water"),
 
	SLC_VAR("economy.infrastructure_base_cost_air"),
 
	SLC_VAR("economy.infrastructure_base_cost_station"),
 
	SLC_VAR("economy.running_cost_multiplier_rail"),
 
	SLC_VAR("economy.running_cost_multiplier_road"),
 
	SLC_VAR("economy.running_cost_multiplier_water"),
 
	SLC_VAR("economy.running_cost_multiplier_air"),
 
	SLC_VAR("economy.dilapidation_max_amount"),
 
	SLC_VAR("economy.dilapidation_increase"),
 
	SLC_VAR("economy.dilapidation_decrease"),
 
	SLC_VAR("economy.dilapidation_pop_rail"),
 
	SLC_VAR("economy.dilapidation_pop_road"),
 
	SLC_VAR("economy.dilapidation_pop_water"),
 
	SLC_VAR("economy.dilapidation_pop_air"),
 
	SLC_VAR("economy.dilapidation_fine_rail"),
 
	SLC_VAR("economy.dilapidation_fine_road"),
 
	SLC_VAR("economy.dilapidation_fine_water"),
 
	SLC_VAR("economy.dilapidation_fine_air"),
 
	SLC_VAR("pf.wait_for_pbs_path"),
 
	SLC_VAR("pf.reserve_paths"),
 
	SLC_VAR("pf.path_backoff_interval"),
 
	SLC_NULL(3, SL_MIN_VERSION, SLV_REMOVE_OPF),
 
	SLC_VAR("pf.npf.npf_max_search_nodes"),
 
	SLC_VAR("pf.npf.npf_rail_firstred_penalty"),
src/saveload/compat/station_sl_compat.h
Show inline comments
 
@@ -114,12 +114,13 @@ const SaveLoadCompat _station_normal_sl_
 
	SLC_VAR("time_since_load"),
 
	SLC_VAR("time_since_unload"),
 
	SLC_VAR("last_vehicle_type"),
 
	SLC_VAR("had_vehicle_of_type"),
 
	SLC_VAR("loading_vehicles"),
 
	SLC_VAR("always_accepted"),
 
	SLC_VAR("dilapidation"),
 
	SLC_VAR("goods"),
 
};
 

	
 
/** Original field order for SlStationWaypoint. */
 
const SaveLoadCompat _station_waypoint_sl_compat[] = {
 
	SLC_VAR("base"),
src/saveload/compat/vehicle_sl_compat.h
Show inline comments
 
@@ -72,12 +72,14 @@ const SaveLoadCompat _vehicle_common_sl_
 
	SLC_VAR("reliability"),
 
	SLC_VAR("reliability_spd_dec"),
 
	SLC_VAR("breakdown_ctr"),
 
	SLC_VAR("breakdown_delay"),
 
	SLC_VAR("breakdowns_since_last_service"),
 
	SLC_VAR("breakdown_chance"),
 
	SLC_VAR("breakdown_type"),
 
	SLC_VAR("breakdown_severity"),
 
	SLC_VAR("build_year"),
 
	SLC_VAR("load_unload_ticks"),
 
	SLC_VAR("cargo_paid_for"),
 
	SLC_VAR("vehicle_flags"),
 
	SLC_VAR("profit_this_year"),
 
	SLC_VAR("profit_last_year"),
src/saveload/extended_ver_sl.cpp
Show inline comments
 
new file 100644
 
/* $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 extended_ver_sl.cpp Functions related to handling save/load extended version info.
 
 *
 
 * Known extended features are stored in _sl_xv_feature_versions, features which are currently enabled/in use and their versions are stored in the savegame.
 
 * On load, the list of features and their versions are loaded from the savegame. If the savegame contains a feature which is either unknown, or has too high a version,
 
 * loading can be either aborted, or the feature can be ignored if the the feature flags in the savegame indicate that it can be ignored. The savegame may also list any additional
 
 * chunk IDs which are associated with an extended feature, these can be discarded if the feature is discarded.
 
 * This information is stored in the SLXI chunk, the contents of which has the following format:
 
 *
 
 * uint32                               chunk version
 
 * uint32                               chunk flags
 
 * uint32                               number of sub chunks/features
 
 *     For each of N sub chunk/feature:
 
 *     uint32                           feature flags (SlxiSubChunkFlags)
 
 *     uint16                           feature version
 
 *     SLE_STR                          feature name
 
 *     uint32*                          extra data length [only present iff feature flags & XSCF_EXTRA_DATA_PRESENT]
 
 *         N bytes                      extra data
 
 *     uint32*                          chunk ID list count [only present iff feature flags & XSCF_CHUNK_ID_LIST_PRESENT]
 
 *         N x uint32                   chunk ID list
 
 *
 
 * Extended features as recorded in the SLXI chunk, above, MAY add, remove, change, or otherwise modify fields in chunks
 
 * not owned by the feature and therefore not listed in the sub chunk/feature information in the SLXI chunk.
 
 * In this case the XSCF_IGNORABLE_UNKNOWN flag SHOULD NOT be set, as it is not possible to correctly load the modified chunk without
 
 * knowledge of the feature.
 
 * In the case where the modifications to other chunks vary with respect to lower feature versions, the XSCF_IGNORABLE_VERSION flag
 
 * also SHOULD NOT be set.
 
 * Use of the XSCF_IGNORABLE_UNKNOWN and XSCF_IGNORABLE_VERSION flags MUST ONLY be used in the cases where the feature and any
 
 * associated chunks can be cleanly dropped, and the savegame can be correctly loaded by a client with no knowledge of the feature.
 
 */
 

	
 
#include "../stdafx.h"
 
#include "../debug.h"
 
#include "saveload.h"
 
#include "saveload_buffer.h"
 
#include "extended_ver_sl.h"
 
#include "../rev.h"
 

	
 
#include <vector>
 
#include <string>
 

	
 
#include "../safeguards.h"
 

	
 
uint16 _sl_xv_feature_versions[XSLFI_SIZE];                 ///< array of all known feature types and their current versions
 
bool _sl_is_ext_version;                                    ///< is this an extended savegame version, with more info in the SLXI chunk?
 
bool _sl_might_be_legacy_patchpack_save;                    ///< might this be a save from an earlier version of the patchpack?
 
SaveLoadVersion _sl_is_faked_ext;                           ///< is this a faked extended savegame version, with no SLXI chunk? See: SlXvCheckSpecialSavegameVersions.
 
std::vector<uint32> _sl_xv_discardable_chunk_ids;           ///< list of chunks IDs which we can discard if no chunk loader exists
 
std::string _sl_xv_version_label;                           ///< optional SLXI version label
 

	
 
static const uint32 _sl_xv_slxi_chunk_version = 0;          ///< current version of SLXI chunk
 

	
 
static void loadVL(const SlxiSubChunkInfo* info, uint32 length);
 
static uint32 saveVL(const SlxiSubChunkInfo* info, bool dry_run);
 

	
 
const SlxiSubChunkInfo _sl_xv_sub_chunk_infos[] = {
 
	{ XSLFI_VERSION_LABEL, XSCF_IGNORABLE_ALL, 1, 1, "version_label", saveVL, loadVL, nullptr },
 
	{ XSLFI_PATCHPACK_MAJOR_VERSION, XSCF_NULL, 6, 6, "ginever_patchpack_version", nullptr, nullptr, nullptr },
 
	{ XSLFI_DAYLENGTH, XSCF_NULL, 1, 1, "ginever_daylength", nullptr, nullptr, nullptr },
 
	{ XSLFI_STATION_NEWGRF, XSCF_NULL, 1, 1, "ginever_station_newgrf", nullptr, nullptr, nullptr },
 
	{ XSLFI_INFRASTRUCTURE, XSCF_NULL, 1, 1, "ginever_infrastructure", nullptr, nullptr, nullptr },
 
	{ XSLFI_RUNNING_COST_MULT, XSCF_NULL, 1, 1, "ginever_running_cost_mult", nullptr, nullptr, nullptr },
 
	{ XSLFI_PLANE_RANGE_MULT, XSCF_NULL, 1, 1, "ginever_plane_range_mult", nullptr, nullptr, nullptr },
 
	{ XSLFI_EXTRA_EXPENSES, XSCF_NULL, 2, 2, "ginever_extra_expenses", nullptr, nullptr, nullptr },
 
	{ XSLFI_IMPROVED_BREAKDOWNS, XSCF_NULL, 1, 1, "ginever_improved_breakdowns", nullptr, nullptr, nullptr },
 
	{ XSLFI_BREAKDOWN_SCALER, XSCF_NULL, 1, 1, "ginever_breakdown_scaler", nullptr, nullptr, nullptr },
 
	{ XSLFI_TOWN_IMPROVEMENTS, XSCF_NULL, 1, 1, "ginever_town_improvements", nullptr, nullptr, nullptr },
 
	{ XSLFI_DILAPIDATION, XSCF_NULL, 1, 1, "ginever_dilapidation", nullptr, nullptr, nullptr },
 

	
 
	{ XSLFI_NULL, XSCF_NULL, 0, 0, nullptr, nullptr, nullptr, nullptr },// This is the end marker
 
};
 

	
 
/**
 
 * Extended save/load feature test
 
 *
 
 * First performs a tradional check on the provided @p savegame_version against @p savegame_version_from and @p savegame_version_to.
 
 * Then, if the feature set in the constructor is not XSLFI_NULL, also check than the feature version is inclusively bounded by @p min_version and @p max_version,
 
 * and return the combination of the two tests using the operator defined in the constructor.
 
 * Otherwise just returns the result of the savegame version test
 
 */
 
bool SlXvFeatureTest::IsFeaturePresent(SaveLoadVersion savegame_version, SaveLoadVersion savegame_version_from, SaveLoadVersion savegame_version_to) const
 
{
 
	bool savegame_version_ok = savegame_version >= savegame_version_from && savegame_version < savegame_version_to;
 

	
 
	if (this->functor) return (*this->functor)(savegame_version, savegame_version_ok);
 

	
 
	if (this->feature == XSLFI_NULL) return savegame_version_ok;
 

	
 
	bool feature_ok = SlXvIsFeaturePresent(this->feature, this->min_version, this->max_version);
 

	
 
	switch (op) {
 
		case XSLFTO_OR:
 
			return savegame_version_ok || feature_ok;
 

	
 
		case XSLFTO_AND:
 
			return savegame_version_ok && feature_ok;
 

	
 
		default:
 
			NOT_REACHED();
 
			return false;
 
	}
 
}
 

	
 
/**
 
 * Returns true if @p feature is present and has a version inclusively bounded by @p min_version and @p max_version
 
 */
 
bool SlXvIsFeaturePresent(SlXvFeatureIndex feature, uint16 min_version, uint16 max_version)
 
{
 
	assert(feature < XSLFI_SIZE);
 
	return _sl_xv_feature_versions[feature] >= min_version && _sl_xv_feature_versions[feature] <= max_version;
 
}
 

	
 
/**
 
 * Returns true if @p feature is present and has a version inclusively bounded by @p min_version and @p max_version
 
 */
 
const char *SlXvGetFeatureName(SlXvFeatureIndex feature)
 
{
 
	const SlxiSubChunkInfo *info = _sl_xv_sub_chunk_infos;
 
	for (; info->index != XSLFI_NULL; ++info) {
 
		if (info->index == feature) {
 
			return info->name;
 
		}
 
	}
 
	return "(unknown feature)";
 
}
 

	
 
/**
 
 * Resets all extended feature versions to 0
 
 */
 
void SlXvResetState()
 
{
 
	_sl_is_ext_version = false;
 
	_sl_might_be_legacy_patchpack_save = false;
 
	_sl_is_faked_ext = SL_MIN_VERSION;
 
	_sl_xv_version_label = "";
 
	_sl_xv_discardable_chunk_ids.clear();
 
	memset(_sl_xv_feature_versions, 0, sizeof(_sl_xv_feature_versions));
 
}
 

	
 
/**
 
 * Resets all extended feature versions to their currently enabled versions, i.e. versions suitable for saving
 
 */
 
void SlXvSetCurrentState()
 
{
 
	SlXvResetState();
 
	_sl_is_ext_version = true;
 
	_sl_might_be_legacy_patchpack_save = false;
 

	
 
	const SlxiSubChunkInfo *info = _sl_xv_sub_chunk_infos;
 
	for (; info->index != XSLFI_NULL; ++info) {
 
		_sl_xv_feature_versions[info->index] = info->save_version;
 
	}
 
}
 

	
 
/**
 
 * Check for "special" savegame versions (i.e. known patchpacks) and set correct savegame version, settings, etc.
 
 * Only called if load_legacy_patchpack_savedata is enabled in settings.
 
 */
 
void SlXvCheckSpecialSavegameVersionsA()
 
{
 
	extern SaveLoadVersion _sl_version;
 
	if (_sl_version == SLV_PP1X
 
		|| _sl_version == SLV_PP2X
 
		|| _sl_version == SLV_PP3X
 
		|| _sl_version == SLV_PP4X
 
		|| _sl_version == SLV_PP5X_DAYLENGTH
 
		|| _sl_version == SLV_PP5X_HOTFIX_174
 
		|| _sl_version == SLV_PP5X_HOTFIX_175
 
		|| _sl_version == SLV_PP5X_HOTFIX_177
 
		|| _sl_version == SLV_PP5X_HOTFIX_187
 
		|| _sl_version == SLV_PP5X_STATION_NEWGRF
 
		|| _sl_version == SLV_PP5X_INFRASTRUCTURE
 
		|| _sl_version == SLV_PP5X_RUNNING_COST_MULT
 
		|| _sl_version == SLV_PP5X_PLANE_RANGE_MULT
 
		|| _sl_version == SLV_PP5X_HOTFIX_196
 
		|| _sl_version == SLV_PP5X_EXTRA_EXPENSES
 
		|| _sl_version == SLV_PP5X_HOTFIX_198
 
		|| _sl_version == SLV_PP5X_IMPROVED_BREAKDOWNS
 
		|| _sl_version == SLV_PP5X_BREAKDOWN_SCALER
 
		|| _sl_version == SLV_PP5X_TOWN_IMPROVEMENTS
 
		|| _sl_version == SLV_PP5X_DILAPIDATION
 
		|| _sl_version == SLV_PP5X_HOTFIX_222)
 
	{
 
		Debug(sl, 1, "Save version {} might be a patchpack save, flagging for save version checks", _sl_version);
 
		_sl_might_be_legacy_patchpack_save = true;
 
	}
 
}
 

	
 
/**
 
 * Check for "special" savegame versions (i.e. known patchpacks) and set correct savegame version, settings, etc.
 
 * Only called if load_legacy_patchpack_savedata is enabled in settings.
 
 */
 
void SlXvCheckSpecialSavegameVersionsB()
 
{
 
	if (!_sl_might_be_legacy_patchpack_save)
 
	{
 
		_load_check_data.save_version_label = _sl_xv_version_label;
 
		return;
 
	}
 
	_sl_might_be_legacy_patchpack_save = false;
 

	
 
	Debug(sl, 1, "Checking potential patchpack save against gamelog revision '{}'", _sl_xv_version_label);
 

	
 
	extern SaveLoadVersion _sl_version;
 
	SaveLoadVersion original_version = _sl_version;
 

	
 
	if (original_version == SLV_PP1X)
 
	{
 
		// the gamelog revision should be in the format "rXXXXXM-TTR Alp"
 
		if (_sl_xv_version_label.find("M-TTR") == 6)
 
		{
 
			_sl_is_faked_ext = original_version;
 
			_sl_xv_version_label = "ginever-1.x";
 
			_sl_version = SLV_100;
 
			_sl_xv_feature_versions[XSLFI_PATCHPACK_MAJOR_VERSION] = 1;
 
			//_sl_xv_feature_versions[XSLFI_LEGACY_DAYLENGTH] = 1;
 

	
 
			Debug(sl, 1, "Loading a patchpack 1.x savegame version {} as version {} with extensions", original_version, _sl_version);
 
			_load_check_data.save_ext_type = PSXT_MODIFIED;
 
			_load_check_data.save_version_label = _sl_xv_version_label;
 
			SlError(STR_GAME_SAVELOAD_ERROR_UNSUPPORTED_GPP); // temporary
 
			return;
 
		}
 
	}
 
	if (original_version == SLV_PP2X)
 
	{
 
		// the gamelog revision should be in the format "v2.XXXXX.YYY" or "Rev XXXXX-YYY"
 
		if (_sl_xv_version_label.find("v2.") == 0 || _sl_xv_version_label.find("-") == 9)
 
		{
 
			_sl_is_faked_ext = original_version;
 
			_sl_xv_version_label = "ginever-2.x";
 
			_sl_version = SLV_125;
 
			_sl_xv_feature_versions[XSLFI_PATCHPACK_MAJOR_VERSION] = 2;
 
			//_sl_xv_feature_versions[XSLFI_LEGACY_DAYLENGTH] = 2;
 

	
 
			Debug(sl, 1, "Loading a patchpack 2.x savegame version {} as version {} with extensions", original_version, _sl_version);
 
			_load_check_data.save_ext_type = PSXT_MODIFIED;
 
			_load_check_data.save_version_label = _sl_xv_version_label;
 
			SlError(STR_GAME_SAVELOAD_ERROR_UNSUPPORTED_GPP); // temporary
 
			return;
 
		}
 
	}
 
	if (original_version == SLV_PP3X)
 
	{
 
		// the gamelog revision should be in the format "v3.XXXXX.YYY"
 
		if (_sl_xv_version_label.find("v3.") == 0)
 
		{
 
			_sl_is_faked_ext = original_version;
 
			_sl_xv_version_label = "ginever-3.x";
 
			_sl_version = SLV_141;
 
			_sl_xv_feature_versions[XSLFI_PATCHPACK_MAJOR_VERSION] = 3;
 
			//_sl_xv_feature_versions[XSLFI_DAYLENGTH] = 1;
 
			//_sl_xv_feature_versions[XSLFI_LEGACY_CARGODIST] = 1;
 

	
 
			Debug(sl, 1, "Loading a patchpack 3.x savegame version {} as version {} with extensions", original_version, _sl_version);
 
			_load_check_data.save_ext_type = PSXT_MODIFIED;
 
			_load_check_data.save_version_label = _sl_xv_version_label;
 
			SlError(STR_GAME_SAVELOAD_ERROR_UNSUPPORTED_GPP); // temporary
 
			return;
 
		}
 
	}
 
	if (original_version == SLV_PP4X)
 
	{
 
		// the gamelog revision should be in the format "v4.XXXXX.YYY"
 
		if (_sl_xv_version_label.find("v4.") == 0)
 
		{
 
			_sl_is_faked_ext = original_version;
 
			_sl_xv_version_label = "ginever-4.x";
 
			_sl_version = SLV_142;
 
			_sl_xv_feature_versions[XSLFI_PATCHPACK_MAJOR_VERSION] = 4;
 
			//_sl_xv_feature_versions[XSLFI_DAYLENGTH] = 1;
 
			//_sl_xv_feature_versions[XSLFI_LEGACY_CARGODIST] = 2;
 

	
 
			Debug(sl, 1, "Loading a patchpack 4.x savegame version {} as version {} with extensions", original_version, _sl_version);
 
			_load_check_data.save_ext_type = PSXT_MODIFIED;
 
			_load_check_data.save_version_label = _sl_xv_version_label;
 
			SlError(STR_GAME_SAVELOAD_ERROR_UNSUPPORTED_GPP); // temporary
 
			return;
 
		}
 
	}
 
	if (original_version == SLV_PP5X_DAYLENGTH
 
		|| original_version == SLV_PP5X_HOTFIX_174
 
		|| original_version == SLV_PP5X_HOTFIX_175
 
		|| original_version == SLV_PP5X_HOTFIX_177
 
		|| original_version == SLV_PP5X_HOTFIX_187
 
		|| original_version == SLV_PP5X_STATION_NEWGRF
 
		|| original_version == SLV_PP5X_INFRASTRUCTURE
 
		|| original_version == SLV_PP5X_RUNNING_COST_MULT
 
		|| original_version == SLV_PP5X_PLANE_RANGE_MULT
 
		|| original_version == SLV_PP5X_HOTFIX_196
 
		|| original_version == SLV_PP5X_EXTRA_EXPENSES
 
		|| original_version == SLV_PP5X_HOTFIX_198
 
		|| original_version == SLV_PP5X_IMPROVED_BREAKDOWNS
 
		|| original_version == SLV_PP5X_BREAKDOWN_SCALER
 
		|| original_version == SLV_PP5X_TOWN_IMPROVEMENTS
 
		|| original_version == SLV_PP5X_DILAPIDATION
 
		|| original_version == SLV_PP5X_HOTFIX_222
 
		)
 
	{
 
		// the gamelog revision should be in the format "v5.XXXXX.YYY", but some of the newer versions have broken log messages and just printed "u"
 
		if (_sl_xv_version_label.find("v5.") == 0 || _sl_xv_version_label == "u")
 
		{
 
			_sl_xv_feature_versions[XSLFI_PATCHPACK_MAJOR_VERSION] = 5;
 
				
 
			if (original_version >= SLV_PP5X_DAYLENGTH)
 
			{
 
				_sl_is_faked_ext = original_version;
 
				_sl_xv_version_label = "ginever-5.x";
 
				_sl_version = SLV_160;
 
				_sl_xv_feature_versions[XSLFI_DAYLENGTH] = 1;
 
			}
 
			if (original_version >= SLV_PP5X_HOTFIX_174)
 
			{
 
				_sl_version = SLV_174;
 
			}
 
			if (original_version >= SLV_PP5X_HOTFIX_175)
 
			{
 
				_sl_version = SLV_175;
 
			}
 
			if (original_version >= SLV_PP5X_HOTFIX_177)
 
			{
 
				_sl_version = SLV_177;
 
			}
 
			if (original_version >= SLV_PP5X_HOTFIX_187)
 
			{
 
				_sl_version = SLV_187;
 
			}
 
			if (original_version >= SLV_PP5X_STATION_NEWGRF)
 
			{
 
				_sl_version = SLV_187;
 
				_sl_xv_feature_versions[XSLFI_STATION_NEWGRF] = 1;
 
			}
 
			if (original_version >= SLV_PP5X_INFRASTRUCTURE)
 
			{
 
				_sl_xv_feature_versions[XSLFI_INFRASTRUCTURE] = 1;
 
			}
 
			if (original_version >= SLV_PP5X_RUNNING_COST_MULT)
 
			{
 
				_sl_version = SLV_194;
 
				_sl_xv_feature_versions[XSLFI_RUNNING_COST_MULT] = 1;
 
			}
 
			if (original_version >= SLV_PP5X_PLANE_RANGE_MULT)
 
			{
 
				_sl_xv_feature_versions[XSLFI_PLANE_RANGE_MULT] = 1;
 
			}
 
			if (original_version >= SLV_PP5X_HOTFIX_196)
 
			{
 
				_sl_version = SLV_196;
 
			}
 
			if (original_version >= SLV_PP5X_EXTRA_EXPENSES)
 
			{
 
				_sl_xv_feature_versions[XSLFI_EXTRA_EXPENSES] = 1;
 
			}
 
			if (original_version >= SLV_PP5X_HOTFIX_198)
 
			{
 
				_sl_version = SLV_198;
 
			}
 
			if (original_version >= SLV_PP5X_IMPROVED_BREAKDOWNS)
 
			{
 
				_sl_version = SLV_MULTITILE_DOCKS;
 
				_sl_xv_feature_versions[XSLFI_IMPROVED_BREAKDOWNS] = 1;
 
			}
 
			if (original_version >= SLV_PP5X_BREAKDOWN_SCALER)
 
			{
 
				_sl_xv_feature_versions[XSLFI_BREAKDOWN_SCALER] = 1;
 
			}
 
			if (original_version >= SLV_PP5X_TOWN_IMPROVEMENTS)
 
			{
 
				_sl_xv_feature_versions[XSLFI_TOWN_IMPROVEMENTS] = 1;
 
			}
 
			if (original_version >= SLV_PP5X_DILAPIDATION)
 
			{
 
				_sl_xv_feature_versions[XSLFI_DILAPIDATION] = 1;
 
				_sl_xv_feature_versions[XSLFI_EXTRA_EXPENSES] = 2;
 
			}
 
			if (original_version >= SLV_PP5X_HOTFIX_222)
 
			{
 
				_sl_version = SLV_ENDING_YEAR;
 
			}
 

	
 
			Debug(sl, 1, "Loading a patchpack 5.x savegame version {} as version {} with extensions", original_version, _sl_version);
 
			_load_check_data.save_ext_type = PSXT_MODIFIED;
 
			_load_check_data.save_version_label = _sl_xv_version_label;
 
			return;
 
		}
 
	}
 

	
 
	Debug(sl, 1, "This doesn't appear to be a patchpack save");
 
	_load_check_data.save_version_label = _sl_xv_version_label;
 

	
 
	// now we have to restore the original OTTD invalid save version check for non-Ginever patchpacks since we had to previously disable it
 
	if (original_version >= SLV_START_PATCHPACKS && original_version < SLV_END_PATCHPACKS) SlError(STR_GAME_SAVELOAD_ERROR_PATCHPACK);
 
}
 

	
 
/**
 
 * Return true if this chunk has been marked as discardable
 
 */
 
bool SlXvIsChunkDiscardable(uint32 id)
 
{
 
	for (size_t i = 0; i < _sl_xv_discardable_chunk_ids.size(); i++) {
 
		if (_sl_xv_discardable_chunk_ids[i] == id) {
 
			return true;
 
		}
 
	}
 
	return false;
 
}
 

	
 
/**
 
 * Writes a chunk ID list string to the savegame, returns the number of chunks written
 
 * In dry run mode, only returns the number of chunk which would have been written
 
 */
 
static uint32 WriteChunkIdList(const char *chunk_list, bool dry_run)
 
{
 
	unsigned int chunk_count = 0;  // number of chunks output
 
	unsigned int id_offset = 0;    // how far are we into the ID
 
	for (; *chunk_list != 0; chunk_list++) {
 
		if (id_offset == 4) {
 
			assert(*chunk_list == ',');
 
			id_offset = 0;
 
		} else {
 
			if (!dry_run) {
 
				SlWriteByte(*chunk_list);
 
			}
 
			if (id_offset == 3) {
 
				chunk_count++;
 
			}
 
			id_offset++;
 
		}
 
	}
 
	assert(id_offset == 4);
 
	return chunk_count;
 
}
 

	
 
char name_buffer[256];
 

	
 
static void loadVL(const SlxiSubChunkInfo* info, uint32 length)
 
{
 
	_sl_xv_version_label.resize(length);
 
	ReadBuffer::GetCurrent()->CopyBytes(reinterpret_cast<byte*>(_sl_xv_version_label.data()), length);
 
	Debug(sl, 2, "SLXI version label: %s", _sl_xv_version_label.c_str());
 
	_load_check_data.save_version_label = _sl_xv_version_label; // allows early print of version if extension is detected
 
}
 

	
 
static uint32 saveVL(const SlxiSubChunkInfo* info, bool dry_run)
 
{
 
	const size_t length = strlen(_openttd_revision);
 
	if (!dry_run) MemoryDumper::GetCurrent()->CopyBytes(const_cast<byte*>(reinterpret_cast<const byte*>(_openttd_revision)), length);
 
	return static_cast<uint32>(length);
 
}
 

	
 
struct SLXIChunkHandler : ChunkHandler {
 
	SLXIChunkHandler() : ChunkHandler('SLXI', CH_RIFF) {}
 

	
 
	void LoadCheck(size_t len = 0) const override
 
	{
 
		Load(); // required to be able to correctly interpret other check data
 
	}
 

	
 
	void Load() const override
 
	{
 
		if (_sl_is_faked_ext != SL_MIN_VERSION || !_sl_is_ext_version || _sl_might_be_legacy_patchpack_save) {
 
			SlErrorCorrupt("SLXI chunk is unexpectedly present");
 
		}
 

	
 
		SlXvResetState();
 
		_sl_is_ext_version = true;
 

	
 
		uint32 version = SlReadUint32();
 
		if (version > _sl_xv_slxi_chunk_version) SlErrorCorruptFmt("SLXI chunk: version: %u is too new (expected max: %u)", version, _sl_xv_slxi_chunk_version);
 

	
 
		uint32 chunk_flags = SlReadUint32();
 
		// flags are not in use yet, reserve for future expansion
 
		if (chunk_flags != 0) SlErrorCorruptFmt("SLXI chunk: unknown chunk header flags: 0x%X", chunk_flags);
 

	
 
		const SaveLoad xlsi_sub_chunk_name_desc[] = {
 
			SLEG_STR("feature", name_buffer, SLE_STRB)
 
		};
 

	
 
		uint32 item_count = SlReadUint32();
 
		for (uint32 i = 0; i < item_count; i++) {
 
			SlxiSubChunkFlags flags = static_cast<SlxiSubChunkFlags>(SlReadUint32());
 
			uint16 version = SlReadUint16();
 
			SlGlobList(xlsi_sub_chunk_name_desc);
 

	
 
			// linearly scan through feature list until found name match
 
			bool found = false;
 
			const SlxiSubChunkInfo* info = _sl_xv_sub_chunk_infos;
 
			for (; info->index != XSLFI_NULL; ++info) {
 
				if (strcmp(name_buffer, info->name) == 0) {
 
					found = true;
 
					break;
 
				}
 
			}
 

	
 
			bool discard_chunks = false;
 
			if (found) {
 
				if (version > info->max_version) {
 
					if (flags & XSCF_IGNORABLE_VERSION) {
 
						// version too large but carry on regardless
 
						discard_chunks = true;
 
						if (flags & XSCF_EXTRA_DATA_PRESENT) {
 
							SlSkipBytes(SlReadUint32()); // skip extra data field
 
						}
 
						Debug(sl, 1, "SLXI chunk: too large version for feature: '{}', version: {}, max version: {}, ignoring", name_buffer, version, info->max_version);
 
					}
 
					else {
 
						SlErrorCorruptFmt("SLXI chunk: too large version for feature: '%s', version: %d, max version: %d", name_buffer, version, info->max_version);
 
					}
 
				}
 
				else {
 
					// success path :)
 

	
 
					_sl_xv_feature_versions[info->index] = version;
 
					if (flags & XSCF_EXTRA_DATA_PRESENT) {
 
						uint32 extra_data_size = SlReadUint32();
 
						if (extra_data_size) {
 
							if (info->load_proc) {
 
								size_t read = SlGetBytesRead();
 
								info->load_proc(info, extra_data_size);
 
								if (SlGetBytesRead() != read + extra_data_size) {
 
									SlErrorCorruptFmt("SLXI chunk: feature: %s, version: %d, extra data length mismatch", name_buffer, version);
 
								}
 
							}
 
							else {
 
								SlErrorCorruptFmt("SLXI chunk: feature: %s, version: %d, unexpectedly includes extra data", name_buffer, version);
 
							}
 
						}
 
					}
 

	
 
					Debug(sl, 1, "SLXI chunk: found known feature: '{}', version: {}, max version: {}", name_buffer, version, info->max_version);
 
				}
 
			}
 
			else {
 
				if (flags & XSCF_IGNORABLE_UNKNOWN) {
 
					// not found but carry on regardless
 
					discard_chunks = true;
 
					if (flags & XSCF_EXTRA_DATA_PRESENT) {
 
						SlSkipBytes(SlReadUint32()); // skip extra data field
 
					}
 
					Debug(sl, 1, "SLXI chunk: unknown feature: '{}', version: {}, ignoring", name_buffer, version);
 
				}
 
				else {
 
					SlErrorCorruptFmt("SLXI chunk: unknown feature: %s, version: %d", name_buffer, version);
 
				}
 
			}
 

	
 
			// at this point the extra data field should have been consumed
 
			// handle chunk ID list field
 
			if (flags & XSCF_CHUNK_ID_LIST_PRESENT) {
 
				uint32 chunk_count = SlReadUint32();
 
				for (uint32 j = 0; j < chunk_count; j++) {
 
					uint32 chunk_id = SlReadUint32();
 
					if (discard_chunks) {
 
						_sl_xv_discardable_chunk_ids.push_back(chunk_id);
 
						Debug(sl, 2, "SLXI chunk: unknown feature: '{}', discarding chunk: {:c}{:c}{:c}{:c}", name_buffer, chunk_id >> 24, chunk_id >> 16, chunk_id >> 8, chunk_id);
 
					}
 
				}
 
			}
 
		}
 
	}
 

	
 
	void Save() const override
 
	{
 
		SlXvSetCurrentState();
 

	
 
		static const SaveLoad _xlsi_sub_chunk_desc[] = {
 
			SLE_STR(SlxiSubChunkInfo, name,           SLE_STR, 0)
 
		};
 

	
 
		// calculate lengths
 
		uint32 item_count = 0;
 
		uint32 length = 12;
 
		std::vector<uint32> extra_data_lengths;
 
		std::vector<uint32> chunk_counts;
 
		extra_data_lengths.resize(XSLFI_SIZE);
 
		chunk_counts.resize(XSLFI_SIZE);
 
		const SlxiSubChunkInfo* info = _sl_xv_sub_chunk_infos;
 
		for (; info->index != XSLFI_NULL; ++info) {
 
			if (_sl_xv_feature_versions[info->index] > 0) {
 
				item_count++;
 
				length += 6;
 
				length += (uint32)SlCalcObjLength(info, _xlsi_sub_chunk_desc);
 
				if (info->save_proc) {
 
					uint32 extra_data_length = info->save_proc(info, true);
 
					if (extra_data_length) {
 
						extra_data_lengths[info->index] = extra_data_length;
 
						length += 4 + extra_data_length;
 
					}
 
				}
 
				if (info->chunk_list) {
 
					uint32 chunk_count = WriteChunkIdList(info->chunk_list, true);
 
					if (chunk_count) {
 
						chunk_counts[info->index] = chunk_count;
 
						length += 4 * (1 + chunk_count);
 
					}
 
				}
 
			}
 
		}
 

	
 
		// write header
 
		SlSetLength(length);
 
		SlWriteUint32(_sl_xv_slxi_chunk_version);               // chunk version
 
		SlWriteUint32(0);                                       // flags
 
		SlWriteUint32(item_count);                              // item count
 

	
 
		// write data
 
		info = _sl_xv_sub_chunk_infos;
 
		for (; info->index != XSLFI_NULL; ++info) {
 
			uint16 save_version = _sl_xv_feature_versions[info->index];
 
			if (save_version > 0) {
 
				SlxiSubChunkFlags flags = info->flags;
 
				assert(!(flags & (XSCF_EXTRA_DATA_PRESENT | XSCF_CHUNK_ID_LIST_PRESENT)));
 
				uint32 extra_data_length = extra_data_lengths[info->index];
 
				uint32 chunk_count = chunk_counts[info->index];
 
				if (extra_data_length > 0) flags |= XSCF_EXTRA_DATA_PRESENT;
 
				if (chunk_count > 0) flags |= XSCF_CHUNK_ID_LIST_PRESENT;
 
				SlWriteUint32(flags);
 
				SlWriteUint16(save_version);
 
				SlObject(const_cast<SlxiSubChunkInfo*>(info), _xlsi_sub_chunk_desc);
 

	
 
				if (extra_data_length > 0) {
 
					SlWriteUint32(extra_data_length);
 
					size_t written = SlGetBytesWritten();
 
					info->save_proc(info, false);
 
					assert(SlGetBytesWritten() == written + extra_data_length);
 
				}
 
				if (chunk_count > 0) {
 
					SlWriteUint32(chunk_count);
 
					size_t written = SlGetBytesWritten();
 
					WriteChunkIdList(info->chunk_list, false);
 
					assert(SlGetBytesWritten() == written + (chunk_count * 4));
 
				}
 
			}
 
		}
 
	}
 
};
 

	
 
static const SLXIChunkHandler SLXI;
 
static const ChunkHandlerRef version_ext_chunk_handlers[] = {
 
	SLXI,
 
};
 

	
 
extern const ChunkHandlerTable _version_ext_chunk_handlers(version_ext_chunk_handlers);
src/saveload/extended_ver_sl.h
Show inline comments
 
new file 100644
 
/* $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 extended_ver_sl.h Functions/types related to handling save/load extended version info. */
 

	
 
#ifndef EXTENDED_VER_SL_H
 
#define EXTENDED_VER_SL_H
 

	
 
#include "../core/bitmath_func.hpp"
 

	
 
#include <vector>
 

	
 
enum SaveLoadVersion : uint16;
 

	
 
/**
 
 * List of extended features, each feature has its own (16 bit) version
 
 */
 
enum SlXvFeatureIndex {
 
	XSLFI_NULL                          = 0,      ///< Unused value, to indicate that no extended feature test is in use
 
	//===================================
 
	// COMMON EXTENDED FEATURES
 
	//===================================
 
	XSLFI_VERSION_LABEL,                          ///< Version label
 
	XSLFI_PATCHPACK_MAJOR_VERSION,                ///< Patchpack major version
 
	//===================================
 
	// PATCHPACK 5.X FEATURES
 
	//===================================
 
	XSLFI_DAYLENGTH,                              ///< Variable day length
 
	XSLFI_STATION_NEWGRF,                         ///< Station names based on industries
 
	XSLFI_INFRASTRUCTURE,                         ///< Custom infrastructure base costs
 
	XSLFI_RUNNING_COST_MULT,                      ///< Running cost multiplier
 
	XSLFI_PLANE_RANGE_MULT,                       ///< Plane range multiplier
 
	XSLFI_EXTRA_EXPENSES,                         ///< Additional expense types in the expenses window
 
	XSLFI_IMPROVED_BREAKDOWNS,                    ///< Improved breakdowns patch
 
	XSLFI_BREAKDOWN_SCALER,                       ///< Additional breakdown chance scaler
 
	XSLFI_TOWN_IMPROVEMENTS,                      ///< Configurable bribe risk and town growth factor
 
	XSLFI_DILAPIDATION,                           ///< Station dilapidation
 
	//===================================
 
	XSLFI_SIZE,                                   ///< Total count of features, including null feature
 
};
 

	
 
extern uint16 _sl_xv_feature_versions[XSLFI_SIZE];
 

	
 
/**
 
 * Operator to use when combining traditional savegame number test with an extended feature version test
 
 */
 
enum SlXvFeatureTestOperator {
 
	XSLFTO_OR                           = 0,      ///< Test if traditional savegame version is in bounds OR extended feature is in version bounds
 
	XSLFTO_AND                                    ///< Test if traditional savegame version is in bounds AND extended feature is in version bounds
 
};
 

	
 
/**
 
 * Structure to describe an extended feature version test, and how it combines with a traditional savegame version test
 
 */
 
struct SlXvFeatureTest {
 
	using TestFunctorPtr = bool (*)(uint16, bool);  ///< Return true if feature present, first parameter is standard savegame version, second is whether standard savegame version is within bounds
 

	
 
	private:
 
	uint16 min_version;
 
	uint16 max_version;
 
	SlXvFeatureIndex feature;
 
	SlXvFeatureTestOperator op;
 
	TestFunctorPtr functor = nullptr;
 

	
 
	public:
 
	SlXvFeatureTest()
 
			: min_version(0), max_version(0), feature(XSLFI_NULL), op(XSLFTO_OR) { }
 

	
 
	SlXvFeatureTest(SlXvFeatureTestOperator op_, SlXvFeatureIndex feature_, uint16 min_version_ = 1, uint16 max_version_ = 0xFFFF)
 
			: min_version(min_version_), max_version(max_version_), feature(feature_), op(op_) { }
 

	
 
	SlXvFeatureTest(TestFunctorPtr functor_)
 
			: min_version(0), max_version(0), feature(XSLFI_NULL), op(XSLFTO_OR), functor(functor_) { }
 

	
 
	bool IsFeaturePresent(SaveLoadVersion savegame_version, SaveLoadVersion savegame_version_from, SaveLoadVersion savegame_version_to) const;
 
};
 

	
 
bool SlXvIsFeaturePresent(SlXvFeatureIndex feature, uint16 min_version = 1, uint16 max_version = 0xFFFF);
 

	
 
/**
 
 * Returns true if @p feature is missing (i.e. has a version of 0)
 
 */
 
inline bool SlXvIsFeatureMissing(SlXvFeatureIndex feature)
 
{
 
	return !SlXvIsFeaturePresent(feature);
 
}
 

	
 
const char *SlXvGetFeatureName(SlXvFeatureIndex feature);
 

	
 
/**
 
 * sub chunk flags, this is saved as-is
 
 * (XSCF_EXTRA_DATA_PRESENT and XSCF_CHUNK_ID_LIST_PRESENT must only be set by the save code, and read by the load code)
 
 */
 
enum SlxiSubChunkFlags {
 
	XSCF_NULL                     = 0,       ///< zero value
 
	XSCF_IGNORABLE_UNKNOWN        = 1 << 0,  ///< the loader is free to ignore this without aborting the load if it doesn't know what it is at all
 
	XSCF_IGNORABLE_VERSION        = 1 << 1,  ///< the loader is free to ignore this without aborting the load if the version is greater than the maximum that can be loaded
 
	XSCF_EXTRA_DATA_PRESENT       = 1 << 2,  ///< extra data field is present, extra data in some sub-chunk/feature specific format, not used for anything yet
 
	XSCF_CHUNK_ID_LIST_PRESENT    = 1 << 3,  ///< chunk ID list field is present, list of chunks which this sub-chunk/feature adds to the save game, this can be used to discard the chunks if the feature is unknown
 

	
 
	XSCF_IGNORABLE_ALL            = XSCF_IGNORABLE_UNKNOWN | XSCF_IGNORABLE_VERSION, ///< all "ignorable" flags
 
};
 
DECLARE_ENUM_AS_BIT_SET(SlxiSubChunkFlags)
 

	
 
struct SlxiSubChunkInfo;
 

	
 
typedef uint32 SlxiSubChunkSaveProc(const SlxiSubChunkInfo *info, bool dry_run);  ///< sub chunk save procedure type, must return length and write no data when dry_run is true
 
typedef void SlxiSubChunkLoadProc(const SlxiSubChunkInfo *info, uint32 length);   ///< sub chunk load procedure, must consume length bytes
 

	
 
/** Handlers and description of chunk. */
 
struct SlxiSubChunkInfo {
 
	SlXvFeatureIndex index;                       ///< feature index, this is saved
 
	SlxiSubChunkFlags flags;                      ///< flags, this is saved
 
	uint16 save_version;                          ///< version to save
 
	uint16 max_version;                           ///< maximum version to accept on load
 
	const char *name;                             ///< feature name, this *IS* saved, so must be globally unique
 
	SlxiSubChunkSaveProc *save_proc;              ///< save procedure of the sub chunk, this may be nullptr in which case no extra chunk data is saved
 
	SlxiSubChunkLoadProc *load_proc;              ///< load procedure of the sub chunk, this may be nullptr in which case the extra chunk data must be missing or of 0 length
 
	const char *chunk_list;                       ///< this is a list of chunks that this feature uses, which should be written to the savegame, this must be a comma-seperated list of 4-character IDs, with no spaces, or nullptr
 
};
 

	
 
void SlXvResetState();
 

	
 
void SlXvSetCurrentState();
 

	
 
void SlXvCheckSpecialSavegameVersionsA();
 

	
 
void SlXvCheckSpecialSavegameVersionsB();
 

	
 
bool SlXvIsChunkDiscardable(uint32 id);
 

	
 
#endif /* EXTENDED_VER_SL_H */
src/saveload/gamelog_sl.cpp
Show inline comments
 
@@ -8,18 +8,21 @@
 
/** @file gamelog_sl.cpp Code handling saving and loading of gamelog data */
 

	
 
#include "../stdafx.h"
 

	
 
#include "saveload.h"
 
#include "compat/gamelog_sl_compat.h"
 
#include "extended_ver_sl.h"
 

	
 
#include "../gamelog_internal.h"
 
#include "../fios.h"
 

	
 
#include "../safeguards.h"
 

	
 
extern bool _sl_is_ext_version;
 
extern std::string _sl_xv_version_label;
 

	
 
class SlGamelogMode : public DefaultSaveLoadHandler<SlGamelogMode, LoggedChange> {
 
public:
 
	inline static const SaveLoad description[] = {
 
		SLE_VAR(LoggedChange, mode.mode,         SLE_UINT8),
 
		SLE_VAR(LoggedChange, mode.landscape,    SLE_UINT8),
 
@@ -58,12 +61,17 @@ public:
 
	}
 

	
 
	void Load(LoggedChange *lc) const override
 
	{
 
		if (lc->ct != GLCT_REVISION) return;
 
		SlObject(lc, this->GetLoadDescription());
 
		// update revision
 
		if (!_sl_is_ext_version)
 
		{
 
			_sl_xv_version_label = lc->revision.text;
 
		}
 
	}
 

	
 
	void LoadCheck(LoggedChange *lc) const override { this->Load(lc); }
 
};
 

	
 
class SlGamelogOldver : public DefaultSaveLoadHandler<SlGamelogOldver, LoggedChange> {
 
@@ -395,17 +403,19 @@ struct GLOGChunkHandler : ChunkHandler {
 
		}
 
	}
 

	
 
	void Load() const override
 
	{
 
		this->LoadCommon(_gamelog_action, _gamelog_actions);
 
		SlXvCheckSpecialSavegameVersionsB();
 
	}
 

	
 
	void LoadCheck(size_t) const override
 
	{
 
		this->LoadCommon(_load_check_data.gamelog_action, _load_check_data.gamelog_actions);
 
		SlXvCheckSpecialSavegameVersionsB();
 
	}
 
};
 

	
 
static const GLOGChunkHandler GLOG;
 
static const ChunkHandlerRef gamelog_chunk_handlers[] = {
 
	GLOG,
src/saveload/saveload.cpp
Show inline comments
 
@@ -49,28 +49,36 @@
 
#ifdef __EMSCRIPTEN__
 
#	include <emscripten.h>
 
#endif
 

	
 
#include "table/strings.h"
 

	
 
#include "saveload_buffer.h"
 
#include "saveload_internal.h"
 
#include "saveload_filter.h"
 

	
 
#include "../safeguards.h"
 

	
 
extern const SaveLoadVersion SAVEGAME_VERSION = (SaveLoadVersion)(SL_MAX_VERSION - 1); ///< Current savegame version of OpenTTD.
 

	
 
const SaveLoadVersion SAVEGAME_VERSION_EXT = (SaveLoadVersion)(0x8000); ///< Savegame extension indicator mask
 

	
 
SavegameType _savegame_type; ///< type of savegame we are loading
 
FileToSaveLoad _file_to_saveload; ///< File to save or load in the openttd loop.
 

	
 
uint32 _ttdp_version;         ///< version of TTDP savegame (if applicable)
 
SaveLoadVersion _sl_version;  ///< the major savegame version identifier
 
byte   _sl_minor_version;     ///< the minor savegame version, DO NOT USE!
 
std::string _savegame_format; ///< how to compress savegames
 
bool _do_autosave;            ///< are we doing an autosave at the moment?
 

	
 
extern bool _sl_is_ext_version;
 
extern bool _sl_might_be_legacy_patchpack_save;
 
extern SaveLoadVersion _sl_is_faked_ext;
 
extern std::string _sl_xv_version_label;
 

	
 
/** What are we currently doing? */
 
enum SaveLoadAction {
 
	SLA_LOAD,        ///< loading
 
	SLA_SAVE,        ///< saving
 
	SLA_PTRS,        ///< fixing pointers
 
	SLA_NULL,        ///< null all pointers (on loading error)
 
@@ -80,119 +88,12 @@ enum SaveLoadAction {
 
enum NeedLength {
 
	NL_NONE = 0,       ///< not working in NeedLength mode
 
	NL_WANTLENGTH = 1, ///< writing length and data
 
	NL_CALCLENGTH = 2, ///< need to calculate the length
 
};
 

	
 
/** Save in chunks of 128 KiB. */
 
static const size_t MEMORY_CHUNK_SIZE = 128 * 1024;
 

	
 
/** A buffer for reading (and buffering) savegame data. */
 
struct ReadBuffer {
 
	byte buf[MEMORY_CHUNK_SIZE]; ///< Buffer we're going to read from.
 
	byte *bufp;                  ///< Location we're at reading the buffer.
 
	byte *bufe;                  ///< End of the buffer we can read from.
 
	LoadFilter *reader;          ///< The filter used to actually read.
 
	size_t read;                 ///< The amount of read bytes so far from the filter.
 

	
 
	/**
 
	 * Initialise our variables.
 
	 * @param reader The filter to actually read data.
 
	 */
 
	ReadBuffer(LoadFilter *reader) : bufp(nullptr), bufe(nullptr), reader(reader), read(0)
 
	{
 
	}
 

	
 
	inline byte ReadByte()
 
	{
 
		if (this->bufp == this->bufe) {
 
			size_t len = this->reader->Read(this->buf, lengthof(this->buf));
 
			if (len == 0) SlErrorCorrupt("Unexpected end of chunk");
 

	
 
			this->read += len;
 
			this->bufp = this->buf;
 
			this->bufe = this->buf + len;
 
		}
 

	
 
		return *this->bufp++;
 
	}
 

	
 
	/**
 
	 * Get the size of the memory dump made so far.
 
	 * @return The size.
 
	 */
 
	size_t GetSize() const
 
	{
 
		return this->read - (this->bufe - this->bufp);
 
	}
 
};
 

	
 

	
 
/** Container for dumping the savegame (quickly) to memory. */
 
struct MemoryDumper {
 
	std::vector<byte *> blocks; ///< Buffer with blocks of allocated memory.
 
	byte *buf;                  ///< Buffer we're going to write to.
 
	byte *bufe;                 ///< End of the buffer we write to.
 

	
 
	/** Initialise our variables. */
 
	MemoryDumper() : buf(nullptr), bufe(nullptr)
 
	{
 
	}
 

	
 
	~MemoryDumper()
 
	{
 
		for (auto p : this->blocks) {
 
			free(p);
 
		}
 
	}
 

	
 
	/**
 
	 * Write a single byte into the dumper.
 
	 * @param b The byte to write.
 
	 */
 
	inline void WriteByte(byte b)
 
	{
 
		/* Are we at the end of this chunk? */
 
		if (this->buf == this->bufe) {
 
			this->buf = CallocT<byte>(MEMORY_CHUNK_SIZE);
 
			this->blocks.push_back(this->buf);
 
			this->bufe = this->buf + MEMORY_CHUNK_SIZE;
 
		}
 

	
 
		*this->buf++ = b;
 
	}
 

	
 
	/**
 
	 * Flush this dumper into a writer.
 
	 * @param writer The filter we want to use.
 
	 */
 
	void Flush(SaveFilter *writer)
 
	{
 
		uint i = 0;
 
		size_t t = this->GetSize();
 

	
 
		while (t > 0) {
 
			size_t to_write = std::min(MEMORY_CHUNK_SIZE, t);
 

	
 
			writer->Write(this->blocks[i++], to_write);
 
			t -= to_write;
 
		}
 

	
 
		writer->Finish();
 
	}
 

	
 
	/**
 
	 * Get the size of the memory dump made so far.
 
	 * @return The size.
 
	 */
 
	size_t GetSize() const
 
	{
 
		return this->blocks.size() * MEMORY_CHUNK_SIZE - (this->bufe - this->buf);
 
	}
 
};
 

	
 
/** The saveload struct, containing reader-writer functions, buffer, version, etc. */
 
struct SaveLoadParams {
 
	SaveLoadAction action;               ///< are we doing a save or a load atm.
 
	NeedLength need_length;              ///< working in NeedLength (Autolength) mode?
 
	byte block_mode;                     ///< ???
 
	bool error;                          ///< did an error occur or not
 
@@ -213,15 +114,133 @@ struct SaveLoadParams {
 
	uint16 game_speed;                   ///< The game speed when saving started.
 
	bool saveinprogress;                 ///< Whether there is currently a save in progress.
 
};
 

	
 
static SaveLoadParams _sl; ///< Parameters used for/at saveload.
 

	
 
void ReadBuffer::SkipBytesSlowPath(size_t bytes)
 
{
 
	bytes -= (this->bufe - this->bufp);
 
	while (true) {
 
		size_t len = this->reader->Read(this->buf, lengthof(this->buf));
 
		if (len == 0) SlErrorCorrupt("Unexpected end of chunk");
 
		this->read += len;
 
		if (len >= bytes) {
 
			this->bufp = this->buf + bytes;
 
			this->bufe = this->buf + len;
 
			return;
 
		}
 
		else {
 
			bytes -= len;
 
		}
 
	}
 
}
 

	
 
void ReadBuffer::AcquireBytes()
 
{
 
	size_t remainder = this->bufe - this->bufp;
 
	if (remainder) {
 
		memmove(this->buf, this->bufp, remainder);
 
	}
 
	size_t len = this->reader->Read(this->buf + remainder, lengthof(this->buf) - remainder);
 
	if (len == 0) SlErrorCorrupt("Unexpected end of chunk");
 

	
 
	this->read += len;
 
	this->bufp = this->buf;
 
	this->bufe = this->buf + remainder + len;
 
}
 

	
 
void MemoryDumper::FinaliseBlock()
 
{
 
	assert(this->saved_buf == nullptr);
 
	if (!this->blocks.empty()) {
 
		size_t s = MEMORY_CHUNK_SIZE - (this->bufe - this->buf);
 
		this->blocks.back().size = s;
 
		this->completed_block_bytes += s;
 
	}
 
	this->buf = this->bufe = nullptr;
 
}
 

	
 
void MemoryDumper::AllocateBuffer()
 
{
 
	if (this->saved_buf) {
 
		const size_t offset = this->buf - this->autolen_buf;
 
		const size_t size = (this->autolen_buf_end - this->autolen_buf) * 2;
 
		this->autolen_buf = ReallocT<byte>(this->autolen_buf, size);
 
		this->autolen_buf_end = this->autolen_buf + size;
 
		this->buf = this->autolen_buf + offset;
 
		this->bufe = this->autolen_buf_end;
 
		return;
 
	}
 
	this->FinaliseBlock();
 
	this->buf = MallocT<byte>(MEMORY_CHUNK_SIZE);
 
	this->blocks.emplace_back(this->buf);
 
	this->bufe = this->buf + MEMORY_CHUNK_SIZE;
 
}
 

	
 
/**
 
 * Flush this dumper into a writer.
 
 * @param writer The filter we want to use.
 
 */
 
void MemoryDumper::Flush(SaveFilter* writer)
 
{
 
	this->FinaliseBlock();
 

	
 
	size_t block_count = this->blocks.size();
 
	for (size_t i = 0; i < block_count; i++) {
 
		writer->Write(this->blocks[i].data, this->blocks[i].size);
 
	}
 

	
 
	writer->Finish();
 
}
 

	
 
void MemoryDumper::StartAutoLength()
 
{
 
	assert(this->saved_buf == nullptr);
 

	
 
	this->saved_buf = this->buf;
 
	this->saved_bufe = this->bufe;
 
	this->buf = this->autolen_buf;
 
	this->bufe = this->autolen_buf_end;
 
}
 

	
 
std::pair<byte*, size_t> MemoryDumper::StopAutoLength()
 
{
 
	assert(this->saved_buf != nullptr);
 
	auto res = std::make_pair(this->autolen_buf, this->buf - this->autolen_buf);
 

	
 
	this->buf = this->saved_buf;
 
	this->bufe = this->saved_bufe;
 
	this->saved_buf = this->saved_bufe = nullptr;
 
	return res;
 
}
 

	
 
/**
 
 * Get the size of the memory dump made so far.
 
 * @return The size.
 
 */
 
size_t MemoryDumper::GetSize() const
 
{
 
	assert(this->saved_buf == nullptr);
 
	return this->completed_block_bytes + (this->bufe ? (MEMORY_CHUNK_SIZE - (this->bufe - this->buf)) : 0);
 
}
 

	
 
ReadBuffer* ReadBuffer::GetCurrent()
 
{
 
	return _sl.reader;
 
}
 

	
 
MemoryDumper* MemoryDumper::GetCurrent()
 
{
 
	return _sl.dumper;
 
}
 

	
 
static const std::vector<ChunkHandlerRef> &ChunkHandlers()
 
{
 
	/* These define the chunks */
 
	extern const ChunkHandlerTable _version_ext_chunk_handlers;
 
	extern const ChunkHandlerTable _gamelog_chunk_handlers;
 
	extern const ChunkHandlerTable _map_chunk_handlers;
 
	extern const ChunkHandlerTable _misc_chunk_handlers;
 
	extern const ChunkHandlerTable _name_chunk_handlers;
 
	extern const ChunkHandlerTable _cheat_chunk_handlers;
 
	extern const ChunkHandlerTable _setting_chunk_handlers;
 
@@ -252,12 +271,13 @@ static const std::vector<ChunkHandlerRef
 
	extern const ChunkHandlerTable _airport_chunk_handlers;
 
	extern const ChunkHandlerTable _object_chunk_handlers;
 
	extern const ChunkHandlerTable _persistent_storage_chunk_handlers;
 

	
 
	/** List of all chunks in a savegame. */
 
	static const ChunkHandlerTable _chunk_handler_tables[] = {
 
		_version_ext_chunk_handlers,
 
		_gamelog_chunk_handlers,
 
		_map_chunk_handlers,
 
		_misc_chunk_handlers,
 
		_name_chunk_handlers,
 
		_cheat_chunk_handlers,
 
		_setting_chunk_handlers,
 
@@ -309,12 +329,13 @@ static void SlNullPointers()
 
	_sl.action = SLA_NULL;
 

	
 
	/* We don't want any savegame conversion code to run
 
	 * during NULLing; especially those that try to get
 
	 * pointers from other pools. */
 
	_sl_version = SAVEGAME_VERSION;
 
	SlXvSetCurrentState();
 

	
 
	for (const ChunkHandler &ch : ChunkHandlers()) {
 
		Debug(sl, 3, "Nulling pointers for {:c}{:c}{:c}{:c}", ch.id >> 24, ch.id >> 16, ch.id >> 8, ch.id);
 
		ch.FixPointers();
 
	}
 

	
 
@@ -432,47 +453,66 @@ byte SlReadByte()
 
 */
 
void SlWriteByte(byte b)
 
{
 
	_sl.dumper->WriteByte(b);
 
}
 

	
 
static inline int SlReadUint16()
 
int SlReadUint16()
 
{
 
	int x = SlReadByte() << 8;
 
	return x | SlReadByte();
 
	_sl.reader->CheckBytes(2);
 
	return _sl.reader->RawReadUint16();
 
}
 

	
 
static inline uint32 SlReadUint32()
 
uint32 SlReadUint32()
 
{
 
	uint32 x = SlReadUint16() << 16;
 
	return x | SlReadUint16();
 
	_sl.reader->CheckBytes(4);
 
	return _sl.reader->RawReadUint32();
 
}
 

	
 
static inline uint64 SlReadUint64()
 
uint64 SlReadUint64()
 
{
 
	uint32 x = SlReadUint32();
 
	uint32 y = SlReadUint32();
 
	return (uint64)x << 32 | y;
 
	_sl.reader->CheckBytes(8);
 
	return _sl.reader->RawReadUint64();
 
}
 

	
 
void SlWriteUint16(uint16 v)
 
{
 
	_sl.dumper->CheckBytes(2);
 
	_sl.dumper->RawWriteUint16(v);
 
}
 

	
 
static inline void SlWriteUint16(uint16 v)
 
void SlWriteUint32(uint32 v)
 
{
 
	SlWriteByte(GB(v, 8, 8));
 
	SlWriteByte(GB(v, 0, 8));
 
	_sl.dumper->CheckBytes(4);
 
	_sl.dumper->RawWriteUint32(v);
 
}
 

	
 
static inline void SlWriteUint32(uint32 v)
 
void SlWriteUint64(uint64 x)
 
{
 
	SlWriteUint16(GB(v, 16, 16));
 
	SlWriteUint16(GB(v,  0, 16));
 
	_sl.dumper->CheckBytes(8);
 
	_sl.dumper->RawWriteUint64(x);
 
}
 

	
 
static inline void SlWriteUint64(uint64 x)
 
/**
 
 * Returns number of bytes read so far
 
 * May only be called during a load/load check action
 
 */
 
size_t SlGetBytesRead()
 
{
 
	SlWriteUint32((uint32)(x >> 32));
 
	SlWriteUint32((uint32)x);
 
	assert(_sl.action == SLA_LOAD || _sl.action == SLA_LOAD_CHECK);
 
	return _sl.reader->GetSize();
 
}
 

	
 
/**
 
 * Returns number of bytes written so far
 
 * May only be called during a save action
 
 */
 
size_t SlGetBytesWritten()
 
{
 
	assert(_sl.action == SLA_SAVE);
 
	return _sl.dumper->GetSize();
 
}
 

	
 
/**
 
 * Read in the header descriptor of an object or an array.
 
 * If the highest bit is set (7), then the index is bigger than 127
 
 * elements, so use the next byte to read in the real value.
 
@@ -1057,12 +1097,15 @@ static void SlStdString(void *ptr, VarTy
 
			break;
 
		}
 

	
 
		case SLA_LOAD_CHECK:
 
		case SLA_LOAD: {
 
			size_t len = SlReadArrayLength();
 
			if (len > 65535) {
 
				SlErrorCorrupt("String too long");
 
			}
 
			if (GetVarMemType(conv) == SLE_VAR_NULL) {
 
				SlSkipBytes(len);
 
				return;
 
			}
 

	
 
			char *buf = AllocaM(char, len + 1);
 
@@ -1551,13 +1594,13 @@ static void SlVector(void *vector, VarTy
 
	}
 
}
 

	
 
/** Are we going to save this object or not? */
 
static inline bool SlIsObjectValidInSavegame(const SaveLoad &sld)
 
{
 
	return (_sl_version >= sld.version_from && _sl_version < sld.version_to);
 
	return sld.ext_feature_test.IsFeaturePresent(_sl_version, sld.version_from, sld.version_to);
 
}
 

	
 
/**
 
 * Calculate the size of the table header.
 
 * @param slt The SaveLoad table with objects to save/load.
 
 * @return size of given object.
 
@@ -2045,25 +2088,26 @@ std::vector<SaveLoad> SlCompatTableHeade
 

	
 
	for (auto &slc : slct) {
 
		if (slc.name.empty()) {
 
			/* In old savegames there can be data we no longer care for. We
 
			 * skip this by simply reading the amount of bytes indicated and
 
			 * send those to /dev/null. */
 
			saveloads.push_back({"", SL_NULL, SLE_FILE_U8 | SLE_VAR_NULL, slc.length, slc.version_from, slc.version_to, 0, nullptr, 0, nullptr});
 
			saveloads.push_back({"", SL_NULL, SLE_FILE_U8 | SLE_VAR_NULL, slc.length, slc.version_from, slc.version_to, 0, nullptr, 0, nullptr, slc.ext_feature_test});
 
		} else {
 
			auto sld_it = key_lookup.find(slc.name);
 
			/* If this branch triggers, it means that an entry in the
 
			 * SaveLoadCompat list is not mentioned in the SaveLoad list. Did
 
			 * you rename a field in one and not in the other? */
 
			if (sld_it == key_lookup.end()) {
 
				/* This isn't an assert, as that leaves no information what
 
				 * field was to blame. This way at least we have breadcrumbs. */
 
				Debug(sl, 0, "internal error: saveload compatibility field '{}' not found", slc.name);
 
				SlErrorCorrupt("Internal error with savegame compatibility");
 
			}
 
			for (auto &sld : sld_it->second) {
 
				if (!slc.ext_feature_test.IsFeaturePresent(_sl_version, SL_MIN_VERSION, SL_MAX_VERSION)) continue;
 
				saveloads.push_back(*sld);
 
			}
 
		}
 
	}
 

	
 
	for (auto &sld : saveloads) {
 
@@ -2183,13 +2227,13 @@ static void SlLoadChunk(const ChunkHandl
 

	
 
/**
 
 * Load a chunk of data for checking savegames.
 
 * If the chunkhandler is nullptr, the chunk is skipped.
 
 * @param ch The chunkhandler that will be used for the operation
 
 */
 
static void SlLoadCheckChunk(const ChunkHandler &ch)
 
static void SlLoadCheckChunk(const ChunkHandler* ch)
 
{
 
	byte m = SlReadByte();
 
	size_t len;
 
	size_t endoffs;
 

	
 
	_sl.block_mode = m & CH_TYPE_MASK;
 
@@ -2203,36 +2247,75 @@ static void SlLoadCheckChunk(const Chunk
 
	}
 

	
 
	switch (_sl.block_mode) {
 
		case CH_TABLE:
 
		case CH_ARRAY:
 
			_sl.array_index = 0;
 
			ch.LoadCheck();
 
			if (ch)
 
			{
 
				ch->LoadCheck();
 
			}
 
			else
 
			{
 
				SlSkipArray();
 
			}
 
			break;
 
		case CH_SPARSE_TABLE:
 
		case CH_SPARSE_ARRAY:
 
			ch.LoadCheck();
 
			if (ch)
 
			{
 
				ch->LoadCheck();
 
			}
 
			else
 
			{
 
				SlSkipArray();
 
			}
 
			break;
 
		case CH_RIFF:
 
			/* Read length */
 
			len = (SlReadByte() << 16) | ((m >> 4) << 24);
 
			len += SlReadUint16();
 
			_sl.obj_len = len;
 
			endoffs = _sl.reader->GetSize() + len;
 
			ch.LoadCheck(len);
 
			if (ch) {
 
				ch->LoadCheck(len);
 
			}
 
			else
 
			{
 
				SlSkipBytes(len);
 
			}
 
			if (_sl.reader->GetSize() != endoffs) SlErrorCorrupt("Invalid chunk size");
 
			break;
 
		default:
 
			SlErrorCorrupt("Invalid chunk type");
 
			break;
 
	}
 

	
 
	if (_sl.expect_table_header) SlErrorCorrupt("Table chunk without header");
 
}
 

	
 
/**
 
 * Load a chunk of data for checking savegames.
 
 * @param ch The chunkhandler that will be used for the operation
 
 */
 
static void SlLoadCheckChunk(const ChunkHandler &ch)
 
{
 
	SlLoadCheckChunk(&ch);
 
}
 

	
 
/**
 
 * Skip unwanted chunk
 
 * @param id Chunk ID
 
 */
 
static void SlLoadSkipChunk(uint32 id)
 
{
 
	Debug(sl, 1, "Discarding chunk {:c}{:c}{:c}{:c}", id >> 24, id >> 16, id >> 8, id);
 
	SlLoadCheckChunk(nullptr);
 
}
 

	
 
/**
 
 * Save a chunk of data (eg. vehicles, stations, etc.). Each chunk is
 
 * prefixed by an ID identifying it, followed by data, and terminator where appropriate
 
 * @param ch The chunkhandler that will be used for the operation
 
 */
 
static void SlSaveChunk(const ChunkHandler &ch)
 
{
 
@@ -2299,14 +2382,23 @@ static void SlLoadChunks()
 
	const ChunkHandler *ch;
 

	
 
	for (id = SlReadUint32(); id != 0; id = SlReadUint32()) {
 
		Debug(sl, 2, "Loading chunk {:c}{:c}{:c}{:c}", id >> 24, id >> 16, id >> 8, id);
 

	
 
		ch = SlFindChunkHandler(id);
 
		if (ch == nullptr) SlErrorCorrupt("Unknown chunk type");
 
		SlLoadChunk(*ch);
 
		if (ch == nullptr) {
 
			if (SlXvIsChunkDiscardable(id)) {
 
				SlLoadSkipChunk(id);
 
			}
 
			else {
 
				SlErrorCorrupt("Unknown chunk type");
 
			}
 
		}
 
		else {
 
			SlLoadChunk(*ch);
 
		}
 
	}
 
}
 

	
 
/** Load all chunks for savegame checking */
 
static void SlLoadCheckChunks()
 
{
 
@@ -2314,14 +2406,23 @@ static void SlLoadCheckChunks()
 
	const ChunkHandler *ch;
 

	
 
	for (id = SlReadUint32(); id != 0; id = SlReadUint32()) {
 
		Debug(sl, 2, "Loading chunk {:c}{:c}{:c}{:c}", id >> 24, id >> 16, id >> 8, id);
 

	
 
		ch = SlFindChunkHandler(id);
 
		if (ch == nullptr) SlErrorCorrupt("Unknown chunk type");
 
		SlLoadCheckChunk(*ch);
 
		if (ch == nullptr) {
 
			if (SlXvIsChunkDiscardable(id)) {
 
				SlLoadSkipChunk(id);
 
			}
 
			else {
 
				SlErrorCorruptFmt("Unknown chunk type %c%c%c%c", id >> 24, id >> 16, id >> 8, id);
 
			}
 
		}
 
		else {
 
			SlLoadCheckChunk(*ch);
 
		}
 
	}
 
}
 

	
 
/** Fix all pointers (convert index -> pointer) */
 
static void SlFixPointers()
 
{
 
@@ -2973,13 +3074,13 @@ static SaveOrLoadResult SaveFileToDisk(b
 
{
 
	try {
 
		byte compression;
 
		const SaveLoadFormat *fmt = GetSavegameFormat(_savegame_format, &compression);
 

	
 
		/* We have written our stuff to memory, now write it to file! */
 
		uint32 hdr[2] = { fmt->tag, TO_BE32(SAVEGAME_VERSION << 16) };
 
		uint32 hdr[2] = { fmt->tag, TO_BE32((uint32)(SAVEGAME_VERSION | SAVEGAME_VERSION_EXT) << 16) };
 
		_sl.sf->Write((byte*)hdr, sizeof(hdr));
 

	
 
		_sl.sf = fmt->init_write(_sl.sf, compression);
 
		_sl.dumper->Flush(_sl.sf);
 

	
 
		ClearSaveLoadState();
 
@@ -3032,12 +3133,13 @@ static SaveOrLoadResult DoSave(SaveFilte
 
	assert(!_sl.saveinprogress);
 

	
 
	_sl.dumper = new MemoryDumper();
 
	_sl.sf = writer;
 

	
 
	_sl_version = SAVEGAME_VERSION;
 
	SlXvSetCurrentState();
 

	
 
	SaveViewportBeforeSaveGame();
 
	SlSaveChunks();
 

	
 
	SaveFileStart();
 

	
 
@@ -3084,24 +3186,27 @@ static SaveOrLoadResult DoLoad(LoadFilte
 
		/* Clear previous check data */
 
		_load_check_data.Clear();
 
		/* Mark SL_LOAD_CHECK as supported for this savegame. */
 
		_load_check_data.checkable = true;
 
	}
 

	
 
	SlXvResetState();
 

	
 
	uint32 hdr[2];
 
	if (_sl.lf->Read((byte*)hdr, sizeof(hdr)) != sizeof(hdr)) SlError(STR_GAME_SAVELOAD_ERROR_FILE_NOT_READABLE);
 

	
 
	/* see if we have any loader for this type. */
 
	const SaveLoadFormat *fmt = _saveload_formats;
 
	for (;;) {
 
		/* No loader found, treat as version 0 and use LZO format */
 
		if (fmt == endof(_saveload_formats)) {
 
			Debug(sl, 0, "Unknown savegame type, trying to load it as the buggy format");
 
			_sl.lf->Reset();
 
			_sl_version = SL_MIN_VERSION;
 
			_sl_minor_version = 0;
 
			SlXvResetState();
 

	
 
			/* Try to find the LZO savegame format; it uses 'OTTD' as tag. */
 
			fmt = _saveload_formats;
 
			for (;;) {
 
				if (fmt == endof(_saveload_formats)) {
 
					/* Who removed LZO support? */
 
@@ -3118,17 +3223,24 @@ static SaveOrLoadResult DoLoad(LoadFilte
 
			_sl_version = (SaveLoadVersion)(TO_BE32(hdr[1]) >> 16);
 
			/* Minor is not used anymore from version 18.0, but it is still needed
 
			 * in versions before that (4 cases) which can't be removed easy.
 
			 * Therefore it is loaded, but never saved (or, it saves a 0 in any scenario). */
 
			_sl_minor_version = (TO_BE32(hdr[1]) >> 8) & 0xFF;
 

	
 
			Debug(sl, 1, "Loading savegame version {}", _sl_version);
 
			if (_sl_version & SAVEGAME_VERSION_EXT) {
 
				_sl_version = (SaveLoadVersion)(_sl_version & ~SAVEGAME_VERSION_EXT);
 
				_sl_is_ext_version = true;
 
			} else if (_settings_client.gui.load_legacy_patchpack_savedata ) {
 
				SlXvCheckSpecialSavegameVersionsA();
 
			}
 

	
 
			Debug(sl, 1, "Loading savegame version {}{}", _sl_version, _sl_is_ext_version ? " (extended)" : "");
 

	
 
			/* Is the version higher than the current? */
 
			if (_sl_version > SAVEGAME_VERSION) SlError(STR_GAME_SAVELOAD_ERROR_TOO_NEW_SAVEGAME);
 
			if (_sl_version >= SLV_START_PATCHPACKS && _sl_version <= SLV_END_PATCHPACKS) SlError(STR_GAME_SAVELOAD_ERROR_PATCHPACK);
 
			if (_sl_version >= SLV_START_PATCHPACKS && _sl_version < SLV_END_PATCHPACKS && !_sl_might_be_legacy_patchpack_save) SlError(STR_GAME_SAVELOAD_ERROR_PATCHPACK);
 
			break;
 
		}
 

	
 
		fmt++;
 
	}
 

	
 
@@ -3179,12 +3291,14 @@ static SaveOrLoadResult DoLoad(LoadFilte
 
		}
 
	}
 

	
 
	if (load_check) {
 
		/* Load chunks into _load_check_data.
 
		 * No pools are loaded. References are not possible, and thus do not need resolving. */
 
		_load_check_data.save_version = _sl_is_faked_ext != SL_MIN_VERSION ? _sl_is_faked_ext : _sl_version;
 
		_load_check_data.save_ext_type = _sl_is_ext_version ? PSXT_EXTENDED : PSXT_NONE;
 
		SlLoadCheckChunks();
 
	} else {
 
		/* Load chunks and resolve references */
 
		SlLoadChunks();
 
		SlFixPointers();
 
	}
 
@@ -3204,12 +3318,13 @@ static SaveOrLoadResult DoLoad(LoadFilte
 
		if (!AfterLoadGame()) {
 
			GamelogStopAction();
 
			return SL_REINIT;
 
		}
 

	
 
		GamelogStopAction();
 
		SlXvSetCurrentState();
 
	}
 

	
 
	return SL_OK;
 
}
 

	
 
/**
 
@@ -3260,18 +3375,20 @@ SaveOrLoadResult SaveOrLoad(const std::s
 
			 * for OTTD savegames which have their own NewGRF logic. */
 
			ClearGRFConfigList(&_grfconfig);
 
			GamelogReset();
 
			if (!LoadOldSaveGame(filename)) return SL_REINIT;
 
			_sl_version = SL_MIN_VERSION;
 
			_sl_minor_version = 0;
 
			SlXvResetState();
 
			GamelogStartAction(GLAT_LOAD);
 
			if (!AfterLoadGame()) {
 
				GamelogStopAction();
 
				return SL_REINIT;
 
			}
 
			GamelogStopAction();
 
			SlXvSetCurrentState();
 
			return SL_OK;
 
		}
 

	
 
		assert(dft == DFT_GAME_FILE);
 
		switch (fop) {
 
			case SLO_CHECK:
src/saveload/saveload.h
Show inline comments
 
@@ -15,12 +15,14 @@
 
#include "../strings_type.h"
 
#include "../core/span_type.hpp"
 
#include <optional>
 
#include <string>
 
#include <vector>
 

	
 
#include "extended_ver_sl.h"
 

	
 
/** SaveLoad versions
 
 * Previous savegame versions, the trunk revision where they were
 
 * introduced and the released version that had that particular
 
 * savegame version.
 
 * Up to savegame version 18 there is a minor version as well.
 
 *
 
@@ -320,12 +322,36 @@ enum SaveLoadVersion : uint16 {
 
	 * indicate they are loading a savegame from a patchpack.
 
	 * For future patchpack creators: please follow a system like JGRPP, where
 
	 * the version is masked with 0x8000, and the true version is stored in
 
	 * its own chunk with feature toggles.
 
	 */
 
	SLV_START_PATCHPACKS,                   ///< 220  First known patchpack to use a version just above ours.
 

	
 
	// legacy patchpack version codes
 
	SLV_PP1X = 100,                         ///< 1.x
 
	SLV_PP2X = 126,                         ///< 2.x
 
	SLV_PP3X = 186,                         ///< 3.x
 
	SLV_PP4X = 189,                         ///< 4.20033.004 (conflicting with 5.x, because FGS)
 
	SLV_PP5X_DAYLENGTH = 160,               ///< 5.22434.001
 
	SLV_PP5X_HOTFIX_174 = 174,              ///< 5.23975.005b
 
	SLV_PP5X_HOTFIX_175 = 175,              ///< 5.24187.007
 
	SLV_PP5X_HOTFIX_177 = 177,              ///< 5.24771.008b
 
	SLV_PP5X_HOTFIX_187 = 187,              ///< 5.25942.009
 
	SLV_PP5X_STATION_NEWGRF = 188,          ///< 5.26167.011
 
	SLV_PP5X_INFRASTRUCTURE = 189,          ///< 5.26167.012
 
	SLV_PP5X_RUNNING_COST_MULT = 194,       ///< 5.27099.013
 
	SLV_PP5X_PLANE_RANGE_MULT = 195,        ///< 5.27500.016
 
	SLV_PP5X_HOTFIX_196 = 196,              ///< 5.27930.017
 
	SLV_PP5X_EXTRA_EXPENSES = 197,          ///< 5.27930.018
 
	SLV_PP5X_HOTFIX_198 = 198,              ///< 5.020
 
	SLV_PP5X_IMPROVED_BREAKDOWNS = 217,     ///< 5.022 dev
 
	SLV_PP5X_BREAKDOWN_SCALER = 218,        ///< 5.022 dev
 
	SLV_PP5X_TOWN_IMPROVEMENTS = 219,       ///< 5.022 dev
 
	SLV_PP5X_DILAPIDATION = 220,            ///< 5.022 final
 
	SLV_PP5X_HOTFIX_222 = 222,              ///< 5.024
 

	
 
	SLV_END_PATCHPACKS = 286,               ///< 286  Last known patchpack to use a version just above ours.
 

	
 
	SLV_GS_INDUSTRY_CONTROL,                ///< 287  PR#7912 and PR#8115 GS industry control.
 
	SLV_VEH_MOTION_COUNTER,                 ///< 288  PR#8591 Desync safe motion counter
 
	SLV_INDUSTRY_TEXT,                      ///< 289  PR#8576 v1.11.0-RC1  Additional GS text for industries.
 

	
 
@@ -660,12 +686,13 @@ struct SaveLoad {
 
	SaveLoadVersion version_from;   ///< Save/load the variable starting from this savegame version.
 
	SaveLoadVersion version_to;     ///< Save/load the variable before this savegame version.
 
	size_t size;                    ///< The sizeof size.
 
	SaveLoadAddrProc *address_proc; ///< Callback proc the get the actual variable address in memory.
 
	size_t extra_data;              ///< Extra data for the callback proc.
 
	std::shared_ptr<SaveLoadHandler> handler; ///< Custom handler for Save/Load procs.
 
	SlXvFeatureTest ext_feature_test;  ///< extended feature test
 
};
 

	
 
/**
 
 * SaveLoad information for backwards compatibility.
 
 *
 
 * At SLV_SETTINGS_NAME a new method of keeping track of fields in a savegame
 
@@ -675,98 +702,115 @@ struct SaveLoad {
 
 */
 
struct SaveLoadCompat {
 
	std::string name;             ///< Name of the field.
 
	uint16 length;                ///< Length of the NULL field.
 
	SaveLoadVersion version_from; ///< Save/load the variable starting from this savegame version.
 
	SaveLoadVersion version_to;   ///< Save/load the variable before this savegame version.
 
	SlXvFeatureTest ext_feature_test;  ///< extended feature test
 
};
 

	
 
/**
 
 * Storage of simple variables, references (pointers), and arrays.
 
 * @param cmd      Load/save type. @see SaveLoadType
 
 * @param base     Name of the class or struct containing the variable.
 
 * @param variable Name of the variable in the class or struct referenced by \a base.
 
 * @param type     Storage of the data in memory and in the savegame.
 
 * @param from     First savegame version that has the field.
 
 * @param to       Last savegame version that has the field.
 
 * @param extra    Extra data to pass to the address callback function.
 
 * @param extver   Extended feature test
 
 * @note In general, it is better to use one of the SLE_* macros below.
 
 */
 
#define SLE_GENERAL(cmd, base, variable, type, length, from, to, extra) SaveLoad {#variable, cmd, type, length, from, to, cpp_sizeof(base, variable), [] (void *b, size_t) -> void * { assert(b != nullptr); return const_cast<void *>(static_cast<const void *>(std::addressof(static_cast<base *>(b)->variable))); }, extra, nullptr}
 
#define SLE_GENERAL_X(cmd, base, variable, type, length, from, to, extra, extver) SaveLoad {#variable, cmd, type, length, from, to, cpp_sizeof(base, variable), [] (void *b, size_t) -> void * { assert(b != nullptr); return const_cast<void *>(static_cast<const void *>(std::addressof(static_cast<base *>(b)->variable))); }, extra, nullptr, extver}
 
#define SLE_GENERAL(cmd, base, variable, type, length, from, to, extra) SLE_GENERAL_X(cmd, base, variable, type, length, from, to, extra, SlXvFeatureTest())
 

	
 
/**
 
 * Storage of a variable in some savegame versions.
 
 * @param base     Name of the class or struct containing the variable.
 
 * @param variable Name of the variable in the class or struct referenced by \a base.
 
 * @param type     Storage of the data in memory and in the savegame.
 
 * @param from     First savegame version that has the field.
 
 * @param to       Last savegame version that has the field.
 
 * @param extver   Extended feature test
 
 */
 
#define SLE_CONDVAR(base, variable, type, from, to) SLE_GENERAL(SL_VAR, base, variable, type, 0, from, to, 0)
 
#define SLE_CONDVAR_X(base, variable, type, from, to, extver) SLE_GENERAL_X(SL_VAR, base, variable, type, 0, from, to, 0, extver)
 
#define SLE_CONDVAR(base, variable, type, from, to) SLE_CONDVAR_X(base, variable, type, from, to, SlXvFeatureTest())
 

	
 
/**
 
 * Storage of a reference in some savegame versions.
 
 * @param base     Name of the class or struct containing the variable.
 
 * @param variable Name of the variable in the class or struct referenced by \a base.
 
 * @param type     Type of the reference, a value from #SLRefType.
 
 * @param from     First savegame version that has the field.
 
 * @param to       Last savegame version that has the field.
 
 * @param extver   Extended feature test
 
 */
 
#define SLE_CONDREF(base, variable, type, from, to) SLE_GENERAL(SL_REF, base, variable, type, 0, from, to, 0)
 
#define SLE_CONDREF_X(base, variable, type, from, to, extver) SLE_GENERAL_X(SL_REF, base, variable, type, 0, from, to, 0, extver)
 
#define SLE_CONDREF(base, variable, type, from, to) SLE_CONDREF_X(base, variable, type, from, to, SlXvFeatureTest())
 

	
 
/**
 
 * Storage of a fixed-size array of #SL_VAR elements in some savegame versions.
 
 * @param base     Name of the class or struct containing the array.
 
 * @param variable Name of the variable in the class or struct referenced by \a base.
 
 * @param type     Storage of the data in memory and in the savegame.
 
 * @param length   Number of elements in the array.
 
 * @param from     First savegame version that has the array.
 
 * @param to       Last savegame version that has the array.
 
 * @param extver   Extended feature test
 
 */
 
#define SLE_CONDARR(base, variable, type, length, from, to) SLE_GENERAL(SL_ARR, base, variable, type, length, from, to, 0)
 
#define SLE_CONDARR_X(base, variable, type, length, from, to, extver) SLE_GENERAL_X(SL_ARR, base, variable, type, length, from, to, 0, extver)
 
#define SLE_CONDARR(base, variable, type, length, from, to) SLE_CONDARR_X(base, variable, type, length, from, to, SlXvFeatureTest())
 

	
 
/**
 
 * Storage of a string in some savegame versions.
 
 * @param base     Name of the class or struct containing the string.
 
 * @param variable Name of the variable in the class or struct referenced by \a base.
 
 * @param type     Storage of the data in memory and in the savegame.
 
 * @param length   Number of elements in the string (only used for fixed size buffers).
 
 * @param from     First savegame version that has the string.
 
 * @param to       Last savegame version that has the string.
 
 * @param extver   Extended feature test
 
 */
 
#define SLE_CONDSTR(base, variable, type, length, from, to) SLE_GENERAL(SL_STR, base, variable, type, length, from, to, 0)
 
#define SLE_CONDSTR_X(base, variable, type, length, from, to, extver) SLE_GENERAL_X(SL_STR, base, variable, type, length, from, to, 0, extver)
 
#define SLE_CONDSTR(base, variable, type, length, from, to) SLE_CONDSTR_X(base, variable, type, length, from, to, SlXvFeatureTest())
 

	
 
/**
 
 * Storage of a \c std::string in some savegame versions.
 
 * @param base     Name of the class or struct containing the string.
 
 * @param variable Name of the variable in the class or struct referenced by \a base.
 
 * @param type     Storage of the data in memory and in the savegame.
 
 * @param from     First savegame version that has the string.
 
 * @param to       Last savegame version that has the string.
 
 * @param extver   Extended feature test
 
 */
 
#define SLE_CONDSSTR(base, variable, type, from, to) SLE_GENERAL(SL_STDSTR, base, variable, type, 0, from, to, 0)
 
#define SLE_CONDSSTR_X(base, variable, type, from, to, extver) SLE_GENERAL_X(SL_STDSTR, base, variable, type, 0, from, to, 0, extver)
 
#define SLE_CONDSSTR(base, variable, type, from, to) SLE_CONDSSTR_X(base, variable, type, from, to, SlXvFeatureTest())
 

	
 
/**
 
 * Storage of a list of #SL_REF elements in some savegame versions.
 
 * @param base     Name of the class or struct containing the list.
 
 * @param variable Name of the variable in the class or struct referenced by \a base.
 
 * @param type     Storage of the data in memory and in the savegame.
 
 * @param from     First savegame version that has the list.
 
 * @param to       Last savegame version that has the list.
 
 * @param extver   Extended feature test
 
 */
 
#define SLE_CONDREFLIST(base, variable, type, from, to) SLE_GENERAL(SL_REFLIST, base, variable, type, 0, from, to, 0)
 
#define SLE_CONDREFLIST_X(base, variable, type, from, to, extver) SLE_GENERAL_X(SL_REFLIST, base, variable, type, 0, from, to, 0, extver)
 
#define SLE_CONDREFLIST(base, variable, type, from, to) SLE_CONDREFLIST_X(base, variable, type, from, to, SlXvFeatureTest())
 

	
 
/**
 
 * Storage of a deque of #SL_VAR elements in some savegame versions.
 
 * @param base     Name of the class or struct containing the list.
 
 * @param variable Name of the variable in the class or struct referenced by \a base.
 
 * @param type     Storage of the data in memory and in the savegame.
 
 * @param from     First savegame version that has the list.
 
 * @param to       Last savegame version that has the list.
 
 * @param extver   Extended feature test
 
 */
 
#define SLE_CONDDEQUE(base, variable, type, from, to) SLE_GENERAL(SL_DEQUE, base, variable, type, 0, from, to, 0)
 
#define SLE_CONDDEQUE_X(base, variable, type, from, to, extver) SLE_GENERAL_X(SL_DEQUE, base, variable, type, 0, from, to, 0, extver)
 
#define SLE_CONDDEQUE(base, variable, type, from, to) SLE_CONDDEQUE_X(base, variable, type, from, to, SlXvFeatureTest())
 

	
 
/**
 
 * Storage of a variable in every version of a savegame.
 
 * @param base     Name of the class or struct containing the variable.
 
 * @param variable Name of the variable in the class or struct referenced by \a base.
 
 * @param type     Storage of the data in memory and in the savegame.
 
@@ -834,104 +878,124 @@ struct SaveLoadCompat {
 
 * @param variable Name of the global variable.
 
 * @param type     Storage of the data in memory and in the savegame.
 
 * @param from     First savegame version that has the field.
 
 * @param to       Last savegame version that has the field.
 
 * @param extra    Extra data to pass to the address callback function.
 
 * @note In general, it is better to use one of the SLEG_* macros below.
 
 * @param extver   Extended feature test
 
 */
 
#define SLEG_GENERAL(name, cmd, variable, type, length, from, to, extra) SaveLoad {name, cmd, type, length, from, to, sizeof(variable), [] (void *, size_t) -> void * { return static_cast<void *>(std::addressof(variable)); }, extra, nullptr}
 
#define SLEG_GENERAL_X(name, cmd, variable, type, length, from, to, extra, extver) SaveLoad {name, cmd, type, length, from, to, sizeof(variable), [] (void *, size_t) -> void * { return static_cast<void *>(std::addressof(variable)); }, extra, nullptr, extver}
 
#define SLEG_GENERAL(name, cmd, variable, type, length, from, to, extra) SLEG_GENERAL_X(name, cmd, variable, type, length, from, to, extra, SlXvFeatureTest())
 

	
 
/**
 
 * Storage of a global variable in some savegame versions.
 
 * @param name     The name of the field.
 
 * @param variable Name of the global variable.
 
 * @param type     Storage of the data in memory and in the savegame.
 
 * @param from     First savegame version that has the field.
 
 * @param to       Last savegame version that has the field.
 
 * @param extver   Extended feature test
 
 */
 
#define SLEG_CONDVAR(name, variable, type, from, to) SLEG_GENERAL(name, SL_VAR, variable, type, 0, from, to, 0)
 
#define SLEG_CONDVAR_X(name, variable, type, from, to, extver) SLEG_GENERAL_X(name, SL_VAR, variable, type, 0, from, to, 0, extver)
 
#define SLEG_CONDVAR(name, variable, type, from, to) SLEG_CONDVAR_X(name, variable, type, from, to, SlXvFeatureTest())
 

	
 
/**
 
 * Storage of a global reference in some savegame versions.
 
 * @param name     The name of the field.
 
 * @param variable Name of the global variable.
 
 * @param type     Storage of the data in memory and in the savegame.
 
 * @param from     First savegame version that has the field.
 
 * @param to       Last savegame version that has the field.
 
 * @param extver   Extended feature test
 
 */
 
#define SLEG_CONDREF(name, variable, type, from, to) SLEG_GENERAL(name, SL_REF, variable, type, 0, from, to, 0)
 
#define SLEG_CONDREF_X(name, variable, type, from, to, extver) SLEG_GENERAL_X(name, SL_REF, variable, type, 0, from, to, 0, extver)
 
#define SLEG_CONDREF(name, variable, type, from, to) SLEG_CONDREF_X(name, variable, type, from, to, SlXvFeatureTest())
 

	
 
/**
 
 * Storage of a global fixed-size array of #SL_VAR elements in some savegame versions.
 
 * @param name     The name of the field.
 
 * @param variable Name of the global variable.
 
 * @param type     Storage of the data in memory and in the savegame.
 
 * @param length   Number of elements in the array.
 
 * @param from     First savegame version that has the array.
 
 * @param to       Last savegame version that has the array.
 
 * @param extver   Extended feature test
 
 */
 
#define SLEG_CONDARR(name, variable, type, length, from, to) SLEG_GENERAL(name, SL_ARR, variable, type, length, from, to, 0)
 
#define SLEG_CONDARR_X(name, variable, type, length, from, to, extver) SLEG_GENERAL_X(name, SL_ARR, variable, type, length, from, to, 0, extver)
 
#define SLEG_CONDARR(name, variable, type, length, from, to) SLEG_CONDARR_X(name, variable, type, length, from, to, SlXvFeatureTest())
 

	
 
/**
 
 * Storage of a global string in some savegame versions.
 
 * @param name     The name of the field.
 
 * @param variable Name of the global variable.
 
 * @param type     Storage of the data in memory and in the savegame.
 
 * @param length   Number of elements in the string (only used for fixed size buffers).
 
 * @param from     First savegame version that has the string.
 
 * @param to       Last savegame version that has the string.
 
 * @param extver   Extended feature test
 
 */
 
#define SLEG_CONDSTR(name, variable, type, length, from, to) SLEG_GENERAL(name, SL_STR, variable, type, length, from, to, 0)
 
#define SLEG_CONDSTR_X(name, variable, type, length, from, to, extver) SLEG_GENERAL_X(name, SL_STR, variable, type, length, from, to, 0, extver)
 
#define SLEG_CONDSTR(name, variable, type, length, from, to) SLEG_CONDSTR_X(name, variable, type, length, from, to, SlXvFeatureTest())
 

	
 
/**
 
 * Storage of a global \c std::string in some savegame versions.
 
 * @param name     The name of the field.
 
 * @param variable Name of the global variable.
 
 * @param type     Storage of the data in memory and in the savegame.
 
 * @param from     First savegame version that has the string.
 
 * @param to       Last savegame version that has the string.
 
 * @param extver   Extended feature test
 
 */
 
#define SLEG_CONDSSTR(name, variable, type, from, to) SLEG_GENERAL(name, SL_STDSTR, variable, type, 0, from, to, 0)
 
#define SLEG_CONDSSTR_X(name, variable, type, from, to, extver) SLEG_GENERAL_X(name, SL_STDSTR, variable, type, 0, from, to, 0, extver)
 
#define SLEG_CONDSSTR(name, variable, type, from, to) SLEG_CONDSSTR_X(name, variable, type, from, to, SlXvFeatureTest())
 

	
 
/**
 
 * Storage of a structs in some savegame versions.
 
 * @param name     The name of the field.
 
 * @param handler  SaveLoadHandler for the structs.
 
 * @param from     First savegame version that has the struct.
 
 * @param to       Last savegame version that has the struct.
 
 * @param extver   Extended feature test
 
 */
 
#define SLEG_CONDSTRUCT(name, handler, from, to) SaveLoad {name, SL_STRUCT, 0, 0, from, to, 0, nullptr, 0, std::make_shared<handler>()}
 
#define SLEG_CONDSTRUCT_X(name, handler, from, to, extver) SaveLoad {name, SL_STRUCT, 0, 0, from, to, 0, nullptr, 0, std::make_shared<handler>(), extver}
 
#define SLEG_CONDSTRUCT(name, handler, from, to) SLEG_CONDSTRUCT_X(name, handler, from, to, SlXvFeatureTest())
 

	
 
/**
 
 * Storage of a global reference list in some savegame versions.
 
 * @param name     The name of the field.
 
 * @param variable Name of the global variable.
 
 * @param type     Storage of the data in memory and in the savegame.
 
 * @param from     First savegame version that has the list.
 
 * @param to       Last savegame version that has the list.
 
 * @param extver   Extended feature test
 
 */
 
#define SLEG_CONDREFLIST(name, variable, type, from, to) SLEG_GENERAL(name, SL_REFLIST, variable, type, 0, from, to, 0)
 
#define SLEG_CONDREFLIST_X(name, variable, type, from, to, extver) SLEG_GENERAL_X(name, SL_REFLIST, variable, type, 0, from, to, 0, extver)
 
#define SLEG_CONDREFLIST(name, variable, type, from, to) SLEG_CONDREFLIST_X(name, variable, type, from, to, SlXvFeatureTest())
 

	
 
/**
 
 * Storage of a global vector of #SL_VAR elements in some savegame versions.
 
 * @param name     The name of the field.
 
 * @param variable Name of the global variable.
 
 * @param type     Storage of the data in memory and in the savegame.
 
 * @param from     First savegame version that has the list.
 
 * @param to       Last savegame version that has the list.
 
 * @param extver   Extended feature test
 
 */
 
#define SLEG_CONDVECTOR(name, variable, type, from, to) SLEG_GENERAL(name, SL_VECTOR, variable, type, 0, from, to, 0)
 
#define SLEG_CONDVECTOR_X(name, variable, type, from, to, extver) SLEG_GENERAL_X(name, SL_VECTOR, variable, type, 0, from, to, 0, extver)
 
#define SLEG_CONDVECTOR(name, variable, type, from, to) SLEG_CONDVECTOR_X(name, variable, type, from, to, SlXvFeatureTest())
 

	
 
/**
 
 * Storage of a list of structs in some savegame versions.
 
 * @param name     The name of the field.
 
 * @param handler  SaveLoadHandler for the list of structs.
 
 * @param from     First savegame version that has the list.
 
 * @param to       Last savegame version that has the list.
 
 * @param extver   Extended feature test
 
 */
 
#define SLEG_CONDSTRUCTLIST(name, handler, from, to) SaveLoad {name, SL_STRUCTLIST, 0, 0, from, to, 0, nullptr, 0, std::make_shared<handler>()}
 
#define SLEG_CONDSTRUCTLIST_X(name, handler, from, to, extver) SaveLoad {name, SL_STRUCTLIST, 0, 0, from, to, 0, nullptr, 0, std::make_shared<handler>(), extver}
 
#define SLEG_CONDSTRUCTLIST(name, handler, from, to) SLEG_CONDSTRUCTLIST_X(name, handler, from, to, SlXvFeatureTest())
 

	
 
/**
 
 * Storage of a global variable in every savegame version.
 
 * @param name     The name of the field.
 
 * @param variable Name of the global variable.
 
 * @param type     Storage of the data in memory and in the savegame.
 
@@ -1001,24 +1065,26 @@ struct SaveLoadCompat {
 
#define SLEG_STRUCTLIST(name, handler) SLEG_CONDSTRUCTLIST(name, handler, SL_MIN_VERSION, SL_MAX_VERSION)
 

	
 
/**
 
 * Field name where the real SaveLoad can be located.
 
 * @param name The name of the field.
 
 */
 
#define SLC_VAR(name) {name, 0, SL_MIN_VERSION, SL_MAX_VERSION}
 
#define SLC_VAR_X(name, extver) {name, 0, SL_MIN_VERSION, SL_MAX_VERSION, extver}
 
#define SLC_VAR(name) {name, 0, SL_MIN_VERSION, SL_MAX_VERSION, SlXvFeatureTest()}
 

	
 
/**
 
 * Empty space in every savegame version.
 
 * @param length Length of the empty space.
 
 * @param from   First savegame version that has the empty space.
 
 * @param to     Last savegame version that has the empty space.
 
 */
 
#define SLC_NULL(length, from, to) {{}, length, from, to}
 
#define SLC_NULL_X(length, from, to, extver) {{}, length, from, to, extver}
 
#define SLC_NULL(length, from, to) {{}, length, from, to, SlXvFeatureTest()}
 

	
 
/** End marker of compat variables save or load. */
 
#define SLC_END() {{}, 0, SL_MIN_VERSION, SL_MIN_VERSION}
 
#define SLC_END() {{}, 0, SL_MIN_VERSION, SL_MIN_VERSION, SlXvFeatureTest()}
 

	
 
/**
 
 * Checks whether the savegame is below \a major.\a minor.
 
 * @param major Major number of the version to check against.
 
 * @param minor Minor number of the version to check against. If \a minor is 0 or not specified, only the major number is checked.
 
 * @return Savegame version is earlier than the specified version.
 
@@ -1121,12 +1187,23 @@ void SlSetLength(size_t length);
 
size_t SlCalcObjMemberLength(const void *object, const SaveLoad &sld);
 
size_t SlCalcObjLength(const void *object, const SaveLoadTable &slt);
 

	
 
byte SlReadByte();
 
void SlWriteByte(byte b);
 

	
 
int SlReadUint16();
 
uint32 SlReadUint32();
 
uint64 SlReadUint64();
 

	
 
void SlWriteUint16(uint16 v);
 
void SlWriteUint32(uint32 v);
 
void SlWriteUint64(uint64 v);
 

	
 
size_t SlGetBytesRead();
 
size_t SlGetBytesWritten();
 

	
 
void SlGlobList(const SaveLoadTable &slt);
 
void SlCopy(void *object, size_t length, VarType conv);
 
std::vector<SaveLoad> SlTableHeader(const SaveLoadTable &slt);
 
std::vector<SaveLoad> SlCompatTableHeader(const SaveLoadTable &slt, const SaveLoadCompatTable &slct);
 
void SlObject(void *object, const SaveLoadTable &slt);
 
void NORETURN SlError(StringID string, const char *extra_msg = nullptr);
src/saveload/saveload_buffer.h
Show inline comments
 
new file 100644
 
/* $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_buffer.h Functions/types related to buffers used for saving and loading games. */
 

	
 
#ifndef SAVELOAD_BUFFER_H
 
#define SAVELOAD_BUFFER_H
 

	
 
#include "../core/alloc_func.hpp"
 
#include "../core/endian_type.hpp"
 
#include "../core/endian_func.hpp"
 
#include "../core/math_func.hpp"
 

	
 
#include <vector>
 
#include <utility>
 

	
 
struct LoadFilter;
 
struct SaveFilter;
 

	
 
/** Save in chunks of 128 KiB. */
 
static const size_t MEMORY_CHUNK_SIZE = 128 * 1024;
 

	
 
/** A buffer for reading (and buffering) savegame data. */
 
struct ReadBuffer {
 
	byte buf[MEMORY_CHUNK_SIZE]; ///< Buffer we're going to read from.
 
	byte *bufp;                  ///< Location we're at reading the buffer.
 
	byte *bufe;                  ///< End of the buffer we can read from.
 
	LoadFilter *reader;          ///< The filter used to actually read.
 
	size_t read;                 ///< The amount of read bytes so far from the filter.
 

	
 
	/**
 
	 * Initialise our variables.
 
	 * @param reader The filter to actually read data.
 
	 */
 
	ReadBuffer(LoadFilter *reader) : bufp(nullptr), bufe(nullptr), reader(reader), read(0)
 
	{
 
	}
 

	
 
	static ReadBuffer *GetCurrent();
 

	
 
	void SkipBytesSlowPath(size_t bytes);
 
	void AcquireBytes();
 

	
 
	inline void SkipBytes(size_t bytes)
 
	{
 
		byte *b = this->bufp + bytes;
 
		if (likely(b <= this->bufe)) {
 
			this->bufp = b;
 
		} else {
 
			SkipBytesSlowPath(bytes);
 
		}
 
	}
 

	
 
	inline byte RawReadByte()
 
	{
 
		return *this->bufp++;
 
	}
 

	
 
	inline byte ReadByte()
 
	{
 
		if (unlikely(this->bufp == this->bufe)) {
 
			this->AcquireBytes();
 
		}
 

	
 
		return RawReadByte();
 
	}
 

	
 
	inline void CheckBytes(size_t bytes)
 
	{
 
		while (unlikely(this->bufp + bytes > this->bufe)) this->AcquireBytes();
 
	}
 

	
 
	inline int RawReadUint16()
 
	{
 
#if OTTD_ALIGNMENT == 0
 
		int x = FROM_BE16(*((const unaligned_uint16*) this->bufp));
 
		this->bufp += 2;
 
		return x;
 
#else
 
		int x = this->RawReadByte() << 8;
 
		return x | this->RawReadByte();
 
#endif
 
	}
 

	
 
	inline uint32 RawReadUint32()
 
	{
 
#if OTTD_ALIGNMENT == 0
 
		uint32 x = FROM_BE32(*((const unaligned_uint32*) this->bufp));
 
		this->bufp += 4;
 
		return x;
 
#else
 
		uint32 x = this->RawReadUint16() << 16;
 
		return x | this->RawReadUint16();
 
#endif
 
	}
 

	
 
	inline uint64 RawReadUint64()
 
	{
 
#if OTTD_ALIGNMENT == 0
 
		uint64 x = FROM_BE64(*((const unaligned_uint64*) this->bufp));
 
		this->bufp += 8;
 
		return x;
 
#else
 
		uint32 x = this->RawReadUint32();
 
		uint32 y = this->RawReadUint32();
 
		return (uint64)x << 32 | y;
 
#endif
 
	}
 

	
 
	inline void CopyBytes(byte *ptr, size_t length)
 
	{
 
		while (length) {
 
			if (unlikely(this->bufp == this->bufe)) {
 
				this->AcquireBytes();
 
			}
 
			size_t to_copy = std::min<size_t>(this->bufe - this->bufp, length);
 
			memcpy(ptr, this->bufp, to_copy);
 
			this->bufp += to_copy;
 
			ptr += to_copy;
 
			length -= to_copy;
 
		}
 
	}
 

	
 
	/**
 
	 * Get the size of the memory dump made so far.
 
	 * @return The size.
 
	 */
 
	inline size_t GetSize() const
 
	{
 
		return this->read - (this->bufe - this->bufp);
 
	}
 
};
 

	
 

	
 
/** Container for dumping the savegame (quickly) to memory. */
 
struct MemoryDumper {
 
	struct BufferInfo {
 
		byte *data;
 
		size_t size = 0;
 

	
 
		BufferInfo(byte *d) : data(d) {}
 
		~BufferInfo() { free(this->data); }
 

	
 
		BufferInfo(const BufferInfo &) = delete;
 
		BufferInfo(BufferInfo &&other) : data(other.data), size(other.size) { other.data = nullptr; };
 
	};
 

	
 
	std::vector<BufferInfo> blocks;         ///< Buffer with blocks of allocated memory.
 
	byte *buf = nullptr;                    ///< Buffer we're going to write to.
 
	byte *bufe = nullptr;                   ///< End of the buffer we write to.
 
	size_t completed_block_bytes = 0;       ///< Total byte count of completed blocks.
 

	
 
	byte *autolen_buf = nullptr;
 
	byte *autolen_buf_end = nullptr;
 
	byte *saved_buf = nullptr;
 
	byte *saved_bufe = nullptr;
 

	
 
	MemoryDumper()
 
	{
 
		const size_t size = 8192;
 
		this->autolen_buf = CallocT<byte>(size);
 
		this->autolen_buf_end = this->autolen_buf + size;
 
	}
 

	
 
	~MemoryDumper()
 
	{
 
		free(this->autolen_buf);
 
	}
 

	
 
	static MemoryDumper *GetCurrent();
 

	
 
	void FinaliseBlock();
 
	void AllocateBuffer();
 

	
 
	inline void CheckBytes(size_t bytes)
 
	{
 
		if (unlikely(this->buf + bytes > this->bufe)) this->AllocateBuffer();
 
	}
 

	
 
	/**
 
	 * Write a single byte into the dumper.
 
	 * @param b The byte to write.
 
	 */
 
	inline void WriteByte(byte b)
 
	{
 
		/* Are we at the end of this chunk? */
 
		if (unlikely(this->buf == this->bufe)) {
 
			this->AllocateBuffer();
 
		}
 

	
 
		*this->buf++ = b;
 
	}
 

	
 
	inline void CopyBytes(byte *ptr, size_t length)
 
	{
 
		while (length) {
 
			if (unlikely(this->buf == this->bufe)) {
 
				this->AllocateBuffer();
 
			}
 
			size_t to_copy = std::min<size_t>(this->bufe - this->buf, length);
 
			memcpy(this->buf, ptr, to_copy);
 
			this->buf += to_copy;
 
			ptr += to_copy;
 
			length -= to_copy;
 
		}
 
	}
 

	
 
	inline void RawWriteByte(byte b)
 
	{
 
		*this->buf++ = b;
 
	}
 

	
 
	inline void RawWriteUint16(uint16 v)
 
	{
 
#if OTTD_ALIGNMENT == 0
 
		*((unaligned_uint16 *) this->buf) = TO_BE16(v);
 
#else
 
		this->buf[0] = GB(v, 8, 8);
 
		this->buf[1] = GB(v, 0, 8);
 
#endif
 
		this->buf += 2;
 
	}
 

	
 
	inline void RawWriteUint32(uint32 v)
 
	{
 
#if OTTD_ALIGNMENT == 0
 
		*((unaligned_uint32 *) this->buf) = TO_BE32(v);
 
#else
 
		this->buf[0] = GB(v, 24, 8);
 
		this->buf[1] = GB(v, 16, 8);
 
		this->buf[2] = GB(v, 8, 8);
 
		this->buf[3] = GB(v, 0, 8);
 
#endif
 
		this->buf += 4;
 
	}
 

	
 
	inline void RawWriteUint64(uint64 v)
 
	{
 
#if OTTD_ALIGNMENT == 0
 
		*((unaligned_uint64 *) this->buf) = TO_BE64(v);
 
#else
 
		this->buf[0] = GB(v, 56, 8);
 
		this->buf[1] = GB(v, 48, 8);
 
		this->buf[2] = GB(v, 40, 8);
 
		this->buf[3] = GB(v, 32, 8);
 
		this->buf[4] = GB(v, 24, 8);
 
		this->buf[5] = GB(v, 16, 8);
 
		this->buf[6] = GB(v, 8, 8);
 
		this->buf[7] = GB(v, 0, 8);
 
#endif
 
		this->buf += 8;
 
	}
 

	
 
	void Flush(SaveFilter *writer);
 
	size_t GetSize() const;
 
	void StartAutoLength();
 
	std::pair<byte *, size_t> StopAutoLength();
 
};
 

	
 
#endif
src/saveload/station_sl.cpp
Show inline comments
 
@@ -591,12 +591,13 @@ public:
 
		    SLE_VAR(Station, time_since_unload,          SLE_UINT8),
 
		    SLE_VAR(Station, last_vehicle_type,          SLE_UINT8),
 
		    SLE_VAR(Station, had_vehicle_of_type,        SLE_UINT8),
 
		SLE_REFLIST(Station, loading_vehicles,           REF_VEHICLE),
 
		SLE_CONDVAR(Station, always_accepted,            SLE_FILE_U32 | SLE_VAR_U64, SLV_127, SLV_EXTEND_CARGOTYPES),
 
		SLE_CONDVAR(Station, always_accepted,            SLE_UINT64,                 SLV_EXTEND_CARGOTYPES, SL_MAX_VERSION),
 
	  	SLE_CONDVAR_X(Station, dilapidation,               SLE_UINT16,                 SL_MIN_VERSION, SL_MAX_VERSION, SlXvFeatureTest(XSLFTO_AND, XSLFI_DILAPIDATION)),
 
		SLEG_STRUCTLIST("goods", SlStationGoods),
 
	};
 
	inline const static SaveLoadCompatTable compat_description = _station_normal_sl_compat;
 

	
 
	void Save(BaseStation *bst) const
 
	{
src/saveload/vehicle_sl.cpp
Show inline comments
 
@@ -677,12 +677,14 @@ public:
 
		    SLE_VAR(Vehicle, reliability,           SLE_UINT16),
 
		    SLE_VAR(Vehicle, reliability_spd_dec,   SLE_UINT16),
 
		    SLE_VAR(Vehicle, breakdown_ctr,         SLE_UINT8),
 
		    SLE_VAR(Vehicle, breakdown_delay,       SLE_UINT8),
 
		    SLE_VAR(Vehicle, breakdowns_since_last_service, SLE_UINT8),
 
		    SLE_VAR(Vehicle, breakdown_chance,      SLE_UINT8),
 
		 SLE_CONDVAR_X(Vehicle, breakdown_type,        SLE_UINT8, SL_MIN_VERSION, SL_MAX_VERSION, SlXvFeatureTest(XSLFTO_AND, XSLFI_IMPROVED_BREAKDOWNS)),
 
		 SLE_CONDVAR_X(Vehicle, breakdown_severity,    SLE_UINT8, SL_MIN_VERSION, SL_MAX_VERSION, SlXvFeatureTest(XSLFTO_AND, XSLFI_IMPROVED_BREAKDOWNS)),
 
		SLE_CONDVAR(Vehicle, build_year,            SLE_FILE_U8 | SLE_VAR_I32,    SL_MIN_VERSION,  SLV_31),
 
		SLE_CONDVAR(Vehicle, build_year,            SLE_INT32,                   SLV_31, SL_MAX_VERSION),
 

	
 
		    SLE_VAR(Vehicle, load_unload_ticks,     SLE_UINT16),
 
		SLEG_CONDVAR("cargo_paid_for", _cargo_paid_for, SLE_UINT16,              SLV_45, SL_MAX_VERSION),
 
		SLE_CONDVAR(Vehicle, vehicle_flags,         SLE_FILE_U8 | SLE_VAR_U16,   SLV_40, SLV_180),
src/script/api/script_company.hpp
Show inline comments
 
@@ -110,12 +110,21 @@ public:
 
		EXPENSES_TRAIN_INC    = ::EXPENSES_TRAIN_INC,    ///< Income from trains.
 
		EXPENSES_ROADVEH_INC  = ::EXPENSES_ROADVEH_INC,  ///< Income from road vehicles.
 
		EXPENSES_AIRCRAFT_INC = ::EXPENSES_AIRCRAFT_INC, ///< Income from aircraft.
 
		EXPENSES_SHIP_INC     = ::EXPENSES_SHIP_INC,     ///< Income from ships.
 
		EXPENSES_LOAN_INT     = ::EXPENSES_LOAN_INT,     ///< Interest payments over the loan.
 
		EXPENSES_OTHER        = ::EXPENSES_OTHER,        ///< Other expenses.
 
		EXPENSES_T_TRAIN_CON  = ::EXPENSES_T_TRAIN_CON,
 
		EXPENSES_T_ROAD_CON   = ::EXPENSES_T_ROAD_CON,
 
		EXPENSES_T_AIRCRAFT_CON = ::EXPENSES_T_AIRCRAFT_CON,
 
		EXPENSES_T_SHIP_CON   = ::EXPENSES_T_SHIP_CON,
 
		EXPENSES_T_TREES_CON  = ::EXPENSES_T_TREES_CON,
 
		EXPENSES_T_SCENERY_CON = ::EXPENSES_T_SCENERY_CON,
 
		EXPENSES_T_LANDSCAPING = ::EXPENSES_T_LANDSCAPING,
 
		EXPENSES_T_DEMOLITION = ::EXPENSES_T_DEMOLITION,
 
		EXPENSES_T_REWARD_INC = ::EXPENSES_T_REWARD_INC,
 
		EXPENSES_INVALID      = ::INVALID_EXPENSES,      ///< Invalid expense type.
 
	};
 

	
 
	/**
 
	 * Resolved the given company index to the correct index for the company. If
 
	 *  the company index was COMPANY_SELF it will be resolved to the index of
src/settings_gui.cpp
Show inline comments
 
@@ -1851,12 +1851,53 @@ static SettingsContainer &GetSettingsTre
 

	
 
		SettingsPage *network = main->Add(new SettingsPage(STR_CONFIG_SETTING_NETWORK));
 
		{
 
			network->Add(new SettingEntry("network.use_relay_service"));
 
		}
 

	
 
		SettingsPage *patchpack = main->Add(new SettingsPage(STR_CONFIG_SETTING_PATCHPACK));
 
		{
 
			SettingsPage *patchpack_daylength = patchpack->Add(new SettingsPage(STR_CONFIG_SETTING_PATCHPACK_DAYLENGTH));
 
			{
 
				patchpack_daylength->Add(new SettingEntry("economy.daylength_multiplier"));
 
				patchpack_daylength->Add(new SettingEntry("economy.town_growth_multiplier"));
 
				patchpack_daylength->Add(new SettingEntry("economy.running_cost_multiplier_rail"));
 
				patchpack_daylength->Add(new SettingEntry("economy.running_cost_multiplier_road"));
 
				patchpack_daylength->Add(new SettingEntry("economy.running_cost_multiplier_water"));
 
				patchpack_daylength->Add(new SettingEntry("economy.running_cost_multiplier_air"));
 
				patchpack_daylength->Add(new SettingEntry("economy.infrastructure_base_cost_rail"));
 
				patchpack_daylength->Add(new SettingEntry("economy.infrastructure_base_cost_road"));
 
				patchpack_daylength->Add(new SettingEntry("economy.infrastructure_base_cost_water"));
 
				patchpack_daylength->Add(new SettingEntry("economy.infrastructure_base_cost_air"));
 
				patchpack_daylength->Add(new SettingEntry("economy.infrastructure_base_cost_station"));
 
			}
 

	
 
			SettingsPage *patchpack_dilapidation = patchpack->Add(new SettingsPage(STR_CONFIG_SETTING_PATCHPACK_DILAPIDATION));
 
			{
 
				patchpack_dilapidation->Add(new SettingEntry("economy.dilapidation_max_amount"));
 
				patchpack_dilapidation->Add(new SettingEntry("economy.dilapidation_increase"));
 
				patchpack_dilapidation->Add(new SettingEntry("economy.dilapidation_decrease"));
 
				patchpack_dilapidation->Add(new SettingEntry("economy.dilapidation_pop_rail"));
 
				patchpack_dilapidation->Add(new SettingEntry("economy.dilapidation_pop_road"));
 
				patchpack_dilapidation->Add(new SettingEntry("economy.dilapidation_pop_water"));
 
				patchpack_dilapidation->Add(new SettingEntry("economy.dilapidation_pop_air"));
 
				patchpack_dilapidation->Add(new SettingEntry("economy.dilapidation_fine_rail"));
 
				patchpack_dilapidation->Add(new SettingEntry("economy.dilapidation_fine_road"));
 
				patchpack_dilapidation->Add(new SettingEntry("economy.dilapidation_fine_water"));
 
				patchpack_dilapidation->Add(new SettingEntry("economy.dilapidation_fine_air"));
 
			}
 

	
 
			patchpack->Add(new SettingEntry("gui.load_legacy_patchpack_savedata"));
 
			patchpack->Add(new SettingEntry("gui.colour_based_on_town_rating"));
 
			patchpack->Add(new SettingEntry("economy.bribe_risky"));
 
			patchpack->Add(new SettingEntry("construction.name_stations_based_on_industries"));
 
			patchpack->Add(new SettingEntry("vehicle.plane_range_multiplier"));
 
			patchpack->Add(new SettingEntry("vehicle.improved_breakdowns"));
 
			patchpack->Add(new SettingEntry("difficulty.vehicle_breakdown_scaler"));
 
		}
 

	
 
		main->Init();
 
	}
 
	return *main;
 
}
 

	
 
static const StringID _game_settings_restrict_dropdown[] = {
src/settings_table.cpp
Show inline comments
 
@@ -285,12 +285,28 @@ static void InvalidateNewGRFChangeWindow
 
static void InvalidateCompanyLiveryWindow(int32 new_value)
 
{
 
	InvalidateWindowClassesData(WC_COMPANY_COLOUR, -1);
 
	ResetVehicleColourMap();
 
}
 

	
 
static void PatchpackInvalidateRunningCostsOrElse(int32 new_value)
 
{
 
	InvalidateWindowClassesData(WC_AIRCRAFT_LIST);
 
	InvalidateWindowClassesData(WC_BUILD_VEHICLE);
 
	InvalidateWindowClassesData(WC_ENGINE_PREVIEW);
 
	InvalidateWindowClassesData(WC_FINANCES);
 
	InvalidateWindowClassesData(WC_PERFORMANCE_DETAIL);
 
	InvalidateWindowClassesData(WC_PERFORMANCE_HISTORY);
 
	InvalidateWindowClassesData(WC_REPLACE_VEHICLE);
 
	InvalidateWindowClassesData(WC_ROADVEH_LIST);
 
	InvalidateWindowClassesData(WC_SHIPS_LIST);
 
	InvalidateWindowClassesData(WC_TRAINS_LIST);
 
	InvalidateWindowClassesData(WC_VEHICLE_DETAILS);
 
	InvalidateWindowClassesData(WC_VEHICLE_VIEW);
 
}
 

	
 
static void DifficultyNoiseChange(int32 new_value)
 
{
 
	if (_game_mode == GM_NORMAL) {
 
		UpdateAirportsNoise();
 
		if (_settings_game.economy.station_noise_level) {
 
			InvalidateWindowClassesData(WC_TOWN_VIEW, 0);
src/settings_type.h
Show inline comments
 
@@ -78,12 +78,13 @@ struct DifficultySettings {
 
	byte   industry_density;                 ///< The industry density. @see IndustryDensity
 
	uint32 max_loan;                         ///< the maximum initial loan
 
	byte   initial_interest;                 ///< amount of interest (to pay over the loan)
 
	byte   vehicle_costs;                    ///< amount of money spent on vehicle running cost
 
	byte   competitor_speed;                 ///< the speed at which the AI builds
 
	byte   vehicle_breakdowns;               ///< likelihood of vehicles breaking down
 
	uint32 vehicle_breakdown_scaler;         ///< likelihood of vehicles breaking down REDUX
 
	byte   subsidy_multiplier;               ///< payment multiplier for subsidized deliveries
 
	uint16 subsidy_duration;                 ///< duration of subsidies
 
	byte   construction_cost;                ///< how expensive is building
 
	byte   terrain_type;                     ///< the mountainousness of the landscape
 
	byte   quantity_sea_lakes;               ///< the amount of seas/lakes
 
	bool   economy;                          ///< how volatile is the economy
 
@@ -135,12 +136,13 @@ struct GUISettings {
 
	bool   threaded_saves;                   ///< should we do threaded saves?
 
	bool   keep_all_autosave;                ///< name the autosave in a different way
 
	bool   autosave_on_exit;                 ///< save an autosave when you quit the game, but do not ask "Do you really want to quit?"
 
	bool   autosave_on_network_disconnect;   ///< save an autosave when you get disconnected from a network game with an error?
 
	uint8  date_format_in_default_names;     ///< should the default savegame/screenshot name use long dates (31th Dec 2008), short dates (31-12-2008) or ISO dates (2008-12-31)
 
	byte   max_num_autosaves;                ///< controls how many autosavegames are made before the game starts to overwrite (names them 0 to max_num_autosaves - 1)
 
	bool   colour_based_on_town_rating;      ///< colour code the town name based on rating?
 
	bool   population_in_label;              ///< show the population of a town in its label?
 
	uint8  right_mouse_btn_emulation;        ///< should we emulate right mouse clicking?
 
	uint8  scrollwheel_scrolling;            ///< scrolling using the scroll wheel?
 
	uint8  scrollwheel_multiplier;           ///< how much 'wheel' per incoming event from the OS?
 
	bool   timetable_arrival_departure;      ///< show arrivals and departures in vehicle timetables
 
	bool   right_mouse_wnd_close;            ///< close window with right click
 
@@ -189,12 +191,14 @@ struct GUISettings {
 
	bool   ai_developer_tools;               ///< activate AI developer tools
 
	bool   scenario_developer;               ///< activate scenario developer: allow modifying NewGRFs in an existing game
 
	uint8  settings_restriction_mode;        ///< selected restriction mode in adv. settings GUI. @see RestrictionMode
 
	bool   newgrf_show_old_versions;         ///< whether to show old versions in the NewGRF list
 
	uint8  newgrf_default_palette;           ///< default palette to use for NewGRFs without action 14 palette information
 

	
 
	bool   load_legacy_patchpack_savedata;   ///< whether or not to handle legacy patchpack savedata from version 5.x
 

	
 
	/**
 
	 * Returns true when the user has sufficient privileges to edit newgrfs on a running game
 
	 * @return whether the user has sufficient privileges to edit newgrfs in an existing game
 
	 */
 
	bool UserIsAllowedToChangeNewGRFs() const
 
	{
 
@@ -295,12 +299,13 @@ struct NetworkSettings {
 
	uint8       max_clients;                              ///< maximum amount of clients
 
	Year        restart_game_year;                        ///< year the server restarts
 
	uint8       min_active_clients;                       ///< minimum amount of active clients to unpause the game
 
	bool        reload_cfg;                               ///< reload the config file before restarting
 
	std::string last_joined;                              ///< Last joined server
 
	bool        no_http_content_downloads;                ///< do not do content downloads over HTTP
 
	bool   save_password;                                 ///< If password file is used
 
	UseRelayService use_relay_service;                        ///< Use relay service?
 
};
 

	
 
/** Settings related to the creation of games. */
 
struct GameCreationSettings {
 
	uint32 generation_seed;                  ///< noise seed for world generation
 
@@ -351,12 +356,14 @@ struct ConstructionSettings {
 
	uint32 terraform_per_64k_frames;         ///< how many tile heights may, over a long period, be terraformed per 65536 frames?
 
	uint16 terraform_frame_burst;            ///< how many tile heights may, over a short period, be terraformed?
 
	uint32 clear_per_64k_frames;             ///< how many tiles may, over a long period, be cleared per 65536 frames?
 
	uint16 clear_frame_burst;                ///< how many tiles may, over a short period, be cleared?
 
	uint32 tree_per_64k_frames;              ///< how many trees may, over a long period, be planted per 65536 frames?
 
	uint16 tree_frame_burst;                 ///< how many trees may, over a short period, be planted?
 

	
 
	uint8 name_stations_based_on_industries;  ///< ttr patchpack: name stations based on nearby industries?
 
};
 

	
 
/** Settings related to the AI. */
 
struct AISettings {
 
	bool   ai_in_multiplayer;                ///< so we allow AIs in multiplayer
 
	bool   ai_disable_veh_train;             ///< disable types for AI
 
@@ -491,18 +498,21 @@ struct VehicleSettings {
 
	uint8  freight_trains;                   ///< value to multiply the weight of cargo by
 
	bool   dynamic_engines;                  ///< enable dynamic allocation of engine data
 
	bool   never_expire_vehicles;            ///< never expire vehicles
 
	byte   extend_vehicle_life;              ///< extend vehicle life by this many years
 
	byte   road_side;                        ///< the side of the road vehicles drive on
 
	uint8  plane_crashes;                    ///< number of plane crashes, 0 = none, 1 = reduced, 2 = normal
 
	uint8  plane_range_multiplier;           ///< ttr patchpack: range multiplier
 
	bool   improved_breakdowns;		 ///< different types, chances and serverities of breakdowns
 
};
 

	
 
/** Settings related to the economy. */
 
struct EconomySettings {
 
	bool   inflation;                        ///< disable inflation
 
	bool   bribe;                            ///< enable bribing the local authority
 
	bool   bribe_risky;                      ///< ttr patchpack: whether or not there is a risk of being caught while bribing
 
	EconomyType type;                        ///< economy type (original/smooth/frozen)
 
	bool   allow_shares;                     ///< allow the buying/selling of shares
 
	uint8  min_years_for_shares;             ///< minimum age of a company for it to trade shares
 
	uint8  feeder_payment_share;             ///< percentage of leg payment to virtually pay in feeder systems
 
	byte   dist_local_authority;             ///< distance for town local authority, default 20
 
	bool   exclusive_rights;                 ///< allow buying exclusive rights
 
@@ -519,12 +529,34 @@ struct EconomySettings {
 
	bool   allow_town_roads;                 ///< towns are allowed to build roads (always allowed when generating world / in SE)
 
	TownFounding found_town;                 ///< town founding.
 
	bool   station_noise_level;              ///< build new airports when the town noise level is still within accepted limits
 
	uint16 town_noise_population[3];         ///< population to base decision on noise evaluation (@see town_council_tolerance)
 
	bool   allow_town_level_crossings;       ///< towns are allowed to build level crossings
 
	bool   infrastructure_maintenance;       ///< enable monthly maintenance fee for owner infrastructure
 
	uint16 infrastructure_base_cost_rail;    ///< ttr patchpack: rail infrastructure cost base price
 
	uint16 infrastructure_base_cost_road;    ///< ttr patchpack: road infrastructure cost base price
 
	uint16 infrastructure_base_cost_water;   ///< ttr patchpack: water infrastructure cost base price
 
	uint16 infrastructure_base_cost_air;     ///< ttr patchpack: air infrastructure cost base price
 
	uint16 infrastructure_base_cost_station; ///< ttr patchpack: station infrastructure cost base price
 
	uint8  daylength_multiplier;             ///< ttr patchpack: day length multiplier
 
	uint8  town_growth_multiplier;           ///< ttr patchpack: town growth multiplier
 
	uint16 running_cost_multiplier_rail;     ///< ttr patchpack: rail running cost multiplier
 
	uint16 running_cost_multiplier_road;     ///< ttr patchpack: road running cost multiplier
 
	uint16 running_cost_multiplier_water;    ///< ttr patchpack: water running cost multiplier
 
	uint16 running_cost_multiplier_air;      ///< ttr patchpack: air running cost multiplier
 
	uint16 dilapidation_max_amount;          ///< ttr patchpack: maximum amount of dilapidation allowed at station
 
	uint16 dilapidation_increase;            ///< ttr patchpack: amount of dilapidation to add on ratings update if station is overcrowded
 
	uint16 dilapidation_decrease;            ///< ttr patchpack: amount of dilapidation to remove on ratings update if station is not overcrowded
 
	uint16 dilapidation_pop_rail;            ///< ttr patchpack: amount of pop allowed at rail station before it is considered dilapidated
 
	uint16 dilapidation_pop_road;            ///< ttr patchpack: amount of pop allowed at road station before it is considered dilapidated
 
	uint16 dilapidation_pop_water;           ///< ttr patchpack: amount of pop allowed at water station before it is considered dilapidated
 
	uint16 dilapidation_pop_air;             ///< ttr patchpack: amount of pop allowed at air station before it is considered dilapidated
 
	uint32 dilapidation_fine_rail;           ///< ttr patchpack: monthly fine per dilapidation point at rail stations
 
	uint32 dilapidation_fine_road;           ///< ttr patchpack: monthly fine per dilapidation point at road stations
 
	uint32 dilapidation_fine_water;          ///< ttr patchpack: monthly fine per dilapidation point at water stations
 
	uint32 dilapidation_fine_air;            ///< ttr patchpack: monthly fine per dilapidation point at air stations
 
};
 

	
 
struct LinkGraphSettings {
 
	uint16 recalc_time;                     ///< time (in days) for recalculating each link graph component.
 
	uint16 recalc_interval;                 ///< time (in days) between subsequent checks for link graphs to be calculated.
 
	DistributionType distribution_pax;      ///< distribution type for passengers
src/ship_cmd.cpp
Show inline comments
 
@@ -216,12 +216,13 @@ void Ship::UpdateCache()
 
}
 

	
 
Money Ship::GetRunningCost() const
 
{
 
	const Engine *e = this->GetEngine();
 
	uint cost_factor = GetVehicleProperty(this, PROP_SHIP_RUNNING_COST_FACTOR, e->u.ship.running_cost);
 
	cost_factor *= _settings_game.economy.running_cost_multiplier_water;
 
	return GetPrice(PR_RUNNING_SHIP, cost_factor, e->GetGRF());
 
}
 

	
 
void Ship::OnNewDay()
 
{
 
	if ((++this->day_counter & 7) == 0) {
 
@@ -411,12 +412,24 @@ static bool ShipAccelerate(Vehicle *v)
 
	uint spd;
 
	byte t;
 

	
 
	spd = std::min<uint>(v->cur_speed + 1, v->vcache.cached_max_speed);
 
	spd = std::min<uint>(spd, v->current_order.GetMaxSpeed() * 2);
 

	
 
	if(v->breakdown_ctr == 1 && v->breakdown_type == BREAKDOWN_LOW_POWER && v->cur_speed > (v->breakdown_severity * ShipVehInfo(v->engine_type)->max_speed) >> 8) {
 
		if((v->tick_counter & 0x7) == 0 && v->cur_speed > 0) {
 
			spd = v->cur_speed - 1;
 
		} else {
 
			spd = v->cur_speed;
 
		}
 
	}
 

	
 
	if(v->breakdown_ctr == 1 && v->breakdown_type == BREAKDOWN_LOW_SPEED) {
 
		spd = std::min<uint>(spd, v->breakdown_severity);
 
	}
 

	
 
	/* updates statusbar only if speed have changed to save CPU time */
 
	if (spd != v->cur_speed) {
 
		v->cur_speed = spd;
 
		SetWindowWidgetDirty(WC_VEHICLE_VIEW, v->index, WID_VV_START_STOP);
 
	}
 

	
 
@@ -875,12 +888,13 @@ CommandCost CmdBuildShip(DoCommandFlag f
 
		v->last_station_visited = INVALID_STATION;
 
		v->last_loading_station = INVALID_STATION;
 
		v->engine_type = e->index;
 

	
 
		v->reliability = e->reliability;
 
		v->reliability_spd_dec = e->reliability_spd_dec;
 
		v->breakdown_chance = 64; // ships have a 50% lower breakdown chance than normal
 
		v->max_age = e->GetLifeLengthInDays();
 

	
 
		v->state = TRACK_BIT_DEPOT;
 

	
 
		v->SetServiceInterval(Company::Get(_current_company)->settings.vehicle.servint_ships);
 
		v->date_of_last_service = _date;
src/signs_type.h
Show inline comments
 
@@ -13,9 +13,9 @@
 
/** The type of the IDs of signs. */
 
typedef uint16 SignID;
 
struct Sign;
 

	
 
static const SignID INVALID_SIGN = 0xFFFF; ///< Sentinel for an invalid sign.
 

	
 
static const uint MAX_LENGTH_SIGN_NAME_CHARS = 32; ///< The maximum length of a sign name in characters including '\0'
 
static const uint MAX_LENGTH_SIGN_NAME_CHARS = 128; ///< The maximum length of a sign name in characters including '\0'
 

	
 
#endif /* SIGNS_TYPE_H */
src/station_base.h
Show inline comments
 
@@ -16,13 +16,12 @@
 
#include "cargopacket.h"
 
#include "industry_type.h"
 
#include "linkgraph/linkgraph_type.h"
 
#include "newgrf_storage.h"
 
#include "bitmap_type.h"
 
#include <map>
 
#include <set>
 

	
 
static const byte INITIAL_STATION_RATING = 175;
 

	
 
/**
 
 * Flow statistics telling how much flow should be sent along a link. This is
 
 * done by creating "flow shares" and using std::map's upper_bound() method to
 
@@ -483,12 +482,14 @@ public:
 
	GoodsEntry goods[NUM_CARGO];  ///< Goods at this station
 
	CargoTypes always_accepted;       ///< Bitmask of always accepted cargo types (by houses, HQs, industry tiles when industry doesn't accept cargo)
 

	
 
	IndustryList industries_near; ///< Cached list of industries near the station that can accept cargo, @see DeliverGoodsToIndustry()
 
	Industry *industry;           ///< NOSAVE: Associated industry for neutral stations. (Rebuilt on load from Industry->st)
 

	
 
	uint16 dilapidation; ///< Current amount of dilapidation at the station. Increases if too many passengers at station. Decreases if not too many. Player will be charged a fine based on how dilapidated the station is
 

	
 
	Station(TileIndex tile = INVALID_TILE);
 
	~Station();
 

	
 
	void AddFacility(StationFacility new_facility_bit, TileIndex facil_xy);
 

	
 
	void MarkTilesDirty(bool cargo_change) const;

Changeset was too big and was cut off... Show full diff anyway

0 comments (0 inline, 0 general)