diff --git a/.gitignore b/.gitignore
index b40d932..f2d77bd 100644
--- a/.gitignore
+++ b/.gitignore
@@ -1,6 +1,10 @@
 Makefile*
+gitmake-all
+gitmake-origin
+gitmake-build
 bin/*
 bundle/*
+bundles/
 !bin/data/chars.grf
 !bin/data/openttdd.grf
 !bin/data/openttdw.grf
@@ -10,3 +14,11 @@ bundle/*
 config.*
 objs/*
 src/rev.cpp
+.cproject
+.project
+data/
+media/openttd.desktop
+patches/
+old/
+grf/
+docs/own
diff --git a/gitmake b/gitmake
new file mode 100644
index 0000000..c05e38e
--- /dev/null
+++ b/gitmake
@@ -0,0 +1,72 @@
+
+VPATH=.:./.git/refs/heads/
+
+gitmake-all: cargodist diaglvl
+	touch gitmake-all
+
+check: clean gitmake-origin
+	echo "check OK"
+
+clean:
+	rm -f gitmake-origin
+	rm -f patches/current/*
+
+%:
+	git checkout ${@}-tmp
+	for branch in $(^F); do git merge $${branch}; done 
+	git checkout $@
+	git merge ${@}-tmp
+	test -e gitmake-build && make -j`cat gitmake-build` || true
+	./make_diffs
+
+gitmake-origin:
+	git fetch git://git.openttd.org/openttd/trunk.git master:trunk
+	git log trunk | grep -m 1 '(svn r[0-9]*)' | awk '{print $$2}' | sed -e 's/)//' > gitmake-origin
+	cp gitmake-origin patches/current/TRUNK_VERSION.txt
+	git diff --numstat trunk master | grep -v .gitignore | grep -v gitmake | grep -v make_diffs && touch gitmake-origin || touch -t 197001010100 gitmake-origin
+
+master: gitmake-origin
+	git checkout $@
+	git rebase trunk
+	test -e gitmake-build && make -j`cat gitmake-build` || true
+
+smallmap-zoom-out: master
+
+zoomlevels: master
+
+smallmap-zoom-in: smallmap-zoom-out zoomlevels 
+
+cargodist: station-gui smallmap-stats warning-sign
+
+station-gui: cargomap
+
+smallmap-stats: smallmap-zoom-in cargomap
+
+texteff: master
+
+flowmapping-core: mcf
+
+mcf: demands
+
+demands: components
+
+components: capacities
+
+capacities: master
+
+diaglvl: tileiter
+
+tileiter: master
+
+cargomap: flowmapping-core texteff multimap reservation
+
+reservation: master
+
+multimap: master
+
+warning-sign: demands
+
+push: master cargodist station-gui smallmap-stats flowmapping-core mcf demands components capacities smallmap-zoom-in smallmap-zoom-out zoomlevels diaglvl tileiter texteff cargomap multimap reservation warning-sign
+	git push -f ulf@laramies.com:~/fonsinchen/openttd.git $(^F)
+	rsync -av --delete patches ulf@laramies.com:~/fonsinchen/
+
diff --git a/make_diffs b/make_diffs
new file mode 100755
index 0000000..00e7aed
--- /dev/null
+++ b/make_diffs
@@ -0,0 +1,6 @@
+#!/bin/sh
+BRANCH=`git status | grep "On branch" | awk '{print $4}'`
+git diff ${BRANCH}-tmp > patches/${BRANCH}_`cat gitmake-origin`.diff
+git diff master > patches/against_trunk/${BRANCH}_`cat gitmake-origin`.diff
+ln -sf ../${BRANCH}_`cat gitmake-origin`.diff patches/current/${BRANCH}.diff
+ln -sf ../against_trunk/${BRANCH}_`cat gitmake-origin`.diff patches/current/trunk-${BRANCH}.diff
diff --git a/projects/openttd_vs80.vcproj b/projects/openttd_vs80.vcproj
index 7193a9f..4819863 100644
--- a/projects/openttd_vs80.vcproj
+++ b/projects/openttd_vs80.vcproj
@@ -584,6 +584,14 @@
 				>
 			</File>
 			<File
+				RelativePath=".\..\src\infrastructure.cpp"
+				>
+			</File>
+			<File
+				RelativePath=".\..\src\infrastructure.cpp"
+				>
+			</File>
+			<File
 				RelativePath=".\..\src\ini.cpp"
 				>
 			</File>
@@ -592,6 +600,22 @@
 				>
 			</File>
 			<File
+				RelativePath=".\..\src\linkgraph\demands.cpp"
+				>
+			</File>
+			<File
+				RelativePath=".\..\src\linkgraph\flowmapper.cpp"
+				>
+			</File>
+			<File
+				RelativePath=".\..\src\linkgraph\linkgraph.cpp"
+				>
+			</File>
+			<File
+				RelativePath=".\..\src\linkgraph\mcf.cpp"
+				>
+			</File>
+			<File
 				RelativePath=".\..\src\map.cpp"
 				>
 			</File>
@@ -1072,6 +1096,14 @@
 				>
 			</File>
 			<File
+				RelativePath=".\..\src\infrastructure_func.h"
+				>
+			</File>
+			<File
+				RelativePath=".\..\src\infrastructure_func.h"
+				>
+			</File>
+			<File
 				RelativePath=".\..\src\ini_type.h"
 				>
 			</File>
@@ -1084,6 +1116,30 @@
 				>
 			</File>
 			<File
+				RelativePath=".\..\src\linkgraph\demands.h"
+				>
+			</File>
+			<File
+				RelativePath=".\..\src\linkgraph\demand_settings.h"
+				>
+			</File>
+			<File
+				RelativePath=".\..\src\linkgraph\flowmapper.h"
+				>
+			</File>
+			<File
+				RelativePath=".\..\src\linkgraph\linkgraph.h"
+				>
+			</File>
+			<File
+				RelativePath=".\..\src\linkgraph\linkgraph_types.h"
+				>
+			</File>
+			<File
+				RelativePath=".\..\src\linkgraph\mcf.h"
+				>
+			</File>
+			<File
 				RelativePath=".\..\src\livery.h"
 				>
 			</File>
@@ -1692,6 +1748,10 @@
 				>
 			</File>
 			<File
+				RelativePath=".\..\src\core\multimap.hpp"
+				>
+			</File>
+			<File
 				RelativePath=".\..\src\core\overflowsafe_type.hpp"
 				>
 			</File>
@@ -2108,6 +2168,10 @@
 				>
 			</File>
 			<File
+				RelativePath=".\..\src\saveload\linkgraph_sl.cpp"
+				>
+			</File>
+			<File
 				RelativePath=".\..\src\saveload\map_sl.cpp"
 				>
 			</File>
diff --git a/projects/openttd_vs90.vcproj b/projects/openttd_vs90.vcproj
index 20f2a96..8bf113c 100644
--- a/projects/openttd_vs90.vcproj
+++ b/projects/openttd_vs90.vcproj
@@ -581,6 +581,14 @@
 				>
 			</File>
 			<File
+				RelativePath=".\..\src\infrastructure.cpp"
+				>
+			</File>
+			<File
+				RelativePath=".\..\src\infrastructure.cpp"
+				>
+			</File>
+			<File
 				RelativePath=".\..\src\ini.cpp"
 				>
 			</File>
@@ -589,6 +597,22 @@
 				>
 			</File>
 			<File
+				RelativePath=".\..\src\linkgraph\demands.cpp"
+				>
+			</File>
+			<File
+				RelativePath=".\..\src\linkgraph\flowmapper.cpp"
+				>
+			</File>
+			<File
+				RelativePath=".\..\src\linkgraph\linkgraph.cpp"
+				>
+			</File>
+			<File
+				RelativePath=".\..\src\linkgraph\mcf.cpp"
+				>
+			</File>
+			<File
 				RelativePath=".\..\src\map.cpp"
 				>
 			</File>
@@ -1069,6 +1093,14 @@
 				>
 			</File>
 			<File
+				RelativePath=".\..\src\infrastructure_func.h"
+				>
+			</File>
+			<File
+				RelativePath=".\..\src\infrastructure_func.h"
+				>
+			</File>
+			<File
 				RelativePath=".\..\src\ini_type.h"
 				>
 			</File>
@@ -1081,6 +1113,30 @@
 				>
 			</File>
 			<File
+				RelativePath=".\..\src\linkgraph\demands.h"
+				>
+			</File>
+			<File
+				RelativePath=".\..\src\linkgraph\demand_settings.h"
+				>
+			</File>
+			<File
+				RelativePath=".\..\src\linkgraph\flowmapper.h"
+				>
+			</File>
+			<File
+				RelativePath=".\..\src\linkgraph\linkgraph.h"
+				>
+			</File>
+			<File
+				RelativePath=".\..\src\linkgraph\linkgraph_types.h"
+				>
+			</File>
+			<File
+				RelativePath=".\..\src\linkgraph\mcf.h"
+				>
+			</File>
+			<File
 				RelativePath=".\..\src\livery.h"
 				>
 			</File>
@@ -1689,6 +1745,10 @@
 				>
 			</File>
 			<File
+				RelativePath=".\..\src\core\multimap.hpp"
+				>
+			</File>
+			<File
 				RelativePath=".\..\src\core\overflowsafe_type.hpp"
 				>
 			</File>
@@ -2105,6 +2165,10 @@
 				>
 			</File>
 			<File
+				RelativePath=".\..\src\saveload\linkgraph_sl.cpp"
+				>
+			</File>
+			<File
 				RelativePath=".\..\src\saveload\map_sl.cpp"
 				>
 			</File>
diff --git a/source.list b/source.list
index e134d29..623d144 100644
--- a/source.list
+++ b/source.list
@@ -31,8 +31,13 @@ gfx.cpp
 gfxinit.cpp
 heightmap.cpp
 highscore.cpp
+infrastructure.cpp
 ini.cpp
 landscape.cpp
+linkgraph/demands.cpp
+linkgraph/flowmapper.cpp
+linkgraph/linkgraph.cpp
+linkgraph/mcf.cpp
 map.cpp
 misc.cpp
 mixer.cpp
@@ -177,9 +182,16 @@ house_type.h
 industry.h
 industry_type.h
 industrytype.h
+infrastructure_func.h
 ini_type.h
 landscape.h
 landscape_type.h
+linkgraph/demands.h
+linkgraph/demand_settings.h
+linkgraph/flowmapper.h
+linkgraph/linkgraph.h
+linkgraph/linkgraph_types.h
+linkgraph/mcf.h
 livery.h
 map_func.h
 map_type.h
@@ -348,6 +360,7 @@ core/geometry_type.hpp
 core/math_func.cpp
 core/math_func.hpp
 core/mem_func.hpp
+core/multimap.hpp
 core/overflowsafe_type.hpp
 core/pool_func.hpp
 core/pool_type.hpp
@@ -456,6 +469,7 @@ saveload/gamelog_sl.cpp
 saveload/group_sl.cpp
 saveload/industry_sl.cpp
 saveload/labelmaps_sl.cpp
+saveload/linkgraph_sl.cpp
 saveload/map_sl.cpp
 saveload/misc_sl.cpp
 saveload/newgrf_sl.cpp
diff --git a/src/aircraft_cmd.cpp b/src/aircraft_cmd.cpp
index b51dc59..f4c7180 100644
--- a/src/aircraft_cmd.cpp
+++ b/src/aircraft_cmd.cpp
@@ -35,6 +35,7 @@
 #include "effectvehicle_func.h"
 #include "station_base.h"
 #include "cargotype.h"
+#include "infrastructure_func.h"
 
 #include "table/strings.h"
 #include "table/sprites.h"
@@ -113,7 +114,7 @@ static StationID FindNearestHangar(const Aircraft *v)
 	const AircraftVehicleInfo *avi = AircraftVehInfo(v->engine_type);
 
 	FOR_ALL_STATIONS(st) {
-		if (st->owner != v->owner || !(st->facilities & FACIL_AIRPORT)) continue;
+		if (!IsInfraUsageAllowed(st->owner, v->owner, VEH_AIRCRAFT) || !(st->facilities & FACIL_AIRPORT)) continue;
 
 		const AirportFTAClass *afc = st->Airport();
 		if (afc->nof_depots == 0 || (
@@ -144,7 +145,7 @@ static bool HaveHangarInOrderList(Aircraft *v)
 
 	FOR_VEHICLE_ORDERS(v, order) {
 		const Station *st = Station::Get(order->station);
-		if (st->owner == v->owner && (st->facilities & FACIL_AIRPORT)) {
+		if (IsInfraUsageAllowed(st->owner, v->owner, VEH_AIRCRAFT) && (st->facilities & FACIL_AIRPORT)) {
 			/* If an airport doesn't have a hangar, skip it */
 			if (st->Airport()->nof_depots != 0)
 				return true;
@@ -247,7 +248,7 @@ CommandCost CmdBuildAircraft(TileIndex tile, DoCommandFlag flags, uint32 p1, uin
 	/* to just query the cost, it is not neccessary to have a valid tile (automation/AI) */
 	if (flags & DC_QUERY_COST) return value;
 
-	if (!IsHangarTile(tile) || !IsTileOwner(tile, _current_company)) return CMD_ERROR;
+	if (!IsHangarTile(tile) || !CheckInfraUsageAllowed(GetTileOwner(tile), VEH_AIRCRAFT)) return CMD_ERROR;
 
 	/* Prevent building aircraft types at places which can't handle them */
 	if (!CanVehicleUseStation(p1, Station::GetByTile(tile))) return CMD_ERROR;
@@ -1311,6 +1312,7 @@ static void AircraftEntersTerminal(Aircraft *v)
 	if (v->current_order.IsType(OT_GOTO_DEPOT)) return;
 
 	Station *st = Station::Get(v->targetairport);
+	StationID previous_station = v->last_station_visited;
 	v->last_station_visited = v->targetairport;
 
 	/* Check if station was ever visited before */
@@ -1327,7 +1329,7 @@ static void AircraftEntersTerminal(Aircraft *v)
 		AI::NewEvent(v->owner, new AIEventStationFirstVehicle(st->index, v->index));
 	}
 
-	v->BeginLoading();
+	v->BeginLoading(previous_station);
 }
 
 static void AircraftLandAirplane(Aircraft *v)
@@ -1533,7 +1535,7 @@ static void AircraftEventHandler_Flying(Aircraft *v, const AirportFTAClass *apc)
 	/* runway busy or not allowed to use this airstation, circle */
 	if ((apc->flags & (v->subtype == AIR_HELICOPTER ? AirportFTAClass::HELICOPTERS : AirportFTAClass::AIRPLANES)) &&
 			st->airport_tile != INVALID_TILE &&
-			(st->owner == OWNER_NONE || st->owner == v->owner)) {
+			IsInfraUsageAllowed(st->owner, v->owner, VEH_AIRCRAFT)) {
 		/* {32,FLYING,NOTHING_block,37}, {32,LANDING,N,33}, {32,HELILANDING,N,41},
 		 * if it is an airplane, look for LANDING, for helicopter HELILANDING
 		 * it is possible to choose from multiple landing runways, so loop until a free one is found */
diff --git a/src/autoreplace_cmd.cpp b/src/autoreplace_cmd.cpp
index 0e7507c..91d931d 100644
--- a/src/autoreplace_cmd.cpp
+++ b/src/autoreplace_cmd.cpp
@@ -119,7 +119,7 @@ static void TransferCargo(Vehicle *old_veh, Vehicle *new_head, bool part_of_chai
 			uint amount = min(src->cargo.Count(), dest->cargo_cap - dest->cargo.Count());
 			if (amount <= 0) continue;
 
-			src->cargo.MoveTo(&dest->cargo, amount, VehicleCargoList::MTA_UNLOAD, NULL);
+			src->cargo.MoveTo(&dest->cargo, amount);
 		}
 	}
 
diff --git a/src/blitter/32bpp_anim.cpp b/src/blitter/32bpp_anim.cpp
index 42fbe3f..3aace2b 100644
--- a/src/blitter/32bpp_anim.cpp
+++ b/src/blitter/32bpp_anim.cpp
@@ -24,8 +24,8 @@ inline void Blitter_32bppAnim::Draw(const Blitter::BlitterParams *bp, ZoomLevel
 {
 	const SpriteData *src = (const SpriteData *)bp->sprite;
 
-	const Colour *src_px = (const Colour *)(src->data + src->offset[zoom][0]);
-	const uint8  *src_n  = (const uint8  *)(src->data + src->offset[zoom][1]);
+	const Colour *src_px = (const Colour *)(src->data + src->offset[zoom - ZOOM_LVL_BLITTER_MIN][0]);
+	const uint8  *src_n  = (const uint8  *)(src->data + src->offset[zoom - ZOOM_LVL_BLITTER_MIN][1]);
 
 	for (uint i = bp->skip_top; i != 0; i--) {
 		src_px = (const Colour *)((const byte *)src_px + *(const uint32 *)src_px);
diff --git a/src/blitter/32bpp_optimized.cpp b/src/blitter/32bpp_optimized.cpp
index bfc8bd4..dfd7ae7 100644
--- a/src/blitter/32bpp_optimized.cpp
+++ b/src/blitter/32bpp_optimized.cpp
@@ -31,11 +31,11 @@ inline void Blitter_32bppOptimized::Draw(const Blitter::BlitterParams *bp, ZoomL
 
 	/* src_px : each line begins with uint32 n = 'number of bytes in this line',
 	 *          then n times is the Colour struct for this line */
-	const Colour *src_px = (const Colour *)(src->data + src->offset[zoom][0]);
+	const Colour *src_px = (const Colour *)(src->data + src->offset[zoom - ZOOM_LVL_BLITTER_MIN][0]);
 	/* src_n  : each line begins with uint32 n = 'number of bytes in this line',
 	 *          then interleaved stream of 'm' and 'n' channels. 'm' is remap,
 	 *          'n' is number of bytes with the same alpha channel class */
-	const uint8  *src_n  = (const uint8  *)(src->data + src->offset[zoom][1]);
+	const uint8  *src_n  = (const uint8  *)(src->data + src->offset[zoom - ZOOM_LVL_BLITTER_MIN][1]);
 
 	/* skip upper lines in src_px and src_n */
 	for (uint i = bp->skip_top; i != 0; i--) {
@@ -261,7 +261,7 @@ Sprite *Blitter_32bppOptimized::Encode(SpriteLoader::Sprite *sprite, Blitter::Al
 	/* streams of pixels (a, r, g, b channels)
 	 *
 	 * stored in separated stream so data are always aligned on 4B boundary */
-	Colour *dst_px_orig[ZOOM_LVL_COUNT];
+	Colour *dst_px_orig[ZOOM_LVL_BLITTER_COUNT];
 
 	/* interleaved stream of 'm' channel and 'n' channel
 	 * 'n' is number if following pixels with the same alpha channel class
@@ -269,21 +269,22 @@ Sprite *Blitter_32bppOptimized::Encode(SpriteLoader::Sprite *sprite, Blitter::Al
 	 *
 	 * it has to be stored in one stream so fewer registers are used -
 	 * x86 has problems with register allocation even with this solution */
-	uint8  *dst_n_orig[ZOOM_LVL_COUNT];
+	uint8  *dst_n_orig[ZOOM_LVL_BLITTER_COUNT];
 
 	/* lengths of streams */
-	uint32 lengths[ZOOM_LVL_COUNT][2];
+	uint32 lengths[ZOOM_LVL_BLITTER_COUNT][2];
 
-	for (ZoomLevel z = ZOOM_LVL_BEGIN; z < ZOOM_LVL_END; z++) {
-		const SpriteLoader::Sprite *src_orig = ResizeSprite(sprite, z);
+	ZoomLevel zoom_value = ZOOM_LVL_BLITTER_MIN;
+	for (int zoom_index = 0; zoom_index < ZOOM_LVL_BLITTER_COUNT; zoom_index++, zoom_value++) {
+		const SpriteLoader::Sprite *src_orig = ResizeSprite(sprite, zoom_value);
 
 		uint size = src_orig->height * src_orig->width;
 
-		dst_px_orig[z] = CallocT<Colour>(size + src_orig->height * 2);
-		dst_n_orig[z]  = CallocT<uint8>(size * 2 + src_orig->height * 4 * 2);
+		dst_px_orig[zoom_index] = CallocT<Colour>(size + src_orig->height * 2);
+		dst_n_orig[zoom_index]  = CallocT<uint8>(size * 2 + src_orig->height * 4 * 2);
 
-		uint32 *dst_px_ln = (uint32 *)dst_px_orig[z];
-		uint32 *dst_n_ln  = (uint32 *)dst_n_orig[z];
+		uint32 *dst_px_ln = (uint32 *)dst_px_orig[zoom_index];
+		uint32 *dst_n_ln  = (uint32 *)dst_n_orig[zoom_index];
 
 		const SpriteLoader::CommonPixel *src = (const SpriteLoader::CommonPixel *)src_orig->data;
 
@@ -350,15 +351,15 @@ Sprite *Blitter_32bppOptimized::Encode(SpriteLoader::Sprite *sprite, Blitter::Al
 			dst_n_ln =  (uint32 *)dst_n;
 		}
 
-		lengths[z][0] = (byte *)dst_px_ln - (byte *)dst_px_orig[z]; // all are aligned to 4B boundary
-		lengths[z][1] = (byte *)dst_n_ln  - (byte *)dst_n_orig[z];
+		lengths[zoom_index][0] = (byte *)dst_px_ln - (byte *)dst_px_orig[zoom_index]; // all are aligned to 4B boundary
+		lengths[zoom_index][1] = (byte *)dst_n_ln  - (byte *)dst_n_orig[zoom_index];
 
 		free(src_orig->data);
 		free((void *)src_orig);
 	}
 
 	uint len = 0; // total length of data
-	for (ZoomLevel z = ZOOM_LVL_BEGIN; z < ZOOM_LVL_END; z++) {
+	for (int z = 0; z < ZOOM_LVL_BLITTER_COUNT; z++) {
 		len += lengths[z][0] + lengths[z][1];
 	}
 
@@ -371,8 +372,8 @@ Sprite *Blitter_32bppOptimized::Encode(SpriteLoader::Sprite *sprite, Blitter::Al
 
 	SpriteData *dst = (SpriteData *)dest_sprite->data;
 
-	for (ZoomLevel z = ZOOM_LVL_BEGIN; z < ZOOM_LVL_END; z++) {
-		dst->offset[z][0] = z == ZOOM_LVL_BEGIN ? 0 : lengths[z - 1][1] + dst->offset[z - 1][1];
+	for (int z = 0; z < ZOOM_LVL_BLITTER_COUNT; z++) {
+		dst->offset[z][0] = z == 0 ? 0 : lengths[z - 1][1] + dst->offset[z - 1][1];
 		dst->offset[z][1] = lengths[z][0] + dst->offset[z][0];
 
 		memcpy(dst->data + dst->offset[z][0], dst_px_orig[z], lengths[z][0]);
diff --git a/src/blitter/32bpp_optimized.hpp b/src/blitter/32bpp_optimized.hpp
index e516865..29231c5 100644
--- a/src/blitter/32bpp_optimized.hpp
+++ b/src/blitter/32bpp_optimized.hpp
@@ -18,7 +18,7 @@
 class Blitter_32bppOptimized : public Blitter_32bppSimple {
 public:
 	struct SpriteData {
-		uint32 offset[ZOOM_LVL_COUNT][2];
+		uint32 offset[ZOOM_LVL_BLITTER_COUNT][2];
 		byte data[];
 	};
 
diff --git a/src/blitter/8bpp_optimized.cpp b/src/blitter/8bpp_optimized.cpp
index 4cc8667..9d0efc1 100644
--- a/src/blitter/8bpp_optimized.cpp
+++ b/src/blitter/8bpp_optimized.cpp
@@ -20,7 +20,7 @@ void Blitter_8bppOptimized::Draw(Blitter::BlitterParams *bp, BlitterMode mode, Z
 {
 	/* Find the offset of this zoom-level */
 	const SpriteData *sprite_src = (const SpriteData *)bp->sprite;
-	uint offset = sprite_src->offset[zoom];
+	uint offset = sprite_src->offset[zoom - ZOOM_LVL_BLITTER_MIN];
 
 	/* Find where to start reading in the source sprite */
 	const uint8 *src = sprite_src->data + offset;
@@ -113,7 +113,7 @@ Sprite *Blitter_8bppOptimized::Encode(SpriteLoader::Sprite *sprite, Blitter::All
 	/* Make memory for all zoom-levels */
 	uint memory = sizeof(SpriteData);
 
-	for (ZoomLevel i = ZOOM_LVL_BEGIN; i < ZOOM_LVL_END; i++) {
+	for (ZoomLevel i = ZOOM_LVL_BLITTER_MIN; i <= ZOOM_LVL_BLITTER_MAX; i++) {
 		memory += UnScaleByZoom(sprite->height, i) * UnScaleByZoom(sprite->width, i);
 	}
 
@@ -128,15 +128,16 @@ Sprite *Blitter_8bppOptimized::Encode(SpriteLoader::Sprite *sprite, Blitter::All
 	byte *dst = temp_dst->data;
 
 	/* Make the sprites per zoom-level */
-	for (ZoomLevel i = ZOOM_LVL_BEGIN; i < ZOOM_LVL_END; i++) {
+	ZoomLevel zoom_value = ZOOM_LVL_BLITTER_MIN;
+	for (int zoom_index = 0; zoom_index < ZOOM_LVL_BLITTER_COUNT; zoom_index++, zoom_value++) {
 		/* Store the index table */
 		uint offset = dst - temp_dst->data;
-		temp_dst->offset[i] = offset;
+		temp_dst->offset[zoom_index] = offset;
 
 		/* cache values, because compiler can't cache it */
-		int scaled_height = UnScaleByZoom(sprite->height, i);
-		int scaled_width  = UnScaleByZoom(sprite->width,  i);
-		int scaled_1      =   ScaleByZoom(1,              i);
+		int scaled_height = UnScaleByZoom(sprite->height, zoom_value);
+		int scaled_width  = UnScaleByZoom(sprite->width,  zoom_value);
+		int scaled_1      =   ScaleByZoom(1,              zoom_value);
 
 		for (int y = 0; y < scaled_height; y++) {
 			uint trans = 0;
@@ -145,7 +146,7 @@ Sprite *Blitter_8bppOptimized::Encode(SpriteLoader::Sprite *sprite, Blitter::All
 			byte *count_dst = NULL;
 
 			/* Store the scaled image */
-			const SpriteLoader::CommonPixel *src = &sprite->data[ScaleByZoom(y, i) * sprite->width];
+			const SpriteLoader::CommonPixel *src = &sprite->data[ScaleByZoom(y, zoom_value) * sprite->width];
 			const SpriteLoader::CommonPixel *src_end = &src[sprite->width];
 
 			for (int x = 0; x < scaled_width; x++) {
diff --git a/src/blitter/8bpp_optimized.hpp b/src/blitter/8bpp_optimized.hpp
index fbc9457..f10b383 100644
--- a/src/blitter/8bpp_optimized.hpp
+++ b/src/blitter/8bpp_optimized.hpp
@@ -18,8 +18,8 @@
 class Blitter_8bppOptimized : public Blitter_8bppBase {
 public:
 	struct SpriteData {
-		uint32 offset[ZOOM_LVL_COUNT]; ///< offsets (from .data) to streams for different zoom levels
-		byte data[];                   ///< data, all zoomlevels
+		uint32 offset[ZOOM_LVL_BLITTER_COUNT]; ///< offsets (from .data) to streams for different zoom levels
+		byte data[];                           ///< data, all zoomlevels
 	};
 
 	/* virtual */ void Draw(Blitter::BlitterParams *bp, BlitterMode mode, ZoomLevel zoom);
diff --git a/src/cargo_type.h b/src/cargo_type.h
index 8d9a1a8..2e0e355 100644
--- a/src/cargo_type.h
+++ b/src/cargo_type.h
@@ -23,6 +23,7 @@ typedef byte CargoID;
 
 /** Available types of cargo */
 enum CargoTypes {
+	CT_BEGIN        =  0,
 	/* Temperate */
 	CT_PASSENGERS   =  0,
 	CT_COAL         =  1,
@@ -64,6 +65,7 @@ enum CargoTypes {
 	CT_FIZZY_DRINKS = 11,
 
 	NUM_CARGO       = 32,   ///< Maximal number of cargo types in a game.
+	CT_END          = 32,
 
 	CT_NO_REFIT     = 0xFE, ///< Do not refit cargo of a vehicle (used in vehicle orders and auto-replace/auto-new).
 	CT_INVALID      = 0xFF, ///< Invalid cargo type.
diff --git a/src/cargopacket.cpp b/src/cargopacket.cpp
index 418eb8e..207daef 100644
--- a/src/cargopacket.cpp
+++ b/src/cargopacket.cpp
@@ -10,6 +10,7 @@
 /** @file cargopacket.cpp Implementation of the cargo packets */
 
 #include "stdafx.h"
+#include "station_base.h"
 #include "core/pool_func.hpp"
 #include "economy_base.h"
 
@@ -47,13 +48,13 @@ CargoPacket::CargoPacket(StationID source, TileIndex source_xy, uint16 count, So
 }
 
 CargoPacket::CargoPacket(uint16 count, byte days_in_transit, StationID source, TileIndex source_xy, TileIndex loaded_at_xy, Money feeder_share, SourceType source_type, SourceID source_id) :
-		feeder_share(feeder_share),
-		count(count),
-		days_in_transit(days_in_transit),
-		source_id(source_id),
-		source(source),
-		source_xy(source_xy),
-		loaded_at_xy(loaded_at_xy)
+	feeder_share(feeder_share),
+	count(count),
+	days_in_transit(days_in_transit),
+	source_id(source_id),
+	source(source),
+	source_xy(source_xy),
+	loaded_at_xy(loaded_at_xy)
 {
 	assert(count != 0);
 	this->source_type = source_type;
@@ -72,6 +73,22 @@ CargoPacket::CargoPacket(uint16 count, byte days_in_transit, StationID source, T
 	}
 }
 
+CargoPacket * CargoPacket::Split(uint new_size)
+{
+	Money fs = this->feeder_share * new_size / static_cast<uint>(this->count);
+	CargoPacket *cp_new = new CargoPacket(new_size, this->days_in_transit, this->source, this->source_xy, this->loaded_at_xy, fs, this->source_type, this->source_id);
+	this->feeder_share -= fs;
+	this->count -= new_size;
+	return cp_new;
+}
+
+void CargoPacket::Merge(CargoPacket *cp)
+{
+	this->count += cp->count;
+	this->feeder_share += cp->feeder_share;
+	delete cp;
+}
+
 /**
  * Invalidates (sets source to INVALID_STATION) all cargo packets from given station
  * @param sid the station that gets removed
@@ -90,37 +107,32 @@ CargoPacket::CargoPacket(uint16 count, byte days_in_transit, StationID source, T
  *
  */
 
-template <class Tinst>
-CargoList<Tinst>::~CargoList()
+template <class Tinst, class Tcont>
+CargoList<Tinst, Tcont>::~CargoList()
 {
 	for (Iterator it(this->packets.begin()); it != this->packets.end(); ++it) {
 		delete *it;
 	}
 }
 
-template <class Tinst>
-void CargoList<Tinst>::RemoveFromCache(const CargoPacket *cp)
+template <class Tinst, class Tcont>
+void CargoList<Tinst, Tcont>::RemoveFromCache(const CargoPacket *cp)
 {
 	this->count                 -= cp->count;
 	this->cargo_days_in_transit -= cp->days_in_transit * cp->count;
 }
 
-template <class Tinst>
-void CargoList<Tinst>::AddToCache(const CargoPacket *cp)
+template <class Tinst, class Tcont>
+void CargoList<Tinst, Tcont>::AddToCache(const CargoPacket *cp)
 {
 	this->count                 += cp->count;
 	this->cargo_days_in_transit += cp->days_in_transit * cp->count;
 }
-
-template <class Tinst>
-void CargoList<Tinst>::Append(CargoPacket *cp)
+void VehicleCargoList::MergeOrPush(CargoPacket *cp)
 {
-	assert(cp != NULL);
-	static_cast<Tinst *>(this)->AddToCache(cp);
-
-	for (List::reverse_iterator it(this->packets.rbegin()); it != this->packets.rend(); it++) {
+	for (CargoPacketList::reverse_iterator it(this->packets.rbegin()); it != this->packets.rend(); it++) {
 		CargoPacket *icp = *it;
-		if (Tinst::AreMergable(icp, cp) && icp->count + cp->count <= CargoPacket::MAX_COUNT) {
+		if (VehicleCargoList::AreMergable(icp, cp) && icp->count + cp->count <= CargoPacket::MAX_COUNT) {
 			icp->count        += cp->count;
 			icp->feeder_share += cp->feeder_share;
 
@@ -134,8 +146,16 @@ void CargoList<Tinst>::Append(CargoPacket *cp)
 }
 
 
-template <class Tinst>
-void CargoList<Tinst>::Truncate(uint max_remaining)
+void VehicleCargoList::Append(CargoPacket *cp)
+{
+	assert(cp != NULL);
+	this->AddToCache(cp);
+	this->MergeOrPush(cp);
+}
+
+
+template <class Tinst, class Tcont>
+void CargoList<Tinst, Tcont>::Truncate(uint max_remaining)
 {
 	for (Iterator it(packets.begin()); it != packets.end(); /* done during loop*/) {
 		CargoPacket *cp = *it;
@@ -161,87 +181,94 @@ void CargoList<Tinst>::Truncate(uint max_remaining)
 	}
 }
 
-template <class Tinst>
-template <class Tother_inst>
-bool CargoList<Tinst>::MoveTo(Tother_inst *dest, uint max_move, MoveToAction mta, CargoPayment *payment, uint data)
+void VehicleCargoList::Reserve(CargoPacket *cp)
 {
-	assert(mta == MTA_FINAL_DELIVERY || dest != NULL);
-	assert(mta == MTA_UNLOAD || mta == MTA_CARGO_LOAD || payment != NULL);
+	assert(cp != NULL);
+	this->AddToCache(cp);
+	this->reserved_count += cp->count;
+	this->reserved.push_back(cp);
+}
 
-	Iterator it(this->packets.begin());
-	while (it != this->packets.end() && max_move > 0) {
+
+void VehicleCargoList::Unreserve(StationID next, StationCargoList *dest)
+{
+	Iterator it(this->reserved.begin());
+	while (it != this->reserved.end()) {
 		CargoPacket *cp = *it;
-		if (cp->source == data && mta == MTA_FINAL_DELIVERY) {
-			/* Skip cargo that originated from this station. */
-			++it;
-			continue;
-		}
+		this->RemoveFromCache(cp);
+		this->reserved_count -= cp->count;
+		dest->Append(next, cp);
+		this->reserved.erase(it++);
+	}
+}
 
+uint VehicleCargoList::LoadReserved(uint max_move)
+{
+	uint orig_max = max_move;
+	Iterator it(this->reserved.begin());
+	while (it != this->reserved.end() && max_move > 0) {
+		CargoPacket *cp = *it;
 		if (cp->count <= max_move) {
 			/* Can move the complete packet */
 			max_move -= cp->count;
-			this->packets.erase(it++);
-			static_cast<Tinst *>(this)->RemoveFromCache(cp);
-			switch(mta) {
-				case MTA_FINAL_DELIVERY:
-					payment->PayFinalDelivery(cp, cp->count);
-					delete cp;
-					continue; // of the loop
-
-				case MTA_CARGO_LOAD:
-					cp->loaded_at_xy = data;
-					break;
-
-				case MTA_TRANSFER:
-					cp->feeder_share += payment->PayTransfer(cp, cp->count);
-					break;
-
-				case MTA_UNLOAD:
-					break;
-			}
-			dest->Append(cp);
-			continue;
-		}
-
-		/* Can move only part of the packet */
-		if (mta == MTA_FINAL_DELIVERY) {
-			/* Final delivery doesn't need package splitting. */
-			payment->PayFinalDelivery(cp, max_move);
-
-			/* Remove the delivered data from the cache */
-			uint left = cp->count - max_move;
-			cp->count = max_move;
-			static_cast<Tinst *>(this)->RemoveFromCache(cp);
-
-			/* Final delivery payment pays the feeder share, so we have to
-			 * reset that so it is not 'shown' twice for partial unloads. */
-			cp->feeder_share = 0;
-			cp->count = left;
+			this->reserved.erase(it++);
+			this->reserved_count -= cp->count;
+			this->MergeOrPush(cp);
 		} else {
-			/* But... the rest needs package splitting. */
-			Money fs = cp->feeder_share * max_move / static_cast<uint>(cp->count);
-			cp->feeder_share -= fs;
 			cp->count -= max_move;
-
-			CargoPacket *cp_new = new CargoPacket(max_move, cp->days_in_transit, cp->source, cp->source_xy, (mta == MTA_CARGO_LOAD) ? data : cp->loaded_at_xy, fs, cp->source_type, cp->source_id);
-			static_cast<Tinst *>(this)->RemoveFromCache(cp_new); // this reflects the changes in cp.
-
-			if (mta == MTA_TRANSFER) {
-				/* Add the feeder share before inserting in dest. */
-				cp_new->feeder_share += payment->PayTransfer(cp_new, max_move);
-			}
-
-			dest->Append(cp_new);
+			CargoPacket *cp_new = new CargoPacket(max_move, cp->days_in_transit, cp->source, cp->source_xy, cp->loaded_at_xy, 0, cp->source_type, cp->source_id);
+			this->MergeOrPush(cp_new);
+			this->reserved_count -= max_move;
+			max_move = 0;
 		}
+	}
+	return orig_max - max_move;
+}
 
-		max_move = 0;
+template<class Tinst, class Tcont>
+uint CargoList<Tinst, Tcont>::MovePacket(VehicleCargoList *dest, Iterator &it, uint cap, TileIndex load_place, bool reserve)
+{
+	CargoPacket *packet = MovePacket(it, cap, load_place);
+	uint ret = packet->count;
+	if (reserve) {
+		dest->Reserve(packet);
+	} else {
+		dest->Append(packet);
 	}
+	return ret;
+}
+
+template<class Tinst, class Tcont>
+uint CargoList<Tinst, Tcont>::MovePacket(StationCargoList *dest, StationID next, Iterator &it, uint cap)
+{
+	CargoPacket *packet = MovePacket(it, cap);
+	uint ret = packet->count;
+	dest->Append(next, packet);
+	return ret;
+}
 
-	return it != packets.end();
+template<class Tinst, class Tcont>
+CargoPacket *CargoList<Tinst, Tcont>::MovePacket(Iterator &it, uint cap, TileIndex load_place)
+{
+	CargoPacket *packet = *it;
+	/* load the packet if possible */
+	if (packet->count > cap) {
+		/* packet needs to be split */
+		packet = packet->Split(cap);
+		assert(packet->count == cap);
+		++it;
+	} else {
+		this->packets.erase(it++);
+	}
+	static_cast<Tinst *>(this)->RemoveFromCache(packet);
+	if (load_place != INVALID_TILE) {
+		packet->loaded_at_xy = load_place;
+	}
+	return packet;
 }
 
-template <class Tinst>
-void CargoList<Tinst>::InvalidateCache()
+template <class Tinst, class Tcont>
+void CargoList<Tinst, Tcont>::InvalidateCache()
 {
 	this->count = 0;
 	this->cargo_days_in_transit = 0;
@@ -251,6 +278,186 @@ void CargoList<Tinst>::InvalidateCache()
 	}
 }
 
+VehicleCargoList::~VehicleCargoList()
+{
+	for (Iterator it(this->reserved.begin()); it != this->reserved.end(); ++it) {
+		delete *it;
+	}
+}
+
+uint VehicleCargoList::DeliverPacket(Iterator &c, uint remaining_unload, CargoPayment *payment) {
+	CargoPacket * p = *c;
+	uint loaded = 0;
+	if (p->count <= remaining_unload) {
+		payment->PayFinalDelivery(p, p->count);
+		packets.erase(c++);
+		this->RemoveFromCache(p);
+		loaded = p->count;
+		delete p;
+	} else {
+		payment->PayFinalDelivery(p, remaining_unload);
+		this->count -= remaining_unload;
+		this->cargo_days_in_transit -= remaining_unload * p->days_in_transit;
+		this->feeder_share -= p->feeder_share;
+		p->feeder_share = 0;
+		p->count -= remaining_unload;
+		loaded = remaining_unload;
+		++c;
+	}
+	return loaded;
+}
+
+uint VehicleCargoList::KeepPacket(Iterator &c)
+{
+	CargoPacket *cp = *c;
+	this->reserved.push_back(cp);
+	this->reserved_count += cp->count;
+	this->packets.erase(c++);
+	return cp->count;
+}
+
+
+uint VehicleCargoList::TransferPacket(Iterator &c, uint remaining_unload, GoodsEntry *dest, CargoPayment *payment, StationID next)
+{
+	CargoPacket *p = this->MovePacket(c, remaining_unload);
+	p->feeder_share += payment->PayTransfer(p, p->count);
+	uint ret = p->count;
+	dest->cargo.Append(next, p);
+	SetBit(dest->acceptance_pickup, GoodsEntry::PICKUP);
+	return ret;
+}
+
+/* static */ UnloadType VehicleCargoList::WillUnloadOld(byte flags, StationID curr_station, StationID source)
+{
+	/* try to unload cargo */
+	bool move = (flags & (UL_DELIVER | UL_ACCEPTED | UL_TRANSFER)) != 0;
+	/* try to deliver cargo if unloading */
+	bool deliver = (flags & UL_ACCEPTED) && !(flags & UL_TRANSFER) && (source != curr_station);
+	/* transfer cargo if delivery was unsuccessful */
+	bool transfer = (flags & (UL_TRANSFER | UL_DELIVER)) != 0;
+	if (move) {
+		if(deliver) {
+			return UL_DELIVER;
+		} else if (transfer) {
+			return UL_TRANSFER;
+		} else {
+			/* this case is for (non-)delivery to the source station without special flags.
+			 * like the code in MoveTo did, we keep the packet in this case
+			 */
+			return UL_KEEP;
+		}
+	} else {
+		return UL_KEEP;
+	}
+}
+
+/* static */ UnloadType VehicleCargoList::WillUnloadCargoDist(byte flags, StationID curr_station, StationID next_station, StationID via, StationID source)
+{
+	if (via == curr_station) {
+		/* this is the final destination, deliver ... */
+		if (flags & UL_TRANSFER) {
+			/* .. except if explicitly told not to do so ... */
+			return UL_TRANSFER;
+		} else if (flags & UL_ACCEPTED) {
+			return UL_DELIVER;
+		} else if (flags & UL_DELIVER) {
+			/* .. or if the station suddenly doesn't accept our cargo, but we have an explicit deliver order... */
+			return UL_TRANSFER;
+		} else {
+			/* .. or else if it doesn't accept. */
+			return UL_KEEP;
+		}
+	} else {
+		/* packet has to travel on, find out if it can stay on board */
+		if (flags & UL_DELIVER) {
+			/* order overrides cargodist:
+			 * play by the old loading rules here as player is interfering with cargodist
+			 * try to deliver, as move has been forced upon us */
+			if ((flags & UL_ACCEPTED) && !(flags & UL_TRANSFER) && source != curr_station) {
+				return UL_DELIVER;
+			} else {
+				/* transfer cargo, as delivering didn't work */
+				/* plan might still be fulfilled as the packet can be picked up by another vehicle travelling to "via" */
+				return UL_TRANSFER;
+			}
+		} else if (flags & UL_TRANSFER) {
+			/* transfer forced, plan still fulfilled as above */
+			return UL_TRANSFER;
+		} else if (next_station == via) {
+			/* vehicle goes to the packet's next hop or has nondeterministic order: keep the packet*/
+			return UL_KEEP;
+		} else {
+			/* vehicle goes somewhere else, transfer the packet*/
+			return UL_TRANSFER;
+		}
+	}
+}
+
+void VehicleCargoList::SwapReserved()
+{
+	assert(this->packets.empty());
+	this->packets.swap(this->reserved);
+	this->reserved_count = 0;
+}
+
+uint VehicleCargoList::MoveToStation(GoodsEntry * dest, uint max_unload, OrderUnloadFlags order_flags, StationID curr_station, StationID next_station, CargoPayment *payment) {
+	uint remaining_unload = max_unload;
+	uint unloaded;
+	UnloadType action;
+	byte flags = GetUnloadFlags(dest, order_flags);
+
+	for(Iterator c = packets.begin(); c != packets.end() && remaining_unload > 0;) {
+		StationID source = (*c)->source;
+		FlowStatSet &flows = dest->flows[source];
+		FlowStatSet::iterator begin = flows.begin();
+		StationID via = (begin != flows.end() ? begin->via : INVALID_STATION);
+		if (via != INVALID_STATION && next_station != INVALID_STATION) {
+			/* use cargodist unloading*/
+			action = WillUnloadCargoDist(flags, curr_station, next_station, via, source);
+		} else {
+			/* there is no plan: use normal unloading */
+			action = WillUnloadOld(flags, curr_station, source);
+		}
+
+		switch(action) {
+			case UL_DELIVER:
+				unloaded = this->DeliverPacket(c, remaining_unload, payment);
+				if (via != INVALID_STATION) {
+					if (via == curr_station) {
+						dest->UpdateFlowStats(flows, begin, unloaded);
+					} else {
+						dest->UpdateFlowStats(flows, unloaded, curr_station);
+					}
+				}
+				remaining_unload -= unloaded;
+				break;
+			case UL_TRANSFER:
+				/* TransferPacket may split the packet and return the transferred part */
+				if (via == curr_station) {
+					via = (++begin != flows.end()) ? begin->via : INVALID_STATION;
+				}
+				unloaded = this->TransferPacket(c, remaining_unload, dest, payment, via);
+				if (via != INVALID_STATION) {
+					dest->UpdateFlowStats(flows, begin, unloaded);
+				}
+				remaining_unload -= unloaded;
+				break;
+			case UL_KEEP:
+				unloaded = this->KeepPacket(c);
+				if (via != INVALID_STATION && next_station != INVALID_STATION) {
+					if (via == next_station) {
+						dest->UpdateFlowStats(flows, begin, unloaded);
+					} else {
+						dest->UpdateFlowStats(flows, unloaded, next_station);
+					}
+				}
+				break;
+			default:
+				NOT_REACHED();
+		}
+	}
+	return max_unload - remaining_unload;
+}
 
 void VehicleCargoList::RemoveFromCache(const CargoPacket *cp)
 {
@@ -264,6 +471,17 @@ void VehicleCargoList::AddToCache(const CargoPacket *cp)
 	this->Parent::AddToCache(cp);
 }
 
+uint VehicleCargoList::MoveTo(VehicleCargoList *dest, uint cap)
+{
+	uint orig_cap = cap;
+	Iterator it = packets.begin();
+	while(it != packets.end() && cap > 0) {
+		cap -= MovePacket(dest, it, cap);
+	}
+	return orig_cap - cap;
+}
+
+
 void VehicleCargoList::AgeCargo()
 {
 	for (ConstIterator it(this->packets.begin()); it != this->packets.end(); it++) {
@@ -276,21 +494,97 @@ void VehicleCargoList::AgeCargo()
 	}
 }
 
+/* static */ byte VehicleCargoList::GetUnloadFlags(GoodsEntry *dest, OrderUnloadFlags order_flags)
+{
+	byte flags = 0;
+	if (HasBit(dest->acceptance_pickup, GoodsEntry::ACCEPTANCE)) {
+		flags |= UL_ACCEPTED;
+	}
+	if (order_flags & OUFB_UNLOAD) {
+		flags |= UL_DELIVER;
+	}
+	if (order_flags & OUFB_TRANSFER) {
+		flags |= UL_TRANSFER;
+	}
+	return flags;
+}
+
+/*
+ *
+ * Station cargo list implementation
+ *
+ */
+
+void StationCargoList::Append(StationID next, CargoPacket *cp)
+{
+	assert(cp != NULL);
+	this->AddToCache(cp);
+	StationCargoPacketMap::List &list = this->packets[next];
+
+	for (StationCargoPacketMap::List::reverse_iterator it(list.rbegin()); it != list.rend(); it++) {
+		CargoPacket *icp = *it;
+		if (StationCargoList::AreMergable(icp, cp) && icp->count + cp->count <= CargoPacket::MAX_COUNT) {
+			icp->Merge(cp);
+			return;
+		}
+	}
+
+	/* The packet could not be merged with another one */
+	list.push_back(cp);
+}
+
+uint StationCargoList::MovePackets(VehicleCargoList *dest, uint cap, Iterator begin, Iterator end, TileIndex load_place, bool reserve) {
+	uint orig_cap = cap;
+	while(begin != end && cap > 0) {
+		cap -= this->MovePacket(dest, begin, cap, load_place, reserve);
+	}
+	return orig_cap - cap;
+}
+
+uint StationCargoList::MoveTo(VehicleCargoList *dest, uint cap, StationID selected_station, TileIndex load_place, bool reserve) {
+	uint orig_cap = cap;
+	if (selected_station != INVALID_STATION) {
+		std::pair<Iterator, Iterator> bounds(packets.equal_range(selected_station));
+		cap -= MovePackets(dest, cap, bounds.first, bounds.second, load_place, reserve);
+		if (cap > 0) {
+			bounds = packets.equal_range(INVALID_STATION);
+			cap -= MovePackets(dest, cap, bounds.first, bounds.second, load_place, reserve);
+		}
+	} else {
+		cap -= MovePackets(dest, cap, packets.begin(), packets.end(), load_place, reserve);
+	}
+	return orig_cap - cap;
+}
+
+void StationCargoList::RerouteStalePackets(StationID curr, StationID to, GoodsEntry * ge) {
+	std::pair<Iterator, Iterator> range(packets.equal_range(to));
+	for(Iterator it(range.first); it != range.second && it.GetKey() == to;) {
+		CargoPacket * packet = *it;
+		packets.erase(it++);
+		StationID next = ge->UpdateFlowStatsTransfer(packet->source, packet->count, curr);
+		assert(next != to);
+
+		/* legal, as insert doesn't invalidate iterators in the MultiMap, however
+		 * this might insert the packet between range.first and range.second (which might be end())
+		 * This is why we check for GetKey above to avoid infinite loops
+		 */
+		packets.Insert(next, packet);
+	}
+}
+
 void VehicleCargoList::InvalidateCache()
 {
 	this->feeder_share = 0;
+	this->reserved_count = 0;
 	this->Parent::InvalidateCache();
+	for (ConstIterator it(this->reserved.begin()); it != this->reserved.end(); it++) {
+		this->AddToCache(*it);
+		this->reserved_count += (*it)->count;
+	}
 }
 
 /*
  * We have to instantiate everything we want to be usable.
  */
-template class CargoList<VehicleCargoList>;
-template class CargoList<StationCargoList>;
-
-/** Autoreplace Vehicle -> Vehicle 'transfer' */
-template bool CargoList<VehicleCargoList>::MoveTo(VehicleCargoList *, uint max_move, MoveToAction mta, CargoPayment *payment, uint data);
-/** Cargo unloading at a station */
-template bool CargoList<VehicleCargoList>::MoveTo(StationCargoList *, uint max_move, MoveToAction mta, CargoPayment *payment, uint data);
-/** Cargo loading at a station */
-template bool CargoList<StationCargoList>::MoveTo(VehicleCargoList *, uint max_move, MoveToAction mta, CargoPayment *payment, uint data);
+template class CargoList<VehicleCargoList, CargoPacketList>;
+template class CargoList<StationCargoList, StationCargoPacketMap>;
diff --git a/src/cargopacket.h b/src/cargopacket.h
index 64fd714..0832a57 100644
--- a/src/cargopacket.h
+++ b/src/cargopacket.h
@@ -16,20 +16,25 @@
 #include "economy_type.h"
 #include "tile_type.h"
 #include "station_type.h"
+#include "order_type.h"
 #include "cargo_type.h"
 #include "vehicle_type.h"
+#include "core/multimap.hpp"
 #include <list>
 
 /** Unique identifier for a single cargo packet. */
 typedef uint32 CargoPacketID;
 struct CargoPacket;
+struct GoodsEntry;
 
 /** Type of the pool for cargo packets. */
 typedef Pool<CargoPacket, CargoPacketID, 1024, 1048576, true, false> CargoPacketPool;
 /** The actual pool with cargo packets */
 extern CargoPacketPool _cargopacket_pool;
 
-template <class Tinst> class CargoList;
+template <class Tinst, class Tcont> class CargoList;
+class StationCargoList;
+class VehicleCargoList;
 extern const struct SaveLoad *GetCargoPacketDesc();
 
 /**
@@ -47,8 +52,9 @@ private:
 	TileIndex loaded_at_xy;     ///< Location where this cargo has been loaded into the vehicle
 
 	/** The CargoList caches, thus needs to know about it. */
-	template <class Tinst> friend class CargoList;
+	template <class Tinst, class Tcont> friend class CargoList;
 	friend class VehicleCargoList;
+	friend class ReservationList;
 	friend class StationCargoList;
 	/** We want this to be saved, right? */
 	friend const struct SaveLoad *GetCargoPacketDesc();
@@ -165,6 +171,8 @@ public:
 		return this->loaded_at_xy;
 	}
 
+	CargoPacket *Split(uint new_size);
+	void Merge(CargoPacket *other);
 
 	static void InvalidateAllFrom(SourceType src_type, SourceID src);
 	static void InvalidateAllFrom(StationID sid);
@@ -184,33 +192,37 @@ public:
  */
 #define FOR_ALL_CARGOPACKETS(var) FOR_ALL_CARGOPACKETS_FROM(var, 0)
 
+enum UnloadType {
+	UL_KEEP     = 0,      ///< keep cargo on vehicle
+	UL_DELIVER  = 1 << 0, ///< deliver cargo
+	UL_TRANSFER = 1 << 1, ///< transfer cargo
+	UL_ACCEPTED = 1 << 2, ///< cargo is accepted
+};
+
+class StationCargoList;
+class VehicleCargoList;
+
 /**
  * Simple collection class for a list of cargo packets
  * @tparam Tinst The actual instantation of this cargo list
  */
-template <class Tinst>
+template <class Tinst, class Tcont>
 class CargoList {
 public:
-	/** Container with cargo packets */
-	typedef std::list<CargoPacket *> List;
 	/** The iterator for our container */
-	typedef List::iterator Iterator;
+	typedef typename Tcont::iterator Iterator;
 	/** The const iterator for our container */
-	typedef List::const_iterator ConstIterator;
-
-	/** Kind of actions that could be done with packets on move */
-	enum MoveToAction {
-		MTA_FINAL_DELIVERY, ///< "Deliver" the packet to the final destination, i.e. destroy the packet
-		MTA_CARGO_LOAD,     ///< Load the packet onto a vehicle, i.e. set the last loaded station ID
-		MTA_TRANSFER,       ///< The cargo is moved as part of a transfer
-		MTA_UNLOAD,         ///< The cargo is moved as part of a forced unload
-	};
+	typedef typename Tcont::const_iterator ConstIterator;
+	/** The reverse iterator for our container */
+	typedef typename Tcont::reverse_iterator ReverseIterator;
+	/** The const reverse iterator for our container */
+	typedef typename Tcont::const_reverse_iterator ConstReverseIterator;
 
 protected:
 	uint count;                 ///< Cache for the number of cargo entities
 	uint cargo_days_in_transit; ///< Cache for the sum of number of days in transit of each entity; comparable to man-hours
 
-	List packets;               ///< The cargo packets in this list
+	Tcont packets;               ///< The cargo packets in this list
 
 	/**
 	 * Update the cache to reflect adding of this packet.
@@ -226,6 +238,12 @@ protected:
 	 */
 	void RemoveFromCache(const CargoPacket *cp);
 
+	CargoPacket *MovePacket(Iterator &it, uint cap, TileIndex load_place = INVALID_TILE);
+
+	uint MovePacket(StationCargoList *dest, StationID next, Iterator &it, uint cap);
+
+	uint MovePacket(VehicleCargoList *dest, Iterator &it, uint cap, TileIndex load_place = INVALID_TILE, bool reserved = false);
+
 public:
 	/** Create the cargo list */
 	CargoList() {}
@@ -236,7 +254,7 @@ public:
 	 * Returns a pointer to the cargo packet list (so you can iterate over it etc).
 	 * @return pointer to the packet list
 	 */
-	FORCEINLINE const List *Packets() const
+	FORCEINLINE const Tcont *Packets() const
 	{
 		return &this->packets;
 	}
@@ -260,15 +278,6 @@ public:
 	}
 
 	/**
-	 * Returns source of the first cargo packet in this list
-	 * @return the before mentioned source
-	 */
-	FORCEINLINE StationID Source() const
-	{
-		return this->Empty() ? INVALID_STATION : this->packets.front()->source;
-	}
-
-	/**
 	 * Returns average number of days in transit for a cargo entity
 	 * @return the before mentioned number
 	 */
@@ -277,16 +286,6 @@ public:
 		return this->count == 0 ? 0 : this->cargo_days_in_transit / this->count;
 	}
 
-
-	/**
-	 * Appends the given cargo packet
-	 * @warning After appending this packet may not exist anymore!
-	 * @note Do not use the cargo packet anymore after it has been appended to this CargoList!
-	 * @param cp the cargo packet to add
-	 * @pre cp != NULL
-	 */
-	void Append(CargoPacket *cp);
-
 	/**
 	 * Truncates the cargo in this list to the given amount. It leaves the
 	 * first count cargo entities and removes the rest.
@@ -294,43 +293,30 @@ public:
 	 */
 	void Truncate(uint max_remaining);
 
-	/**
-	 * Moves the given amount of cargo to another list.
-	 * Depending on the value of mta the side effects of this function differ:
-	 *  - MTA_FINAL_DELIVERY: destroys the packets that do not originate from a specific station
-	 *  - MTA_CARGO_LOAD:     sets the loaded_at_xy value of the moved packets
-	 *  - MTA_TRANSFER:       just move without side effects
-	 *  - MTA_UNLOAD:         just move without side effects
-	 * @param dest  the destination to move the cargo to
-	 * @param count the amount of cargo entities to move
-	 * @param mta   how to handle the moving (side effects)
-	 * @param data  Depending on mta the data of this variable differs:
-	 *              - MTA_FINAL_DELIVERY - station ID of packet's origin not to remove
-	 *              - MTA_CARGO_LOAD     - station's tile index of load
-	 *              - MTA_TRANSFER       - unused
-	 *              - MTA_UNLOAD         - unused
-	 * @param payment The payment helper
-	 *
-	 * @pre mta == MTA_FINAL_DELIVERY || dest != NULL
-	 * @pre mta == MTA_UNLOAD || mta == MTA_CARGO_LOAD || payment != NULL
-	 * @return true if there are still packets that might be moved from this cargo list
-	 */
-	template <class Tother_inst>
-	bool MoveTo(Tother_inst *dest, uint count, MoveToAction mta, CargoPayment *payment, uint data = 0);
-
 	/** Invalidates the cached data and rebuild it */
 	void InvalidateCache();
 };
 
+typedef std::list<CargoPacket *> CargoPacketList;
+
 /**
  * CargoList that is used for vehicles.
  */
-class VehicleCargoList : public CargoList<VehicleCargoList> {
+class VehicleCargoList : public CargoList<VehicleCargoList, CargoPacketList> {
 protected:
+	static UnloadType WillUnloadOld(byte flags, StationID curr_station, StationID source);
+	static UnloadType WillUnloadCargoDist(byte flags, StationID curr_station, StationID next_station, StationID via, StationID source);
+
+	uint TransferPacket(Iterator &c, uint remaining_unload, GoodsEntry *dest, CargoPayment *payment, StationID next);
+	uint DeliverPacket(Iterator &c, uint remaining_unload, CargoPayment *payment);
+	uint KeepPacket(Iterator &c);
+
 	/** The (direct) parent of this class */
-	typedef CargoList<VehicleCargoList> Parent;
+	typedef CargoList<VehicleCargoList, CargoPacketList> Parent;
 
-	Money feeder_share; ///< Cache for the feeder share
+	CargoPacketList reserved; ///< The packets reserved for unloading in this list
+	Money feeder_share;       ///< Cache for the feeder share
+	uint reserved_count;      ///< count(reserved)
 
 	/**
 	 * Update the cache to reflect adding of this packet.
@@ -346,11 +332,37 @@ protected:
 	 */
 	void RemoveFromCache(const CargoPacket *cp);
 
+	static byte GetUnloadFlags(GoodsEntry *dest, OrderUnloadFlags order_flags);
+
 public:
 	/** The super class ought to know what it's doing */
-	friend class CargoList<VehicleCargoList>;
+	friend class CargoList<VehicleCargoList, CargoPacketList>;
 	/** The vehicles have a cargo list (and we want that saved). */
 	friend const struct SaveLoad *GetVehicleDescription(VehicleType vt);
+	/**
+	 * Moves the given amount of cargo from a vehicle to a station.
+	 * Depending on the value of flags and dest the side effects of this function differ:
+	 *  - dest->acceptance_pickup & GoodsEntry::ACCEPTANCE:
+	 *                        => MoveToStation sets OUF_UNLOAD_IF_POSSIBLE in the flags
+	 *                        packets are accepted here and may be unloaded and/or delivered (=destroyed);
+	 *                        if not using cargodist: all packets are unloaded and delivered
+	 *                        if using cargodist: only packets which have this station as final destination are unloaded and delivered
+	 *                        if using cargodist: other packets may or may not be unloaded, depending on next_station
+	 *                        if not set and using cargodist: packets may still be unloaded, but not delivered.
+	 *  - OUFB_UNLOAD: unload all packets unconditionally;
+	 *                        if OUF_UNLOAD_IF_POSSIBLE set and OUFB_TRANSFER not set: also deliver packets (no matter if using cargodist)
+	 *  - OUFB_TRANSFER: don't deliver any packets;
+	 *                        overrides delivering aspect of OUF_UNLOAD_IF_POSSIBLE
+	 * @param dest         the destination to move the cargo to
+	 * @param max_unload   the maximum amount of cargo entities to move
+	 * @param flags        how to handle the moving (side effects)
+	 * @param curr_station the station where the cargo currently resides
+	 * @param next_station the next unloading station in the vehicle's order list
+	 * @return the number of cargo entities actually moved
+	 */
+	uint MoveToStation(GoodsEntry * dest, uint max_unload, OrderUnloadFlags flags, StationID curr_station, StationID next_station, CargoPayment *payment);
+
+	~VehicleCargoList();
 
 	/**
 	 * Returns total sum of the feeder share for all packets
@@ -362,6 +374,92 @@ public:
 	}
 
 	/**
+	 * tries to merge the packet with another one in the packets list.
+	 * if no fitting packet is found, appends it.
+	 * @param cp the packet to be inserted
+	 */
+	void MergeOrPush(CargoPacket *cp);
+
+	/**
+	 * Appends the given cargo packet
+	 * @warning After appending this packet may not exist anymore!
+	 * @note Do not use the cargo packet anymore after it has been appended to this CargoList!
+	 * @param cp the cargo packet to add
+	 * @param check_merge if true, check existing packets in the list for mergability
+	 * @pre cp != NULL
+	 */
+	void Append(CargoPacket *cp);
+
+	/**
+	 * Returns sum of cargo on board the vehicle (ie not only
+	 * reserved)
+	 * @return cargo on board the vehicle
+	 */
+	FORCEINLINE uint OnboardCount() const
+	{
+		return this->count - this->reserved_count;
+	}
+
+	/**
+	 * Returns sum of cargo reserved for the vehicle
+	 * @return cargo reserved for the vehicle
+	 */
+	FORCEINLINE uint ReservedCount() const
+	{
+		return this->reserved_count;
+	}
+
+	/**
+	 * Returns a pointer to the reserved cargo list (so you can iterate over it etc).
+	 * @return pointer to the reserved list
+	 */
+	FORCEINLINE const CargoPacketList *Reserved() const
+	{
+		return &this->reserved;
+	}
+
+	/**
+	 * Returns source of the first cargo packet in this list
+	 * If the regular packets list is empty but there are packets
+	 * in the reservation list it returns the source of the first
+	 * reserved packet.
+	 * @return the before mentioned source
+	 */
+	FORCEINLINE StationID Source() const
+	{
+		if (this->Empty()) {
+			return INVALID_STATION;
+		} else if (this->packets.empty()) {
+			return this->reserved.front()->source;
+		} else {
+			return this->packets.front()->source;
+		}
+	}
+
+	/**
+	 * Reserves a packet for later loading
+	 */
+	void Reserve(CargoPacket *cp);
+
+	/**
+	 * Returns all reserved cargo to the station
+	 */
+	void Unreserve(StationID next, StationCargoList *dest);
+
+	/**
+	 * load packets from the reserved list
+	 * @params count the number of cargo to load
+	 * @return true if there are still packets that might be loaded from the reservation list
+	 */
+	uint LoadReserved(uint count);
+
+	/**
+	 * swap the reserved and packets lists when starting to load cargo.
+	 * @pre this->packets.empty()
+	 */
+	void SwapReserved();
+
+	/**
 	 * Ages the all cargo in this list
 	 */
 	void AgeCargo();
@@ -370,6 +468,13 @@ public:
 	void InvalidateCache();
 
 	/**
+	 * Moves the given amount of cargo to another vehicle (during autoreplace).
+	 * @param dest         the destination to move the cargo to
+	 * @param max_load     the maximum amount of cargo entities to move
+	 */
+	uint MoveTo(VehicleCargoList *dest, uint cap);
+
+	/**
 	 * Are two the two CargoPackets mergeable in the context of
 	 * a list of CargoPackets for a Vehicle?
 	 * @param cp1 the first CargoPacket
@@ -386,17 +491,28 @@ public:
 	}
 };
 
+typedef MultiMap<StationID, CargoPacket *> StationCargoPacketMap;
+
 /**
  * CargoList that is used for stations.
  */
-class StationCargoList : public CargoList<StationCargoList> {
+class StationCargoList : public CargoList<StationCargoList, StationCargoPacketMap> {
 public:
 	/** The super class ought to know what it's doing */
-	friend class CargoList<StationCargoList>;
+	friend class CargoList<StationCargoList, StationCargoPacketMap>;
 	/** The stations, via GoodsEntry, have a CargoList. */
 	friend const struct SaveLoad *GetGoodsDesc();
 
 	/**
+	 * Returns source of the first cargo packet in this list
+	 * @return the before mentioned source
+	 */
+	FORCEINLINE StationID Source() const
+	{
+		return this->Empty() ? INVALID_STATION : this->packets.begin()->second.front()->source;
+	}
+
+	/**
 	 * Are two the two CargoPackets mergeable in the context of
 	 * a list of CargoPackets for a Vehicle?
 	 * @param cp1 the first CargoPacket
@@ -410,6 +526,28 @@ public:
 				cp1->source_type     == cp2->source_type &&
 				cp1->source_id       == cp2->source_id;
 	}
+
+	uint MoveTo(VehicleCargoList *dest, uint cap, StationID next_station, TileIndex load_place = INVALID_TILE, bool reserve = false);
+
+	/**
+	 * Appends the given cargo packet to the range of packets with the same next station
+	 * @warning After appending this packet may not exist anymore!
+	 * @note Do not use the cargo packet anymore after it has been appended to this CargoList!
+	 * @param next the next hop
+	 * @param cp the cargo packet to add
+	 * @pre cp != NULL
+	 */
+	void Append(StationID next, CargoPacket *cp);
+
+	/**
+	 * route all packets with station "to" as next hop to a different place, except "curr"
+	 */
+	void RerouteStalePackets(StationID curr, StationID to, GoodsEntry * ge);
+
+	static void InvalidateAllFrom(SourceType src_type, SourceID src);
+
+protected:
+	uint MovePackets(VehicleCargoList *dest, uint cap, Iterator begin, Iterator end, TileIndex load_place, bool reserve);
 };
 
 #endif /* CARGOPACKET_H */
diff --git a/src/cargotype.h b/src/cargotype.h
index 29c7b54..a1a531d 100644
--- a/src/cargotype.h
+++ b/src/cargotype.h
@@ -131,6 +131,9 @@ void SetupCargoForClimate(LandscapeID l);
 CargoID GetCargoIDByLabel(CargoLabel cl);
 CargoID GetCargoIDByBitnum(uint8 bitnum);
 
+/* set up the cargos to be displayed in the smallmap's route legend */
+void BuildLinkStatsLegend();
+
 /** Does cargo \a c have cargo class \a cc?
  * @param c  Cargo type.
  * @param cc Cargo class.
diff --git a/src/core/math_func.cpp b/src/core/math_func.cpp
index 7f0630a..1a9f398 100644
--- a/src/core/math_func.cpp
+++ b/src/core/math_func.cpp
@@ -46,3 +46,21 @@ int GreatestCommonDivisor(int a, int b)
 	return a;
 
 }
+
+
+/**
+ * Deterministic approximate division.
+ * Cancels out division errors stemming from the integer nature of the division over multiple runs.
+ */
+int DivideApprox(int a, int b) {
+	int random_like = ((a + b) * (a - b)) % b;
+
+	int remainder = a % b;
+
+	int ret = a / b;
+	if (abs(random_like) < abs(remainder)) {
+		ret += ((a < 0) ^ (b < 0)) ? -1 : 1;
+	}
+
+	return ret;
+}
diff --git a/src/core/math_func.hpp b/src/core/math_func.hpp
index e0b9dbc..9d7319c 100644
--- a/src/core/math_func.hpp
+++ b/src/core/math_func.hpp
@@ -317,5 +317,6 @@ static FORCEINLINE uint ToPercent16(uint i)
 
 int LeastCommonMultiple(int a, int b);
 int GreatestCommonDivisor(int a, int b);
+int DivideApprox(int a, int b);
 
 #endif /* MATH_FUNC_HPP */
diff --git a/src/core/multimap.hpp b/src/core/multimap.hpp
new file mode 100644
index 0000000..58e8e97
--- /dev/null
+++ b/src/core/multimap.hpp
@@ -0,0 +1,245 @@
+/*
+ * multimap.hpp
+ *
+ *  Created on: Sep 15, 2009
+ *      Author: alve
+ */
+
+#ifndef MULTIMAP_HPP_
+#define MULTIMAP_HPP_
+
+#include <map>
+#include <list>
+
+template<typename KEY, typename VALUE, typename COMPARE>
+class MultiMap;
+
+template<class MAP_ITER, class LIST_ITER, class KEY, class VALUE, class COMPARE>
+class MultiMapIterator {
+protected:
+	friend class MultiMap<KEY, VALUE, COMPARE>;
+	typedef MultiMapIterator<MAP_ITER, LIST_ITER, KEY, VALUE, COMPARE> Self;
+	LIST_ITER list_iter;
+	MAP_ITER map_iter;
+	bool list_valid;
+
+public:
+	MultiMapIterator() : list_valid(false) {}
+	template<class NONCONST>
+	MultiMapIterator(NONCONST mi) : map_iter(mi), list_valid(false) {}
+	MultiMapIterator(MAP_ITER mi, LIST_ITER li) : list_iter(li), map_iter(mi)
+	{
+		list_valid = (list_iter != map_iter->second.begin());
+	}
+
+	template<class NONCONST>
+	Self &operator=(NONCONST mi)
+	{
+		map_iter = mi;
+		list_valid = false;
+	}
+
+	VALUE &operator*() const
+	{
+		assert(!map_iter->second.empty());
+		if (list_valid) {
+			return list_iter.operator*();
+		} else {
+			return map_iter->second.begin().operator*();
+		}
+	}
+
+	VALUE *operator->() const
+	{
+		assert(!map_iter->second.empty());
+		if (list_valid) {
+			return list_iter.operator->();
+		} else {
+			return map_iter->second.begin().operator->();
+		}
+	}
+
+	FORCEINLINE const MAP_ITER &GetMapIter() const {return map_iter;}
+	FORCEINLINE const LIST_ITER &GetListIter() const {return list_iter;}
+	FORCEINLINE bool ListValid() const {return list_valid;}
+
+	const KEY &GetKey() const {return map_iter->first;}
+
+	Self &operator++()
+	{
+		assert(!map_iter->second.empty());
+		if (list_valid) {
+			if(++list_iter == map_iter->second.end()) {
+				++map_iter;
+				list_valid = false;
+			}
+		} else {
+			list_iter = ++(map_iter->second.begin());
+			if (list_iter == map_iter->second.end()) {
+				++map_iter;
+			} else {
+				list_valid = true;
+			}
+		}
+		return *this;
+	}
+
+	Self operator++(int)
+	{
+		Self tmp = *this;
+		this->operator++();
+		return tmp;
+	}
+
+	Self &operator--()
+	{
+		assert(!map_iter->second.empty());
+		if (!list_valid) {
+			--map_iter;
+			list_iter = map_iter->second.end();
+			assert(!map_iter->second.empty());
+		}
+
+		if(--list_iter == map_iter->second.begin()) {
+			list_valid = false;
+		} else {
+			list_valid = true;
+		}
+
+		return *this;
+	}
+
+	Self operator--(int)
+	{
+		Self tmp = *this;
+		this->operator--();
+		return tmp;
+	}
+};
+
+/* generic comparison functions for const/non-const multimap iterators and map iterators */
+
+template<class MAP_ITER1, class LIST_ITER1, class MAP_ITER2, class LIST_ITER2, class KEY, class VALUE1, class VALUE2, class COMPARE>
+bool operator==(const MultiMapIterator<MAP_ITER1, LIST_ITER1, KEY, VALUE1, COMPARE> &iter1, const MultiMapIterator<MAP_ITER2, LIST_ITER2, KEY, VALUE2, COMPARE> &iter2)
+{
+	if (iter1.ListValid()) {
+		if (!iter2.ListValid()) {
+			return false;
+		} else if (iter1.GetListIter() != iter2.GetListIter()) {
+			return false;
+		}
+	} else if (iter2.ListValid()) {
+		return false;
+	}
+	return (iter1.GetMapIter() == iter2.GetMapIter());
+}
+
+template<class MAP_ITER1, class LIST_ITER1, class MAP_ITER2, class LIST_ITER2, class KEY, class VALUE1, class VALUE2, class COMPARE>
+bool operator!=(const MultiMapIterator<MAP_ITER1, LIST_ITER1, KEY, VALUE1, COMPARE> &iter1, const MultiMapIterator<MAP_ITER2, LIST_ITER2, KEY, VALUE2, COMPARE> &iter2)
+{
+	return !(iter1 == iter2);
+}
+
+template<class MAP_ITER1, class LIST_ITER1, class MAP_ITER2, class KEY, class VALUE, class COMPARE >
+bool operator==(const MultiMapIterator<MAP_ITER1, LIST_ITER1, KEY, VALUE, COMPARE> &iter1, const MAP_ITER2 &iter2)
+{
+	return !iter1.ListValid() && iter1.GetMapIter() == iter2;
+}
+
+template<class MAP_ITER1, class LIST_ITER1, class MAP_ITER2, class KEY, class VALUE, class COMPARE >
+bool operator!=(const MultiMapIterator<MAP_ITER1, LIST_ITER1, KEY, VALUE, COMPARE> &iter1, const MAP_ITER2 &iter2)
+{
+	return iter1.ListValid() || iter1.GetMapIter() != iter2;
+}
+
+template<class MAP_ITER1, class LIST_ITER1, class MAP_ITER2, class KEY, class VALUE, class COMPARE >
+bool operator==(const MAP_ITER2 &iter2, const MultiMapIterator<MAP_ITER1, LIST_ITER1, KEY, VALUE, COMPARE> &iter1)
+{
+	return !iter1.ListValid() && iter1.GetMapIter() == iter2;
+}
+
+template<class MAP_ITER1, class LIST_ITER1, class MAP_ITER2, class KEY, class VALUE, class COMPARE >
+bool operator!=(const MAP_ITER2 &iter2, const MultiMapIterator<MAP_ITER1, LIST_ITER1, KEY, VALUE, COMPARE> &iter1)
+{
+	return iter1.ListValid() || iter1.GetMapIter() != iter2;
+}
+
+
+/**
+ * hand-rolled multimap as map of lists. behaves mostly like a list, but is sorted
+ * by KEY.
+ */
+template<typename KEY, typename VALUE, typename COMPARE = std::less<KEY> >
+class MultiMap : public std::map<KEY, std::list<VALUE>, COMPARE > {
+public:
+	typedef typename std::list<VALUE> List;
+	typedef typename List::iterator ListIterator;
+	typedef typename List::const_iterator ConstListIterator;
+
+	typedef typename std::map<KEY, List, COMPARE > Map;
+	typedef typename Map::iterator MapIterator;
+	typedef typename Map::const_iterator ConstMapIterator;
+
+	typedef MultiMapIterator<MapIterator, ListIterator, KEY, VALUE, COMPARE> iterator;
+	typedef MultiMapIterator<ConstMapIterator, ConstListIterator, KEY, const VALUE, COMPARE> const_iterator;
+
+	void erase(iterator it)
+	{
+		List &list = it.map_iter->second;
+		assert(!list.empty());
+		if (it.ListValid()) {
+			list.erase(it.list_iter);
+		} else {
+			list.erase(list.begin());
+		}
+
+		if (list.empty()) {
+			Map::erase(it.map_iter);
+		}
+	}
+
+	void Insert(const KEY &key, const VALUE &val)
+	{
+		List &list = (*this)[key];
+		list.push_back(val);
+		assert(!list.empty());
+	}
+
+	size_t size() const
+	{
+		size_t ret = 0;
+		for(ConstMapIterator it = Map::begin(); it != Map::end(); ++it) {
+			ret += it->second.size();
+		}
+		return ret;
+	}
+
+	size_t MapSize() const
+	{
+		return Map::size();
+	}
+
+	std::pair<iterator, iterator> equal_range(const KEY &key)
+	{
+		MapIterator begin(lower_bound(key));
+		if (begin != Map::end() && begin->first == key) {
+			MapIterator end = begin;
+			return std::make_pair(begin, ++end);
+		} else {
+			return std::make_pair(begin, begin);
+		}
+	}
+
+	std::pair<const_iterator, const_iterator> equal_range(const KEY &key) const
+	{
+		ConstMapIterator begin(lower_bound(key));
+		if (begin != Map::end() && begin->first == key) {
+			ConstMapIterator end = begin;
+			return std::make_pair(begin, ++end);
+		} else {
+			return std::make_pair(begin, begin);
+		}
+	}
+};
+
+#endif /* MULTIMAP_HPP_ */
diff --git a/src/date.cpp b/src/date.cpp
index 9122b21..8bf5afa 100644
--- a/src/date.cpp
+++ b/src/date.cpp
@@ -26,6 +26,7 @@ Year      _cur_year;   ///< Current year, starting at 0
 Month     _cur_month;  ///< Current month (0..11)
 Date      _date;       ///< Current date in days (day counter)
 DateFract _date_fract;
+uint8     _date_daylength_factor;	///< Setting for DAY_LENGTH
 
 
 void SetDate(Date date)
@@ -167,8 +168,12 @@ extern void TownsYearlyLoop();
 extern void ShowEndGameChart();
 
 
-static const Month _autosave_months[] = {
+static const int _autosave_intervals[] = {
 	 0, ///< never
+	
+	 1,  ///< every day
+ 	 7,  ///< every week
+
 	 1, ///< every month
 	 3, ///< every 3 months
 	 6, ///< every 6 months
@@ -224,7 +229,7 @@ static void OnNewMonth()
 		SaveOrLoad(name, SL_SAVE, AUTOSAVE_DIR);
 	}
 
-	if (_settings_client.gui.autosave != 0 && (_cur_month % _autosave_months[_settings_client.gui.autosave]) == 0) {
+	if (_settings_client.gui.autosave >= 3 && (_cur_month % _autosave_intervals[_settings_client.gui.autosave]) == 0) {
 		_do_autosave = true;
 		RedrawAutosave();
 	}
@@ -246,6 +251,12 @@ static void OnNewMonth()
  */
 static void OnNewDay()
 {
+	// XXX: use day-of-month?
+	if (_settings_client.gui.autosave != 0 && _settings_client.gui.autosave <= 2 && (_date % _autosave_intervals[_settings_client.gui.autosave]) == 0) {
+		_do_autosave = true;
+		RedrawAutosave();
+	}
+
 #ifdef ENABLE_NETWORK
 	NetworkChatMessageDailyLoop();
 #endif /* ENABLE_NETWORK */
diff --git a/src/date_type.h b/src/date_type.h
index e0481ff..208478c 100644
--- a/src/date_type.h
+++ b/src/date_type.h
@@ -19,11 +19,21 @@
  * 1 day is thus about 2 seconds (74 * 30 = 2220) on a machine that can run OpenTTD normally
  */
 enum {
-	DAY_TICKS = 74,          ///< ticks per day
+	ORIG_DAY_TICKS = 74,     ///< unscaled ticks per day
 	DAYS_IN_YEAR = 365,      ///< days per year
 	DAYS_IN_LEAP_YEAR = 366, ///< sometimes, you need one day more...
 };
 
+/**
+ * Scale factor for DAY_TICKS
+ */
+extern uint8 _date_daylength_factor;
+
+/**
+ * Day length in ticks, calculated using a configureable scale factor.
+ */
+#define DAY_TICKS (ORIG_DAY_TICKS * _date_daylength_factor)
+
 /*
  * ORIGINAL_BASE_YEAR, ORIGINAL_MAX_YEAR and DAYS_TILL_ORIGINAL_BASE_YEAR are
  * primarily used for loading newgrf and savegame data and returning some
diff --git a/src/depot_gui.cpp b/src/depot_gui.cpp
index 9c918a4..fc46b5e 100644
--- a/src/depot_gui.cpp
+++ b/src/depot_gui.cpp
@@ -29,6 +29,7 @@
 #include "tilehighlight_func.h"
 #include "window_gui.h"
 #include "vehiclelist.h"
+#include "infrastructure_func.h"
 
 #include "table/strings.h"
 #include "table/sprites.h"
@@ -696,7 +697,7 @@ struct DepotWindow : Window {
 
 		/* Setup disabled buttons. */
 		TileIndex tile = this->window_number;
-		this->SetWidgetsDisabledState(!IsTileOwner(tile, _local_company),
+		this->SetWidgetsDisabledState(!(Company::IsValidID(_local_company) && IsInfraTileUsageAllowed(tile, _local_company, this->type)),
 			DEPOT_WIDGET_STOP_ALL,
 			DEPOT_WIDGET_START_ALL,
 			DEPOT_WIDGET_SELL,
diff --git a/src/economy.cpp b/src/economy.cpp
index 83fa47b..21acd96 100644
--- a/src/economy.cpp
+++ b/src/economy.cpp
@@ -46,6 +46,8 @@
 #include "waypoint_base.h"
 #include "economy_base.h"
 #include "core/pool_func.hpp"
+#include "debug.h"
+#include "infrastructure_func.h"
 
 #include "table/strings.h"
 #include "table/sprites.h"
@@ -366,7 +368,8 @@ void ChangeOwnershipOfCompanyItems(Owner old_owner, Owner new_owner)
 		ClrBit(t->have_ratings, old_owner);
 	}
 
-	{
+	/* Change ownership of vehicles */
+	if (new_owner != INVALID_OWNER) {
 		FreeUnitIDGenerator unitidgen[] = {
 			FreeUnitIDGenerator(VEH_TRAIN, new_owner), FreeUnitIDGenerator(VEH_ROAD,     new_owner),
 			FreeUnitIDGenerator(VEH_SHIP,  new_owner), FreeUnitIDGenerator(VEH_AIRCRAFT, new_owner)
@@ -375,16 +378,16 @@ void ChangeOwnershipOfCompanyItems(Owner old_owner, Owner new_owner)
 		Vehicle *v;
 		FOR_ALL_VEHICLES(v) {
 			if (v->owner == old_owner && IsCompanyBuildableVehicleType(v->type)) {
-				if (new_owner == INVALID_OWNER) {
-					if (v->Previous() == NULL) delete v;
-				} else {
-					v->owner = new_owner;
-					v->colourmap = PAL_NONE;
-					if (v->IsEngineCountable()) Company::Get(new_owner)->num_engines[v->engine_type]++;
-					if (v->IsPrimaryVehicle()) v->unitnumber = unitidgen[v->type].NextID();
-				}
+				v->owner = new_owner;
+				v->colourmap = PAL_NONE;
+				if (v->IsEngineCountable()) Company::Get(new_owner)->num_engines[v->engine_type]++;
+				if (v->IsPrimaryVehicle()) v->unitnumber = unitidgen[v->type].NextID();
 			}
 		}
+	} else {
+		/* Depending on sharing settings, other companies could be affected too.
+		 * Let the infrastructure sharing code handle this. */
+		HandleSharingCompanyDeletion(old_owner);
 	}
 
 	/*  Change ownership of tiles */
@@ -399,22 +402,13 @@ void ChangeOwnershipOfCompanyItems(Owner old_owner, Owner new_owner)
 			 * and signals were not propagated
 			 * Similiar with crossings - it is needed to bar crossings that weren't before
 			 * because of different owner of crossing and approaching train */
-			tile = 0;
-
-			do {
-				if (IsTileType(tile, MP_RAILWAY) && IsTileOwner(tile, new_owner) && HasSignals(tile)) {
-					TrackBits tracks = GetTrackBits(tile);
-					do { // there may be two tracks with signals for TRACK_BIT_HORZ and TRACK_BIT_VERT
-						Track track = RemoveFirstTrack(&tracks);
-						if (HasSignalOnTrack(tile, track)) AddTrackToSignalBuffer(tile, track, new_owner);
-					} while (tracks != TRACK_BIT_NONE);
-				} else if (IsLevelCrossingTile(tile) && IsTileOwner(tile, new_owner)) {
-					UpdateLevelCrossing(tile);
-				}
-			} while (++tile != MapSize());
+			UpdateAllBlockSignals(new_owner);
+		} else if (_settings_game.sharing.sharing_rail) {
+			/* tracks are being removed while sharing is enabled.
+			 * Thus, update all signals and crossings. */
+			UpdateAllBlockSignals();
 		}
-
-		/* update signals in buffer */
+		/* Update any signals in the buffer */
 		UpdateSignalsInBuffer();
 	}
 
@@ -1013,22 +1007,26 @@ CargoPayment::~CargoPayment()
 
 	this->front->cargo_payment = NULL;
 
-	if (this->visual_profit == 0) return;
+	if (this->visual_profit == 0 && this->visual_transfer == 0) return;
 
 	CompanyID old_company = _current_company;
 	_current_company = this->front->owner;
 
 	SubtractMoneyFromCompany(CommandCost(this->front->GetExpenseType(true), -this->route_profit));
-	this->front->profit_this_year += this->visual_profit << 8;
+	this->front->profit_this_year += (this->visual_profit + this->visual_transfer) << 8;
 
+	int transfer_offset = 0;
 	if (this->route_profit != 0) {
 		if (IsLocalCompany() && !PlayVehicleSound(this->front, VSE_LOAD_UNLOAD)) {
 			SndPlayVehicleFx(SND_14_CASHTILL, this->front);
 		}
 
 		ShowCostOrIncomeAnimation(this->front->x_pos, this->front->y_pos, this->front->z_pos, -this->visual_profit);
-	} else {
-		ShowFeederIncomeAnimation(this->front->x_pos, this->front->y_pos, this->front->z_pos, this->visual_profit);
+		transfer_offset = 6;
+	}
+
+	if (this->visual_transfer != 0){
+		ShowFeederIncomeAnimation(this->front->x_pos + transfer_offset, this->front->y_pos + transfer_offset, this->front->z_pos, this->visual_transfer);
 	}
 
 	_current_company = old_company;
@@ -1070,7 +1068,7 @@ Money CargoPayment::PayTransfer(const CargoPacket *cp, uint count)
 
 	profit = profit * _settings_game.economy.feeder_payment_share / 100;
 
-	this->visual_profit += profit; // accumulate transfer profits for whole vehicle
+	this->visual_transfer += profit; // accumulate transfer profits for whole vehicle
 	return profit; // account for the (virtual) profit already made for the cargo packet
 }
 
@@ -1078,7 +1076,7 @@ Money CargoPayment::PayTransfer(const CargoPacket *cp, uint count)
  * Prepare the vehicle to be unloaded.
  * @param front_v the vehicle to be unloaded
  */
-void PrepareUnload(Vehicle *front_v)
+void PrepareUnload(Station * curr_station, Vehicle *front_v, StationID next_station_id)
 {
 	/* At this moment loading cannot be finished */
 	ClrBit(front_v->vehicle_flags, VF_LOADING_FINISHED);
@@ -1086,7 +1084,10 @@ void PrepareUnload(Vehicle *front_v)
 	/* Start unloading in at the first possible moment */
 	front_v->load_unload_ticks = 1;
 
-	if ((front_v->current_order.GetUnloadType() & OUFB_NO_UNLOAD) == 0) {
+	if ((front_v->current_order.GetUnloadType() & OUFB_NO_UNLOAD) != 0) {
+		/* vehicle will keep all its cargo and LoadUnloadVehicle will never call MoveToStation */
+		UpdateFlows(curr_station, front_v, next_station_id);
+	} else {
 		for (Vehicle *v = front_v; v != NULL; v = v->Next()) {
 			if (v->cargo_cap > 0 && !v->cargo.Empty()) {
 				SetBit(v->vehicle_flags, VF_CARGO_UNLOADING);
@@ -1099,40 +1100,67 @@ void PrepareUnload(Vehicle *front_v)
 }
 
 /**
+ * reserves cargo if the full load order and improved_load is set.
+ * @param st The station where the consist is loading at the moment
+ * @param u The front of the loading vehicle consist
+ * @param next_station The next station the vehicle will stop at
+ * @return bit field for the cargo classes with bit for the reserved cargos set (if anything was reserved).
+ */
+uint32 ReserveConsist(Station * st, Vehicle * u, StationID next_station)
+{
+	uint32 ret = 0;
+	if (_settings_game.order.improved_load && (u->current_order.GetLoadType() & OLFB_FULL_LOAD)) {
+		/* Update reserved cargo */
+		for (Vehicle * v = u; v != NULL; v = v->Next()) {
+			// only reserve if the vehicle is not unloading anymore. Otherwise we'll swap in reserved cargo
+			if (HasBit(v->vehicle_flags, VF_CARGO_UNLOADING)) continue;
+			int cap = v->cargo_cap - v->cargo.Count();
+			if (cap > 0) {
+				StationCargoList & list = st->goods[v->cargo_type].cargo;
+				if (list.MoveTo(&v->cargo, cap, next_station, st->xy, true) > 0) {
+					SetBit(ret, v->cargo_type);
+				}
+			}
+		}
+	}
+	return ret;
+}
+
+
+/**
  * Loads/unload the vehicle if possible.
  * @param v the vehicle to be (un)loaded
- * @param cargo_left the amount of each cargo type that is
- *                   virtually left on the platform to be
- *                   picked up by another vehicle when all
- *                   previous vehicles have loaded.
+ * @param cargos_reserved bit field: the cargo classes for which cargo has been reserved in this loading cycle
+ * @return the updated cargo_reserved
  */
-static void LoadUnloadVehicle(Vehicle *v, int *cargo_left)
+static uint32 LoadUnloadVehicle(Vehicle *v, uint32 cargos_reserved)
 {
 	assert(v->current_order.IsType(OT_LOADING));
 
 	assert(v->load_unload_ticks != 0);
 
+	StationID last_visited = v->last_station_visited;
+	Station *st = Station::Get(last_visited);
+
+	StationID next_station = INVALID_STATION;
+	OrderList * orders = v->orders.list;
+	if (orders != NULL) {
+		next_station = orders->GetNextStoppingStation(v->cur_order_index, v->type == VEH_TRAIN || v->type == VEH_ROAD);
+	}
+
 	/* We have not waited enough time till the next round of loading/unloading */
 	if (--v->load_unload_ticks != 0) {
-		if (_settings_game.order.improved_load && (v->current_order.GetLoadType() & OLFB_FULL_LOAD)) {
-			/* 'Reserve' this cargo for this vehicle, because we were first. */
-			for (; v != NULL; v = v->Next()) {
-				int cap_left = v->cargo_cap - v->cargo.Count();
-				if (cap_left > 0) cargo_left[v->cargo_type] -= cap_left;
-			}
-		}
-		return;
+		return cargos_reserved | ReserveConsist(st, v, next_station);
 	}
 
-	StationID last_visited = v->last_station_visited;
-	Station *st = Station::Get(last_visited);
+	OrderUnloadFlags unload_flags = v->current_order.GetUnloadType();
 
 	if (v->type == VEH_TRAIN && (!IsTileType(v->tile, MP_STATION) || GetStationIndex(v->tile) != st->index)) {
 		/* The train reversed in the station. Take the "easy" way
 		 * out and let the train just leave as it always did. */
 		SetBit(v->vehicle_flags, VF_LOADING_FINISHED);
 		v->load_unload_ticks = 1;
-		return;
+		return cargos_reserved;
 	}
 
 	int unloading_time = 0;
@@ -1166,56 +1194,30 @@ static void LoadUnloadVehicle(Vehicle *v, int *cargo_left)
 
 		GoodsEntry *ge = &st->goods[v->cargo_type];
 
-		if (HasBit(v->vehicle_flags, VF_CARGO_UNLOADING) && (u->current_order.GetUnloadType() & OUFB_NO_UNLOAD) == 0) {
-			uint cargo_count = v->cargo.Count();
+		if (HasBit(v->vehicle_flags, VF_CARGO_UNLOADING)) {
+			/* vehicle wants to unload something */
+
+			uint cargo_count = v->cargo.OnboardCount();
 			uint amount_unloaded = _settings_game.order.gradual_loading ? min(cargo_count, load_amount) : cargo_count;
-			bool remaining = false; // Are there cargo entities in this vehicle that can still be unloaded here?
-			bool accepted  = false; // Is the cargo accepted by the station?
 
 			payment->SetCargo(v->cargo_type);
+			uint delivered = v->cargo.MoveToStation(ge, amount_unloaded, unload_flags, last_visited, next_station, payment);
 
-			if (HasBit(ge->acceptance_pickup, GoodsEntry::ACCEPTANCE) && !(u->current_order.GetUnloadType() & OUFB_TRANSFER)) {
-				/* The cargo has reached it's final destination, the packets may now be destroyed */
-				remaining = v->cargo.MoveTo<StationCargoList>(NULL, amount_unloaded, VehicleCargoList::MTA_FINAL_DELIVERY, payment, last_visited);
-
-				dirty_vehicle = true;
-				accepted = true;
-			}
-
-			/* The !accepted || v->cargo.Count == cargo_count clause is there
-			 * to make it possible to force unload vehicles at the station where
-			 * they were loaded, but to not force unload the vehicle when the
-			 * station is still accepting the cargo in the vehicle. It doesn't
-			 * accept cargo that was loaded at the same station. */
-			if ((u->current_order.GetUnloadType() & (OUFB_UNLOAD | OUFB_TRANSFER)) && (!accepted || v->cargo.Count() == cargo_count)) {
-				remaining = v->cargo.MoveTo(&ge->cargo, amount_unloaded, u->current_order.GetUnloadType() & OUFB_TRANSFER ? VehicleCargoList::MTA_TRANSFER : VehicleCargoList::MTA_UNLOAD, payment);
-				SetBit(ge->acceptance_pickup, GoodsEntry::PICKUP);
-
-				dirty_vehicle = dirty_station = true;
-			} else if (!accepted) {
-				/* The order changed while unloading (unset unload/transfer) or the
-				 * station does not accept our goods. */
-				ClrBit(v->vehicle_flags, VF_CARGO_UNLOADING);
-
-				/* Say we loaded something, otherwise we'll think we didn't unload
-				 * something and we didn't load something, so we must be finished
-				 * at this station. Setting the unloaded means that we will get a
-				 * retry for loading in the next cycle. */
-				anything_unloaded = true;
-				continue;
-			}
-
-			/* Deliver goods to the station */
 			st->time_since_unload = 0;
-
-			unloading_time += amount_unloaded;
-
+			unloading_time += delivered;
 			anything_unloaded = true;
-			if (_settings_game.order.gradual_loading && remaining) {
-				completely_emptied = false;
-			} else {
-				/* We have finished unloading (cargo count == 0) */
+			dirty_vehicle = true;
+
+			/* load_amount might (theoretically) be 0, which would make delivered == 0 even though there is still cargo
+			 * in the vehicle. Thus OnboardCount > 0. In that case we can't stop unloading as SwapReserved wouldn't work.
+			 * v->cargo also contains the cargo reserved for the vehicle which is not on board at the moment, but will be
+			 * swapped back when done unloading.
+			 */
+			if (v->cargo.OnboardCount() == 0) {
+				/* done delivering */
+				if (!v->cargo.Empty()) completely_emptied = false;
 				ClrBit(v->vehicle_flags, VF_CARGO_UNLOADING);
+				v->cargo.SwapReserved();
 			}
 
 			continue;
@@ -1241,28 +1243,19 @@ static void LoadUnloadVehicle(Vehicle *v, int *cargo_left)
 
 		/* If there's goods waiting at the station, and the vehicle
 		 * has capacity for it, load it on the vehicle. */
-		int cap_left = v->cargo_cap - v->cargo.Count();
-		if (!ge->cargo.Empty() && cap_left > 0) {
+		int cap_left = v->cargo_cap - v->cargo.OnboardCount();
+		if (cap_left > 0) {
 			uint cap = cap_left;
-			uint count = ge->cargo.Count();
-
-			/* Skip loading this vehicle if another train/vehicle is already handling
-			 * the same cargo type at this station */
-			if (_settings_game.order.improved_load && cargo_left[v->cargo_type] <= 0) {
-				SetBit(cargo_not_full, v->cargo_type);
-				continue;
-			}
-
-			if (cap > count) cap = count;
 			if (_settings_game.order.gradual_loading) cap = min(cap, load_amount);
+
+			uint loaded = 0;
 			if (_settings_game.order.improved_load) {
-				/* Don't load stuff that is already 'reserved' for other vehicles */
-				cap = min((uint)cargo_left[v->cargo_type], cap);
-				cargo_left[v->cargo_type] -= cap;
+				loaded += v->cargo.LoadReserved(cap);
+			}
+			if (loaded < cap) {
+				assert(v->cargo.ReservedCount() == 0);
+				loaded += ge->cargo.MoveTo(&v->cargo, cap - loaded, next_station, st->xy);
 			}
-
-			if (v->cargo.Empty()) TriggerVehicle(v, VEHICLE_TRIGGER_NEW_CARGO);
-
 			/* TODO: Regarding this, when we do gradual loading, we
 			 * should first unload all vehicles and then start
 			 * loading them. Since this will cause
@@ -1270,22 +1263,27 @@ static void LoadUnloadVehicle(Vehicle *v, int *cargo_left)
 			 * the whole vehicle chain is really totally empty, the
 			 * completely_emptied assignment can then be safely
 			 * removed; that's how TTDPatch behaves too. --pasky */
-			completely_emptied = false;
-			anything_loaded = true;
-
-			ge->cargo.MoveTo(&v->cargo, cap, StationCargoList::MTA_CARGO_LOAD, NULL, st->xy);
+			if (loaded > 0) {
+				completely_emptied = false;
+				anything_loaded = true;
 
-			st->time_since_load = 0;
-			st->last_vehicle_type = v->type;
+				st->time_since_load = 0;
+				st->last_vehicle_type = v->type;
 
-			StationAnimationTrigger(st, st->xy, STAT_ANIM_CARGO_TAKEN, v->cargo_type);
+				StationAnimationTrigger(st, st->xy, STAT_ANIM_CARGO_TAKEN, v->cargo_type);
 
-			unloading_time += cap;
+				unloading_time += loaded;
 
-			dirty_vehicle = dirty_station = true;
+				dirty_vehicle = dirty_station = true;
+			} else if  (_settings_game.order.improved_load && HasBit(cargos_reserved, v->cargo_type)) {
+				/* Skip loading this vehicle if another train/vehicle is already handling
+				 * the same cargo type at this station */
+				SetBit(cargo_not_full, v->cargo_type);
+				continue;
+			}
 		}
 
-		if (v->cargo.Count() >= v->cargo_cap) {
+		if (v->cargo.OnboardCount() >= v->cargo_cap) {
 			SetBit(cargo_full, v->cargo_type);
 		} else {
 			SetBit(cargo_not_full, v->cargo_type);
@@ -1295,18 +1293,6 @@ static void LoadUnloadVehicle(Vehicle *v, int *cargo_left)
 	/* Only set completly_emptied, if we just unloaded all remaining cargo */
 	completely_emptied &= anything_unloaded;
 
-	/* We update these variables here, so gradual loading still fills
-	 * all wagons at the same time instead of using the same 'improved'
-	 * loading algorithm for the wagons (only fill wagon when there is
-	 * enough to fill the previous wagons) */
-	if (_settings_game.order.improved_load && (u->current_order.GetLoadType() & OLFB_FULL_LOAD)) {
-		/* Update left cargo */
-		for (v = u; v != NULL; v = v->Next()) {
-			int cap_left = v->cargo_cap - v->cargo.Count();
-			if (cap_left > 0) cargo_left[v->cargo_type] -= cap_left;
-		}
-	}
-
 	v = u;
 
 	if (!anything_unloaded) delete payment;
@@ -1325,7 +1311,7 @@ static void LoadUnloadVehicle(Vehicle *v, int *cargo_left)
 			if (v->current_order.GetLoadType() == OLF_FULL_LOAD_ANY) {
 				/* if the aircraft carries passengers and is NOT full, then
 				 * continue loading, no matter how much mail is in */
-				if ((v->type == VEH_AIRCRAFT && IsCargoInClass(v->cargo_type, CC_PASSENGERS) && v->cargo_cap > v->cargo.Count()) ||
+				if ((v->type == VEH_AIRCRAFT && IsCargoInClass(v->cargo_type, CC_PASSENGERS) && v->cargo_cap > v->cargo.OnboardCount()) ||
 						(cargo_not_full && (cargo_full & ~cargo_not_full) == 0)) { // There are stull non-full cargos
 					finished_loading = false;
 				}
@@ -1379,6 +1365,7 @@ static void LoadUnloadVehicle(Vehicle *v, int *cargo_left)
 		st->MarkTilesDirty(true);
 		SetWindowDirty(WC_STATION_VIEW, last_visited);
 	}
+	return cargos_reserved;
 }
 
 /**
@@ -1391,14 +1378,12 @@ void LoadUnloadStation(Station *st)
 	/* No vehicle is here... */
 	if (st->loading_vehicles.empty()) return;
 
-	int cargo_left[NUM_CARGO];
-
-	for (uint i = 0; i < NUM_CARGO; i++) cargo_left[i] = st->goods[i].cargo.Count();
+	uint32 cargos_reserved = 0;
 
 	std::list<Vehicle *>::iterator iter;
 	for (iter = st->loading_vehicles.begin(); iter != st->loading_vehicles.end(); ++iter) {
 		Vehicle *v = *iter;
-		if (!(v->vehstatus & (VS_STOPPED | VS_CRASHED))) LoadUnloadVehicle(v, cargo_left);
+		if (!(v->vehstatus & (VS_STOPPED | VS_CRASHED))) cargos_reserved = LoadUnloadVehicle(v, cargos_reserved);
 	}
 
 	/* Call the production machinery of industries */
diff --git a/src/economy_base.h b/src/economy_base.h
index 5d7124c..c6b9241 100644
--- a/src/economy_base.h
+++ b/src/economy_base.h
@@ -24,9 +24,10 @@ extern CargoPaymentPool _cargo_payment_pool;
  * Helper class to perform the cargo payment.
  */
 struct CargoPayment : CargoPaymentPool::PoolItem<&_cargo_payment_pool> {
-	Vehicle *front;      ///< The front vehicle to do the payment of
-	Money route_profit;  ///< The amount of money to add/remove from the bank account
-	Money visual_profit; ///< The visual profit to show
+	Vehicle *front;        ///< The front vehicle to do the payment of
+	Money route_profit;    ///< The amount of money to add/remove from the bank account
+	Money visual_profit;   ///< The visual profit to show
+	Money visual_transfer; ///< The transfer credits to be shown
 
 	/* Unsaved variables */
 	Company *owner;            ///< The owner of the vehicle
diff --git a/src/economy_func.h b/src/economy_func.h
index e03d057..b56d512 100644
--- a/src/economy_func.h
+++ b/src/economy_func.h
@@ -38,7 +38,7 @@ void StartupIndustryDailyChanges(bool init_counter);
 Money GetTransportedGoodsIncome(uint num_pieces, uint dist, byte transit_days, CargoID cargo_type);
 uint MoveGoodsToStation(CargoID type, uint amount, SourceType source_type, SourceID source_id, const StationList *all_stations);
 
-void PrepareUnload(Vehicle *front_v);
+void PrepareUnload(Station * curr_station, Vehicle *front_v, StationID next_station_id);
 void LoadUnloadStation(Station *st);
 
 Money GetPrice(Price index, uint cost_factor, const struct GRFFile *grf_file, int shift = 0);
diff --git a/src/infrastructure.cpp b/src/infrastructure.cpp
new file mode 100644
index 0000000..6f6a72d
--- /dev/null
+++ b/src/infrastructure.cpp
@@ -0,0 +1,335 @@
+/* $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 infrastructure.cpp Implementation of infrastructure sharing */
+
+#include "stdafx.h"
+#include "infrastructure_func.h"
+#include "train.h"
+#include "aircraft.h"
+#include "vehicle_func.h"
+#include "station_base.h"
+#include "depot_base.h"
+#include "pbs.h"
+#include "signal_func.h"
+#include "window_func.h"
+#include "gui.h"
+#include "pathfinder/yapf/yapf_cache.h"
+
+#include "table/strings.h"
+
+/**
+ * Helper function for transferring sharing fees
+ * @param v The vehicle involved
+ * @param infra_owner The owner of the infrastructure
+ * @param cost Amount to transfer as money fraction (shifted 8 bits to the left)
+ */
+static void PaySharingFee(Vehicle *v, Owner infra_owner, Money cost)
+{
+	Company *c = Company::Get(v->owner);
+	if (!_settings_game.sharing.payment_in_debt) {
+		/* Do not allow fee payment to drop (money - loan) below 0. */
+		cost = min(cost, (c->money - c->current_loan) << 8);
+		if (cost <= 0) return;
+	}
+	v->profit_this_year -= cost;
+	SubtractMoneyFromCompanyFract(v->owner, CommandCost(v->GetExpenseType(false), cost));
+	SubtractMoneyFromCompanyFract(infra_owner, CommandCost(v->GetExpenseType(true), -cost));
+}
+
+/**
+ * Pay the fee for spending a single tick inside a station.
+ * @param v The vehicle that is using the station.
+ * @param st The station that it uses.
+ */
+void PayStationSharingFee(Vehicle *v, const Station *st)
+{
+	if (v->owner == st->owner || st->owner == OWNER_NONE || v->type == VEH_TRAIN) return;
+	Money cost;
+	switch (v->type) {
+		case VEH_ROAD:     cost = _settings_game.sharing.fee_road;  break;
+		case VEH_SHIP:     cost = _settings_game.sharing.fee_water; break;
+		case VEH_AIRCRAFT: cost = _settings_game.sharing.fee_air;   break;
+		default: NOT_REACHED();
+	}
+	PaySharingFee(v, st->owner, (cost << 8) / DAY_TICKS);
+}
+
+/**
+ * Pay the daily fee for trains on foreign tracks.
+ * @param v The vehicle to pay the fee for.
+ */
+void PayDailyTrackSharingFee(Train *v)
+{
+	Owner owner = GetTileOwner(v->tile);
+	if (owner == v->owner) return;
+	Money cost = _settings_game.sharing.fee_rail << 8;
+	/* Cost is calculated per 1000 tonnes */
+	cost = cost * v->tcache.cached_weight / 1000;
+	/* Only pay the required fraction */
+	cost = cost * v->running_ticks / DAY_TICKS;
+	if (cost != 0) PaySharingFee(v, owner, cost);
+}
+
+/**
+ * Check whether a vehicle is in an allowed position.
+ * @param v     The vehicle to check.
+ * @param owner Owner whose infrastructure is not allowed, because the company will be removed. Ignored if INVALID_OWNER.
+ * @return      True if the vehicle is compeletely in an allowed position.
+ */
+static bool VehiclePositionIsAllowed(const Vehicle *v, Owner owner = INVALID_OWNER)
+{
+	switch (v->type) {
+		case VEH_TRAIN:
+			for (const Vehicle *u = v; u != NULL; u = u->Next()) {
+				if (!IsInfraUsageAllowed(GetTileOwner(u->tile), v->owner, VEH_TRAIN) || GetTileOwner(u->tile) == owner) return false;
+			}
+			return true;
+		case VEH_ROAD:
+			for (const Vehicle *u = v; u != NULL; u = u->Next()) {
+				if (IsRoadDepotTile(u->tile) || IsStandardRoadStopTile(u->tile)) {
+					if (!IsInfraUsageAllowed(GetTileOwner(u->tile), v->owner, VEH_ROAD) || GetTileOwner(u->tile) == owner) return false;
+				}
+			}
+			return true;
+		case VEH_SHIP:
+			if (IsShipDepotTile(v->tile) && v->IsStoppedInDepot()) {
+				return IsInfraUsageAllowed(GetTileOwner(v->tile), v->owner, VEH_SHIP) && GetTileOwner(v->tile) != owner;
+			}
+			return true;
+		case VEH_AIRCRAFT: {
+			const Aircraft *a = Aircraft::From(v);
+			if (a->state != FLYING && Station::IsValidID(a->targetairport)) {
+				Owner station_owner = Station::Get(a->targetairport)->owner;
+				return IsInfraUsageAllowed(station_owner, a->owner, VEH_AIRCRAFT) && station_owner != owner;
+			}
+			return true;
+		}
+		default: return true;
+	}
+}
+
+/**
+ * Check whether an order has a destination that is allowed.
+ * I.e. it refers to a station/depot/waypoint the vehicle is allowed to visit.
+ * @param order The order to check
+ * @param v     The vehicle this order belongs to.
+ * @param owner Owner whose infrastructure is not allowed, because the company will be removed. Ignored if INVALID_OWNER.
+ * @return      True if the order has an allowed destination.
+ */
+static bool OrderDestinationIsAllowed(const Order *order, const Vehicle *v, Owner owner = INVALID_OWNER)
+{
+	Owner dest_owner;
+	switch (order->GetType()) {
+		case OT_GOTO_STATION:
+		case OT_GOTO_WAYPOINT: dest_owner = BaseStation::Get(order->GetDestination())->owner; break;
+		case OT_GOTO_DEPOT:    dest_owner = (v->type == VEH_AIRCRAFT) ? Station::Get(order->GetDestination())->owner : GetTileOwner(Depot::Get(order->GetDestination())->xy); break;
+		case OT_LOADING:       dest_owner = Station::Get(v->last_station_visited)->owner; break;
+		default: return true;
+	}
+	return dest_owner != owner && IsInfraUsageAllowed(dest_owner, v->owner, v->type);
+}
+
+/**
+ * Sell a vehicle, no matter where it may be.
+ * @param v The vehicle to sell
+ * @param give_money Do we actually need to give money to the vehicle owner?
+ */
+static void RemoveAndSellVehicle(Vehicle *v, bool give_money)
+{
+	assert(v->Previous() == NULL);
+
+	if (give_money) {
+		/* compute total value and give that to the owner */
+		Money value = 0;
+		for (Vehicle *u = v->First(); u != NULL; u = u->Next()) {
+			value += v->value;
+		}
+		CompanyID old = _current_company;
+		_current_company = v->owner;
+		SubtractMoneyFromCompany(CommandCost(EXPENSES_NEW_VEHICLES, -value));
+		_current_company = old;
+	}
+
+	/* take special measures for trains, but not when sharing is disabled or when the train is a free wagon chain */
+	if (_settings_game.sharing.sharing_rail && v->type == VEH_TRAIN && Train::From(v)->IsFrontEngine()) {
+ 		DeleteVisibleTrain(Train::From(v));
+	} else {
+		delete v;
+	}
+}
+
+/**
+ * Check all path reservations, and reserve a new path if the current path is invalid.
+ */
+static void FixAllReservations()
+{
+	/* if this function is called, we can safely assume that sharing of rails is being switched off */
+	assert(!_settings_game.sharing.sharing_rail);
+	Train *v;
+	FOR_ALL_TRAINS(v) {
+		if (!v->IsPrimaryVehicle() || (v->vehstatus & VS_CRASHED) != 0) continue;
+		/* It might happen that the train reserved additional tracks,
+		 * but FollowTrainReservation can't detect those because they are no longer reachable.
+		 * detect this by first finding the end of the reservation,
+		 * then switch sharing on and try again. If these two ends differ,
+		 * unreserve the path, switch sharing off and try to reserve a new path */
+		PBSTileInfo end_tile_info = FollowTrainReservation(v);
+
+		/* first do a quick test to determine whether the next tile has any reservation at all */
+		TileIndex next_tile = end_tile_info.tile + TileOffsByDiagDir(TrackdirToExitdir(end_tile_info.trackdir));
+		/* If the next tile doesn't have a reservation at all, the reservation surely ends here. Thus all is well */
+		if (GetReservedTrackbits(next_tile) == TRACK_BIT_NONE) continue;
+
+		/* change sharing setting temporarily */
+		_settings_game.sharing.sharing_rail = true;
+		PBSTileInfo end_tile_info2 = FollowTrainReservation(v);
+		/* if these two reservation ends differ, unreserve the path and try to reserve a new path */
+		if (end_tile_info.tile != end_tile_info2.tile || end_tile_info.trackdir != end_tile_info2.trackdir) {
+			FreeTrainTrackReservation(v);
+			_settings_game.sharing.sharing_rail = false;
+			TryPathReserve(v, true);
+		} else {
+			_settings_game.sharing.sharing_rail = false;
+		}
+	}
+}
+
+/**
+ * Check if a sharing change is possible.
+ * If vehicles are still on others' infrastructure or using others' stations,
+ * The change is not possible and false is returned.
+ * @param type The type of vehicle whose setting will be changed.
+ * @return True if the change can take place, false otherwise.
+ */
+bool CheckSharingChangePossible(VehicleType type)
+{
+	if (type != VEH_AIRCRAFT) YapfNotifyTrackLayoutChange(INVALID_TILE, INVALID_TRACK);
+	/* Only do something when sharing is being disabled */
+	if (GetSharingSetting(type)) return true;
+
+	StringID error_message = STR_NULL;
+	Vehicle *v;
+	FOR_ALL_VEHICLES(v) {
+		if (type != v->type) continue;
+		if (v->Previous() != NULL) continue;
+
+		/* Check vehicle positiion */
+		if (!VehiclePositionIsAllowed(v)) {
+			error_message = STR_CONFIG_SETTING_SHARING_USED_BY_VEHICLES;
+			/* Break immediately, this error message takes precedence over the others. */
+			break;
+		}
+
+		/* Check current order */
+		if (!OrderDestinationIsAllowed(&v->current_order, v)) {
+			error_message = STR_CONFIG_SETTING_SHARING_ORDERS_TO_OTHERS;
+		}
+
+		/* Check order list */
+		if (v->FirstShared() != v) continue;
+		Order *o;
+		FOR_VEHICLE_ORDERS(v, o) {
+			if (!OrderDestinationIsAllowed(o, v)) {
+				error_message = STR_CONFIG_SETTING_SHARING_ORDERS_TO_OTHERS;
+			}
+		}
+	}
+
+	if (error_message != STR_NULL) {
+		ShowErrorMessage(error_message, INVALID_STRING_ID, 0, 0);
+		return false;
+	}
+
+	if (type == VEH_TRAIN) FixAllReservations();
+
+	return true;
+}
+
+/**
+ * Handle the removal (through reset_company or bankruptcy) of a company.
+ * i.e. remove all vehicles owned by that company or on its infrastructure,
+ * and delete all now-invalid orders.
+ * @param Owner the company to be removed.
+ */
+void HandleSharingCompanyDeletion(Owner owner)
+{
+	YapfNotifyTrackLayoutChange(INVALID_TILE, INVALID_TRACK);
+
+	Vehicle *v;
+	FOR_ALL_VEHICLES(v) {
+		if (!IsCompanyBuildableVehicleType(v) || v->Previous() != NULL) continue;
+		/* vehicle position */
+		if (v->owner == owner || !VehiclePositionIsAllowed(v, owner)) {
+			RemoveAndSellVehicle(v, v->owner != owner);
+			continue;
+		}
+		/* current order */
+		if (!OrderDestinationIsAllowed(&v->current_order, v, owner)) {
+			if (v->current_order.IsType(OT_LOADING)) {
+				v->LeaveStation();
+			} else {
+				v->current_order.MakeDummy();
+			}
+			SetWindowDirty(WC_VEHICLE_VIEW, v->index);
+		}
+
+		/* order list */
+		if (v->FirstShared() != v) continue;
+
+		Order *o;
+		int id = -1;
+		FOR_VEHICLE_ORDERS(v, o) {
+			id++;
+			if (OrderDestinationIsAllowed(o, v, owner)) continue;
+
+			o->MakeDummy();
+			for (const Vehicle *w = v; w != NULL; w = w->NextShared()) {
+				/* In GUI, simulate by removing the order and adding it back */
+				InvalidateVehicleOrder(w, id | (INVALID_VEH_ORDER_ID << 8));
+				InvalidateVehicleOrder(w, (id << 8) | INVALID_VEH_ORDER_ID);
+			}
+		}
+	}
+}
+
+/**
+ * Update all block signals on the map.
+ * To be called after the setting for sharing of rails changes.
+ * @param owner Owner whose signals to update. If INVALID_OWNER, update everything.
+ */
+void UpdateAllBlockSignals(Owner owner)
+{
+	Owner last_owner = INVALID_OWNER;
+	TileIndex tile = 0;
+	do {
+		if (IsTileType(tile, MP_RAILWAY) && HasSignals(tile)) {
+			Owner track_owner = GetTileOwner(tile);
+			if (owner != INVALID_OWNER && track_owner != owner) continue;
+
+			if (!IsOneSignalBlock(track_owner, last_owner)) {
+				/* Cannot update signals of two different companies in one run,
+				 * if these signal blocks are not joined */
+				UpdateSignalsInBuffer();
+				last_owner = track_owner;
+			}
+			TrackBits bits = GetTrackBits(tile);
+			do {
+				Track track = RemoveFirstTrack(&bits);
+				if (HasSignalOnTrack(tile, track)) {
+					AddTrackToSignalBuffer(tile, track, track_owner);
+				}
+			} while (bits != TRACK_BIT_NONE);
+		} else if (IsLevelCrossingTile(tile) && (owner == INVALID_OWNER || GetTileOwner(tile) == owner)) {
+			UpdateLevelCrossing(tile);
+		}
+	} while (++tile != MapSize());
+
+	UpdateSignalsInBuffer();
+}
diff --git a/src/infrastructure_func.h b/src/infrastructure_func.h
new file mode 100644
index 0000000..765294d
--- /dev/null
+++ b/src/infrastructure_func.h
@@ -0,0 +1,130 @@
+/* $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 infrastructure_func.h Functions for infrastructure sharing */
+
+#ifndef INFRASTRUCTURE_FUNC_H
+#define INFRASTRUCTURE_FUNC_H
+
+#include "vehicle_base.h"
+#include "functions.h"
+#include "company_func.h"
+
+void PayStationSharingFee(Vehicle *v, const Station *st);
+void PayDailyTrackSharingFee(Train *v);
+bool CheckSharingChangePossible(VehicleType type);
+void HandleSharingCompanyDeletion(Owner owner);
+void UpdateAllBlockSignals(Owner owner = INVALID_OWNER);
+
+/**
+ * Get the value of the sharing setting for a vehicle type.
+ * @param type The type of vehicle.
+ * @return     True if sharing is enabled for the given vehicle type.
+ */
+static FORCEINLINE bool GetSharingSetting(VehicleType type)
+{
+	switch (type) {
+		case VEH_TRAIN:    return _settings_game.sharing.sharing_rail;
+		case VEH_ROAD:     return _settings_game.sharing.sharing_road;
+		case VEH_SHIP:     return _settings_game.sharing.sharing_water;
+		case VEH_AIRCRAFT: return _settings_game.sharing.sharing_air;
+		default: NOT_REACHED();
+	}
+}
+
+/**
+ * Check whether a vehicle of a given owner and type can use the infrastrucutre of a given company.
+ * @param infra_owner The owner of the infrastructure.
+ * @param veh_owner   Owner of the vehicle in question.
+ * @param type        Type of vehicle we are talking about.
+ * @return            True if infrastructure usage is allowed, false otherwise.
+ */
+static FORCEINLINE bool IsInfraUsageAllowed(Owner infra_owner, Owner veh_owner, VehicleType type)
+{
+	return infra_owner == veh_owner || infra_owner == OWNER_NONE || GetSharingSetting(type);
+}
+
+/**
+ * Check whether a vehicle of a given owner and type can use the infrastrucutre on a given tile.
+ * @param infra_owner The tile that may or may not be used.
+ * @param veh_owner   Owner of the vehicle in question.
+ * @param type        Type of vehicle we are talking about.
+ * @return            True if infrastructure usage is allowed, false otherwise.
+ */
+static FORCEINLINE bool IsInfraTileUsageAllowed(TileIndex tile, Owner veh_owner, VehicleType type)
+{
+	return IsInfraUsageAllowed(GetTileOwner(tile), veh_owner, type);
+}
+
+/**
+ * Is a vehicle owned by _current_company allowed to use the infrastructure of infra_owner?
+ * @see IsInfraUsageAllowed
+ * @note This function is to be called from DoCommands.
+ * @param infra_owner Owner of the infrastructure.
+ * @param type        Type of vehicle.
+ * @return            True if infrastructure usage is allowed, false otherwise.
+ */
+static FORCEINLINE bool CheckInfraUsageAllowed(Owner infra_owner, VehicleType type)
+{
+	return infra_owner == OWNER_NONE || GetSharingSetting(type) || CheckOwnership(infra_owner);
+}
+
+/**
+ * Is a a vehicle owned by _current_company allowed to use any of the facilities of the station owned by infra_owner?
+ */
+static FORCEINLINE bool CheckStationUsageAllowed(Owner infra_owner, StationFacilityByte facilities)
+{
+    return (
+            !Company::IsValidID(infra_owner)
+        ||  (facilities & FACIL_TRAIN) && CheckInfraUsageAllowed(infra_owner, VEH_TRAIN)
+        ||  (facilities & (FACIL_TRUCK_STOP | FACIL_BUS_STOP)) && CheckInfraUsageAllowed(infra_owner, VEH_ROAD)
+        ||  (facilities & FACIL_AIRPORT) && CheckInfraUsageAllowed(infra_owner, VEH_AIRCRAFT)
+        ||  (facilities & FACIL_DOCK) && CheckInfraUsageAllowed(infra_owner, VEH_SHIP)
+        /*  (facilities & FACIL_WAYPOINT) && ??? */
+    );
+}
+
+/**
+ * Check whether a given company can control this vehicle.
+ * Controlling a vehicle means permission to start, stop or reverse it or to make it ignore signals.
+ * @param v The vehicle which may or may not be controlled.
+ * @param o The company which may or may not control this vehicle.
+ * @return  True if the given company is allowed to control this vehicle.
+ */
+static FORCEINLINE bool IsVehicleControlAllowed(const Vehicle *v, Owner o)
+{
+	return v->owner == o || (v->type == VEH_TRAIN && IsTileOwner(v->tile, o));
+}
+
+/**
+ * Check whether _current_company can control this vehicle.
+ * @note This function is to be called from DoCommands.
+ * @see IsVehicleControlAllowed
+ * @param v The vehicle which may or may not be controlled.
+ * @return  True if _current_company is allowed to control this vehicle.
+ */
+static FORCEINLINE bool CheckVehicleControlAllowed(const Vehicle *v)
+{
+	return (v->type == VEH_TRAIN && IsTileOwner(v->tile, _current_company)) || CheckOwnership(v->owner);
+}
+
+/**
+ * Do signal states propagate from the tracks of one owner to the other?
+ * @note This function should be consistent, so if it returns true for (a, b) and (b, c),
+ * it should also return true for (a, c).
+ * @param o1 First track owner.
+ * @param o2 Second track owner.
+ * @return   True if tracks of the two owners are part of the same signal block.
+ */
+static FORCEINLINE bool IsOneSignalBlock(Owner o1, Owner o2)
+{
+	return o1 == o2 || _settings_game.sharing.sharing_rail;
+}
+
+#endif /* INFRASTRUCTURE_FUNC_H */
diff --git a/src/landscape.cpp b/src/landscape.cpp
index 3cc7a6f..0b11955 100644
--- a/src/landscape.cpp
+++ b/src/landscape.cpp
@@ -938,6 +938,7 @@ void OnTick_Station();
 void OnTick_Industry();
 
 void OnTick_Companies();
+void OnTick_LinkGraph();
 
 void CallLandscapeTick()
 {
@@ -947,4 +948,5 @@ void CallLandscapeTick()
 	OnTick_Industry();
 
 	OnTick_Companies();
+	OnTick_LinkGraph();
 }
diff --git a/src/lang/english.txt b/src/lang/english.txt
index d2d9f65..26614a0 100644
--- a/src/lang/english.txt
+++ b/src/lang/english.txt
@@ -23,6 +23,7 @@ STR_NULL                                                        :
 STR_EMPTY                                                       :
 STR_UNDEFINED                                                   :(undefined string)
 STR_JUST_NOTHING                                                :Nothing
+STR_TINY_NOTHING                                                :{TINYFONT}Nothing
 
 # Cargo related strings
 # Plural cargo name
@@ -220,6 +221,7 @@ STR_LIST_FILTER_OSKTITLE                                        :{BLACK}Enter fi
 STR_LIST_FILTER_TOOLTIP                                         :{BLACK}Enter a keyword to filter the list for
 STR_LIST_FILTER_TITLE                                           :{BLACK}Filter string:
 
+STR_TOOLTIP_GROUP_ORDER                                         :{BLACK}Select grouping order
 STR_TOOLTIP_SORT_ORDER                                          :{BLACK}Select sorting order (descending/ascending)
 STR_TOOLTIP_SORT_CRITERIAP                                      :{BLACK}Select sorting criteria
 STR_TOOLTIP_FILTER_CRITERIA                                     :{BLACK}Select filtering criteria
@@ -279,6 +281,8 @@ STR_SORT_BY_FACILITY                                            :Station type
 STR_SORT_BY_WAITING                                             :Waiting cargo value
 STR_SORT_BY_RATING_MAX                                          :Highest cargo rating
 STR_SORT_BY_RATING_MIN                                          :Lowest cargo rating
+STR_SORT_BY_AMOUNT                                              :{BLACK}Amount
+STR_SORT_BY_STATION                                             :{BLACK}Station
 STR_SORT_BY_ENGINE_ID                                           :EngineID (classic sort)
 STR_SORT_BY_COST                                                :Cost
 STR_SORT_BY_POWER                                               :Power
@@ -643,12 +647,14 @@ STR_SMALLMAP_CAPTION                                            :{WHITE}Map - {S
 STR_SMALLMAP_TYPE_CONTOURS                                      :Contours
 STR_SMALLMAP_TYPE_VEHICLES                                      :Vehicles
 STR_SMALLMAP_TYPE_INDUSTRIES                                    :Industries
+STR_SMALLMAP_TYPE_ROUTEMAP                                      :Link Statistics
 STR_SMALLMAP_TYPE_ROUTES                                        :Routes
 STR_SMALLMAP_TYPE_VEGETATION                                    :Vegetation
 STR_SMALLMAP_TYPE_OWNERS                                        :Owners
 STR_SMALLMAP_TOOLTIP_SHOW_LAND_CONTOURS_ON_MAP                  :{BLACK}Show land contours on map
 STR_SMALLMAP_TOOLTIP_SHOW_VEHICLES_ON_MAP                       :{BLACK}Show vehicles on map
 STR_SMALLMAP_TOOLTIP_SHOW_INDUSTRIES_ON_MAP                     :{BLACK}Show industries on map
+STR_SMALLMAP_TOOLTIP_SHOW_LINK_STATS_ON_MAP                     :{BLACK}Show link statistics on map
 STR_SMALLMAP_TOOLTIP_SHOW_TRANSPORT_ROUTES_ON                   :{BLACK}Show transport routes on map
 STR_SMALLMAP_TOOLTIP_SHOW_VEGETATION_ON_MAP                     :{BLACK}Show vegetation on map
 STR_SMALLMAP_TOOLTIP_SHOW_LAND_OWNERS_ON_MAP                    :{BLACK}Show land owners on map
@@ -686,10 +692,26 @@ STR_SMALLMAP_LEGENDA_TOWNS                                      :{TINYFONT}{BLAC
 STR_SMALLMAP_LEGENDA_INDUSTRIES                                 :{TINYFONT}{BLACK}Industries
 STR_SMALLMAP_LEGENDA_DESERT                                     :{TINYFONT}{BLACK}Desert
 STR_SMALLMAP_LEGENDA_SNOW                                       :{TINYFONT}{BLACK}Snow
+STR_SMALLMAP_LEGENDA_CAPACITY                                   :{TINYFONT}{BLACK}Capacity
+STR_SMALLMAP_LEGENDA_USAGE                                      :{TINYFONT}{BLACK}Usage
+STR_SMALLMAP_LEGENDA_PLANNED                                    :{TINYFONT}{BLACK}Planned Flow
+STR_SMALLMAP_LEGENDA_SENT                                       :{TINYFONT}{BLACK}Sent Cargo
+STR_SMALLMAP_LEGENDA_SHOW_TEXT                                  :{TINYFONT}{BLACK}As Text
+STR_SMALLMAP_LEGENDA_SHOW_GRAPH                                 :{TINYFONT}{BLACK}As Graphs
+
+STR_SMALLMAP_SUPPLY_CAPTION                                     :{BLACK}Monthly supply at {STATION}
+STR_SMALLMAP_SUPPLY                                             :{TINYFONT}{CARGO}
+STR_SMALLMAP_LINK_CAPTION                                       :{BLACK}Monthly traffic from {STATION} to {STATION}
+STR_SMALLMAP_LINK                                               :{TINYFONT}{STRING} -
+STR_SMALLMAP_LINK_CAPACITY                                      :{TINYFONT}  cap: {NUM}
+STR_SMALLMAP_LINK_USAGE                                         :{TINYFONT}  usg: {NUM}
+STR_SMALLMAP_LINK_PLANNED                                       :{TINYFONT}  pln: {NUM}
+STR_SMALLMAP_LINK_SENT                                          :{TINYFONT}  snt: {NUM}
 
 STR_SMALLMAP_TOOLTIP_TOGGLE_TOWN_NAMES_ON_OFF                   :{BLACK}Toggle town names on/off on map
 STR_SMALLMAP_CENTER                                             :{BLACK}Center the smallmap on the current position
 STR_SMALLMAP_INDUSTRY                                           :{TINYFONT}{STRING} ({NUM})
+STR_SMALLMAP_LINKSTATS_LEGEND                                   :{TINYFONT}{STRING}
 STR_SMALLMAP_TOWN                                               :{TINYFONT}{WHITE}{TOWN}
 STR_SMALLMAP_DISABLE_ALL                                        :{BLACK}Disable all
 STR_SMALLMAP_ENABLE_ALL                                         :{BLACK}Enable all
@@ -922,6 +944,8 @@ STR_GAME_OPTIONS_AUTOSAVE_FRAME                                 :{BLACK}Autosave
 STR_GAME_OPTIONS_AUTOSAVE_DROPDOWN_TOOLTIP                      :{BLACK}Select interval between automatic game saves
 
 STR_GAME_OPTIONS_AUTOSAVE_DROPDOWN_OFF                          :Off
+STR_GAME_OPTIONS_AUTOSAVE_DROPDOWN_EVERY_1_DAY                  :Every day
+STR_GAME_OPTIONS_AUTOSAVE_DROPDOWN_EVERY_1_WEEK                 :Every week
 STR_GAME_OPTIONS_AUTOSAVE_DROPDOWN_EVERY_1_MONTH                :Every month
 STR_GAME_OPTIONS_AUTOSAVE_DROPDOWN_EVERY_3_MONTHS               :Every 3 months
 STR_GAME_OPTIONS_AUTOSAVE_DROPDOWN_EVERY_6_MONTHS               :Every 6 months
@@ -1225,6 +1249,18 @@ STR_CONFIG_SETTING_AI_BUILDS_SHIPS                              :{LTBLUE}Disable
 STR_CONFIG_SETTING_AI_IN_MULTIPLAYER                            :{LTBLUE}Allow AIs in multiplayer: {ORANGE}{STRING1}
 STR_CONFIG_SETTING_AI_MAX_OPCODES                               :{LTBLUE}#opcodes before AI is suspended: {ORANGE}{STRING1}
 
+STR_CONFIG_SETTING_SHARING_RAIL                                 :{LTBLUE}Enable sharing of railways: {ORANGE}{STRING1}
+STR_CONFIG_SETTING_SHARING_ROAD                                 :{LTBLUE}Enable sharing of road stops and depots: {ORANGE}{STRING1}
+STR_CONFIG_SETTING_SHARING_WATER                                :{LTBLUE}Enable sharing of docks and ship depots: {ORANGE}{STRING1}
+STR_CONFIG_SETTING_SHARING_AIR                                  :{LTBLUE}Enable sharing of airports: {ORANGE}{STRING1}
+STR_CONFIG_SETTING_SHARING_FEE_RAIL                             :{LTBLUE}Daily track toll for trains: {ORANGE}{STRING1} per 1000 tonnes
+STR_CONFIG_SETTING_SHARING_FEE_ROAD                             :{LTBLUE}Stopping fee for road vehicles: {ORANGE}{STRING1} per day
+STR_CONFIG_SETTING_SHARING_FEE_WATER                            :{LTBLUE}Docking fee for ships: {ORANGE}{STRING1} per day
+STR_CONFIG_SETTING_SHARING_FEE_AIR                              :{LTBLUE}Terminal fee for aircraft: {ORANGE}{STRING1} per day
+STR_CONFIG_SETTING_SHARING_PAYMENT_IN_DEBT                      :{LTBLUE}Allow companies in debt to pay sharing fees: {ORANGE}{STRING1}
+STR_CONFIG_SETTING_SHARING_USED_BY_VEHICLES                     :{WHITE}Can't change this setting, vehicles are using shared infrastructure.
+STR_CONFIG_SETTING_SHARING_ORDERS_TO_OTHERS                     :{WHITE}Can't change this setting, vehicles have orders to destinations of others.
+
 STR_CONFIG_SETTING_SERVINT_ISPERCENT                            :{LTBLUE}Service intervals are in percents: {ORANGE}{STRING1}
 STR_CONFIG_SETTING_SERVINT_TRAINS                               :{LTBLUE}Default service interval for trains: {ORANGE}{STRING1} days/%
 STR_CONFIG_SETTING_SERVINT_TRAINS_DISABLED                      :{LTBLUE}Default service interval for trains: {ORANGE}disabled
@@ -1267,6 +1303,8 @@ STR_CONFIG_SETTING_TOWN_FOUNDING                                :{LTBLUE}Foundin
 STR_CONFIG_SETTING_TOWN_FOUNDING_FORBIDDEN                      :forbidden
 STR_CONFIG_SETTING_TOWN_FOUNDING_ALLOWED                        :allowed
 STR_CONFIG_SETTING_TOWN_FOUNDING_ALLOWED_CUSTOM_LAYOUT          :allowed, custom town layout
+STR_CONFIG_SETTING_DAYLENGTH_FACTOR                             :{LTBLUE}Day length factor: {ORANGE}{STRING}
+STR_CONFIG_SETTING_TOWN_CARGO_FACTOR                            :{LTBLUE}Town cargo generation factor (less < 0 < more): {ORANGE}{STRING1}
 
 STR_CONFIG_SETTING_EXTRA_TREE_PLACEMENT                         :{LTBLUE}In game placement of trees: {ORANGE}{STRING1}
 STR_CONFIG_SETTING_EXTRA_TREE_PLACEMENT_NONE                    :none {RED}(breaks lumber mill)
@@ -1291,18 +1329,48 @@ STR_CONFIG_SETTING_LARGER_TOWNS                                 :{LTBLUE}Proport
 STR_CONFIG_SETTING_LARGER_TOWNS_DISABLED                        :{LTBLUE}Proportion of towns that will become cities: {ORANGE}None
 STR_CONFIG_SETTING_CITY_SIZE_MULTIPLIER                         :{LTBLUE}Initial city size multiplier: {ORANGE}{STRING1}
 STR_CONFIG_SETTING_MODIFIED_ROAD_REBUILD                        :{LTBLUE}Remove absurd road-elements during the road construction: {ORANGE}{STRING1}
+STR_CONFIG_SETTING_AVERAGE_UNIT                                 :{LTBLUE}Moving Average Unit: {ORANGE}{STRING1} days
+STR_CONFIG_SETTING_AVERAGE_LENGTH                               :{LTBLUE}Moving Average Length: {ORANGE}{STRING1} units
+
+STR_CONFIG_SETTING_LINKGRAPH_INTERVAL                           :{LTBLUE}Link graph recalculation interval: {ORANGE}{STRING1} days
+STR_CONFIG_SETTING_DEMAND_PAX                                   :{LTBLUE}Demand function for passengers: {ORANGE}{STRING1}
+STR_CONFIG_SETTING_DEMAND_PAX_SYMMETRIC                         :symmetric
+STR_CONFIG_SETTING_DEMAND_PAX_ANTISYMMETRIC                     :antisymmetric
+STR_CONFIG_SETTING_DEMAND_PAX_UNHANDLED                         :unhandled
+STR_CONFIG_SETTING_DEMAND_MAIL                                  :{LTBLUE}Demand function for mail: {ORANGE}{STRING1}
+STR_CONFIG_SETTING_DEMAND_MAIL_SYMMETRIC                        :symmetric
+STR_CONFIG_SETTING_DEMAND_MAIL_ANTISYMMETRIC                    :antisymmetric
+STR_CONFIG_SETTING_DEMAND_MAIL_UNHANDLED                        :unhandled
+STR_CONFIG_SETTING_DEMAND_EXPRESS                               :{LTBLUE}Demand function for the EXPRESS cargo class: {ORANGE}{STRING1}
+STR_CONFIG_SETTING_DEMAND_EXPRESS_SYMMETRIC                     :symmetric
+STR_CONFIG_SETTING_DEMAND_EXPRESS_ANTISYMMETRIC                 :antisymmetric
+STR_CONFIG_SETTING_DEMAND_EXPRESS_UNHANDLED                     :unhandled
+STR_CONFIG_SETTING_DEMAND_ARMOURED                              :{LTBLUE}Demand function for the ARMOURED cargo class: {ORANGE}{STRING1}
+STR_CONFIG_SETTING_DEMAND_ARMOURED_SYMMETRIC                    :symmetric
+STR_CONFIG_SETTING_DEMAND_ARMOURED_ANTISYMMETRIC                :antisymmetric
+STR_CONFIG_SETTING_DEMAND_ARMOURED_UNHANDLED                    :unhandled
+STR_CONFIG_SETTING_DEMAND_DEFAULT                               :{LTBLUE}Demand function for other cargo classes: {ORANGE}{STRING1}
+STR_CONFIG_SETTING_DEMAND_DEFAULT_SYMMETRIC                     :symmetric
+STR_CONFIG_SETTING_DEMAND_DEFAULT_ANTISYMMETRIC                 :antisymmetric
+STR_CONFIG_SETTING_DEMAND_DEFAULT_UNHANDLED                     :unhandled
+STR_CONFIG_SETTING_LINKGRAPH_ACCURACY                           :{LTBLUE}Accuracy when calculating things on the link graph: {ORANGE}{STRING1}
+STR_CONFIG_SETTING_DEMAND_DISTANCE                              :{LTBLUE}Effect of distance on demands: {ORANGE}{STRING1}%
+STR_CONFIG_SETTING_DEMAND_SIZE                                  :{LTBLUE}Effect of remote station's popularity on symmetric demands: {ORANGE}{STRING1}%
+STR_CONFIG_SETTING_SHORT_PATH_SATURATION                        :{LTBLUE}Saturation of short paths before using capacious paths: {ORANGE}{STRING1}%
 
 STR_CONFIG_SETTING_GUI                                          :{ORANGE}Interface
 STR_CONFIG_SETTING_CONSTRUCTION                                 :{ORANGE}Construction
 STR_CONFIG_SETTING_VEHICLES                                     :{ORANGE}Vehicles
 STR_CONFIG_SETTING_STATIONS                                     :{ORANGE}Stations
 STR_CONFIG_SETTING_ECONOMY                                      :{ORANGE}Economy
+STR_CONFIG_SETTING_LINKGRAPH                                    :{ORANGE}Link graph
 STR_CONFIG_SETTING_AI                                           :{ORANGE}Competitors
 STR_CONFIG_SETTING_DISPLAY_OPTIONS                              :{ORANGE}Display options
 STR_CONFIG_SETTING_INTERACTION                                  :{ORANGE}Interaction
 STR_CONFIG_SETTING_CONSTRUCTION_SIGNALS                         :{ORANGE}Signals
 STR_CONFIG_SETTING_STATIONS_CARGOHANDLING                       :{ORANGE}Cargo handling
 STR_CONFIG_SETTING_AI_NPC                                       :{ORANGE}Computer players
+STR_CONFIG_SETTING_SHARING                                      :{ORANGE}Infrastructure sharing
 STR_CONFIG_SETTING_VEHICLES_AUTORENEW                           :{ORANGE}Autorenew
 STR_CONFIG_SETTING_VEHICLES_SERVICING                           :{ORANGE}Servicing
 STR_CONFIG_SETTING_VEHICLES_ROUTING                             :{ORANGE}Routing
@@ -2490,8 +2558,31 @@ STR_STATION_VIEW_ACCEPTS_CARGO                                  :{BLACK}Accepts:
 
 STR_STATION_VIEW_RATINGS_BUTTON                                 :{BLACK}Ratings
 STR_STATION_VIEW_RATINGS_TOOLTIP                                :{BLACK}Show station ratings
-STR_STATION_VIEW_CARGO_RATINGS_TITLE                            :{BLACK}Local rating of transport service:
+STR_STATION_VIEW_CARGO_RATINGS_TITLE                            :{BLACK}Monthly supply and local rating:
 STR_STATION_VIEW_CARGO_RATING                                   :{WHITE}{STRING}: {YELLOW}{STRING} ({COMMA}%)
+STR_STATION_VIEW_CARGO_SUPPLY_RATING                            :{WHITE}{STRING}: {YELLOW}{COMMA} / {STRING} ({COMMA}%)
+
+STR_STATION_VIEW_WAITING                                        :Waiting
+STR_STATION_VIEW_PLANNED                                        :Planned
+STR_STATION_VIEW_SENT                                           :Sent
+STR_STATION_VIEW_TOGGLE_CARGO_VIEW                              :{BLACK}Toggle between waiting, planned and sent cargo
+STR_STATION_VIEW_FROM                                           :{YELLOW}{SHORTCARGO} from {STATION}
+STR_STATION_VIEW_VIA                                            :{YELLOW}{SHORTCARGO} via {STATION}
+STR_STATION_VIEW_TO                                             :{YELLOW}{SHORTCARGO} to {STATION}
+STR_STATION_VIEW_FROM_ANY                                       :{RED}{SHORTCARGO} from unknown station
+STR_STATION_VIEW_TO_ANY                                         :{RED}{SHORTCARGO} to any station
+STR_STATION_VIEW_VIA_ANY                                        :{RED}{SHORTCARGO} via any station
+STR_STATION_VIEW_FROM_HERE                                      :{GREEN}{SHORTCARGO} from this station
+STR_STATION_VIEW_VIA_HERE                                       :{GREEN}{SHORTCARGO} stopping at this station
+STR_STATION_VIEW_TO_HERE                                        :{GREEN}{SHORTCARGO} to this station
+STR_STATION_VIEW_NONSTOP                                        :{YELLOW}{SHORTCARGO} non-stop
+
+STR_STATION_VIEW_GROUP_S_V_D                                    :Source-Via-Destination
+STR_STATION_VIEW_GROUP_S_D_V                                    :Source-Destination-Via
+STR_STATION_VIEW_GROUP_V_S_D                                    :Via-Source-Destination
+STR_STATION_VIEW_GROUP_V_D_S                                    :Via-Destination-Source
+STR_STATION_VIEW_GROUP_D_S_V                                    :Destination-Source-Via
+STR_STATION_VIEW_GROUP_D_V_S                                    :Destination-Via-Source
 
 ############ range for rating starts
 STR_CARGO_RATING_APPALLING                                      :Appalling
@@ -3280,6 +3371,7 @@ STR_GAME_SAVELOAD_ERROR_FILE_NOT_READABLE                       :File not readab
 STR_GAME_SAVELOAD_ERROR_FILE_NOT_WRITEABLE                      :File not writeable
 STR_GAME_SAVELOAD_ERROR_DATA_INTEGRITY_CHECK_FAILED             :Data integrity check failed
 STR_WARNING_LOADGAME_REMOVED_TRAMS                              :{WHITE}Game was saved in version without tram support. All trams have been removed.
+STR_WARNING_NONSTOP_CARGODIST                                   :{WHITE}You have set at least one cargo class to be automatically distributed in{}"Avanced Settings -> Link Graph"{}and disabled{}"New orders are 'non-stop' by default"{}in{}"Advanced Settings -> Vehicles"{}This is a BAD idea.
 
 # Map generation messages
 STR_ERROR_COULD_NOT_CREATE_TOWN                                 :{WHITE}Map generation aborted...{}... no suitable town locations
@@ -4092,3 +4184,8 @@ STR_BUS                                                         :{BLACK}{BUS}
 STR_LORRY                                                       :{BLACK}{LORRY}
 STR_PLANE                                                       :{BLACK}{PLANE}
 STR_SHIP                                                        :{BLACK}{SHIP}
+
+STR_NUM                                                         :{BLACK}{NUM}
+STR_NUM_RELATION_2                                              :{BLACK}{NUM}/{NUM}
+STR_NUM_RELATION_3                                              :{BLACK}{NUM}/{NUM}/{NUM}
+STR_NUM_RELATION_4                                              :{BLACK}{NUM}/{NUM}/{NUM}/{NUM}
diff --git a/src/linkgraph/demand_settings.h b/src/linkgraph/demand_settings.h
new file mode 100644
index 0000000..8131a6b
--- /dev/null
+++ b/src/linkgraph/demand_settings.h
@@ -0,0 +1,20 @@
+/** @file demand_settings.h Declaration of distribution types for demand calculation. */
+
+#ifndef DEMAND_SETTINGS_H_
+#define DEMAND_SETTINGS_H_
+
+enum DistributionType {
+	DT_BEGIN = 0,
+	DT_SYMMETRIC = 0,
+	DT_ANTISYMMETRIC,
+	DT_UNHANDLED,
+	DT_NUM = 3,
+	DT_END = 3
+};
+
+/** It needs to be 8bits, because we save and load it as such
+ * Define basic enum properties */
+template <> struct EnumPropsT<DistributionType> : MakeEnumPropsT<DistributionType, byte, DT_BEGIN, DT_END, DT_NUM> {};
+typedef TinyEnumT<DistributionType> DistributionTypeByte; // typedefing-enumification of DistributionType
+
+#endif /* DEMAND_SETTINGS_H_ */
diff --git a/src/linkgraph/demands.cpp b/src/linkgraph/demands.cpp
new file mode 100644
index 0000000..85f6fd4
--- /dev/null
+++ b/src/linkgraph/demands.cpp
@@ -0,0 +1,166 @@
+/** @file demands.h Definition of demand calculating link graph handler. */
+
+#include "demands.h"
+#include "../station_base.h"
+#include "../settings_type.h"
+#include "../newgrf_cargo.h"
+#include "../cargotype.h"
+#include "../core/math_func.hpp"
+#include <list>
+#include <iostream>
+
+typedef std::list<NodeID> NodeList;
+
+void DemandCalculator::PrintDemandMatrix(LinkGraphComponent * graph) {
+	for (NodeID from = 0; from < graph->GetSize(); ++from) {
+		std::cout << graph->GetNode(from).station << "\t";
+		for(NodeID to = 0; to < graph->GetSize(); ++to) {
+			if (from == to) {
+				std::cout << graph->GetNode(from).supply << "\t";
+			} else {
+				std::cout << graph->GetEdge(from, to).distance << ":" << graph->GetEdge(from, to).demand << "\t";
+			}
+		}
+		std::cout << "\n";
+	}
+}
+
+void DemandCalculator::CalcDemand(LinkGraphComponent * graph) {
+	NodeList supplies;
+	NodeList demands;
+	uint supply_sum = 0;
+	uint num_demands = 0;
+	uint num_supplies = 0;
+	for(NodeID node = 0; node < graph->GetSize(); node++) {
+		Node & n = graph->GetNode(node);
+		if (n.supply > 0) {
+			supplies.push_back(node);
+			supply_sum += n.supply;
+			num_supplies++;
+		}
+		if (n.demand > 0) {
+			demands.push_back(node);
+			num_demands++;
+		}
+	}
+
+	if (supply_sum == 0 || num_demands == 0) {
+		return;
+	}
+
+	uint demand_per_node = max(supply_sum / num_demands, (uint)1);
+	uint chance = 0;
+
+	while(!supplies.empty() && !demands.empty()) {
+		NodeID node1 = supplies.front();
+		supplies.pop_front();
+
+		Node & from = graph->GetNode(node1);
+
+		for(uint i = 0; i < num_demands; ++i) {
+			assert(!demands.empty());
+			NodeID node2 = demands.front();
+			demands.pop_front();
+			if (node1 == node2) {
+				if (demands.empty() && supplies.empty()) {
+					/* only one node with supply and demand left */
+					return;
+				} else {
+					demands.push_back(node2);
+					continue;
+				}
+			}
+			Node & to = graph->GetNode(node2);
+			Edge & forward = graph->GetEdge(node1, node2);
+			Edge & backward = graph->GetEdge(node2, node1);
+
+			int32 supply = from.supply;
+			if (this->mod_size > 0) {
+				supply = max(1, (int32)(supply * to.supply * this->mod_size / 100 / demand_per_node));
+			}
+			assert(supply > 0);
+
+			/* scale the distance by mod_dist around max_distance */
+			int32 distance = this->max_distance - (this->max_distance - (int32)forward.distance) * this->mod_dist / 100;
+
+			/* scale the accuracy by distance around accuracy / 2 */
+			int32 divisor = this->accuracy * (this->mod_dist - 50) / 100 + this->accuracy * distance / this->max_distance + 1;
+			assert(divisor > 0);
+
+			uint demand_forw = 0;
+			if (divisor < supply) {
+				demand_forw = supply / divisor;
+			} else if (++chance > this->accuracy * num_demands * num_supplies) {
+				/* after some trying distribute demand also to other nodes */
+				demand_forw = 1;
+			}
+
+			demand_forw = min(demand_forw, from.undelivered_supply);
+
+			if (this->mod_size > 0 && from.demand > 0) {
+				uint demand_back = demand_forw * this->mod_size / 100;
+				if (demand_back > to.undelivered_supply) {
+					demand_back = to.undelivered_supply;
+					demand_forw = demand_back * 100 / this->mod_size;
+				}
+				backward.demand += demand_back;
+				backward.unsatisfied_demand += demand_back;
+				to.undelivered_supply -= demand_back;
+			}
+
+			forward.demand += demand_forw;
+			forward.unsatisfied_demand += demand_forw;
+			from.undelivered_supply -= demand_forw;
+
+			if (this->mod_size == 0 || to.undelivered_supply > 0) {
+				demands.push_back(node2);
+			} else {
+				num_demands--;
+			}
+
+			if (from.undelivered_supply == 0) {
+				break;
+			}
+		}
+		if (from.undelivered_supply != 0) {
+			supplies.push_back(node1);
+		}
+	}
+}
+
+void DemandCalculator::Run(LinkGraphComponent * graph) {
+	CargoID cargo = graph->GetCargo();
+	const LinkGraphSettings & settings = graph->GetSettings();
+	DistributionType type = settings.demand_default;
+	if (IsCargoInClass(cargo, CC_PASSENGERS)) {
+		type = settings.demand_pax;
+	} else if (IsCargoInClass(cargo, CC_MAIL)) {
+		type = settings.demand_mail;
+	} else if (IsCargoInClass(cargo, CC_EXPRESS)) {
+		type = settings.demand_express;
+	} else if (IsCargoInClass(cargo, CC_ARMOURED)) {
+		type = settings.demand_armoured;
+	}
+
+	this->accuracy = settings.accuracy;
+	this->mod_size = settings.demand_size;
+	this->mod_dist = settings.demand_distance;
+	if (this->mod_dist > 100) {
+		/* increase effect of mod_dist > 100 */
+		int over100 = this->mod_dist - 100;
+		this->mod_dist = 100 + over100 * over100;
+	}
+
+	switch (type) {
+	case DT_SYMMETRIC:
+		CalcDemand(graph);
+		break;
+	case DT_ANTISYMMETRIC:
+		this->mod_size = 0;
+		CalcDemand(graph);
+		break;
+	default:
+		/* ignore */
+		break;
+	}
+}
diff --git a/src/linkgraph/demands.h b/src/linkgraph/demands.h
new file mode 100644
index 0000000..61b9436
--- /dev/null
+++ b/src/linkgraph/demands.h
@@ -0,0 +1,26 @@
+/** @file demands.h Declaration of demand calculating link graph handler. */
+
+#ifndef DEMANDS_H_
+#define DEMANDS_H_
+
+#include "linkgraph.h"
+#include "demand_settings.h"
+#include "../stdafx.h"
+#include "../cargo_type.h"
+#include "../map_func.h"
+
+class DemandCalculator : public ComponentHandler {
+public:
+	DemandCalculator() : max_distance(MapSizeX() + MapSizeY() + 1) {}
+	virtual void Run(LinkGraphComponent * graph);
+	static void PrintDemandMatrix(LinkGraphComponent * graph);
+	virtual ~DemandCalculator() {}
+private:
+	int32 max_distance;
+	int32 mod_size;
+	int32 mod_dist;
+	int32 accuracy;
+	void CalcDemand(LinkGraphComponent * graph);
+};
+
+#endif /* DEMANDS_H_ */
diff --git a/src/linkgraph/flowmapper.cpp b/src/linkgraph/flowmapper.cpp
new file mode 100644
index 0000000..b2ec3e6
--- /dev/null
+++ b/src/linkgraph/flowmapper.cpp
@@ -0,0 +1,38 @@
+/** @file flowmapper.cpp Definition of flowmapper */
+
+#include "flowmapper.h"
+
+void FlowMapper::Run(LinkGraphComponent * c) {
+	component = c;
+	for (NodeID node_id = 0; node_id < component->GetSize(); ++node_id) {
+		Node & prev_node = component->GetNode(node_id);
+		StationID prev = prev_node.station;
+		PathSet & paths = prev_node.paths;
+		for(PathSet::iterator i = paths.begin(); i != paths.end(); ++i) {
+			Path * path = *i;
+			uint flow = path->GetFlow();
+			if (flow == 0) continue;
+			Node & node = component->GetNode(path->GetNode());
+			StationID via = node.station;
+			assert(prev != via);
+			StationID origin = component->GetNode(path->GetOrigin()).station;
+			assert(via != origin);
+			/* mark all of the flow for local consumation at first */
+			node.flows[origin][via] += flow;
+			/* pass some of the flow marked for local consumation at prev on to this node */
+			prev_node.flows[origin][via] += flow;
+			/* find simple circular flows ... */
+			assert(node.flows[origin][prev] == 0);
+			if (prev != origin) {
+				prev_node.flows[origin][prev] -= flow;
+			}
+		}
+	}
+	for (NodeID node_id = 0; node_id < component->GetSize(); ++node_id) {
+		PathSet & paths = component->GetNode(node_id).paths;
+		for (PathSet::iterator i = paths.begin(); i != paths.end(); ++i) {
+			delete (*i);
+		}
+		paths.clear();
+	}
+}
diff --git a/src/linkgraph/flowmapper.h b/src/linkgraph/flowmapper.h
new file mode 100644
index 0000000..56c3fb1
--- /dev/null
+++ b/src/linkgraph/flowmapper.h
@@ -0,0 +1,16 @@
+/** @file flowmapper.h Declaration of flow mapper; merges paths into flows at nodes */
+
+#ifndef FLOWMAPPER_H_
+#define FLOWMAPPER_H_
+
+#include "linkgraph.h"
+
+class FlowMapper : public ComponentHandler {
+public:
+	FlowMapper() : component(NULL) {}
+	void Run(LinkGraphComponent * c);
+private:
+	LinkGraphComponent * component;
+};
+
+#endif /* FLOWMAPPER_H_ */
diff --git a/src/linkgraph/linkgraph.cpp b/src/linkgraph/linkgraph.cpp
new file mode 100644
index 0000000..b05e946
--- /dev/null
+++ b/src/linkgraph/linkgraph.cpp
@@ -0,0 +1,418 @@
+/** @file linkgraph.cpp Definition of link graph classes used for cargo distribution. */
+
+#include "linkgraph.h"
+#include "demands.h"
+#include "mcf.h"
+#include "flowmapper.h"
+#include "../date_func.h"
+#include "../variables.h"
+#include "../map_func.h"
+#include "../core/bitmath_func.hpp"
+#include "../debug.h"
+#include "../window_func.h"
+#include "../window_gui.h"
+#include <queue>
+
+LinkGraph _link_graphs[NUM_CARGO];
+
+typedef std::map<StationID, NodeID> ReverseNodeIndex;
+
+void LinkGraph::CreateComponent(Station * first) {
+	ReverseNodeIndex index;
+	NodeID node = 0;
+	std::queue<Station *> search_queue;
+	LinkGraphComponent * component = NULL;
+
+	search_queue.push(first);
+
+	first->goods[this->cargo].last_component = this->current_component_id;
+	component = new LinkGraphComponent(this->cargo, this->current_component_id);
+	GoodsEntry & good = first->goods[this->cargo];
+	node = component->AddNode(this->current_station_id, good.supply, HasBit(good.acceptance_pickup, GoodsEntry::ACCEPTANCE));
+	index[this->current_station_id++] = node;
+	// find all stations belonging to the current component
+	while(!search_queue.empty()) {
+		Station * source = search_queue.front();
+		StationID source_id = source->index;
+		search_queue.pop();
+		GoodsEntry & good = source->goods[cargo];
+		LinkStatMap & links = good.link_stats;
+		for(LinkStatMap::iterator i = links.begin(); i != links.end(); ++i) {
+			StationID target_id = i->first;
+			if (!Station::IsValidID(target_id)) {
+				continue;
+			}
+			assert(target_id != source_id);
+			LinkStat & link_stat = i->second;
+			ReverseNodeIndex::iterator index_it = index.find(target_id);
+			if (index_it == index.end()) {
+				Station * target = Station::Get(target_id);
+				GoodsEntry & good = target->goods[cargo];
+				good.last_component = this->current_component_id;
+				search_queue.push(target);
+				node = component->AddNode(target_id, good.supply, HasBit(good.acceptance_pickup, GoodsEntry::ACCEPTANCE));
+				index[target_id] = node;
+			} else {
+				node = index_it->second;
+			}
+			component->AddEdge(index[source_id], node, link_stat.capacity);
+		}
+	}
+	// here the list of nodes and edges for this component is complete.
+	component->CalculateDistances();
+	LinkGraphJob * job = new LinkGraphJob(component);
+	assert(job != NULL);
+	job->SpawnThread(this->cargo);
+	this->jobs.push_back(job);
+}
+
+void LinkGraph::NextComponent()
+{
+	while (!Station::IsValidID(this->current_station_id) && this->current_station_id > 0) {
+		--this->current_station_id;
+	}
+	StationID last_station_id = this->current_station_id;
+
+	do {
+		// find first station of next component
+		if (Station::IsValidID(this->current_station_id)) {
+			Station * station = Station::Get(this->current_station_id);
+			GoodsEntry & ge = station->goods[this->cargo];
+			if ((ge.last_component + this->current_component_id) % 2 != 0) {
+				// has not been seen in this run through the graph
+
+				LinkStatMap & links = station->goods[cargo].link_stats;
+				if (!links.empty()) {
+					this->current_component_id += 2;
+					CreateComponent(station);
+					return;
+				}
+			}
+		}
+
+		if (++this->current_station_id == Station::GetPoolSize()) {
+			this->current_station_id = 0;
+			if (this->current_component_id % 2 == 0) {
+				this->current_component_id = 1;
+			} else {
+				this->current_component_id = 0;
+			}
+		}
+	} while (this->current_station_id != last_station_id);
+}
+
+void OnTick_LinkGraph()
+{
+	bool spawn = (_tick_counter + LinkGraph::COMPONENTS_SPAWN_TICK) % DAY_TICKS == 0;
+	bool join =  (_tick_counter + LinkGraph::COMPONENTS_JOIN_TICK)  % DAY_TICKS == 0;
+	if (spawn || join) {
+		for(CargoID cargo = CT_BEGIN; cargo != CT_END; ++cargo) {
+			if ((_date + cargo) % _settings_game.linkgraph.recalc_interval == 0) {
+				LinkGraph & graph = _link_graphs[cargo];
+				if (spawn) {
+					graph.NextComponent();
+				} else {
+					graph.Join();
+				}
+			}
+		}
+	}
+}
+
+LinkGraph::LinkGraph()  : current_component_id(1), current_station_id(0), cargo(CT_INVALID)
+{
+	for (CargoID i = CT_BEGIN; i != CT_END; ++i) {
+		if (this == &(_link_graphs[i])) {
+			cargo = i;
+		}
+	}
+}
+
+NodeID LinkGraphComponent::AddNode(StationID st, uint supply, uint demand) {
+	nodes.push_back(Node(st, supply, demand));
+	for(NodeID i = 0; i < num_nodes; ++i) {
+		edges[i].push_back(Edge());
+	}
+	edges.push_back(std::vector<Edge>(++num_nodes));
+	return num_nodes - 1;
+}
+
+void LinkGraphComponent::AddEdge(NodeID from, NodeID to, uint capacity) {
+	assert(capacity > 0);
+	assert(from != to);
+	Edge & edge = edges[from][to];
+	Edge & first = edges[from][from];
+	edge.capacity = capacity;
+	edge.next_edge = first.next_edge;
+	first.next_edge = to;
+	edges[from][to].capacity = capacity;
+}
+
+void LinkGraphComponent::CalculateDistances() {
+	for(NodeID i = 0; i < num_nodes; ++i) {
+		for(NodeID j = 0; j < i; ++j) {
+			Station * st1 = Station::Get(nodes[i].station);
+			Station * st2 = Station::Get(nodes[j].station);
+			uint distance = DistanceManhattan(st1->xy, st2->xy);
+			edges[i][j].distance = distance;
+			edges[j][i].distance = distance;
+		}
+	}
+}
+
+void LinkGraphComponent::SetSize(uint size) {
+	num_nodes = size;
+	nodes.resize(num_nodes);
+	edges.resize(num_nodes, std::vector<Edge>(num_nodes));
+}
+
+LinkGraphComponent::LinkGraphComponent(CargoID car, LinkGraphComponentID col) :
+	settings(_settings_game.linkgraph),
+	cargo(car),
+	num_nodes(0),
+	index(col)
+{
+}
+
+void LinkGraph::Join() {
+	while (!this->jobs.empty()) {
+		LinkGraphJob * job = this->jobs.front();
+		assert(job != NULL);
+
+		/* also join if join date is far in the future. This prevents excessive memory use when resetting time */
+		if (job->GetJoinDate() > _date && job->GetJoinDate() <= _date + _settings_game.linkgraph.recalc_interval) {
+			return;
+		}
+		job->Join();
+
+		LinkGraphComponent * comp = job->GetComponent();
+
+		for(NodeID node_id = 0; node_id < comp->GetSize(); ++node_id) {
+			Node & node = comp->GetNode(node_id);
+			if (Station::IsValidID(node.station)) {
+				FlowStatMap & station_flows = Station::Get(node.station)->goods[cargo].flows;
+				node.ExportFlows(station_flows, cargo);
+				Window *station_gui = FindWindowById(WC_STATION_VIEW, node.station);
+				if (station_gui != NULL) {
+					station_gui->OnInvalidateData(comp->GetCargo());
+				}
+			}
+		}
+		delete job;
+		this->jobs.pop_front();
+	}
+}
+
+/**
+ * exports all entries in the FlowViaMap pointed to by source_flows it and erases it afterwards
+ */
+void Node::ExportNewFlows(FlowMap::iterator & source_flows_it, FlowStatSet & via_set, CargoID cargo) {
+	StationID source = source_flows_it->first;
+	FlowViaMap & source_flows = source_flows_it->second;
+	if (!Station::IsValidID(source)) {
+		source_flows.clear();
+	} else {
+		for (FlowViaMap::iterator update = source_flows.begin(); update != source_flows.end();) {
+			assert(update->second >= 0);
+			StationID next = update->first;
+			if (update->second > 0 && Station::IsValidID(next)) {
+				if (next != station) {
+					LinkStatMap & ls = Station::Get(station)->goods[cargo].link_stats;
+					if (ls.find(next) != ls.end()) {
+						via_set.insert(FlowStat(update->first, update->second, 0));
+					}
+				} else {
+					via_set.insert(FlowStat(update->first, update->second, 0));
+				}
+			}
+			source_flows.erase(update++);
+		}
+	}
+	assert(source_flows.empty());
+
+	flows.erase(source_flows_it++);
+}
+
+void Node::ExportFlows(FlowStatMap & station_flows, CargoID cargo) {
+	FlowStatSet new_flows;
+	/* loop over all existing flows in the station and update them */
+	for(FlowStatMap::iterator flowmap_it = station_flows.begin(); flowmap_it != station_flows.end();) {
+		FlowMap::iterator source_flows_it = flows.find(flowmap_it->first);
+		if (source_flows_it == flows.end()) {
+			/* there are no flows for this source node anymore */
+			station_flows.erase(flowmap_it++);
+		} else {
+			FlowViaMap & source_flows = source_flows_it->second;
+			FlowStatSet & via_set = flowmap_it->second;
+			/* loop over the station's flow stats for this source node and update them */
+			for (FlowStatSet::iterator flowset_it = via_set.begin(); flowset_it != via_set.end();) {
+				FlowViaMap::iterator update = source_flows.find(flowset_it->via);
+				if (update != source_flows.end()) {
+					assert(update->second >= 0);
+					if (update->second > 0) {
+						new_flows.insert(FlowStat(flowset_it->via, update->second, flowset_it->sent));
+					}
+					source_flows.erase(update);
+				}
+				via_set.erase(flowset_it++);
+			}
+			/* swap takes constant time, so we swap instead of adding all entries */
+			via_set.swap(new_flows);
+			assert(new_flows.empty());
+			/* insert remaining flows for this source node */
+			ExportNewFlows(source_flows_it, via_set, cargo);
+			/* source_flows is dangling here */
+			++flowmap_it;
+		}
+	}
+	/* loop over remaining flows (for other sources) in the node's map and insert them into the station */
+	for (FlowMap::iterator source_flows_it = flows.begin(); source_flows_it != flows.end();) {
+		FlowStatSet & via_set = station_flows[source_flows_it->first];
+		ExportNewFlows(source_flows_it, via_set, cargo);
+	}
+	assert(flows.empty());
+}
+
+void LinkGraph::AddComponent(LinkGraphComponent * component, uint join) {
+	LinkGraphComponentID index = component->GetIndex();
+	for(NodeID i = 0; i < component->GetSize(); ++i) {
+		StationID station_id = component->GetNode(i).station;
+		if (Station::IsValidID(station_id)) {
+			Station::Get(station_id)->goods[cargo].last_component = index;
+		}
+	}
+	LinkGraphJob * job = new LinkGraphJob(component, join);
+	assert(job != NULL);
+	job->SpawnThread(this->cargo);
+	this->jobs.push_back(job);
+}
+
+void LinkGraphJob::Run() {
+	for (HandlerList::iterator i = this->handlers.begin(); i != this->handlers.end(); ++i) {
+		ComponentHandler * handler = *i;
+		handler->Run(this->component);
+	}
+}
+
+LinkGraphJob::~LinkGraphJob() {
+	for (HandlerList::iterator i = this->handlers.begin(); i != this->handlers.end(); ++i) {
+		ComponentHandler * handler = *i;
+		delete handler;
+	}
+	this->handlers.clear();
+	DEBUG(misc, 2, "removing job for cargo %d with index %d and join date %d at %d", this->component->GetCargo(),
+			this->component->GetIndex(), this->join_date, _date);
+	delete this->component;
+	delete this->thread;
+}
+
+void RunLinkGraphJob(void * j) {
+	LinkGraphJob * job = (LinkGraphJob *)j;
+	job->Run();
+}
+
+void Path::Fork(Path * base, int cap, uint dist) {
+	capacity = min(base->capacity, cap);
+	distance = base->distance + dist;
+	assert(distance > 0);
+	if (parent != base) {
+		if (parent != NULL) {
+			parent->num_children--;
+		}
+		parent = base;
+		parent->num_children++;
+	}
+	origin = base->origin;
+}
+
+uint Path::AddFlow(uint f, LinkGraphComponent * graph, bool only_positive) {
+	if (parent != NULL) {
+		Edge & edge = graph->GetEdge(parent->node, node);
+		if (only_positive) {
+			uint usable_cap = edge.capacity * graph->GetSettings().short_path_saturation / 100;
+			if(usable_cap > edge.flow) {
+				f = min(f, usable_cap - edge.flow);
+			} else {
+				return 0;
+			}
+		}
+		f = parent->AddFlow(f, graph, only_positive);
+		if (f > 0) {
+			graph->GetNode(parent->node).paths.insert(this);
+		}
+		edge.flow += f;
+	}
+	flow += f;
+	return f;
+}
+
+void Path::UnFork() {
+	if (parent != NULL) {
+		parent->num_children--;
+		parent = NULL;
+	}
+}
+
+Path::Path(NodeID n, bool source)  :
+	distance(source ? 0 : UINT_MAX),
+	capacity(source ? INT_MAX : INT_MIN),
+	flow(0), node(n), origin(source ? n : Node::INVALID),
+	num_children(0), parent(NULL)
+{}
+
+void LinkGraphJob::SpawnThread(CargoID cargo) {
+	AddHandler(new DemandCalculator);
+	AddHandler(new MCF1stPass);
+	AddHandler(new FlowMapper);
+	AddHandler(new MCF2ndPass);
+	AddHandler(new FlowMapper);
+	if (!ThreadObject::New(&(RunLinkGraphJob), this, &thread)) {
+		thread = NULL;
+		// Of course this will hang a bit.
+		// On the other hand, if you want to play games which make this hang noticably
+		// on a platform without threads then you'll probably get other problems first.
+		// OK:
+		// If someone comes and tells me that this hangs for him/her, I'll implement a
+		// smaller grained "Step" method for all handlers and add some more ticks where
+		// "Step" is called. No problem in principle.
+		RunLinkGraphJob(this);
+	}
+}
+
+LinkGraphJob::LinkGraphJob(LinkGraphComponent * c) :
+	thread(NULL),
+	join_date(_date + c->GetSettings().recalc_interval),
+	component(c)
+{
+	DEBUG(misc, 2, "new job for cargo %d with index %d and join date %d at %d", c->GetCargo(), c->GetIndex(), join_date, _date);
+}
+
+LinkGraphJob::LinkGraphJob(LinkGraphComponent * c, Date join) :
+	thread(NULL),
+	join_date(join),
+	component(c)
+{
+	DEBUG(misc, 2, "new job for cargo %d with index %d and join date %d at %d", c->GetCargo(), c->GetIndex(), join_date, _date);
+}
+
+Node::~Node() {
+	for (PathSet::iterator i = paths.begin(); i != paths.end(); ++i) {
+		delete (*i);
+	}
+}
+
+void LinkGraph::Clear() {
+	for (JobList::iterator i = jobs.begin(); i != jobs.end(); ++i) {
+		LinkGraphJob * job = *i;
+		assert(job != NULL);
+		job->Join();
+		delete job;
+	}
+	jobs.clear();
+	current_component_id = 1;
+	current_station_id = 0;
+}
+
+void InitializeLinkGraphs() {
+	for (CargoID c = CT_BEGIN; c != CT_END; ++c) _link_graphs[c].Clear();
+}
diff --git a/src/linkgraph/linkgraph.h b/src/linkgraph/linkgraph.h
new file mode 100644
index 0000000..c58fb13
--- /dev/null
+++ b/src/linkgraph/linkgraph.h
@@ -0,0 +1,182 @@
+/** @file linkgraph.h Declaration of link graph classes used for cargo distribution. */
+
+#ifndef LINKGRAPH_H_
+#define LINKGRAPH_H_
+
+#include "../stdafx.h"
+#include "../station_base.h"
+#include "../cargo_type.h"
+#include "../thread/thread.h"
+#include "../settings_type.h"
+#include "linkgraph_types.h"
+#include <list>
+#include <vector>
+#include <set>
+
+struct SaveLoad;
+class Path;
+
+typedef std::set<Path *> PathSet;
+typedef std::map<NodeID, Path *> PathViaMap;
+typedef std::map<StationID, int> FlowViaMap;
+typedef std::map<StationID, FlowViaMap> FlowMap;
+
+class Node {
+public:
+	static const NodeID INVALID = UINT_MAX;
+	Node() : supply(0), undelivered_supply(0), demand(0), station(INVALID_STATION) {}
+	Node(StationID st, uint sup, uint dem) : supply(sup), undelivered_supply(sup), demand(dem), station(st) {}
+	~Node();
+	void ExportFlows(FlowStatMap & station_flows, CargoID cargo);
+	uint supply;
+	uint undelivered_supply;
+	uint demand;
+	StationID station;
+	PathSet paths;
+	FlowMap flows;
+private:
+	void ExportNewFlows(FlowMap::iterator & source_flows_it, FlowStatSet & via_set, CargoID cargo);
+};
+
+typedef std::set<NodeID> ViaSet;
+
+class Edge {
+public:
+	Edge() : distance(0), capacity(0), demand(0), unsatisfied_demand(0), flow(0), next_edge(Node::INVALID) {}
+	uint distance;
+	uint capacity;
+	uint demand;
+	uint unsatisfied_demand;
+	uint flow;
+	NodeID next_edge;
+};
+
+class LinkGraphComponent {
+	typedef std::vector<Node> NodeVector;
+	typedef std::vector<std::vector<Edge> > EdgeMatrix;
+
+public:
+	LinkGraphComponent(CargoID cargo, LinkGraphComponentID c = 0);
+	Edge & GetEdge(NodeID from, NodeID to) {return edges[from][to];}
+	Node & GetNode(NodeID num) {return nodes[num];}
+	uint GetSize() const {return num_nodes;}
+	void SetSize(uint size);
+	NodeID AddNode(StationID st, uint supply, uint demand);
+	void AddEdge(NodeID from, NodeID to, uint capacity);
+	void CalculateDistances();
+	LinkGraphComponentID GetIndex() const {return index;}
+	CargoID GetCargo() const {return cargo;}
+	const LinkGraphSettings & GetSettings() const {return settings;}
+	NodeID GetFirstEdge(NodeID from) {return edges[from][from].next_edge;}
+private:
+	friend const SaveLoad * GetLinkGraphComponentDesc();
+	LinkGraphSettings settings;
+	CargoID cargo;
+	uint num_nodes;
+	LinkGraphComponentID index;
+	NodeVector nodes;
+	EdgeMatrix edges;
+};
+
+class ComponentHandler {
+public:
+	virtual void Run(LinkGraphComponent * component) = 0;
+	virtual ~ComponentHandler() {}
+};
+
+class LinkGraphJob {
+	typedef std::list<ComponentHandler *> HandlerList;
+public:
+	LinkGraphJob(LinkGraphComponent * c);
+	LinkGraphJob(LinkGraphComponent * c, Date join);
+
+	void AddHandler(ComponentHandler * handler) {this->handlers.push_back(handler);}
+	void Run();
+	void SpawnThread(CargoID cargo);
+	void Join() {if (this->thread != NULL) this->thread->Join();}
+	Date GetJoinDate() {return this->join_date;}
+	LinkGraphComponent * GetComponent() {return this->component;}
+	~LinkGraphJob();
+private:
+	/**
+	 * there cannot be two identical LinkGraphJobs,
+	 */
+	LinkGraphJob(const LinkGraphJob & other) {NOT_REACHED();}
+	ThreadObject * thread;
+	Date join_date;
+	LinkGraphComponent * component;
+	HandlerList handlers;
+};
+
+typedef std::list<LinkGraphJob *> JobList;
+
+class LinkGraph {
+public:
+	LinkGraph();
+	void Clear();
+	CargoID GetCargo() const {return cargo;}
+	/**
+	 * Starts calcluation of the next component of the link graph.
+	 * Uses a breadth first search on the graph spanned by the
+	 * stations' link stats.
+	 *
+	 * TODO: This method could be changed to only search a defined number
+	 * of stations in each run, thus decreasing the delay. The state of
+	 * the search queue would have to be saved and loaded then.
+	 */
+	void NextComponent();
+
+	/**
+	 * Merges the results of the link graph calculation into the main
+	 * game state.
+	 *
+	 * TODO: This method could be changed to only merge a fixed number of
+	 * nodes in each run. In order to do so, the ID of last node merged
+	 * would have to be saved and loaded. Merging only a fixed  number
+	 * of nodes is faster than merging all nodes of the component.
+	 */
+	void Join();
+	size_t GetNumJobs() const {return jobs.size();}
+	JobList & GetJobs() {return jobs;}
+	void AddComponent(LinkGraphComponent * component, uint join);
+
+	const static uint COMPONENTS_JOIN_TICK  = 21;
+	const static uint COMPONENTS_SPAWN_TICK = 58;
+
+private:
+	friend const SaveLoad * GetLinkGraphDesc(uint);
+	void CreateComponent(Station * first);
+	LinkGraphComponentID current_component_id;
+	StationID current_station_id;
+	CargoID cargo;
+	JobList jobs;
+};
+
+class Path {
+public:
+	Path(NodeID n, bool source = false);
+	NodeID GetNode() const {return node;}
+	NodeID GetOrigin() const {return origin;}
+	Path * GetParent() {return parent;}
+	int GetCapacity() const {return capacity;}
+	uint GetDistance() const {return distance;}
+	void Fork(Path * base, int cap, uint dist);
+	void ReduceFlow(uint f) {flow -= f;}
+	void AddFlow(uint f) {flow += f;}
+	uint AddFlow(uint f, LinkGraphComponent * graph, bool only_positive);
+	uint GetFlow() const {return flow;}
+	uint GetNumChildren() const {return num_children;}
+	void UnFork();
+protected:
+	uint distance;
+	int capacity;      ///< this capacity is edge.capacity - edge.flow for the current run of dijkstra
+	uint flow;         ///< this is the flow the current run of the mcf solver assigns
+	NodeID node;
+	NodeID origin;
+	uint num_children;
+	Path * parent;
+};
+
+extern LinkGraph _link_graphs[NUM_CARGO];
+
+#endif /* LINKGRAPH_H_ */
diff --git a/src/linkgraph/linkgraph_types.h b/src/linkgraph/linkgraph_types.h
new file mode 100644
index 0000000..8b5ed9a
--- /dev/null
+++ b/src/linkgraph/linkgraph_types.h
@@ -0,0 +1,10 @@
+/** @file linkgraph_types.h Declaration of link graph types used for cargo distribution. */
+
+#ifndef LINKGRAPH_TYPES_H_
+#define LINKGRAPH_TYPES_H_
+
+typedef uint16 LinkGraphComponentID;
+typedef uint NodeID;
+
+
+#endif /* LINKGRAPH_TYPES_H_ */
diff --git a/src/linkgraph/mcf.cpp b/src/linkgraph/mcf.cpp
new file mode 100644
index 0000000..ae93998
--- /dev/null
+++ b/src/linkgraph/mcf.cpp
@@ -0,0 +1,308 @@
+/** @file mcf.cpp Definition of Multi-Commodity-Flow solver */
+
+#include "mcf.h"
+#include "../core/math_func.hpp"
+
+MultiCommodityFlow::MultiCommodityFlow() :
+	graph(NULL)
+{}
+
+void MultiCommodityFlow::Run(LinkGraphComponent * g) {
+	assert(g->GetSettings().accuracy >= 1);
+	graph = g;
+}
+
+bool DistanceAnnotation::IsBetter(const DistanceAnnotation * base, int cap, uint dist) const {
+	if (cap > 0 && base->capacity > 0) {
+		if (this->capacity <= 0) {
+			return true; // if the other path has capacity left and this one hasn't, the other one's better
+		} else {
+			return base->distance + dist < this->distance;
+		}
+	} else {
+		if (this->capacity > 0 || base->distance == UINT_MAX) {
+			return false; // if the other path doesn't have capacity left or is disconnected, but this one has, this one is always better
+		} else {
+			/* if both paths are out of capacity, do the regular distance comparison again */
+			return base->distance + dist < this->distance;
+		}
+	}
+}
+
+bool CapacityAnnotation::IsBetter(const CapacityAnnotation * base, int cap, uint dist) const {
+	int min_cap = min(base->capacity, cap);
+	if (min_cap == this->capacity) {
+		if (base->distance != UINT_MAX) { // if the capacities are the same, choose the shorter path
+			return (base->distance + dist < this->distance);
+		} else {
+			return false;
+		}
+	} else {
+		return min_cap > this->capacity;
+	}
+}
+
+template<class ANNOTATION>
+void MultiCommodityFlow::Dijkstra(NodeID source_node, PathVector & paths, bool create_new_paths) {
+	typedef std::set<ANNOTATION *, typename ANNOTATION::comp> AnnoSet;
+	uint size = this->graph->GetSize();
+	StationID source_station = this->graph->GetNode(source_node).station;
+	AnnoSet annos;
+	paths.resize(size, NULL);
+	for (NodeID node = 0; node < size; ++node) {
+		ANNOTATION * anno = new ANNOTATION(node, node == source_node);
+		annos.insert(anno);
+		paths[node] = anno;
+	}
+	while(!annos.empty()) {
+		typename AnnoSet::iterator i = annos.begin();
+		ANNOTATION * source = *i;
+		annos.erase(i);
+		NodeID from = source->GetNode();
+		NodeID to = this->graph->GetFirstEdge(from);
+		while (to != Node::INVALID) {
+			Edge & edge = graph->GetEdge(from, to);
+			assert(edge.capacity > 0 && edge.distance < UINT_MAX);
+			if (create_new_paths || this->graph->GetNode(from).flows[source_station][graph->GetNode(to).station] > 0) {
+				int capacity = edge.capacity;
+				if (create_new_paths) {
+					capacity *= this->graph->GetSettings().short_path_saturation;
+					capacity /= 100;
+					if (capacity == 0) {
+						capacity = 1;
+					}
+					assert(capacity > 0);
+				}
+				capacity -= edge.flow;
+				uint distance = edge.distance + 1; // punish in-between stops a little
+				ANNOTATION * dest = static_cast<ANNOTATION *>(paths[to]);
+				if (dest->IsBetter(source, capacity, distance)) {
+					annos.erase(dest);
+					dest->Fork(source, capacity, distance);
+					annos.insert(dest);
+				}
+			}
+			to = edge.next_edge;
+		}
+	}
+}
+
+
+
+void MultiCommodityFlow::CleanupPaths(NodeID source_id, PathVector & paths) {
+	Path * source = paths[source_id];
+	paths[source_id] = NULL;
+	for(PathVector::iterator i = paths.begin(); i != paths.end(); ++i) {
+		Path * path = *i;
+		if (path != NULL) {
+			if (path->GetParent() == source) {
+				path->UnFork();
+			}
+			while (path != source && path != NULL && path->GetFlow() == 0) {
+				Path * parent = path->GetParent();
+				path->UnFork();
+				if (path->GetNumChildren() == 0) {
+					paths[path->GetNode()] = NULL;
+					delete path;
+				}
+				path = parent;
+			}
+		}
+	}
+	delete source;
+	paths.clear();
+}
+
+uint MultiCommodityFlow::PushFlow(Edge &edge, Path * path, uint accuracy, bool positive_cap) {
+	uint flow = edge.unsatisfied_demand / accuracy;
+	if (flow == 0) flow = 1;
+	flow = path->AddFlow(flow, graph, positive_cap);
+	edge.unsatisfied_demand -= flow;
+	return flow;
+}
+
+uint MCF1stPass::FindCycleFlow(const PathVector & path, const Path * cycle_begin)
+{
+	uint flow = UINT_MAX;
+	const Path * cycle_end = cycle_begin;
+	do {
+		flow = min(flow, cycle_begin->GetFlow());
+		cycle_begin = path[cycle_begin->GetNode()];
+	} while(cycle_begin != cycle_end);
+	return flow;
+}
+
+void MCF1stPass::EliminateCycle(PathVector & path, Path * cycle_begin, uint flow)
+{
+	Path * cycle_end = cycle_begin;
+	do {
+		NodeID prev = cycle_begin->GetNode();
+		cycle_begin->ReduceFlow(flow);
+		cycle_begin = path[cycle_begin->GetNode()];
+		Edge & edge = this->graph->GetEdge(prev, cycle_begin->GetNode());
+		edge.flow -= flow;
+	} while(cycle_begin != cycle_end);
+}
+
+bool MCF1stPass::EliminateCycles(PathVector & path, NodeID origin_id, NodeID next_id)
+{
+	static Path * invalid_path = new Path(Node::INVALID, true);
+	Path * at_next_pos = path[next_id];
+	if (at_next_pos == invalid_path) {
+		/* this node has already been searched */
+		return false;
+	} else if (at_next_pos == NULL) {
+		/* summarize paths; add up the paths with the same source and next hop in one path each */
+		PathSet & paths = this->graph->GetNode(next_id).paths;
+		PathViaMap next_hops;
+		for(PathSet::iterator i = paths.begin(); i != paths.end(); ++i) {
+			Path * new_child = *i;
+			if (new_child->GetOrigin() == origin_id) {
+				PathViaMap::iterator via_it = next_hops.find(new_child->GetNode());
+				if (via_it == next_hops.end()) {
+					next_hops[new_child->GetNode()] = new_child;
+				} else {
+					Path * child = via_it->second;
+					uint new_flow = new_child->GetFlow();
+					child->AddFlow(new_flow);
+					new_child->ReduceFlow(new_flow);
+				}
+			}
+		}
+		bool found = false;
+		/* search the next hops for nodes we have already visited */
+		for (PathViaMap::iterator via_it = next_hops.begin(); via_it != next_hops.end(); ++via_it) {
+			Path * child = via_it->second;
+			if (child->GetFlow() > 0) {
+				/* push one child into the path vector and search this child's children */
+				path[next_id] = child;
+				found = EliminateCycles(path, origin_id, child->GetNode()) || found;
+			}
+		}
+		/* All paths departing from this node have been searched. Mark as resolved if no cycles found.
+		 * If cycles were found further cycles could be found in this branch, thus it has to be
+		 * searched again next time we spot it.
+		 */
+		if (found) {
+			path[next_id] = NULL;
+		} else {
+			path[next_id] = invalid_path;
+		}
+		return found;
+	} else {
+		/* this node has already been visited => we have a cycle
+		 * backtrack to find the exact flow
+		 */
+		uint flow = FindCycleFlow(path, at_next_pos);
+		if (flow > 0) {
+			EliminateCycle(path, at_next_pos, flow);
+			return true;
+		} else {
+			return false;
+		}
+	}
+}
+
+bool MCF1stPass::EliminateCycles()
+{
+	bool cycles_found = false;
+	uint size = this->graph->GetSize();
+	PathVector path(size, NULL);
+	for (NodeID node = 0; node < size; ++node) {
+		/* starting at each node in the graph find all cycles involving this node */
+		std::fill(path.begin(), path.end(), (Path *)NULL);
+		cycles_found = EliminateCycles(path, node, node) || cycles_found;
+	}
+	return cycles_found;
+}
+
+void MCF1stPass::Run(LinkGraphComponent * graph) {
+	MultiCommodityFlow::Run(graph);
+	PathVector paths;
+	uint size = graph->GetSize();
+	uint accuracy = graph->GetSettings().accuracy;
+	bool more_loops = true;
+
+	while (more_loops) {
+		more_loops = false;
+
+		for (NodeID source = 0; source < size; ++source) {
+			/* first saturate the shortest paths */
+			Dijkstra<DistanceAnnotation>(source, paths, true);
+
+			for (NodeID dest = 0; dest < size; ++dest) {
+				Edge & edge = graph->GetEdge(source, dest);
+				if (edge.unsatisfied_demand > 0) {
+					Path * path = paths[dest];
+					/* generally only allow paths that don't exceed the available capacity.
+					 * but if no demand has been assigned yet, make an exception and allow
+					 * any valid path *once*.
+					 */
+					if (path->GetCapacity() > 0 && PushFlow(edge, path, accuracy, true) > 0) {
+						more_loops = (edge.unsatisfied_demand > 0); {
+							/* if a path has been found there is a chance we can find more */
+							more_loops = true;
+						}
+					} else if (edge.unsatisfied_demand == edge.demand && path->GetCapacity() > INT_MIN) {
+						PushFlow(edge, path, accuracy, false);
+					}
+				}
+			}
+			CleanupPaths(source, paths);
+		}
+		if (!more_loops) {
+			more_loops = EliminateCycles();
+		}
+		if (accuracy > 1) --accuracy;
+	}
+}
+
+void MCF2ndPass::Run(LinkGraphComponent * graph) {
+	MultiCommodityFlow::Run(graph);
+	PathVector paths;
+	uint size = graph->GetSize();
+	uint accuracy = graph->GetSettings().accuracy;
+	bool demand_left = true;
+	while (demand_left) {
+		demand_left = false;
+		for (NodeID source = 0; source < size; ++source) {
+			/* Then assign all remaining demands */
+			Dijkstra<CapacityAnnotation>(source, paths, false);
+			for (NodeID dest = 0; dest < size; ++dest) {
+				Edge & edge = graph->GetEdge(source, dest);
+				Path * path = paths[dest];
+				if (edge.unsatisfied_demand > 0 && path->GetCapacity() > INT_MIN) {
+					PushFlow(edge, path, accuracy, false);
+					if (edge.unsatisfied_demand > 0) {
+						demand_left = true;
+					}
+				}
+			}
+			CleanupPaths(source, paths);
+		}
+		if (accuracy > 1) --accuracy;
+	}
+}
+
+/**
+ * avoid accidentally deleting different paths of the same capacity/distance in a set.
+ * When the annotation is the same node IDs are compared, so there are no equal ranges.
+ */
+template <typename T>
+bool greater(T x_anno, T y_anno, const Path * x, const Path * y) {
+	if (x_anno > y_anno) {
+		return true;
+	} else if (x_anno < y_anno) {
+		return false;
+	} else {
+		return x->GetNode() > y->GetNode();
+	}
+}
+
+bool CapacityAnnotation::comp::operator()(const CapacityAnnotation * x, const CapacityAnnotation * y) const {
+	return greater<int>(x->GetAnnotation(), y->GetAnnotation(), x, y);
+}
+
+bool DistanceAnnotation::comp::operator()(const DistanceAnnotation * x, const DistanceAnnotation * y) const {
+	return x != y && !greater<uint>(x->GetAnnotation(), y->GetAnnotation(), x, y);
+}
diff --git a/src/linkgraph/mcf.h b/src/linkgraph/mcf.h
new file mode 100644
index 0000000..e79611b
--- /dev/null
+++ b/src/linkgraph/mcf.h
@@ -0,0 +1,62 @@
+/** @file mcf.h Declaration of Multi-Commodity-Flow solver */
+
+#ifndef MCF_H_
+#define MCF_H_
+
+#include "linkgraph.h"
+#include <vector>
+
+class DistanceAnnotation : public Path {
+public:
+	DistanceAnnotation(NodeID n, bool source = false) : Path(n, source) {}
+	bool IsBetter(const DistanceAnnotation * base, int cap, uint dist) const;
+	uint GetAnnotation() const {return this->distance;}
+	struct comp {
+		bool operator()(const DistanceAnnotation * x, const DistanceAnnotation * y) const;
+	};
+};
+
+class CapacityAnnotation : public Path {
+public:
+	CapacityAnnotation(NodeID n, bool source = false) : Path(n, source) {}
+	bool IsBetter(const CapacityAnnotation * base, int cap, uint dist) const;
+	int GetAnnotation() const {return this->capacity;}
+	struct comp {
+		bool operator()(const CapacityAnnotation * x, const CapacityAnnotation * y) const;
+	};
+};
+
+
+typedef std::vector<Path *> PathVector;
+
+class MultiCommodityFlow : public ComponentHandler {
+public:
+	virtual void Run(LinkGraphComponent * graph);
+	MultiCommodityFlow();
+	virtual ~MultiCommodityFlow() {}
+protected:
+	template<class ANNOTATION>
+		void Dijkstra(NodeID from, PathVector & paths, bool create_new_paths);
+	uint PushFlow(Edge & edge, Path * path, uint accuracy, bool positive_cap);
+	void SetVia(NodeID source, Path * path);
+	void CleanupPaths(NodeID source, PathVector & paths);
+	LinkGraphComponent * graph;
+};
+
+class MCF1stPass : public MultiCommodityFlow {
+private:
+	bool EliminateCycles();
+	bool EliminateCycles(PathVector & path, NodeID origin, Path * next);
+	bool EliminateCycles(PathVector & path, NodeID origin_id, NodeID next_id);
+	void EliminateCycle(PathVector & path, Path * cycle_begin, uint flow);
+	uint FindCycleFlow(const PathVector & path, const Path * cycle_begin);
+public:
+	virtual void Run(LinkGraphComponent * graph);
+};
+
+class MCF2ndPass : public MultiCommodityFlow {
+public:
+	virtual void Run(LinkGraphComponent * graph);
+};
+
+#endif /* MCF_H_ */
diff --git a/src/main_gui.cpp b/src/main_gui.cpp
index 5ba32df..88747de 100644
--- a/src/main_gui.cpp
+++ b/src/main_gui.cpp
@@ -138,7 +138,7 @@ bool DoZoomInOutWindow(int how, Window *w)
 
 	switch (how) {
 		case ZOOM_IN:
-			if (vp->zoom == ZOOM_LVL_MIN) return false;
+			if (vp->zoom == ZOOM_LVL_BLITTER_MIN) return false;
 			vp->zoom = (ZoomLevel)((int)vp->zoom - 1);
 			vp->virtual_width >>= 1;
 			vp->virtual_height >>= 1;
@@ -149,7 +149,7 @@ bool DoZoomInOutWindow(int how, Window *w)
 			w->viewport->dest_scrollpos_y = w->viewport->scrollpos_y;
 			break;
 		case ZOOM_OUT:
-			if (vp->zoom == ZOOM_LVL_MAX) return false;
+			if (vp->zoom == ZOOM_LVL_BLITTER_MAX) return false;
 			vp->zoom = (ZoomLevel)((int)vp->zoom + 1);
 
 			w->viewport->scrollpos_x -= vp->virtual_width >> 1;
@@ -176,7 +176,7 @@ void ZoomInOrOutToCursorWindow(bool in, Window *w)
 
 	if (_game_mode != GM_MENU) {
 		ViewPort *vp = w->viewport;
-		if ((in && vp->zoom == ZOOM_LVL_MIN) || (!in && vp->zoom == ZOOM_LVL_MAX))
+		if ((in && vp->zoom == ZOOM_LVL_BLITTER_MIN) || (!in && vp->zoom == ZOOM_LVL_BLITTER_MAX))
 			return;
 
 		Point pt = GetTileZoomCenterWindow(in, w);
diff --git a/src/misc.cpp b/src/misc.cpp
index 995f0dd..c93acd6 100644
--- a/src/misc.cpp
+++ b/src/misc.cpp
@@ -53,6 +53,7 @@ void InitializeCompanies();
 void InitializeCheats();
 void InitializeNPF();
 void InitializeOldNames();
+void InitializeLinkGraphs();
 
 void InitializeGame(uint size_x, uint size_y, bool reset_date, bool reset_settings)
 {
@@ -114,6 +115,7 @@ void InitializeGame(uint size_x, uint size_y, bool reset_date, bool reset_settin
 #endif /* ENABLE_NETWORK */
 	InitializeAnimatedTiles();
 
+	InitializeLinkGraphs();
 	InitializeEconomy();
 
 	ResetObjectToPlace();
diff --git a/src/newgrf.cpp b/src/newgrf.cpp
index 37f830b..54ae4b1 100644
--- a/src/newgrf.cpp
+++ b/src/newgrf.cpp
@@ -6337,6 +6337,9 @@ static void AfterLoadGRFs()
 	/* Create dynamic list of industry legends for smallmap_gui.cpp */
 	BuildIndustriesLegend();
 
+	/* build the routemap legend, based on the available cargos */
+	BuildLinkStatsLegend();
+
 	/* Update the townname generators list */
 	InitGRFTownGeneratorNames();
 
diff --git a/src/openttd.cpp b/src/openttd.cpp
index e790e37..1843e12 100644
--- a/src/openttd.cpp
+++ b/src/openttd.cpp
@@ -1073,6 +1073,13 @@ void SwitchToMode(SwitchMode new_mode)
 		default: NOT_REACHED();
 	}
 
+	LinkGraphSettings &lg = _settings_game.linkgraph;
+	if (!_settings_client.gui.new_nonstop) {
+		if ((lg.demand_armoured | lg.demand_default | lg.demand_express | lg.demand_mail | lg.demand_pax) & (DT_SYMMETRIC | DT_ANTISYMMETRIC)) {
+			ShowErrorMessage(STR_WARNING_NONSTOP_CARGODIST, INVALID_STRING_ID, 0, 0, true);
+		}
+	}
+
 	if (_switch_mode_errorstr != INVALID_STRING_ID) {
 		ShowErrorMessage(_switch_mode_errorstr, INVALID_STRING_ID, 0, 0, true);
 	}
diff --git a/src/order_base.h b/src/order_base.h
index e75733a..ef3c6f2 100644
--- a/src/order_base.h
+++ b/src/order_base.h
@@ -260,6 +260,8 @@ struct OrderList : OrderListPool::PoolItem<&_orderlist_pool> {
 private:
 	friend void AfterLoadVehicles(bool part_of_load); ///< For instantiating the shared vehicle chain
 	friend const struct SaveLoad *GetOrderListDescription(); ///< Saving and loading of order lists.
+	const Order * GetNext(const Order * curr) const;
+	const Order * GetNextStoppingOrder(const Order * next, uint hops, bool check_nonstop) const;
 
 	Order *first;                   ///< First order of the order list
 	VehicleOrderID num_orders;      ///< NOSAVE: How many orders there are in the list
@@ -309,6 +311,8 @@ public:
 	 */
 	inline Order *GetLastOrder() const { return this->GetOrderAt(this->num_orders - 1); }
 
+	StationID GetNextStoppingStation(VehicleOrderID curr, bool check_nonstop) const;
+
 	/**
 	 * Get number of orders in the order list.
 	 * @return number of orders in the chain. */
diff --git a/src/order_cmd.cpp b/src/order_cmd.cpp
index ccf2fe9..f1c5967 100644
--- a/src/order_cmd.cpp
+++ b/src/order_cmd.cpp
@@ -27,6 +27,7 @@
 #include "station_base.h"
 #include "waypoint_base.h"
 #include "roadstop_base.h"
+#include "infrastructure_func.h"
 
 #include "table/strings.h"
 
@@ -257,6 +258,67 @@ Order *OrderList::GetOrderAt(int index) const
 	return order;
 }
 
+const Order * OrderList::GetNext(const Order * curr) const
+{
+	const Order * next = curr->next;
+	if (next == NULL) {
+		next = GetFirstOrder();
+	}
+	return next;
+}
+
+const Order * OrderList::GetNextStoppingOrder(const Order * next, uint hops, bool check_nonstop) const
+{
+	if (next == NULL || hops > GetNumOrders()) {
+		return NULL;
+	}
+
+	if (next->GetType() == OT_CONDITIONAL) {
+		const Order * skip_to = GetNextStoppingOrder(GetOrderAt(next->GetConditionSkipToOrder()), hops + 1, check_nonstop);
+		const Order * advance = GetNextStoppingOrder(next, hops + 1, check_nonstop);
+		if (skip_to == advance) {
+			return skip_to; // skipping over non-stopping orders
+		} else {
+			return NULL; // nondeterministic
+		}
+	}
+
+	bool is_station = (next->GetType() == OT_GOTO_STATION);
+
+	if (check_nonstop) {
+		switch(next->GetNonStopType()) {
+		case ONSF_NO_STOP_AT_INTERMEDIATE_STATIONS:
+			if (is_station) return next; // else fall through
+		case ONSF_NO_STOP_AT_ANY_STATION:
+			return GetNextStoppingOrder(GetNext(next), hops + 1, check_nonstop);
+		default: // nondeterministic
+			return NULL;
+		}
+	} else {
+		if (is_station) {
+			return next;
+		} else {
+			return GetNextStoppingOrder(GetNext(next), hops + 1, check_nonstop);
+		}
+	}
+}
+
+StationID OrderList::GetNextStoppingStation(VehicleOrderID curr_id, bool check_nonstop) const {
+	const Order * curr = GetOrderAt(curr_id);
+	if (curr == NULL) {
+		curr = GetFirstOrder();
+		if (curr == NULL) {
+			return INVALID_STATION;
+		}
+	}
+	const Order * next = GetNextStoppingOrder(GetNext(curr), 1, check_nonstop);
+	if (next == NULL) {
+		return INVALID_STATION;
+	} else {
+		return next->GetDestination();
+	}
+}
+
 void OrderList::InsertOrderAt(Order *new_order, int index)
 {
 	if (this->first == NULL) {
@@ -464,7 +526,7 @@ CommandCost CmdInsertOrder(TileIndex tile, DoCommandFlag flags, uint32 p1, uint3
 			const Station *st = Station::GetIfValid(new_order.GetDestination());
 			if (st == NULL) return CMD_ERROR;
 
-			if (st->owner != OWNER_NONE && !CheckOwnership(st->owner)) return CMD_ERROR;
+			if (!CheckInfraUsageAllowed(st->owner, v->type)) return CMD_ERROR;
 
 			if (!CanVehicleUseStation(v, st)) return_cmd_error(STR_ERROR_CAN_T_ADD_ORDER);
 			for (Vehicle *u = v->FirstShared(); u != NULL; u = u->NextShared()) {
@@ -508,7 +570,7 @@ CommandCost CmdInsertOrder(TileIndex tile, DoCommandFlag flags, uint32 p1, uint3
 				if (v->type == VEH_AIRCRAFT) {
 					const Station *st = Station::GetIfValid(new_order.GetDestination());
 
-					if (st == NULL || !CheckOwnership(st->owner) ||
+					if (st == NULL || !CheckInfraUsageAllowed(st->owner, v->type) ||
 							!CanVehicleUseStation(v, st) ||
 							st->Airport()->nof_depots == 0) {
 						return CMD_ERROR;
@@ -516,7 +578,7 @@ CommandCost CmdInsertOrder(TileIndex tile, DoCommandFlag flags, uint32 p1, uint3
 				} else {
 					const Depot *dp = Depot::GetIfValid(new_order.GetDestination());
 
-					if (dp == NULL || !CheckOwnership(GetTileOwner(dp->xy))) return CMD_ERROR;
+					if (dp == NULL || !CheckInfraUsageAllowed(GetTileOwner(dp->xy), v->type)) return CMD_ERROR;
 
 					switch (v->type) {
 						case VEH_TRAIN:
@@ -554,14 +616,13 @@ CommandCost CmdInsertOrder(TileIndex tile, DoCommandFlag flags, uint32 p1, uint3
 
 				case VEH_TRAIN:
 					if (!(wp->facilities & FACIL_TRAIN)) return_cmd_error(STR_ERROR_CAN_T_ADD_ORDER);
-					if (!CheckOwnership(wp->owner)) return CMD_ERROR;
 					break;
 
 				case VEH_SHIP:
 					if (!(wp->facilities & FACIL_DOCK)) return_cmd_error(STR_ERROR_CAN_T_ADD_ORDER);
-					if (!CheckOwnership(wp->owner) && wp->owner != OWNER_NONE) return CMD_ERROR;
 					break;
 			}
+			if (!CheckInfraUsageAllowed(wp->owner, v->type)) return CMD_ERROR;
 
 			/* Order flags can be any of the following for waypoints:
 			 * [non-stop]
@@ -661,6 +722,8 @@ CommandCost CmdInsertOrder(TileIndex tile, DoCommandFlag flags, uint32 p1, uint3
 			}
 			/* Update any possible open window of the vehicle */
 			InvalidateVehicleOrder(u, INVALID_VEH_ORDER_ID | (sel_ord << 8));
+
+			RecalcFrozenIfLoading(u);
 		}
 
 		/* As we insert an order, the order to skip to will be 'wrong'. */
@@ -695,6 +758,9 @@ static CommandCost DecloneOrder(Vehicle *dst, DoCommandFlag flags)
 	if (flags & DC_EXEC) {
 		DeleteVehicleOrders(dst);
 		InvalidateVehicleOrder(dst, -1);
+
+		RecalcFrozenIfLoading(dst);
+
 		InvalidateWindowClassesData(GetWindowClassForVehicleType(dst->type), 0);
 	}
 	return CommandCost();
@@ -744,6 +810,8 @@ CommandCost CmdDeleteOrder(TileIndex tile, DoCommandFlag flags, uint32 p1, uint3
 
 			/* Update any possible open window of the vehicle */
 			InvalidateVehicleOrder(u, sel_ord | (INVALID_VEH_ORDER_ID << 8));
+
+			RecalcFrozenIfLoading(u);
 		}
 
 		/* As we delete an order, the order to skip to will be 'wrong'. */
@@ -788,10 +856,10 @@ CommandCost CmdSkipToOrder(TileIndex tile, DoCommandFlag flags, uint32 p1, uint3
 	}
 
 	if (flags & DC_EXEC) {
-		v->cur_order_index = sel_ord;
-
 		if (v->current_order.IsType(OT_LOADING)) v->LeaveStation();
 
+		v->cur_order_index = sel_ord;
+
 		InvalidateVehicleOrder(v, -2);
 	}
 
@@ -854,6 +922,8 @@ CommandCost CmdMoveOrder(TileIndex tile, DoCommandFlag flags, uint32 p1, uint32
 			assert(v->orders.list == u->orders.list);
 			/* Update any possible open window of the vehicle */
 			InvalidateVehicleOrder(u, moving_order | (target_order << 8));
+
+			RecalcFrozenIfLoading(u);
 		}
 
 		/* As we move an order, the order to skip to will be 'wrong'. */
@@ -1104,6 +1174,8 @@ CommandCost CmdModifyOrder(TileIndex tile, DoCommandFlag flags, uint32 p1, uint3
 				u->current_order.SetLoadType(order->GetLoadType());
 			}
 			InvalidateVehicleOrder(u, -2);
+
+			RecalcFrozenIfLoading(u);
 		}
 	}
 
@@ -1229,6 +1301,8 @@ CommandCost CmdCloneOrder(TileIndex tile, DoCommandFlag flags, uint32 p1, uint32
 		default: return CMD_ERROR;
 	}
 
+	RecalcFrozenIfLoading(dst);
+
 	return CommandCost();
 }
 
@@ -1526,6 +1600,8 @@ void RemoveOrderFromAllVehicles(OrderType type, DestinationID destination)
 					/* In GUI, simulate by removing the order and adding it back */
 					InvalidateVehicleOrder(w, id | (INVALID_VEH_ORDER_ID << 8));
 					InvalidateVehicleOrder(w, (INVALID_VEH_ORDER_ID << 8) | id);
+
+					RecalcFrozenIfLoading(w);
 				}
 			}
 		}
@@ -1569,6 +1645,8 @@ void DeleteVehicleOrders(Vehicle *v, bool keep_orderlist)
 		v->orders.list->FreeChain(keep_orderlist);
 		if (!keep_orderlist) v->orders.list = NULL;
 	}
+
+	RecalcFrozenIfLoading(v);
 }
 
 uint16 GetServiceIntervalClamped(uint interval, CompanyID company_id)
@@ -1769,7 +1847,7 @@ bool ProcessOrders(Vehicle *v)
 	if (((v->current_order.IsType(OT_GOTO_STATION) && (v->current_order.GetNonStopType() & ONSF_NO_STOP_AT_DESTINATION_STATION)) || v->current_order.IsType(OT_GOTO_WAYPOINT)) &&
 			IsTileType(v->tile, MP_STATION) &&
 			v->current_order.GetDestination() == GetStationIndex(v->tile)) {
-		if (v->current_order.IsType(OT_GOTO_STATION)) v->last_station_visited = v->current_order.GetDestination();
+		/* treat it like a waypoint and don't set last_station_visited */
 		UpdateVehicleTimetable(v, true);
 		v->IncrementOrderIndex();
 	}
diff --git a/src/order_gui.cpp b/src/order_gui.cpp
index f148433..88abf13 100644
--- a/src/order_gui.cpp
+++ b/src/order_gui.cpp
@@ -31,6 +31,7 @@
 #include "network/network.h"
 #include "station_base.h"
 #include "waypoint_base.h"
+#include "infrastructure_func.h"
 
 #include "table/sprites.h"
 #include "table/strings.h"
@@ -316,7 +317,7 @@ static Order GetOrderCmdFromTile(const Vehicle *v, TileIndex tile)
 	if (_settings_game.order.gotodepot) {
 		switch (GetTileType(tile)) {
 			case MP_RAILWAY:
-				if (v->type == VEH_TRAIN && IsTileOwner(tile, _local_company)) {
+				if (v->type == VEH_TRAIN && IsInfraTileUsageAllowed(tile, v->owner, VEH_TRAIN)) {
 					if (IsRailDepot(tile)) {
 						order.MakeGoToDepot(GetDepotIndex(tile), ODTFB_PART_OF_ORDERS,
 								_settings_client.gui.new_nonstop ? ONSF_NO_STOP_AT_INTERMEDIATE_STATIONS : ONSF_STOP_EVERYWHERE);
@@ -327,7 +328,7 @@ static Order GetOrderCmdFromTile(const Vehicle *v, TileIndex tile)
 				break;
 
 			case MP_ROAD:
-				if (IsRoadDepot(tile) && v->type == VEH_ROAD && IsTileOwner(tile, _local_company)) {
+				if (IsRoadDepot(tile) && v->type == VEH_ROAD && IsInfraTileUsageAllowed(tile, v->owner, VEH_ROAD)) {
 					order.MakeGoToDepot(GetDepotIndex(tile), ODTFB_PART_OF_ORDERS,
 							_settings_client.gui.new_nonstop ? ONSF_NO_STOP_AT_INTERMEDIATE_STATIONS : ONSF_STOP_EVERYWHERE);
 					if (_ctrl_pressed) order.SetDepotOrderType((OrderDepotTypeFlags)(order.GetDepotOrderType() ^ ODTFB_SERVICE));
@@ -337,7 +338,7 @@ static Order GetOrderCmdFromTile(const Vehicle *v, TileIndex tile)
 
 			case MP_STATION:
 				if (v->type != VEH_AIRCRAFT) break;
-				if (IsHangar(tile) && IsTileOwner(tile, _local_company)) {
+				if (IsHangar(tile) && IsInfraTileUsageAllowed(tile, v->owner, VEH_AIRCRAFT)) {
 					order.MakeGoToDepot(GetStationIndex(tile), ODTFB_PART_OF_ORDERS, ONSF_STOP_EVERYWHERE);
 					if (_ctrl_pressed) order.SetDepotOrderType((OrderDepotTypeFlags)(order.GetDepotOrderType() ^ ODTFB_SERVICE));
 					return order;
@@ -346,7 +347,7 @@ static Order GetOrderCmdFromTile(const Vehicle *v, TileIndex tile)
 
 			case MP_WATER:
 				if (v->type != VEH_SHIP) break;
-				if (IsShipDepot(tile) && IsTileOwner(tile, _local_company)) {
+				if (IsShipDepot(tile) && IsInfraTileUsageAllowed(tile, v->owner, VEH_SHIP)) {
 					order.MakeGoToDepot(GetDepotIndex(tile), ODTFB_PART_OF_ORDERS, ONSF_STOP_EVERYWHERE);
 					if (_ctrl_pressed) order.SetDepotOrderType((OrderDepotTypeFlags)(order.GetDepotOrderType() ^ ODTFB_SERVICE));
 					return order;
@@ -360,7 +361,7 @@ static Order GetOrderCmdFromTile(const Vehicle *v, TileIndex tile)
 	/* check waypoint */
 	if (IsRailWaypointTile(tile) &&
 			v->type == VEH_TRAIN &&
-			IsTileOwner(tile, _local_company)) {
+			IsInfraTileUsageAllowed(tile, v->owner, VEH_TRAIN)) {
 		order.MakeGoToWaypoint(Waypoint::GetByTile(tile)->index);
 		if (_settings_client.gui.new_nonstop != _ctrl_pressed) order.SetNonStopType(ONSF_NO_STOP_AT_ANY_STATION);
 		return order;
@@ -375,7 +376,7 @@ static Order GetOrderCmdFromTile(const Vehicle *v, TileIndex tile)
 		StationID st_index = GetStationIndex(tile);
 		const Station *st = Station::Get(st_index);
 
-		if (st->owner == _local_company || st->owner == OWNER_NONE) {
+		if (IsInfraUsageAllowed(st->owner, v->owner, v->type)) {
 			byte facil;
 			(facil = FACIL_DOCK, v->type == VEH_SHIP) ||
 			(facil = FACIL_TRAIN, v->type == VEH_TRAIN) ||
diff --git a/src/pathfinder/follow_track.hpp b/src/pathfinder/follow_track.hpp
index 62e36e1..93f7034 100644
--- a/src/pathfinder/follow_track.hpp
+++ b/src/pathfinder/follow_track.hpp
@@ -19,6 +19,7 @@
 #include "../train.h"
 #include "../tunnelbridge.h"
 #include "../tunnelbridge_map.h"
+#include "../infrastructure_func.h"
 #include "pf_performance_timer.hpp"
 
 /** Track follower helper template class (can serve pathfinders and vehicle
@@ -283,6 +284,11 @@ protected:
 				m_err = EC_NO_WAY;
 				return false;
 			}
+			/* road stops shouldn't be entered unless allowed to */
+			if (!IsInfraTileUsageAllowed(m_new_tile, m_veh_owner, VEH_ROAD)) {
+				m_err = EC_OWNER;
+				return false;
+			}
 		}
 
 		/* single tram bits can only be entered from one direction */
@@ -301,8 +307,8 @@ protected:
 				m_err = EC_NO_WAY;
 				return false;
 			}
-			/* don't try to enter other company's depots */
-			if (GetTileOwner(m_new_tile) != m_veh_owner) {
+			/* don't try to enter other company's depots if not allowed */
+			if (!IsInfraTileUsageAllowed(m_new_tile, m_veh_owner, VEH_ROAD)) {
 				m_err = EC_OWNER;
 				return false;
 			}
@@ -315,8 +321,8 @@ protected:
 			}
 		}
 
-		/* rail transport is possible only on tiles with the same owner as vehicle */
-		if (IsRailTT() && GetTileOwner(m_new_tile) != m_veh_owner) {
+		/* rail transport is possible only on tiles with the same owner as vehicle if sharing of tracks is disabled */
+		if (IsRailTT() && !IsInfraTileUsageAllowed(m_new_tile, m_veh_owner, VEH_TRAIN)) {
 			/* different owner */
 			m_err = EC_NO_WAY;
 			return false;
diff --git a/src/pathfinder/npf/npf.cpp b/src/pathfinder/npf/npf.cpp
index 4a59795..7d833d8 100644
--- a/src/pathfinder/npf/npf.cpp
+++ b/src/pathfinder/npf/npf.cpp
@@ -22,6 +22,7 @@
 #include "../../ship.h"
 #include "../../train.h"
 #include "../../roadstop_base.h"
+#include "../../infrastructure_func.h"
 #include "../pathfinder_func.h"
 #include "../pathfinder_type.h"
 #include "../follow_track.hpp"
@@ -674,25 +675,31 @@ static void NPFSaveTargetData(AyStar *as, OpenListNode *current)
  */
 static bool CanEnterTileOwnerCheck(Owner owner, TileIndex tile, DiagDirection enterdir)
 {
-	if (IsTileType(tile, MP_RAILWAY) || // Rail tile (also rail depot)
-			HasStationTileRail(tile) ||     // Rail station tile/waypoint
-			IsRoadDepotTile(tile) ||        // Road depot tile
-			IsStandardRoadStopTile(tile)) { // Road station tile (but not drive-through stops)
-		return IsTileOwner(tile, owner);  // You need to own these tiles entirely to use them
-	}
-
 	switch (GetTileType(tile)) {
+		case MP_RAILWAY:
+			return IsInfraTileUsageAllowed(tile, owner, VEH_TRAIN); // Rail tile (also rail depot)
+
 		case MP_ROAD:
 			/* rail-road crossing : are we looking at the railway part? */
 			if (IsLevelCrossing(tile) &&
 					DiagDirToAxis(enterdir) != GetCrossingRoadAxis(tile)) {
-				return IsTileOwner(tile, owner); // Railway needs owner check, while the street is public
+				return IsInfraTileUsageAllowed(tile, owner, VEH_TRAIN); // Railway needs owner check, while the street is public
+			} else if (IsRoadDepot(tile)) { // Road depot tile
+				return IsInfraTileUsageAllowed(tile, owner, VEH_ROAD);
+			}
+			break;
+
+		case MP_STATION:
+			if (HasStationRail(tile)) { // Rail station tile/waypoint
+				return IsInfraTileUsageAllowed(tile, owner, VEH_TRAIN);
+			} else if (IsStandardRoadStopTile(tile)) { // Road station tile (but not drive-through stops)
+				return IsInfraTileUsageAllowed(tile, owner, VEH_ROAD);
 			}
 			break;
 
 		case MP_TUNNELBRIDGE:
 			if (GetTunnelBridgeTransportType(tile) == TRANSPORT_RAIL) {
-				return IsTileOwner(tile, owner);
+				return IsInfraTileUsageAllowed(tile, owner, VEH_TRAIN);
 			}
 			break;
 
diff --git a/src/roadveh_cmd.cpp b/src/roadveh_cmd.cpp
index 5df4572..0bc0b6e 100644
--- a/src/roadveh_cmd.cpp
+++ b/src/roadveh_cmd.cpp
@@ -38,6 +38,7 @@
 #include "cargotype.h"
 #include "spritecache.h"
 #include "debug.h"
+#include "infrastructure_func.h"
 
 #include "table/strings.h"
 #include "table/sprites.h"
@@ -209,7 +210,7 @@ CommandCost CmdBuildRoadVeh(TileIndex tile, DoCommandFlag flags, uint32 p1, uint
 	/* The ai_new queries the vehicle cost before building the route,
 	 * so we must check against cheaters no sooner than now. --pasky */
 	if (!IsRoadDepotTile(tile)) return CMD_ERROR;
-	if (!IsTileOwner(tile, _current_company)) return CMD_ERROR;
+	if (!CheckInfraUsageAllowed(GetTileOwner(tile), VEH_ROAD)) return CMD_ERROR;
 
 	if (HasTileRoadType(tile, ROADTYPE_TRAM) != HasBit(e->info.misc_flags, EF_ROAD_TRAM)) return_cmd_error(STR_ERROR_DEPOT_WRONG_DEPOT_TYPE);
 
@@ -414,7 +415,7 @@ CommandCost CmdSendRoadVehToDepot(TileIndex tile, DoCommandFlag flags, uint32 p1
 CommandCost CmdTurnRoadVeh(TileIndex tile, DoCommandFlag flags, uint32 p1, uint32 p2, const char *text)
 {
 	RoadVehicle *v = RoadVehicle::GetIfValid(p1);
-	if (v == NULL || !CheckOwnership(v->owner)) return CMD_ERROR;
+	if (v == NULL || !CheckVehicleControlAllowed(v)) return CMD_ERROR;
 
 	if ((v->vehstatus & VS_STOPPED) ||
 			(v->vehstatus & VS_CRASHED) ||
@@ -938,14 +939,14 @@ static Trackdir RoadFindPathToDest(RoadVehicle *v, TileIndex tile, DiagDirection
 	TrackdirBits trackdirs = TrackStatusToTrackdirBits(ts);
 
 	if (IsTileType(tile, MP_ROAD)) {
-		if (IsRoadDepot(tile) && (!IsTileOwner(tile, v->owner) || GetRoadDepotDirection(tile) == enterdir || (GetRoadTypes(tile) & v->compatible_roadtypes) == 0)) {
+		if (IsRoadDepot(tile) && (!IsInfraTileUsageAllowed(tile, v->owner, VEH_ROAD) || GetRoadDepotDirection(tile) == enterdir || (GetRoadTypes(tile) & v->compatible_roadtypes) == 0)) {
 			/* Road depot owned by another company or with the wrong orientation */
 			trackdirs = TRACKDIR_BIT_NONE;
 		}
 	} else if (IsTileType(tile, MP_STATION) && IsStandardRoadStopTile(tile)) {
 		/* Standard road stop (drive-through stops are treated as normal road) */
 
-		if (!IsTileOwner(tile, v->owner) || GetRoadStopDir(tile) == enterdir || v->HasArticulatedPart()) {
+		if (!IsInfraTileUsageAllowed(tile, v->owner, VEH_ROAD) || GetRoadStopDir(tile) == enterdir || v->HasArticulatedPart()) {
 			/* different station owner or wrong orientation or the vehicle has articulated parts */
 			trackdirs = TRACKDIR_BIT_NONE;
 		} else {
@@ -1439,12 +1440,13 @@ again:
 			/* In case an RV is stopped in a road stop, why not try to load? */
 			if (v->cur_speed == 0 && IsInsideMM(v->state, RVSB_IN_DT_ROAD_STOP, RVSB_IN_DT_ROAD_STOP_END) &&
 					v->current_order.ShouldStopAtStation(v, GetStationIndex(v->tile)) &&
-					v->owner == GetTileOwner(v->tile) && !v->current_order.IsType(OT_LEAVESTATION) &&
+					IsInfraTileUsageAllowed(v->tile, v->owner, VEH_ROAD) && !v->current_order.IsType(OT_LEAVESTATION) &&
 					GetRoadStopType(v->tile) == (v->IsBus() ? ROADSTOP_BUS : ROADSTOP_TRUCK)) {
 				Station *st = Station::GetByTile(v->tile);
+				StationID previous_station = v->last_station_visited;
 				v->last_station_visited = st->index;
 				RoadVehArrivesAt(v, st);
-				v->BeginLoading();
+				v->BeginLoading(previous_station);
 			}
 			return false;
 		}
@@ -1473,7 +1475,7 @@ again:
 			_road_veh_data_1[v->state - RVSB_IN_ROAD_STOP + (_settings_game.vehicle.road_side << RVS_DRIVE_SIDE)] == v->frame) ||
 			(IsInsideMM(v->state, RVSB_IN_DT_ROAD_STOP, RVSB_IN_DT_ROAD_STOP_END) &&
 			v->current_order.ShouldStopAtStation(v, GetStationIndex(v->tile)) &&
-			v->owner == GetTileOwner(v->tile) &&
+			IsInfraTileUsageAllowed(v->tile, v->owner, VEH_ROAD) &&
 			GetRoadStopType(v->tile) == (v->IsBus() ? ROADSTOP_BUS : ROADSTOP_TRUCK) &&
 			v->frame == RVC_DRIVE_THROUGH_STOP_FRAME))) {
 
@@ -1498,12 +1500,12 @@ again:
 			}
 
 			rs->SetEntranceBusy(false);
-
+			StationID previous_station = v->last_station_visited;
 			v->last_station_visited = st->index;
 
 			if (IsDriveThroughStopTile(v->tile) || (v->current_order.IsType(OT_GOTO_STATION) && v->current_order.GetDestination() == st->index)) {
 				RoadVehArrivesAt(v, st);
-				v->BeginLoading();
+				v->BeginLoading(previous_station);
 				return false;
 			}
 		} else {
diff --git a/src/saveload/cargopacket_sl.cpp b/src/saveload/cargopacket_sl.cpp
index 8090ed6..fa41231 100644
--- a/src/saveload/cargopacket_sl.cpp
+++ b/src/saveload/cargopacket_sl.cpp
@@ -26,7 +26,7 @@
 		 * to the current tile of the vehicle to prevent excessive profits
 		 */
 		FOR_ALL_VEHICLES(v) {
-			const VehicleCargoList::List *packets = v->cargo.Packets();
+			const CargoPacketList *packets = v->cargo.Packets();
 			for (VehicleCargoList::ConstIterator it(packets->begin()); it != packets->end(); it++) {
 				CargoPacket *cp = *it;
 				cp->source_xy = Station::IsValidID(cp->source) ? Station::Get(cp->source)->xy : v->tile;
@@ -44,7 +44,7 @@
 			for (CargoID c = 0; c < NUM_CARGO; c++) {
 				GoodsEntry *ge = &st->goods[c];
 
-				const StationCargoList::List *packets = ge->cargo.Packets();
+				const StationCargoPacketMap *packets = ge->cargo.Packets();
 				for (StationCargoList::ConstIterator it(packets->begin()); it != packets->end(); it++) {
 					CargoPacket *cp = *it;
 					cp->source_xy = Station::IsValidID(cp->source) ? Station::Get(cp->source)->xy : st->xy;
diff --git a/src/saveload/economy_sl.cpp b/src/saveload/economy_sl.cpp
index cce2d7f..1c1bed8 100644
--- a/src/saveload/economy_sl.cpp
+++ b/src/saveload/economy_sl.cpp
@@ -63,11 +63,11 @@ static void Load_ECMY()
 }
 
 static const SaveLoad _cargopayment_desc[] = {
-	SLE_REF(CargoPayment, front,         REF_VEHICLE),
-	SLE_VAR(CargoPayment, route_profit,  SLE_INT64),
-	SLE_VAR(CargoPayment, visual_profit, SLE_INT64),
-
-	SLE_END()
+	    SLE_REF(CargoPayment, front,           REF_VEHICLE),
+	    SLE_VAR(CargoPayment, route_profit,    SLE_INT64),
+	    SLE_VAR(CargoPayment, visual_profit,   SLE_INT64),
+	SLE_CONDVAR(CargoPayment, visual_transfer, SLE_INT64, CARGOMAP_SV, SL_MAX_VERSION),
+	    SLE_END()
 };
 
 static void Save_CAPY()
diff --git a/src/saveload/linkgraph_sl.cpp b/src/saveload/linkgraph_sl.cpp
new file mode 100644
index 0000000..19240cb
--- /dev/null
+++ b/src/saveload/linkgraph_sl.cpp
@@ -0,0 +1,147 @@
+/** @file linkgraph_sl.cpp Code handling saving and loading of link graphs */
+
+#include "../linkgraph/linkgraph.h"
+#include "../linkgraph/demands.h"
+#include "../settings_internal.h"
+#include "saveload.h"
+#include <vector>
+
+const SettingDesc *GetSettingDescription(uint index);
+
+static uint32 _num_components;
+static Date _join_date;
+
+enum {
+	LGRP_GRAPH = 0,
+	LGRP_COMPONENT = 1,
+	LGRP_NODE = 2,
+	LGRP_EDGE = 3,
+};
+
+const SaveLoad * GetLinkGraphComponentDesc() {
+
+	static const SaveLoad _component_desc[] = {
+		 SLE_CONDVAR(LinkGraphComponent, num_nodes,        SLE_UINT32, LINKGRAPH_SV, SL_MAX_VERSION),
+		 SLE_CONDVAR(LinkGraphComponent, index,            SLE_UINT16, LINKGRAPH_SV, SL_MAX_VERSION),
+		SLEG_CONDVAR(                    _join_date,       SLE_INT32,  LINKGRAPH_SV, SL_MAX_VERSION),
+		 SLE_END()
+	};
+
+	size_t offset_gamesettings = cpp_offsetof(GameSettings, linkgraph);
+	size_t offset_component = cpp_offsetof(LinkGraphComponent, settings);
+
+	typedef std::vector<SaveLoad> SaveLoadVector;
+	static SaveLoadVector saveloads;
+	static const char * prefix = "linkgraph.";
+	size_t prefixlen = strlen(prefix);
+
+	int setting = 0;
+	const SettingDesc * desc = GetSettingDescription(setting);
+	while (desc->save.cmd != SL_END) {
+		if (desc->desc.name != NULL && strncmp(desc->desc.name, prefix, prefixlen) == 0) {
+			SaveLoad sl = desc->save;
+			char *& address = reinterpret_cast<char *&>(sl.address);
+			address -= offset_gamesettings;
+			address += offset_component;
+			saveloads.push_back(sl);
+		}
+		desc = GetSettingDescription(++setting);
+	}
+
+	int i = 0;
+	do {
+		saveloads.push_back(_component_desc[i++]);
+	} while (saveloads.back().cmd != SL_END);
+
+	return &saveloads[0];
+}
+
+const SaveLoad * GetLinkGraphDesc(uint type) {
+
+	static const SaveLoad _linkgraph_desc[] = {
+		SLEG_CONDVAR(           _num_components,      SLE_UINT32, LINKGRAPH_SV, SL_MAX_VERSION),
+		 SLE_CONDVAR(LinkGraph, current_component_id, SLE_UINT16, LINKGRAPH_SV, SL_MAX_VERSION),
+		 SLE_CONDVAR(LinkGraph, current_station_id,   SLE_UINT16, LINKGRAPH_SV, SL_MAX_VERSION),
+		 SLE_CONDVAR(LinkGraph, cargo,                SLE_UINT8,  LINKGRAPH_SV, SL_MAX_VERSION),
+		 SLE_END()
+	};
+
+	static const SaveLoad * _component_desc = GetLinkGraphComponentDesc();
+
+	// edges and nodes are saved in the correct order, so we don't need to save their ids.
+
+	static const SaveLoad _node_desc[] = {
+		 SLE_CONDVAR(Node, supply,    SLE_UINT32, LINKGRAPH_SV, SL_MAX_VERSION),
+		 SLE_CONDVAR(Node, demand,    SLE_UINT32, LINKGRAPH_SV, SL_MAX_VERSION),
+		 SLE_CONDVAR(Node, station,   SLE_UINT16, LINKGRAPH_SV, SL_MAX_VERSION),
+		 SLE_END()
+	};
+
+	static const SaveLoad _edge_desc[] = {
+		 SLE_CONDVAR(Edge, distance,  SLE_UINT32, LINKGRAPH_SV, SL_MAX_VERSION),
+		 SLE_CONDVAR(Edge, capacity,  SLE_UINT32, LINKGRAPH_SV, SL_MAX_VERSION),
+		 SLE_CONDVAR(Edge, next_edge, SLE_UINT32,       MCF_SV, SL_MAX_VERSION),
+		 SLE_END()
+	};
+
+	static const SaveLoad *_lgrp_descs[] = {
+		_linkgraph_desc,
+		_component_desc,
+		_node_desc,
+		_edge_desc,
+	};
+
+	return _lgrp_descs[type];
+
+}
+
+static void SaveLoad_LinkGraphComponent(LinkGraphComponent * comp) {
+	for (NodeID from = 0; from < comp->GetSize(); ++from) {
+		Node * node = &comp->GetNode(from);
+		SlObject(node, GetLinkGraphDesc(LGRP_NODE));
+		node->undelivered_supply = node->supply;
+		for (NodeID to = 0; to < comp->GetSize(); ++to) {
+			SlObject(&comp->GetEdge(from, to), GetLinkGraphDesc(LGRP_EDGE));
+		}
+	}
+}
+
+static void DoSave_LGRP(void *)
+{
+	for(CargoID cargo = CT_BEGIN; cargo != CT_END; ++cargo) {
+		LinkGraph & graph = _link_graphs[cargo];
+		_num_components = (uint32)graph.GetNumJobs();
+		SlObject(&graph, GetLinkGraphDesc(LGRP_GRAPH));
+		JobList & jobs = graph.GetJobs();
+		for (JobList::iterator i = jobs.begin(); i != jobs.end(); ++i) {
+			LinkGraphJob * job = *i;
+			LinkGraphComponent * comp = job->GetComponent();
+			_join_date = job->GetJoinDate();
+			SlObject(comp, GetLinkGraphDesc(LGRP_COMPONENT));
+			SaveLoad_LinkGraphComponent(comp);
+		}
+	}
+}
+
+static void Load_LGRP()
+{
+	for(CargoID cargo = CT_BEGIN; cargo != CT_END; ++cargo) {
+		LinkGraph & graph = _link_graphs[cargo];
+		SlObject(&graph, GetLinkGraphDesc(LGRP_GRAPH));
+		for (uint32 i = 0; i < _num_components; ++i) {
+			LinkGraphComponent * comp = new LinkGraphComponent(cargo);
+			SlObject(comp, GetLinkGraphDesc(LGRP_COMPONENT));
+			comp->SetSize(comp->GetSize());
+			SaveLoad_LinkGraphComponent(comp);
+			graph.AddComponent(comp, _join_date);
+		}
+	}
+}
+
+static void Save_LGRP() {
+	SlAutolength((AutolengthProc*)DoSave_LGRP, NULL);
+}
+
+extern const ChunkHandler _linkgraph_chunk_handlers[] = {
+	{ 'LGRP', Save_LGRP,      Load_LGRP,	NULL,      CH_LAST},
+};
diff --git a/src/saveload/misc_sl.cpp b/src/saveload/misc_sl.cpp
index 06775d4..8b244a9 100644
--- a/src/saveload/misc_sl.cpp
+++ b/src/saveload/misc_sl.cpp
@@ -52,7 +52,7 @@ void ResetViewportAfterLoadGame()
 	w->viewport->dest_scrollpos_y = _saved_scrollpos_y;
 
 	ViewPort *vp = w->viewport;
-	vp->zoom = (ZoomLevel)min(_saved_scrollpos_zoom, ZOOM_LVL_MAX);
+	vp->zoom = (ZoomLevel)Clamp(_saved_scrollpos_zoom, ZOOM_LVL_BLITTER_MIN, ZOOM_LVL_BLITTER_MAX);
 	vp->virtual_width = ScaleByZoom(vp->width, vp->zoom);
 	vp->virtual_height = ScaleByZoom(vp->height, vp->zoom);
 
diff --git a/src/saveload/oldloader_sl.cpp b/src/saveload/oldloader_sl.cpp
index a78a452..b765890 100644
--- a/src/saveload/oldloader_sl.cpp
+++ b/src/saveload/oldloader_sl.cpp
@@ -698,7 +698,7 @@ static bool LoadOldGood(LoadgameState *ls, int num)
 	SB(ge->acceptance_pickup, GoodsEntry::ACCEPTANCE, 1, HasBit(_waiting_acceptance, 15));
 	SB(ge->acceptance_pickup, GoodsEntry::PICKUP, 1, _cargo_source != 0xFF);
 	if (GB(_waiting_acceptance, 0, 12) != 0) {
-		ge->cargo.Append(new CargoPacket(GB(_waiting_acceptance, 0, 12), _cargo_days, (_cargo_source == 0xFF) ? INVALID_STATION : _cargo_source, 0, 0));
+		ge->cargo.Append(INVALID_STATION, new CargoPacket(GB(_waiting_acceptance, 0, 12), _cargo_days, (_cargo_source == 0xFF) ? INVALID_STATION : _cargo_source, 0, 0));
 	}
 
 	return true;
diff --git a/src/saveload/saveload.cpp b/src/saveload/saveload.cpp
index 5b4637c..21d93e4 100644
--- a/src/saveload/saveload.cpp
+++ b/src/saveload/saveload.cpp
@@ -47,7 +47,7 @@
 
 #include "saveload_internal.h"
 
-extern const uint16 SAVEGAME_VERSION = 136;
+extern const uint16 SAVEGAME_VERSION = CARGOMAP_SV;
 
 SavegameType _savegame_type; ///< type of savegame we are loading
 
@@ -129,6 +129,7 @@ extern const ChunkHandler _group_chunk_handlers[];
 extern const ChunkHandler _cargopacket_chunk_handlers[];
 extern const ChunkHandler _autoreplace_chunk_handlers[];
 extern const ChunkHandler _labelmaps_chunk_handlers[];
+extern const ChunkHandler _linkgraph_chunk_handlers[];
 
 static const ChunkHandler * const _chunk_handlers[] = {
 	_gamelog_chunk_handlers,
@@ -156,6 +157,7 @@ static const ChunkHandler * const _chunk_handlers[] = {
 	_cargopacket_chunk_handlers,
 	_autoreplace_chunk_handlers,
 	_labelmaps_chunk_handlers,
+	_linkgraph_chunk_handlers,
 	NULL,
 };
 
diff --git a/src/saveload/saveload.h b/src/saveload/saveload.h
index 91ccf34..b964d76 100644
--- a/src/saveload/saveload.h
+++ b/src/saveload/saveload.h
@@ -326,4 +326,12 @@ bool SlObjectMember(void *object, const SaveLoad *sld);
 
 extern char _savegame_format[8];
 
+#define CAPACITIES_SV 150
+#define LINKGRAPH_SV 155
+#define DEMANDS_SV 160
+#define MCF_SV 165
+#define FLOWMAP_SV 170
+#define RESERVATION_SV 240
+#define CARGOMAP_SV 250
+
 #endif /* SAVELOAD_H */
diff --git a/src/saveload/station_sl.cpp b/src/saveload/station_sl.cpp
index a25eb55..0262c35 100644
--- a/src/saveload/station_sl.cpp
+++ b/src/saveload/station_sl.cpp
@@ -206,9 +206,11 @@ static const SaveLoad _old_station_desc[] = {
 };
 
 static uint16 _waiting_acceptance;
+static uint16 _num_links;
+static uint32 _num_flows;
 static uint16 _cargo_source;
 static uint32 _cargo_source_xy;
-static uint16 _cargo_days;
+static uint8  _cargo_days;
 static Money  _cargo_feeder_share;
 
 static const SaveLoad _station_speclist_desc[] = {
@@ -218,6 +220,34 @@ static const SaveLoad _station_speclist_desc[] = {
 	SLE_END()
 };
 
+static StationID _station_id;
+
+static const SaveLoad _linkstat_desc[] = {
+		SLEG_CONDVAR(             _station_id,         SLE_UINT16,      CAPACITIES_SV, SL_MAX_VERSION),
+		 SLE_CONDVAR(LinkStat,    capacity,            SLE_UINT32,      CAPACITIES_SV, SL_MAX_VERSION),
+		 SLE_CONDVAR(LinkStat,    frozen,              SLE_UINT32,      CAPACITIES_SV, SL_MAX_VERSION),
+		 SLE_CONDVAR(LinkStat,    usage,               SLE_UINT32,      CAPACITIES_SV, SL_MAX_VERSION),
+		 SLE_END()
+};
+
+static const SaveLoad _flowstat_desc[] = {
+		SLEG_CONDVAR(             _station_id,         SLE_UINT16,         FLOWMAP_SV, SL_MAX_VERSION),
+		 SLE_CONDVAR(FlowStat,    via,                 SLE_UINT16,         FLOWMAP_SV, SL_MAX_VERSION),
+		 SLE_CONDVAR(FlowStat,    planned,             SLE_UINT32,         FLOWMAP_SV, SL_MAX_VERSION),
+		 SLE_CONDVAR(FlowStat,    sent,                SLE_UINT32,         FLOWMAP_SV, SL_MAX_VERSION),
+		 SLE_END()
+};
+
+void CountFlows(FlowStatMap & flows) {
+	_num_flows = 0;
+	for(FlowStatMap::iterator i = flows.begin(); i != flows.end(); ++i) {
+		_num_flows += (uint32)i->second.size();
+	}
+}
+
+std::list<CargoPacket *> _packets;
+uint32 _num_dests;
+
 /**
  * Wrapper function to get the GoodsEntry's internal structure while
  * some of the variables itself are private.
@@ -239,14 +269,43 @@ const SaveLoad *GetGoodsDesc()
 		     SLE_VAR(GoodsEntry, last_age,            SLE_UINT8),
 		SLEG_CONDVAR(            _cargo_feeder_share, SLE_FILE_U32 | SLE_VAR_I64, 14, 64),
 		SLEG_CONDVAR(            _cargo_feeder_share, SLE_INT64,                  65, 67),
-		 SLE_CONDLST(GoodsEntry, cargo.packets,       REF_CARGO_PACKET,           68, SL_MAX_VERSION),
-
+		SLEG_CONDLST(            _packets,            REF_CARGO_PACKET,           68, CARGOMAP_SV - 1),
+		SLEG_CONDVAR(            _num_dests,          SLE_UINT32,        CARGOMAP_SV, SL_MAX_VERSION),
+		 SLE_CONDVAR(GoodsEntry, supply,              SLE_UINT32,      CAPACITIES_SV, SL_MAX_VERSION),
+		SLEG_CONDVAR(            _num_links,          SLE_UINT16,      CAPACITIES_SV, SL_MAX_VERSION),
+		SLEG_CONDVAR(            _num_flows,          SLE_UINT32,         FLOWMAP_SV, SL_MAX_VERSION),
+		 SLE_CONDVAR(GoodsEntry, last_component,      SLE_UINT16,       LINKGRAPH_SV, SL_MAX_VERSION),
 		SLE_END()
 	};
 
 	return goods_desc;
 }
 
+typedef std::pair<const StationID, std::list<CargoPacket *> > StationCargoPair;
+
+static const SaveLoad _cargo_list_desc[] = {
+	SLE_VAR(StationCargoPair, first,  SLE_UINT16),
+	SLE_LST(StationCargoPair, second, REF_CARGO_PACKET),
+	SLE_END()
+};
+
+static void SwapPackets(GoodsEntry *ge)
+{
+	StationCargoPacketMap &ge_packets = const_cast<StationCargoPacketMap &>(*ge->cargo.Packets());
+
+	if (_packets.empty()) {
+		std::map<StationID, std::list<CargoPacket *> >::iterator it  = ge_packets.find(INVALID_STATION);
+		if (it == ge_packets.end()) {
+			return;
+		} else {
+			it->second.swap(_packets);
+		}
+	} else {
+		std::list<CargoPacket *> &list = ge_packets[INVALID_STATION];
+		assert(list.empty());
+		list.swap(_packets);
+	}
+}
 
 static void Load_STNS()
 {
@@ -262,6 +321,7 @@ static void Load_STNS()
 		for (CargoID i = 0; i < num_cargo; i++) {
 			GoodsEntry *ge = &st->goods[i];
 			SlObject(ge, GetGoodsDesc());
+			SwapPackets(ge);
 			if (CheckSavegameVersion(68)) {
 				SB(ge->acceptance_pickup, GoodsEntry::ACCEPTANCE, 1, HasBit(_waiting_acceptance, 15));
 				if (GB(_waiting_acceptance, 0, 12) != 0) {
@@ -269,7 +329,8 @@ static void Load_STNS()
 					StationID source = (CheckSavegameVersion(7) && _cargo_source == 0xFF) ? INVALID_STATION : _cargo_source;
 
 					/* Don't construct the packet with station here, because that'll fail with old savegames */
-					ge->cargo.Append(new CargoPacket(GB(_waiting_acceptance, 0, 12), _cargo_days, source, _cargo_source_xy, _cargo_source_xy, _cargo_feeder_share));
+					CargoPacket *cp = new CargoPacket(GB(_waiting_acceptance, 0, 12), _cargo_days, source, _cargo_source_xy, _cargo_source_xy, _cargo_feeder_share);
+					ge->cargo.Append(INVALID_STATION, cp);
 					SB(ge->acceptance_pickup, GoodsEntry::PICKUP, 1, 1);
 				}
 			}
@@ -295,7 +356,9 @@ static void Ptrs_STNS()
 		if (!CheckSavegameVersion(68)) {
 			for (CargoID i = 0; i < NUM_CARGO; i++) {
 				GoodsEntry *ge = &st->goods[i];
+				SwapPackets(ge);
 				SlObject(ge, GetGoodsDesc());
+				SwapPackets(ge);
 			}
 		}
 		SlObject(st, _old_station_desc);
@@ -378,7 +441,29 @@ static void RealSave_STNN(BaseStation *bst)
 	if (!waypoint) {
 		Station *st = Station::From(bst);
 		for (CargoID i = 0; i < NUM_CARGO; i++) {
-			SlObject(&st->goods[i], GetGoodsDesc());
+			GoodsEntry *ge = &st->goods[i];
+			_num_dests = (uint32)ge->cargo.Packets()->MapSize();
+			LinkStatMap &stats = ge->link_stats;
+			_num_links = (uint16)stats.size();
+			FlowStatMap & flows = ge->flows;
+			CountFlows(flows);
+			SlObject(ge, GetGoodsDesc());
+			for (LinkStatMap::iterator i = stats.begin(); i != stats.end(); ++i) {
+				_station_id = i->first;
+				assert(i->second.capacity > 0);
+				SlObject(&(i->second), _linkstat_desc);
+			}
+			for (FlowStatMap::iterator i = flows.begin(); i != flows.end(); ++i) {
+				_station_id = i->first;
+				FlowStatSet & flow_set = i->second;
+				for (FlowStatSet::iterator j = flow_set.begin(); j != flow_set.end(); ++j) {
+					FlowStat fs = *j;
+					SlObject(&fs, _flowstat_desc);
+				}
+			}
+			for (StationCargoPacketMap::ConstMapIterator it = ge->cargo.Packets()->begin(); it != ge->cargo.Packets()->end(); ++it) {
+				SlObject(const_cast<StationCargoPacketMap::value_type *>(&(*it)), _cargo_list_desc);
+			}
 		}
 	}
 
@@ -410,7 +495,32 @@ static void Load_STNN()
 		if (!waypoint) {
 			Station *st = Station::From(bst);
 			for (CargoID i = 0; i < NUM_CARGO; i++) {
-				SlObject(&st->goods[i], GetGoodsDesc());
+				GoodsEntry *ge = &st->goods[i];
+				LinkStatMap &stats = ge->link_stats;
+				FlowStatMap & flows = ge->flows;
+				SlObject(ge, GetGoodsDesc());
+
+				LinkStat ls;
+				for (uint16 i = 0; i < _num_links; ++i) {
+					SlObject(&ls, _linkstat_desc);
+					assert(ls.capacity > 0);
+					stats[_station_id] = ls;
+				}
+				FlowStat fs;
+				for (uint32 i = 0; i < _num_flows; ++i) {
+					SlObject(&fs, _flowstat_desc);
+					flows[_station_id].insert(fs);
+				}
+				if (CheckSavegameVersion(CARGOMAP_SV -1)) {
+					SwapPackets(ge);
+				} else {
+					StationCargoPair pair;
+					for(uint i = 0; i < _num_dests; ++i) {
+						SlObject(&pair, _cargo_list_desc);
+						const_cast<StationCargoPacketMap &>(*ge->cargo.Packets())[pair.first].swap(pair.second);
+						assert(pair.second.empty());
+					}
+				}
 			}
 		}
 
@@ -433,7 +543,17 @@ static void Ptrs_STNN()
 	FOR_ALL_STATIONS(st) {
 		for (CargoID i = 0; i < NUM_CARGO; i++) {
 			GoodsEntry *ge = &st->goods[i];
-			SlObject(ge, GetGoodsDesc());
+			if (CheckSavegameVersion(CARGOMAP_SV)) {
+				SwapPackets(ge);
+				SlObject(ge, GetGoodsDesc());
+				SwapPackets(ge);
+			} else {
+				SlObject(ge, GetGoodsDesc());
+				for (StationCargoPacketMap::ConstMapIterator it = ge->cargo.Packets()->begin(); it != ge->cargo.Packets()->end(); ++it) {
+					SlObject(const_cast<StationCargoPair *>(&(*it)), _cargo_list_desc);
+				}
+			}
+			// as there are no pointers in the link stats we don't have to consider them
 		}
 		SlObject(st, _station_desc);
 	}
diff --git a/src/saveload/vehicle_sl.cpp b/src/saveload/vehicle_sl.cpp
index e787420..bfd74ee 100644
--- a/src/saveload/vehicle_sl.cpp
+++ b/src/saveload/vehicle_sl.cpp
@@ -446,6 +446,7 @@ const SaveLoad *GetVehicleDescription(VehicleType vt)
 		     SLE_VAR(Vehicle, cargo_cap,             SLE_UINT16),
 		SLEG_CONDVAR(         _cargo_count,          SLE_UINT16,                   0,  67),
 		 SLE_CONDLST(Vehicle, cargo.packets,         REF_CARGO_PACKET,            68, SL_MAX_VERSION),
+		 SLE_CONDLST(Vehicle, cargo.reserved,        REF_CARGO_PACKET,RESERVATION_SV, SL_MAX_VERSION),
 
 		     SLE_VAR(Vehicle, day_counter,           SLE_UINT8),
 		     SLE_VAR(Vehicle, tick_counter,          SLE_UINT8),
diff --git a/src/settings.cpp b/src/settings.cpp
index d4980b0..0b276dc 100644
--- a/src/settings.cpp
+++ b/src/settings.cpp
@@ -64,6 +64,7 @@
 
 #include "void_map.h"
 #include "station_base.h"
+#include "infrastructure_func.h"
 
 #include "table/strings.h"
 #include "table/settings.h"
@@ -983,6 +984,28 @@ static bool StationCatchmentChanged(int32 p1)
 	return true;
 }
 
+static bool CheckSharingRail(int32 p1)
+{
+	if (!CheckSharingChangePossible(VEH_TRAIN)) return false;
+	UpdateAllBlockSignals();
+	return true;
+}
+
+static bool CheckSharingRoad(int32 p1)
+{
+	return CheckSharingChangePossible(VEH_ROAD);
+}
+
+static bool CheckSharingWater(int32 p1)
+{
+	return CheckSharingChangePossible(VEH_SHIP);
+}
+
+static bool CheckSharingAir(int32 p1)
+{
+	return CheckSharingChangePossible(VEH_AIRCRAFT);
+}
+
 #ifdef ENABLE_NETWORK
 
 static bool UpdateClientName(int32 p1)
@@ -1408,7 +1431,7 @@ void DeleteGRFPresetFromConfig(const char *config_name)
 	delete ini;
 }
 
-static const SettingDesc *GetSettingDescription(uint index)
+const SettingDesc *GetSettingDescription(uint index)
 {
 	if (index >= lengthof(_settings)) return NULL;
 	return &_settings[index];
diff --git a/src/settings_gui.cpp b/src/settings_gui.cpp
index 9f35482..cd20e15 100644
--- a/src/settings_gui.cpp
+++ b/src/settings_gui.cpp
@@ -53,6 +53,8 @@ static const StringID _driveside_dropdown[] = {
 
 static const StringID _autosave_dropdown[] = {
 	STR_GAME_OPTIONS_AUTOSAVE_DROPDOWN_OFF,
+	STR_GAME_OPTIONS_AUTOSAVE_DROPDOWN_EVERY_1_DAY,
+	STR_GAME_OPTIONS_AUTOSAVE_DROPDOWN_EVERY_1_WEEK,
 	STR_GAME_OPTIONS_AUTOSAVE_DROPDOWN_EVERY_1_MONTH,
 	STR_GAME_OPTIONS_AUTOSAVE_DROPDOWN_EVERY_3_MONTHS,
 	STR_GAME_OPTIONS_AUTOSAVE_DROPDOWN_EVERY_6_MONTHS,
@@ -1354,6 +1356,7 @@ static SettingEntry _settings_economy_towns[] = {
 	SettingEntry("economy.town_growth_rate"),
 	SettingEntry("economy.larger_towns"),
 	SettingEntry("economy.initial_city_size"),
+	SettingEntry("economy.town_cargo_factor"),
 };
 /** Towns sub-page */
 static SettingsPage _settings_economy_towns_page = {_settings_economy_towns, lengthof(_settings_economy_towns)};
@@ -1372,11 +1375,29 @@ static SettingEntry _settings_economy[] = {
 	SettingEntry(&_settings_economy_industries_page, STR_CONFIG_SETTING_ECONOMY_INDUSTRIES),
 	SettingEntry("economy.inflation"),
 	SettingEntry("economy.smooth_economy"),
+	SettingEntry("economy.moving_average_unit"),
+	SettingEntry("economy.moving_average_length"),
 	SettingEntry("economy.feeder_payment_share"),
+	SettingEntry("daylength_factor"),
 };
 /** Economy sub-page */
 static SettingsPage _settings_economy_page = {_settings_economy, lengthof(_settings_economy)};
 
+static SettingEntry _settings_linkgraph[] = {
+	SettingEntry("linkgraph.recalc_interval"),
+	SettingEntry("linkgraph.demand_pax"),
+	SettingEntry("linkgraph.demand_mail"),
+	SettingEntry("linkgraph.demand_express"),
+	SettingEntry("linkgraph.demand_armoured"),
+	SettingEntry("linkgraph.demand_default"),
+	SettingEntry("linkgraph.accuracy"),
+	SettingEntry("linkgraph.demand_distance"),
+	SettingEntry("linkgraph.demand_size"),
+	SettingEntry("linkgraph.short_path_saturation"),
+};
+/** Linkgraph sub-page */
+static SettingsPage _settings_linkgraph_page = {_settings_linkgraph, lengthof(_settings_linkgraph)};
+
 static SettingEntry _settings_ai_npc[] = {
 	SettingEntry("ai.ai_in_multiplayer"),
 	SettingEntry("ai.ai_disable_veh_train"),
@@ -1388,8 +1409,23 @@ static SettingEntry _settings_ai_npc[] = {
 /** Computer players sub-page */
 static SettingsPage _settings_ai_npc_page = {_settings_ai_npc, lengthof(_settings_ai_npc)};
 
+static SettingEntry _settings_sharing[] = {
+	SettingEntry("sharing.sharing_rail"),
+	SettingEntry("sharing.sharing_road"),
+	SettingEntry("sharing.sharing_water"),
+	SettingEntry("sharing.sharing_air"),
+	SettingEntry("sharing.fee_rail"),
+	SettingEntry("sharing.fee_road"),
+	SettingEntry("sharing.fee_water"),
+	SettingEntry("sharing.fee_air"),
+	SettingEntry("sharing.payment_in_debt"),
+};
+/** Infrastructure sharing sub-page */
+static SettingsPage _settings_sharing_page = {_settings_sharing, lengthof(_settings_sharing)};
+
 static SettingEntry _settings_ai[] = {
 	SettingEntry(&_settings_ai_npc_page, STR_CONFIG_SETTING_AI_NPC),
+	SettingEntry(&_settings_sharing_page, STR_CONFIG_SETTING_SHARING),
 	SettingEntry("economy.give_money"),
 	SettingEntry("economy.allow_shares"),
 };
@@ -1466,6 +1502,7 @@ static SettingEntry _settings_main[] = {
 	SettingEntry(&_settings_vehicles_page,     STR_CONFIG_SETTING_VEHICLES),
 	SettingEntry(&_settings_stations_page,     STR_CONFIG_SETTING_STATIONS),
 	SettingEntry(&_settings_economy_page,      STR_CONFIG_SETTING_ECONOMY),
+	SettingEntry(&_settings_linkgraph_page,    STR_CONFIG_SETTING_LINKGRAPH),
 	SettingEntry(&_settings_ai_page,           STR_CONFIG_SETTING_AI),
 };
 
diff --git a/src/settings_type.h b/src/settings_type.h
index 5fef70d..d3a75b0 100644
--- a/src/settings_type.h
+++ b/src/settings_type.h
@@ -17,6 +17,7 @@
 #include "transport_type.h"
 #include "network/core/config.h"
 #include "company_type.h"
+#include "linkgraph/demand_settings.h"
 
 /** Settings related to the difficulty of the game */
 struct DifficultySettings {
@@ -100,6 +101,10 @@ struct GUISettings {
 
 	uint16 console_backlog_timeout;          ///< the minimum amount of time items should be in the console backlog before they will be removed in ~3 seconds granularity.
 	uint16 console_backlog_length;           ///< the minimum amount of items in the console backlog before items will be removed.
+
+	uint8  station_gui_group_order;          ///< the order of grouping cargo entries in the station gui
+	uint8  station_gui_sort_by;              ///< sort cargo entries in the station gui by station name or amount
+	uint8  station_gui_sort_order;           ///< the sort order of entries in the station gui - ascending or descending
 #ifdef ENABLE_NETWORK
 	uint16 network_chat_box_width;           ///< width of the chat box in pixels
 	uint8  network_chat_box_height;          ///< height of the chat box in lines
@@ -343,6 +348,22 @@ struct EconomySettings {
 	TownFoundingByte found_town;             ///< town founding, @see TownFounding
 	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)
+	uint16 moving_average_unit;              ///< unit of time to use for calculating the moving average of capacities and usage of links
+	uint16 moving_average_length;            ///< length of the moving average for capacities and usage of links
+	int8   town_cargo_factor;                ///< power-of-two multiplier for town (passenger, mail) generation. May be negative.
+};
+
+struct LinkGraphSettings {
+	uint16 recalc_interval;                  ///< minimum interval (in days) between subsequent recalculations of the same component of the link graph
+	DistributionTypeByte demand_pax;         ///< demand calculation for passengers
+	DistributionTypeByte demand_mail;        ///< demand calculation for mail
+	DistributionTypeByte demand_express;     ///< demand calculation for express cargo class
+	DistributionTypeByte demand_armoured;    ///< demand calculation for armoured cargo class
+	DistributionTypeByte demand_default;     ///< demand calculation for all other goods
+	uint8 accuracy;                          ///< accuracy when calculating things on the link graph. low accuracy => low running time
+	uint8 demand_size;                       ///< influence of supply ("station size") on the demand function
+	uint8 demand_distance;                   ///< influence of distance between stations on the demand function
+	uint8 short_path_saturation;             ///< percentage up to which short paths are saturated before saturating most capacious paths
 };
 
 /** Settings related to stations. */
@@ -356,6 +377,19 @@ struct StationSettings {
 	byte   station_spread;                   ///< amount a station may spread
 };
 
+/** Settings related to infrastructure sharing */
+struct SharingSettings {
+	bool   sharing_rail;                     ///< Enable infrastructure sharing for rails, including stations and depots
+	bool   sharing_road;                     ///< Enable infrastructure sharing for road stops and depots
+	bool   sharing_water;                    ///< Enable infrastructure sharing for docks and ship depots
+	bool   sharing_air;                      ///< Enable infrastructure sharing for airports
+	uint   fee_rail;                         ///< Track toll for trains
+	uint   fee_road;                         ///< Loading fee for road vehicles
+	uint   fee_water;                        ///< Loading fee for aircraft
+	uint   fee_air;                          ///< Loading fee for aircraft
+	bool   payment_in_debt;                  ///< Whether to allow fee payment for companies with more loan than money. Switch off to prevent MP exploits.
+};
+
 /** Default settings for vehicles. */
 struct VehicleDefaultSettings {
 	bool   servint_ispercent;                ///< service intervals are in percents
@@ -385,8 +419,10 @@ struct GameSettings {
 	OrderSettings        order;              ///< settings related to orders
 	VehicleSettings      vehicle;            ///< options for vehicles
 	EconomySettings      economy;            ///< settings to change the economy
+	LinkGraphSettings    linkgraph;          ///< settings for link graph calculations
 	StationSettings      station;            ///< settings related to station management
 	LocaleSettings       locale;             ///< settings related to used currency/unit system in the current game
+	SharingSettings      sharing;            ///< settings related to infrastructure sharing
 };
 
 /** All settings that are only important for the local client. */
diff --git a/src/ship_cmd.cpp b/src/ship_cmd.cpp
index acd5754..0078fd5 100644
--- a/src/ship_cmd.cpp
+++ b/src/ship_cmd.cpp
@@ -36,6 +36,7 @@
 #include "ai/ai.hpp"
 #include "pathfinder/opf/opf_ship.h"
 #include "landscape_type.h"
+#include "infrastructure_func.h"
 
 #include "table/strings.h"
 #include "table/sprites.h"
@@ -111,7 +112,7 @@ static const Depot *FindClosestShipDepot(const Vehicle *v, uint max_distance)
 
 	FOR_ALL_DEPOTS(depot) {
 		TileIndex tile = depot->xy;
-		if (IsShipDepotTile(tile) && IsTileOwner(tile, v->owner)) {
+		if (IsShipDepotTile(tile) && IsInfraTileUsageAllowed(tile, v->owner, VEH_SHIP)) {
 			uint dist = DistanceManhattan(tile, v->tile);
 			if (dist < best_dist) {
 				best_dist = dist;
@@ -514,13 +515,14 @@ static void ShipController(Ship *v)
 									return;
 								}
 							} else if (v->current_order.IsType(OT_GOTO_STATION)) {
+								StationID previous_station = v->last_station_visited;
 								v->last_station_visited = v->current_order.GetDestination();
 
 								/* Process station in the orderlist. */
 								Station *st = Station::Get(v->current_order.GetDestination());
 								if (st->facilities & FACIL_DOCK) { // ugly, ugly workaround for problem with ships able to drop off cargo at wrong stations
 									ShipArrivesAt(v, st);
-									v->BeginLoading();
+									v->BeginLoading(previous_station);
 								} else { // leave stations without docks right aways
 									v->current_order.MakeLeaveStation();
 									v->IncrementOrderIndex();
@@ -623,7 +625,7 @@ CommandCost CmdBuildShip(TileIndex tile, DoCommandFlag flags, uint32 p1, uint32
 	/* The ai_new queries the vehicle cost before building the route,
 	 * so we must check against cheaters no sooner than now. --pasky */
 	if (!IsShipDepotTile(tile)) return CMD_ERROR;
-	if (!IsTileOwner(tile, _current_company)) return CMD_ERROR;
+	if (!CheckInfraUsageAllowed(GetTileOwner(tile), VEH_SHIP)) return CMD_ERROR;
 
 	unit_num = (flags & DC_AUTOREPLACE) ? 0 : GetFreeUnitNumber(VEH_SHIP);
 
diff --git a/src/signal.cpp b/src/signal.cpp
index b85c386..b3337ae 100644
--- a/src/signal.cpp
+++ b/src/signal.cpp
@@ -16,6 +16,7 @@
 #include "vehicle_func.h"
 #include "functions.h"
 #include "train.h"
+#include "infrastructure_func.h"
 
 
 /** these are the maximums used for updating signal blocks */
@@ -278,7 +279,7 @@ static SigFlags ExploreSegment(Owner owner)
 
 		switch (GetTileType(tile)) {
 			case MP_RAILWAY: {
-				if (GetTileOwner(tile) != owner) continue; // do not propagate signals on others' tiles (remove for tracksharing)
+				if (!IsOneSignalBlock(owner, GetTileOwner(tile))) continue;
 
 				if (IsRailDepot(tile)) {
 					if (enterdir == INVALID_DIAGDIR) { // from 'inside' - train just entered or left the depot
@@ -351,7 +352,7 @@ static SigFlags ExploreSegment(Owner owner)
 
 			case MP_STATION:
 				if (!HasStationRail(tile)) continue;
-				if (GetTileOwner(tile) != owner) continue;
+				if (!IsOneSignalBlock(owner, GetTileOwner(tile))) continue;
 				if (DiagDirToAxis(enterdir) != GetRailStationAxis(tile)) continue; // different axis
 				if (IsStationTileBlocked(tile)) continue; // 'eye-candy' station tile
 
@@ -361,7 +362,7 @@ static SigFlags ExploreSegment(Owner owner)
 
 			case MP_ROAD:
 				if (!IsLevelCrossing(tile)) continue;
-				if (GetTileOwner(tile) != owner) continue;
+				if (!IsOneSignalBlock(owner, GetTileOwner(tile))) continue;
 				if (DiagDirToAxis(enterdir) == GetCrossingRoadAxis(tile)) continue; // different axis
 
 				if (!(flags & SF_TRAIN) && HasVehicleOnPos(tile, NULL, &TrainOnTileEnum)) flags |= SF_TRAIN;
@@ -369,7 +370,7 @@ static SigFlags ExploreSegment(Owner owner)
 				break;
 
 			case MP_TUNNELBRIDGE: {
-				if (GetTileOwner(tile) != owner) continue;
+				if (!IsOneSignalBlock(owner, GetTileOwner(tile))) continue;
 				if (GetTunnelBridgeTransportType(tile) != TRANSPORT_RAIL) continue;
 				DiagDirection dir = GetTunnelBridgeDirection(tile);
 
@@ -583,8 +584,9 @@ void AddTrackToSignalBuffer(TileIndex tile, Track track, Owner owner)
 		DIAGDIR_SW, DIAGDIR_NW, DIAGDIR_NW, DIAGDIR_SW, DIAGDIR_NW, DIAGDIR_NE
 	};
 
-	/* do not allow signal updates for two companies in one run */
-	assert(_globset.IsEmpty() || owner == _last_owner);
+	/* do not allow signal updates for two companies in one run,
+	 * if these companies are not part of the same signal block */
+	assert(_globset.IsEmpty() || IsOneSignalBlock(owner, _last_owner));
 
 	_last_owner = owner;
 
@@ -608,8 +610,9 @@ void AddTrackToSignalBuffer(TileIndex tile, Track track, Owner owner)
  */
 void AddSideToSignalBuffer(TileIndex tile, DiagDirection side, Owner owner)
 {
-	/* do not allow signal updates for two companies in one run */
-	assert(_globset.IsEmpty() || owner == _last_owner);
+	/* do not allow signal updates for two companies in one run,
+	 * if these companies are not part of the same signal block */
+	assert(_globset.IsEmpty() || IsOneSignalBlock(owner, _last_owner));
 
 	_last_owner = owner;
 
diff --git a/src/smallmap_gui.cpp b/src/smallmap_gui.cpp
index 5956af7..ab28bbf 100644
--- a/src/smallmap_gui.cpp
+++ b/src/smallmap_gui.cpp
@@ -26,32 +26,61 @@
 #include "vehicle_base.h"
 #include "sound_func.h"
 #include "window_func.h"
+#include "cargotype.h"
+#include "openttd.h"
+#include "company_func.h"
+#include "station_base.h"
+#include "zoom_func.h"
+#include "infrastructure_func.h"
 
 #include "table/strings.h"
 #include "table/sprites.h"
 
+#include <cmath>
+#include <vector>
+
 /** Widget numbers of the small map window. */
 enum SmallMapWindowWidgets {
 	SM_WIDGET_CAPTION,           ///< Caption widget.
 	SM_WIDGET_MAP_BORDER,        ///< Border around the smallmap.
 	SM_WIDGET_MAP,               ///< Panel containing the smallmap.
 	SM_WIDGET_LEGEND,            ///< Bottom panel to display smallmap legends.
+	SM_WIDGET_BLANK,
+	SM_WIDGET_ZOOM_IN,
+	SM_WIDGET_ZOOM_OUT,
 	SM_WIDGET_CONTOUR,           ///< Button to select the contour view (height map).
 	SM_WIDGET_VEHICLES,          ///< Button to select the vehicles view.
 	SM_WIDGET_INDUSTRIES,        ///< Button to select the industries view.
+	SM_WIDGET_LINKSTATS,
 	SM_WIDGET_ROUTES,            ///< Button to select the routes view.
 	SM_WIDGET_VEGETATION,        ///< Button to select the vegetation view.
 	SM_WIDGET_OWNERS,            ///< Button to select the owners view.
 	SM_WIDGET_CENTERMAP,         ///< Button to move smallmap center to main window center.
 	SM_WIDGET_TOGGLETOWNNAME,    ///< Toggle button to display town names.
 	SM_WIDGET_SELECTINDUSTRIES,  ///< Selection widget for the buttons at the industry mode.
-	SM_WIDGET_ENABLEINDUSTRIES,  ///< Button to enable display of all industries.
-	SM_WIDGET_DISABLEINDUSTRIES, ///< Button to disable display of all industries.
+	SM_WIDGET_ENABLE_ALL,        ///< Button to enable display of all industries or link stats.
+	SM_WIDGET_DISABLE_ALL,       ///< Button to disable display of all industries or link stats.
 	SM_WIDGET_SHOW_HEIGHT,       ///< Show heightmap toggle button.
 };
 
+
 static int _smallmap_industry_count; ///< Number of used industries
 
+/* number of cargos in the link stats legend */
+static int _smallmap_cargo_count;
+
+enum SmallMapStats {
+	STAT_CAPACITY,
+	STAT_BEGIN = STAT_CAPACITY,
+	STAT_USAGE,
+	STAT_PLANNED,
+	STAT_SENT,
+	STAT_TEXT,
+	STAT_GRAPH,
+	STAT_END,
+	NUM_STATS = STAT_END,
+};
+
 /** Macro for ordinary entry of LegendAndColour */
 #define MK(a, b) {a, b, INVALID_INDUSTRYTYPE, true, false, false}
 /** Macro for end of list marker in arrays of LegendAndColour */
@@ -142,8 +171,8 @@ static const LegendAndColour _legend_land_owners[] = {
 static LegendAndColour _legend_from_industries[NUM_INDUSTRYTYPES + 1];
 /* For connecting industry type to position in industries list(small map legend) */
 static uint _industry_to_list_pos[NUM_INDUSTRYTYPES];
-/** Show heightmap in industry mode of smallmap window. */
-static bool _smallmap_industry_show_heightmap;
+/** Show heightmap in smallmap window. */
+static bool _smallmap_show_heightmap;
 
 /**
  * Fills an array for the industries legends.
@@ -175,10 +204,81 @@ void BuildIndustriesLegend()
 	_smallmap_industry_count = j;
 }
 
+static LegendAndColour _legend_linkstats[NUM_CARGO + NUM_STATS + 1];
+
+/**
+ * Populate legend table for the route map view.
+ */
+void BuildLinkStatsLegend()
+{
+	/* Clear the legend */
+	memset(_legend_linkstats, 0, sizeof(_legend_linkstats));
+
+	uint i = 0;
+
+	for (CargoID c = CT_BEGIN; c != CT_END; ++c) {
+		const CargoSpec *cs = CargoSpec::Get(c);
+		if (!cs->IsValid()) continue;
+
+		_legend_linkstats[i].legend = cs->name;
+		_legend_linkstats[i].colour = cs->legend_colour;
+		_legend_linkstats[i].type = c;
+		_legend_linkstats[i].show_on_map = true;
+
+		i++;
+	}
+
+	_legend_linkstats[i].col_break = true;
+
+	_smallmap_cargo_count = i;
+
+	/* the colours cannot be resolved before the gfx system is initialized.
+	 * So we have to build the legend when creating the window.
+	 */
+	for (uint st = 0; st < NUM_STATS; ++st) {
+		LegendAndColour & legend_entry = _legend_linkstats[i + st];
+		switch(st) {
+		case STAT_CAPACITY:
+			legend_entry.colour = _colour_gradient[COLOUR_WHITE][7];
+			legend_entry.legend = STR_SMALLMAP_LEGENDA_CAPACITY;
+			legend_entry.show_on_map = true;
+			break;
+		case STAT_USAGE:
+			legend_entry.colour = _colour_gradient[COLOUR_GREY][1];
+			legend_entry.legend = STR_SMALLMAP_LEGENDA_USAGE;
+			legend_entry.show_on_map = false;
+			break;
+		case STAT_PLANNED:
+			legend_entry.colour = _colour_gradient[COLOUR_RED][5];
+			legend_entry.legend = STR_SMALLMAP_LEGENDA_PLANNED;
+			legend_entry.show_on_map = true;
+			break;
+		case STAT_SENT:
+			legend_entry.colour = _colour_gradient[COLOUR_YELLOW][5];
+			legend_entry.legend = STR_SMALLMAP_LEGENDA_SENT;
+			legend_entry.show_on_map = false;
+			break;
+		case STAT_TEXT:
+			legend_entry.colour = _colour_gradient[COLOUR_GREY][7];
+			legend_entry.legend = STR_SMALLMAP_LEGENDA_SHOW_TEXT;
+			legend_entry.show_on_map = false;
+			break;
+		case STAT_GRAPH:
+			legend_entry.colour = _colour_gradient[COLOUR_GREY][7];
+			legend_entry.legend = STR_SMALLMAP_LEGENDA_SHOW_GRAPH;
+			legend_entry.show_on_map = true;
+			break;
+		}
+	}
+
+	_legend_linkstats[i + NUM_STATS].end = true;
+}
+
 static const LegendAndColour * const _legend_table[] = {
 	_legend_land_contours,
 	_legend_vehicles,
 	_legend_from_industries,
+	_legend_linkstats,
 	_legend_routes,
 	_legend_vegetation,
 	_legend_land_owners,
@@ -316,7 +416,7 @@ static inline uint32 GetSmallMapIndustriesPixels(TileIndex tile)
 		}
 	}
 
-	return ApplyMask(_smallmap_industry_show_heightmap ? _map_height_bits[TileHeight(tile)] : MKCOLOUR(0x54545454), &_smallmap_vehicles_andor[t]);
+	return ApplyMask(_smallmap_show_heightmap ? _map_height_bits[TileHeight(tile)] : MKCOLOUR(0x54545454), &_smallmap_vehicles_andor[t]);
 }
 
 /**
@@ -344,6 +444,17 @@ static inline uint32 GetSmallMapRoutesPixels(TileIndex tile)
 	return ApplyMask(MKCOLOUR(0x54545454), &_smallmap_contours_andor[t]);
 }
 
+/**
+ * Return the colour a tile would be displayed with in the small map in mode "link stats".
+ *
+ * @param tile The tile of which we would like to get the colour.
+ * @return The colour of tile in the small map in mode "link stats"
+ */
+static inline uint32 GetSmallMapLinkStatsPixels(TileIndex tile)
+{
+	return _smallmap_show_heightmap ? GetSmallMapContoursPixels(tile) : GetSmallMapRoutesPixels(tile);
+}
+
 
 static const uint32 _vegetation_clear_bits[] = {
 	MKCOLOUR(0x54545454), ///< full grass
@@ -411,18 +522,6 @@ static inline uint32 GetSmallMapOwnerPixels(TileIndex tile)
 }
 
 
-static const uint32 _smallmap_mask_left[3] = {
-	MKCOLOUR(0xFF000000),
-	MKCOLOUR(0xFFFF0000),
-	MKCOLOUR(0xFFFFFF00),
-};
-
-static const uint32 _smallmap_mask_right[] = {
-	MKCOLOUR(0x000000FF),
-	MKCOLOUR(0x0000FFFF),
-	MKCOLOUR(0x00FFFFFF),
-};
-
 /* Each tile has 4 x pixels and 1 y pixel */
 
 /** Holds function pointers to determine tile colour in the smallmap for each smallmap mode. */
@@ -430,6 +529,7 @@ static GetSmallMapPixels * const _smallmap_draw_procs[] = {
 	GetSmallMapContoursPixels,
 	GetSmallMapVehiclesPixels,
 	GetSmallMapIndustriesPixels,
+	GetSmallMapLinkStatsPixels,
 	GetSmallMapRoutesPixels,
 	GetSmallMapVegetationPixels,
 	GetSmallMapOwnerPixels,
@@ -441,6 +541,22 @@ static const byte _vehicle_type_colours[6] = {
 };
 
 
+void DrawVertex(int x, int y, int size, int colour, int boder_colour)
+{
+	size--;
+	int w1 = size / 2;
+	int w2 = size / 2 + size % 2;
+
+	GfxFillRect(x - w1, y - w1, x + w2, y + w2, colour);
+
+	w1++;
+	w2++;
+	GfxDrawLine(x - w1, y - w1, x + w2, y - w1, boder_colour);
+	GfxDrawLine(x - w1, y + w2, x + w2, y + w2, boder_colour);
+	GfxDrawLine(x - w1, y - w1, x - w1, y + w2, boder_colour);
+	GfxDrawLine(x + w2, y - w1, x + w2, y + w2, boder_colour);
+}
+
 /** Class managing the smallmap window. */
 class SmallMapWindow : public Window {
 	/** Types of legends in the #SM_WIDGET_LEGEND widget. */
@@ -448,11 +564,30 @@ class SmallMapWindow : public Window {
 		SMT_CONTOUR,
 		SMT_VEHICLES,
 		SMT_INDUSTRY,
+		SMT_LINKSTATS,
 		SMT_ROUTES,
 		SMT_VEGETATION,
 		SMT_OWNER,
 	};
 
+	enum SmallmapWindowDistances {
+		SD_MAP_COLUMN_WIDTH = 4,
+		SD_MAP_ROW_OFFSET = 2,
+		SD_MAP_MIN_INDUSTRY_WIDTH = 3,
+	};
+
+	/**
+	 * save the Vehicle's old position here, so that we don't get glitches when redrawing
+	 */
+	struct VehicleAndPosition {
+		VehicleAndPosition(const Vehicle *v) : tile(v->tile), vehicle(v->index) {}
+		TileIndex tile;
+		VehicleID vehicle;
+	};
+
+	typedef std::list<VehicleAndPosition> VehicleList;
+	VehicleList vehicles_on_map;
+	
 	static SmallMapType map_type; ///< Currently displayed legends.
 	static bool show_towns;       ///< Display town names in the smallmap.
 
@@ -464,79 +599,231 @@ class SmallMapWindow : public Window {
 
 	int32 scroll_x;
 	int32 scroll_y;
-	int32 subscroll;
+
+	/**
+	 * zoom level of the smallmap.
+	 * May be something between ZOOM_LVL_MIN and ZOOM_LVL_MAX.
+	 */
+	ZoomLevel zoom;
+
+	bool HasButtons()
+	{
+		return this->map_type == SMT_INDUSTRY || this->map_type == SMT_LINKSTATS;
+	}
+
+	Point cursor;
+
+	struct BaseCargoDetail {
+		BaseCargoDetail() :
+			scale(_settings_game.economy.moving_average_length * _settings_game.economy.moving_average_unit)
+		{
+			this->Clear();
+		}
+
+		void AddLink(const LinkStat & orig_link, const FlowStat & orig_flow)
+		{
+			this->capacity += orig_link.capacity;
+			this->usage += orig_link.usage;
+			this->planned += orig_flow.planned;
+			this->sent += orig_flow.sent;
+		}
+
+		void Scale()
+		{
+			this->capacity = this->capacity * 30 / this->scale;
+			this->usage = this->usage * 30 / this->scale;
+			this->planned = this->planned * 30 / this->scale;
+			this->sent = this->sent * 30 / this->scale;
+		}
+
+		void Clear()
+		{
+			capacity = usage = planned = sent = 0;
+		}
+
+		uint capacity;
+		uint usage;
+		uint planned;
+		uint sent;
+		uint scale;
+	};
+
+	struct CargoDetail : public BaseCargoDetail {
+		CargoDetail(const LegendAndColour * c, const LinkStat &ls, const FlowStat &fs) : legend(c)
+		{
+			this->AddLink(ls, fs);
+			this->Scale();
+		}
+
+		const LegendAndColour *legend;
+	};
+
+	typedef std::vector<CargoDetail> StatVector;
+
+	struct LinkDetails {
+		LinkDetails() {Clear();}
+
+		StationID sta;
+		StationID stb;
+		StatVector a_to_b;
+		StatVector b_to_a;
+
+		void Clear()
+		{
+			this->sta = INVALID_STATION;
+			this->stb = INVALID_STATION;
+			this->a_to_b.clear();
+			this->b_to_a.clear();
+		}
+
+		bool Empty() const
+		{
+			return this->sta == INVALID_STATION;
+		}
+	};
+
+	/**
+	 * those are detected while drawing the links and used when drawing
+	 * the legend. They don't represent game state.
+	 */
+	mutable LinkDetails link_details;
+	mutable StationID supply_details;
 
 	static const uint8 FORCE_REFRESH_PERIOD = 0x1F; ///< map is redrawn after that many ticks
+	static const uint8 REFRESH_NEXT_TICK = 1;
 	uint8 refresh; ///< refresh counter, zeroed every FORCE_REFRESH_PERIOD ticks
 
+	/* The order of calculations when remapping is _very_ important as it introduces rounding errors.
+	 * Everything has to be done just like when drawing the background otherwise the rounding errors are
+	 * different on the background and on the overlay which creates "jumping" behaviour. This means:
+	 * 1. UnScaleByZoom
+	 * 2. divide by TILE_SIZE
+	 * 3. subtract or add things or RemapCoords
+	 * Note:
+	 * We can't divide scroll_{x|y} by TILE_SIZE before scaling as that would mean we can only scroll full tiles.
+	 */
+
+	/**
+	 * remap coordinates on the main map into coordinates on the smallmap
+	 * @param pos_x X position on the main map
+	 * @param pos_y Y position on the main map
+	 * @return Point in the smallmap
+	 */
+	inline Point RemapPlainCoords(int pos_x, int pos_y) const
+	{
+		return RemapCoords(
+				RemapX(pos_x),
+				RemapY(pos_y),
+				0
+				);
+	}
+
+	/**
+	 * remap a tile coordinate into coordinates on the smallmap
+	 * @param tile the tile to be remapped
+	 * @return Point with coordinates of the tile's upper left corner in the smallmap
+	 */
+	inline Point RemapTileCoords(TileIndex tile) const
+	{
+		return RemapPlainCoords(TileX(tile) * TILE_SIZE, TileY(tile) * TILE_SIZE);
+	}
+
+	/**
+	 * scale a coordinate from the main map into the smallmap dimension
+	 * @param pos coordinate to be scaled
+	 * @return scaled coordinate
+	 */
+	inline int UnScalePlainCoord(int pos) const
+	{
+		return UnScaleByZoomLower(pos, this->zoom) / TILE_SIZE;
+	}
+
 	/**
-	 * Remap a map's tile X coordinate (TileX(TileIndex)) to
-	 * a location on this smallmap.
-	 * @param tile_x the tile's X coordinate.
+	 * Remap a map X coordinate to a location on this smallmap.
+	 * @param pos_x the tile's X coordinate.
 	 * @return the X coordinate to draw on.
 	 */
-	inline int RemapX(int tile_x) const
+	inline int RemapX(int pos_x) const
 	{
-		return tile_x - this->scroll_x / TILE_SIZE;
+		return UnScalePlainCoord(pos_x) - UnScalePlainCoord(this->scroll_x);
 	}
 
 	/**
-	 * Remap a map's tile Y coordinate (TileY(TileIndex)) to
-	 * a location on this smallmap.
-	 * @param tile_y the tile's Y coordinate.
+	 * Remap a map Y coordinate to a location on this smallmap.
+	 * @param pos_y the tile's Y coordinate.
 	 * @return the Y coordinate to draw on.
 	 */
-	inline int RemapY(int tile_y) const
+	inline int RemapY(int pos_y) const
 	{
-		return tile_y - this->scroll_y / TILE_SIZE;
+		return UnScalePlainCoord(pos_y) - UnScalePlainCoord(this->scroll_y);
 	}
 
 	/**
-	 * Draws one column of the small map in a certain mode onto the screen buffer. This
-	 * function looks exactly the same for all types
+	 * choose a different tile from the tiles to be drawn in one pixel
+	 * each time. This decreases the chance that certain structures
+	 * (railway lines, roads) disappear completely when zooming out.
+	 * @param x the X coordinate of the upper right corner of the drawn area
+	 * @param y the Y coordinate of the upper right corner of the drawn area
+	 * @param xc the unscaled X coordinate x was calcluated from
+	 * @param yc the unscaled Y coordinate y was calcluated from
+	 */
+	void AntiAlias(uint &x, uint &y, uint xc, uint yc) const
+	{
+		int bits_needed = this->zoom - ZOOM_LVL_NORMAL;
+		if (bits_needed <= 0) return;
+		for(int i = 0; i < bits_needed; ++i) {
+			x += ((xc ^ yc) & 0x1) << i;
+			yc >>= 1;
+			y += ((xc ^ yc) & 0x1) << i;
+			xc >>= 1;
+		}
+		x = min(x, MapMaxX() - 1);
+		y = min(y, MapMaxY() - 1);
+	}
+
+	/**
+	 * Draws at most MAP_COLUMN_WIDTH columns (of one pixel each) of the small map in a certain
+	 * mode onto the screen buffer. This function looks exactly the same for all types. Due to
+	 * the constraints that no less than MAP_COLUMN_WIDTH pixels can be resolved at once via a
+	 * GetSmallMapPixels function and that a single tile may be mapped onto more than one pixel
+	 * in the smallmap dst, xc and yc may point to a place outside the area to be drawn.
+	 *
+	 * col_start, col_end, row_start and row_end give a more precise description of that area which
+	 * is respected when drawing.
 	 *
 	 * @param dst Pointer to a part of the screen buffer to write to.
-	 * @param xc The X coordinate of the first tile in the column.
-	 * @param yc The Y coordinate of the first tile in the column
-	 * @param pitch Number of pixels to advance in the screen buffer each time a pixel is written.
-	 * @param reps Number of lines to draw
-	 * @param mask Some bytes may need to be masked out when at the border of drawn area
-	 * @param blitter current blitter
-	 * @param proc Pointer to the colour function
+	 * @param xc First unscaled X coordinate of the first tile in the column.
+	 * @param yc First unscaled Y coordinate of the first tile in the column
+	 * @param col_start the first column in the buffer to be actually drawn
+	 * @param col_end the last column to be actually drawn
+	 * @param row_start the first row to be actually drawn
+	 * @param row_end the last row to be actually drawn
 	 * @see GetSmallMapPixels(TileIndex)
 	 */
-	void DrawSmallMapStuff(void *dst, uint xc, uint yc, int pitch, int reps, uint32 mask, Blitter *blitter, GetSmallMapPixels *proc) const
+	void DrawSmallMapStuff(void *dst, uint xc, uint yc, int col_start, int col_end, int row_start, int row_end, Blitter *blitter, GetSmallMapPixels *proc) const
 	{
-		void *dst_ptr_abs_end = blitter->MoveTo(_screen.dst_ptr, 0, _screen.height);
-		void *dst_ptr_end = blitter->MoveTo(dst_ptr_abs_end, -4, 0);
-
-		do {
-			/* Check if the tile (xc,yc) is within the map range */
-			uint min_xy = _settings_game.construction.freeform_edges ? 1 : 0;
-			if (IsInsideMM(xc, min_xy, MapMaxX()) && IsInsideMM(yc, min_xy, MapMaxY())) {
-				/* Check if the dst pointer points to a pixel inside the screen buffer */
-				if (dst < _screen.dst_ptr) continue;
-				if (dst >= dst_ptr_abs_end) continue;
-
-				uint32 val = proc(TileXY(xc, yc)) & mask;
+		for (int row = 0; row < row_end; row += SD_MAP_ROW_OFFSET) {
+			if (row >= row_start) {
+				/* check if the tile (xc,yc) is within the map range */
+				uint min_xy = _settings_game.construction.freeform_edges ? 1 : 0;
+				uint x = ScaleByZoomLower(xc, this->zoom);
+				uint y = ScaleByZoomLower(yc, this->zoom);
+				uint32 val = 0;
+				if (IsInsideMM(x, min_xy, MapMaxX()) && IsInsideMM(y, min_xy, MapMaxY())) {
+					AntiAlias(x, y, xc, yc);
+					val = proc(TileXY(x, y));
+				}
 				uint8 *val8 = (uint8 *)&val;
-
-				if (dst <= dst_ptr_end) {
-					blitter->SetPixelIfEmpty(dst, 0, 0, val8[0]);
-					blitter->SetPixelIfEmpty(dst, 1, 0, val8[1]);
-					blitter->SetPixelIfEmpty(dst, 2, 0, val8[2]);
-					blitter->SetPixelIfEmpty(dst, 3, 0, val8[3]);
-				} else {
-					/* It happens that there are only 1, 2 or 3 pixels left to fill, so
-					 * in that special case, write till the end of the video-buffer */
-					int i = 0;
-					do {
-						blitter->SetPixelIfEmpty(dst, 0, 0, val8[i]);
-					} while (i++, dst = blitter->MoveTo(dst, 1, 0), dst < dst_ptr_abs_end);
+				for (int i = col_start; i < col_end; ++i ) {
+					blitter->SetPixel(dst, i, 0, val8[i]);
 				}
 			}
-		/* Switch to next tile in the column */
-		} while (xc++, yc++, dst = blitter->MoveTo(dst, pitch, 0), --reps != 0);
+
+			/* switch to next row in the column */
+			xc++;
+			yc++;
+			dst = blitter->MoveTo(dst, 0, SD_MAP_ROW_OFFSET);
+		}
 	}
 
 	/**
@@ -546,46 +833,482 @@ class SmallMapWindow : public Window {
 	 */
 	void DrawVehicles(const DrawPixelInfo *dpi, Blitter *blitter) const
 	{
-		const Vehicle *v;
-		FOR_ALL_VEHICLES(v) {
-			if (v->type == VEH_EFFECT) continue;
-			if (v->vehstatus & (VS_HIDDEN | VS_UNCLICKABLE)) continue;
+		for(VehicleList::const_iterator i = this->vehicles_on_map.begin(); i != this->vehicles_on_map.end(); ++i) {
+			const Vehicle *v = Vehicle::GetIfValid((*i).vehicle);
+			if (v == NULL) continue;
+
 
 			/* Remap into flat coordinates. */
-			Point pt = RemapCoords(
-					this->RemapX(v->x_pos / TILE_SIZE),
-					this->RemapY(v->y_pos / TILE_SIZE),
-					0);
-			int x = pt.x;
-			int y = pt.y;
+			Point pos = RemapTileCoords((*i).tile);
+
+			pos.x -= dpi->left;
+			pos.y -= dpi->top;
 
-			/* Check if y is out of bounds? */
-			y -= dpi->top;
-			if (!IsInsideMM(y, 0, dpi->height)) continue;
-
-			/* Default is to draw both pixels. */
-			bool skip = false;
-
-			/* Offset X coordinate */
-			x -= this->subscroll + 3 + dpi->left;
-
-			if (x < 0) {
-				/* if x+1 is 0, that means we're on the very left edge,
-				 * and should thus only draw a single pixel */
-				if (++x != 0) continue;
-				skip = true;
-			} else if (x >= dpi->width - 1) {
-				/* Check if we're at the very right edge, and if so draw only a single pixel */
-				if (x != dpi->width - 1) continue;
-				skip = true;
+			int scale = GetVehicleScale();
+			/* Check if rhombus is inside bounds */
+			if (!IsInsideMM(pos.x, -2 * scale, dpi->width + 2 * scale) ||
+				!IsInsideMM(pos.y, -2 * scale, dpi->height + 2 * scale)) {
+				continue;
 			}
 
-			/* Calculate pointer to pixel and the colour */
-			byte colour = (this->map_type == SMT_VEHICLES) ? _vehicle_type_colours[v->type] : 0xF;
+			byte colour = (this->map_type == SMT_VEHICLES) ? _vehicle_type_colours[v->type]	: 0xF;
 
-			/* And draw either one or two pixels depending on clipping */
-			blitter->SetPixel(dpi->dst_ptr, x, y, colour);
-			if (!skip) blitter->SetPixel(dpi->dst_ptr, x + 1, y, colour);
+			/* Draw rhombus */
+			for (int dy = 0; dy < scale; dy++) {
+				for (int dx = 0; dx < scale; dx++) {
+					Point pt = RemapCoords(-dx, -dy, 0);
+					if (IsInsideMM(pos.y + pt.y, 0, dpi->height)) {
+						if (IsInsideMM(pos.x + pt.x, 0, dpi->width)) {
+							blitter->SetPixel(dpi->dst_ptr, pos.x + pt.x, pos.y + pt.y, colour);
+						}
+						if (IsInsideMM(pos.x + pt.x + 1, 0, dpi->width)) {
+							blitter->SetPixel(dpi->dst_ptr, pos.x + pt.x + 1, pos.y + pt.y, colour);
+						}
+					}
+				}
+			}
+		}
+	}
+
+
+	FORCEINLINE int GetVehicleScale() const
+	{
+		int scale = 1;
+		if (this->zoom < ZOOM_LVL_NORMAL) {
+			scale = 1 << (ZOOM_LVL_NORMAL - this->zoom);
+		}
+		return scale;
+	}
+
+	inline Point GetStationMiddle(const Station * st) const {
+		int x = (st->rect.right + st->rect.left - 1) * TILE_SIZE / 2;
+		int y = (st->rect.bottom + st->rect.top - 1) * TILE_SIZE / 2;
+		return RemapPlainCoords(x, y);
+	}
+
+	StationID DrawStationDots() const {
+		const Station *supply_details = NULL;
+
+		const Station *st;
+		FOR_ALL_STATIONS(st) {
+			if (!CheckStationUsageAllowed(st->owner, st->facilities)) continue;
+
+			Point pt = GetStationMiddle(st);
+
+			if (supply_details == NULL && CheckStationSelected(&pt)) {
+				supply_details = st;
+			}
+
+			/* Add up cargo supplied for each selected cargo type */
+			uint q = 0;
+			int colour = 0;
+			int numCargos = 0;
+			for (int i = 0; i < _smallmap_cargo_count; ++i) {
+				const LegendAndColour &tbl = _legend_table[this->map_type][i];
+				if (!tbl.show_on_map && supply_details != st) continue;
+				CargoID c = tbl.type;
+				int add = st->goods[c].supply;
+				if (add > 0) {
+					q += add * 30 / _settings_game.economy.moving_average_length / _settings_game.economy.moving_average_unit;
+					colour += tbl.colour;
+					numCargos++;
+				}
+			}
+			if (numCargos > 1)
+				colour /= numCargos;
+
+			uint r = 2;
+			if (q >= 10) r++;
+			if (q >= 20) r++;
+			if (q >= 40) r++;
+			if (q >= 80) r++;
+			if (q >= 160) r++;
+
+			DrawVertex(pt.x, pt.y, r, colour, _colour_gradient[COLOUR_GREY][supply_details == st ? 3 : 1]);
+		}
+		return (supply_details == NULL) ? INVALID_STATION : supply_details->index;
+	}
+
+	class LinkDrawer {
+
+	protected:
+		virtual void DrawContent() = 0;
+		virtual void Highlight() {}
+		virtual void AddLink(const LinkStat & orig_link, const FlowStat & orig_flow, const LegendAndColour &cargo_entry) = 0;
+
+		Point pta, ptb;
+		bool search_link_details;
+		LinkDetails link_details;
+		const SmallMapWindow * window;
+
+		void DrawLink(StationID sta, StationID stb) {
+
+			this->pta = window->GetStationMiddle(Station::Get(sta));
+			this->ptb = window->GetStationMiddle(Station::Get(stb));
+
+			bool highlight_empty = this->search_link_details && this->link_details.Empty();
+			bool highlight =
+					(sta == this->link_details.sta && stb == this->link_details.stb) ||
+					(highlight_empty && window->CheckLinkSelected(&pta, &ptb));
+			bool reverse_empty = this->link_details.b_to_a.empty();
+			bool reverse_highlight = (sta == this->link_details.stb && stb == this->link_details.sta);
+			if (highlight_empty && highlight) {
+				this->link_details.sta = sta;
+				this->link_details.stb = stb;
+			}
+
+			if (highlight || reverse_highlight) {
+				this->Highlight();
+			}
+
+			for (int i = 0; i < _smallmap_cargo_count; ++i) {
+				const LegendAndColour &cargo_entry = _legend_table[window->map_type][i];
+				CargoID cargo = cargo_entry.type;
+				if (cargo_entry.show_on_map || highlight || reverse_highlight) {
+					GoodsEntry &ge = Station::Get(sta)->goods[cargo];
+					FlowStat sum_flows = ge.GetSumFlowVia(stb);
+					const LinkStatMap &ls_map = ge.link_stats;
+					LinkStatMap::const_iterator i = ls_map.find(stb);
+					if (i != ls_map.end()) {
+						const LinkStat &link_stat = i->second;
+						AddLink(link_stat, sum_flows, cargo_entry);
+						if (highlight_empty && highlight) {
+							this->link_details.a_to_b.push_back(CargoDetail(&cargo_entry, link_stat, sum_flows));
+						} else if (reverse_empty && reverse_highlight) {
+							this->link_details.b_to_a.push_back(CargoDetail(&cargo_entry, link_stat, sum_flows));
+						}
+					}
+				}
+			}
+		}
+
+		virtual void DrawForwBackLinks(StationID sta, StationID stb) {
+			DrawLink(sta, stb);
+			DrawContent();
+			DrawLink(stb, sta);
+			DrawContent();
+		}
+
+	public:
+		virtual ~LinkDrawer() {}
+
+		LinkDetails DrawLinks(const SmallMapWindow * w, bool search)
+		{
+			this->link_details.Clear();
+			this->window = w;
+			this->search_link_details = search;
+			std::set<StationID> seen_stations;
+			std::set<std::pair<StationID, StationID> > seen_links;
+
+			const Station * sta;
+			FOR_ALL_STATIONS(sta) {
+				if (!CheckStationUsageAllowed(sta->owner, sta->facilities)) continue;
+				for (int i = 0; i < _smallmap_cargo_count; ++i) {
+					const LegendAndColour &tbl = _legend_table[window->map_type][i];
+					if (!tbl.show_on_map) continue;
+
+					CargoID c = tbl.type;
+					const LinkStatMap & links = sta->goods[c].link_stats;
+					for (LinkStatMap::const_iterator i = links.begin(); i != links.end(); ++i) {
+						StationID from = sta->index;
+						StationID to = i->first;
+						if (Station::IsValidID(to) && seen_stations.find(to) == seen_stations.end()) {
+							const Station *stb = Station::Get(to);
+
+							if (!CheckStationUsageAllowed(stb->owner, stb->facilities)) continue;
+							if (seen_links.find(std::make_pair(to, from)) != seen_links.end()) continue;
+
+							DrawForwBackLinks(sta->index, stb->index);
+							seen_stations.insert(to);
+						}
+						seen_links.insert(std::make_pair(from, to));
+					}
+				}
+				seen_stations.clear();
+			}
+			return this->link_details;
+		}
+
+	};
+
+	class LinkLineDrawer : public LinkDrawer {
+	public:
+		LinkLineDrawer() : highlight(false) {}
+
+	protected:
+		typedef std::set<uint16> ColourSet;
+		ColourSet colours;
+		bool highlight;
+
+		virtual void DrawForwBackLinks(StationID sta, StationID stb) {
+			DrawLink(sta, stb);
+			DrawLink(stb, sta);
+			DrawContent();
+		}
+
+		virtual void AddLink(const LinkStat & orig_link, const FlowStat & orig_flow, const LegendAndColour &cargo_entry) {
+			this->colours.insert(cargo_entry.colour);
+		}
+
+		virtual void Highlight() {
+			this->highlight = true;
+		}
+
+		virtual void DrawContent() {
+			uint colour = 0;
+			uint num_colours = 0;
+			for (ColourSet::iterator i = colours.begin(); i != colours.end(); ++i) {
+				colour += *i;
+				num_colours++;
+			}
+			colour /= num_colours;
+			byte border_colour = _colour_gradient[COLOUR_GREY][highlight ? 3 : 1];
+			GfxDrawLine(this->pta.x - 1, this->pta.y, this->ptb.x - 1, this->ptb.y, border_colour);
+			GfxDrawLine(this->pta.x + 1, this->pta.y, this->ptb.x + 1, this->ptb.y, border_colour);
+			GfxDrawLine(this->pta.x, this->pta.y - 1, this->ptb.x, this->ptb.y - 1, border_colour);
+			GfxDrawLine(this->pta.x, this->pta.y + 1, this->ptb.x, this->ptb.y + 1, border_colour);
+			GfxDrawLine(this->pta.x, this->pta.y, this->ptb.x, this->ptb.y, colour);
+			this->colours.clear();
+			this->highlight = false;
+		}
+	};
+
+	class LinkValueDrawer : public LinkDrawer, public BaseCargoDetail {
+	protected:
+
+		virtual void AddLink(const LinkStat & orig_link, const FlowStat & orig_flow, const LegendAndColour &cargo_entry)
+		{
+			this->BaseCargoDetail::AddLink(orig_link, orig_flow);
+		}
+	};
+
+	class LinkTextDrawer : public LinkValueDrawer {
+	protected:
+		virtual void DrawContent() {
+			Scale();
+			Point ptm;
+			ptm.x = (this->pta.x + 2*this->ptb.x) / 3;
+			ptm.y = (this->pta.y + 2*this->ptb.y) / 3;
+			int nums = 0;
+			if (_legend_linkstats[_smallmap_cargo_count + STAT_CAPACITY].show_on_map) {
+				SetDParam(nums++, this->capacity);
+			}
+			if (_legend_linkstats[_smallmap_cargo_count + STAT_USAGE].show_on_map) {
+				SetDParam(nums++, this->usage);
+			}
+			if (_legend_linkstats[_smallmap_cargo_count + STAT_PLANNED].show_on_map) {
+				SetDParam(nums++, this->planned);
+			}
+			if (_legend_linkstats[_smallmap_cargo_count + STAT_SENT].show_on_map) {
+				SetDParam(nums++, this->sent);
+			}
+			StringID str;
+			switch (nums) {
+			case 0:
+				str = STR_EMPTY; break;
+			case 1:
+				str = STR_NUM; break;
+			case 2:
+				str = STR_NUM_RELATION_2; break;
+			case 3:
+				str = STR_NUM_RELATION_3; break;
+			case 4:
+				str = STR_NUM_RELATION_4; break;
+			default:
+				NOT_REACHED();
+			}
+			DrawString(ptm.x, ptm.x + this->window->ColumnWidth(), ptm.y, str , TC_BLACK);
+			this->Clear();
+		}
+	};
+
+	class LinkGraphDrawer : public LinkValueDrawer {
+		typedef std::multimap<uint, byte, std::greater<uint> > SizeMap;
+	protected:
+		virtual void DrawContent() {
+			Scale();
+			Point ptm;
+			SizeMap sizes;
+			/* these floats only serve to calculate the size of the coloured boxes for capacity, usage, planned, sent
+			 * they are not reused anywhere, so it's network safe.
+			 */
+			const LegendAndColour *legend_entry = _legend_linkstats + _smallmap_cargo_count + STAT_USAGE;
+			if (legend_entry->show_on_map && this->usage > 0) {
+				sizes.insert(std::make_pair((uint)sqrt((float)this->usage), legend_entry->colour));
+			}
+			legend_entry = _legend_linkstats + _smallmap_cargo_count + STAT_CAPACITY;
+			if (legend_entry->show_on_map && this->capacity > 0) {
+				sizes.insert(std::make_pair((uint)sqrt((float)this->capacity), legend_entry->colour));
+			}
+			legend_entry = _legend_linkstats + _smallmap_cargo_count + STAT_PLANNED;
+			if (legend_entry->show_on_map && this->planned > 0) {
+				sizes.insert(std::make_pair((uint)sqrt((float)this->planned),  legend_entry->colour));
+			}
+			legend_entry = _legend_linkstats + _smallmap_cargo_count + STAT_SENT;
+			if (legend_entry->show_on_map && this->sent > 0) {
+				sizes.insert(std::make_pair((uint)sqrt((float)this->sent), legend_entry->colour));
+			}
+
+			ptm.x = (this->pta.x + this->ptb.x) / 2;
+			ptm.y = (this->pta.y + this->ptb.y) / 2;
+
+			for (SizeMap::iterator i = sizes.begin(); i != sizes.end(); ++i) {
+				if (this->pta.x > this->ptb.x) {
+					ptm.x -= 1;
+					GfxFillRect(ptm.x - i->first / 2, ptm.y - i->first * 2, ptm.x, ptm.y, i->second);
+				} else {
+					ptm.x += 1;
+					GfxFillRect(ptm.x, ptm.y - i->first * 2, ptm.x + i->first / 2, ptm.y, i->second);
+				}
+			}
+			this->Clear();
+		}
+	};
+
+	void DrawIndustries(DrawPixelInfo *dpi) const {
+		/* Emphasize all industries if current view is zoomed out "Industreis" */
+		Blitter *blitter = BlitterFactoryBase::GetCurrentBlitter();
+		if ((this->map_type == SMT_INDUSTRY) && (this->zoom > ZOOM_LVL_NORMAL)) {
+			const Industry *i;
+			FOR_ALL_INDUSTRIES(i) {
+				if (_legend_from_industries[_industry_to_list_pos[i->type]].show_on_map) {
+					Point pt = RemapTileCoords(i->location.tile);
+
+					int y = pt.y - dpi->top;
+					if (!IsInsideMM(y, 0, dpi->height)) continue;
+
+					int x = pt.x - dpi->left;
+					byte colour = GetIndustrySpec(i->type)->map_colour;
+
+					for (int offset = 0; offset < SD_MAP_MIN_INDUSTRY_WIDTH; ++offset) {
+						if (IsInsideMM(x + offset, 0, dpi->width)) {
+							blitter->SetPixel(dpi->dst_ptr, x + offset, y, colour);
+						}
+					}
+				}
+			}
+		}
+	}
+
+	static const uint MORE_SPACE_NEEDED = 0x1000;
+
+	uint DrawLinkDetails(StatVector &details, uint x, uint y, uint right, uint bottom) const {
+		uint x_orig = x;
+		SetDParam(0, 9999);
+		static uint entry_width = LEGEND_BLOB_WIDTH +
+				GetStringBoundingBox(STR_ABBREV_PASSENGERS).width +
+				GetStringBoundingBox(STR_SMALLMAP_LINK_CAPACITY).width +
+				GetStringBoundingBox(STR_SMALLMAP_LINK_USAGE).width +
+				GetStringBoundingBox(STR_SMALLMAP_LINK_PLANNED).width +
+				GetStringBoundingBox(STR_SMALLMAP_LINK_SENT).width;
+		uint entries_per_row = (right - x_orig) / entry_width;
+		if (details.empty()) {
+			DrawString(x, x + entry_width, y, STR_TINY_NOTHING, TC_BLACK);
+			return y + FONT_HEIGHT_SMALL;
+		}
+		for (uint i = 0; i < details.size(); ++i) {
+			CargoDetail &detail = details[i];
+			if (x + entry_width >= right) {
+				x = x_orig;
+				y += FONT_HEIGHT_SMALL;
+				if (y + 2 * FONT_HEIGHT_SMALL > bottom && details.size() - i > entries_per_row) {
+					return y | MORE_SPACE_NEEDED;
+				}
+			}
+			uint x_next = x + entry_width;
+			if (detail.legend->show_on_map) {
+				GfxFillRect(x, y + 1, x + LEGEND_BLOB_WIDTH, y + FONT_HEIGHT_SMALL - 1, 0); // outer border of the legend colour
+			}
+			GfxFillRect(x + 1, y + 2, x + LEGEND_BLOB_WIDTH - 1, y + FONT_HEIGHT_SMALL - 2, detail.legend->colour); // legend colour
+			x += LEGEND_BLOB_WIDTH + WD_FRAMERECT_LEFT;
+			TextColour textcol[4];
+			for (int stat = STAT_CAPACITY; stat <= STAT_SENT; ++stat) {
+				textcol[stat] = (detail.legend->show_on_map && _legend_linkstats[_smallmap_cargo_count + stat].show_on_map) ?
+						TC_BLACK : TC_GREY;
+			}
+
+			SetDParam(0, STR_ABBREV_PASSENGERS + detail.legend->type);
+			x = DrawString(x, x_next - 1, y, STR_SMALLMAP_LINK, detail.legend->show_on_map ? TC_BLACK : TC_GREY);
+			SetDParam(0, detail.capacity);
+			x = DrawString(x, x_next - 1, y, STR_SMALLMAP_LINK_CAPACITY, textcol[STAT_CAPACITY]);
+			SetDParam(0, detail.usage);
+			x = DrawString(x, x_next - 1, y, STR_SMALLMAP_LINK_USAGE, textcol[STAT_USAGE]);
+			SetDParam(0, detail.planned);
+			x = DrawString(x, x_next - 1, y, STR_SMALLMAP_LINK_PLANNED, textcol[STAT_PLANNED]);
+			SetDParam(0, detail.sent);
+			x = DrawString(x, x_next - 1, y, STR_SMALLMAP_LINK_SENT, textcol[STAT_SENT]);
+			x = x_next;
+		}
+		return y + FONT_HEIGHT_SMALL;
+	}
+
+	uint DrawLinkDetailCaption(uint x, uint y, uint right, StationID sta, StationID stb) const {
+		SetDParam(0, sta);
+		SetDParam(1, stb);
+		static uint height = GetStringBoundingBox(STR_SMALLMAP_LINK_CAPTION).height;
+		DrawString(x, right - 1, y, STR_SMALLMAP_LINK_CAPTION, TC_BLACK);
+		y += height;
+		return y;
+	}
+
+	void DrawLinkDetails(uint x, uint y, uint right, uint bottom) const {
+		y = DrawLinkDetailCaption(x, y, right, this->link_details.sta, this->link_details.stb);
+		if (y + 2 * FONT_HEIGHT_SMALL > bottom) {
+			DrawString(x, right, y, "...", TC_BLACK);
+			return;
+		}
+		y = DrawLinkDetails(this->link_details.a_to_b, x, y, right, bottom);
+		if (y + 3 * FONT_HEIGHT_SMALL > bottom) {
+			/* caption takes more space -> 3 * row height */
+			DrawString(x, right, y, "...", TC_BLACK);
+			return;
+		}
+		y = DrawLinkDetailCaption(x, y + 2, right, this->link_details.stb, this->link_details.sta);
+		if (y + 2 * FONT_HEIGHT_SMALL > bottom) {
+			DrawString(x, right, y, "...", TC_BLACK);
+			return;
+		}
+		y = DrawLinkDetails(this->link_details.b_to_a, x, y, right, bottom);
+		if (y & MORE_SPACE_NEEDED) {
+			/* only draw "..." if more entries would have been drawn */
+			DrawString(x, right, y ^ MORE_SPACE_NEEDED, "...", TC_BLACK);
+			return;
+		}
+	}
+
+	void DrawSupplyDetails(uint x, uint y_org, uint bottom) const {
+		const Station *st = Station::GetIfValid(this->supply_details);
+		if (st == NULL) return;
+		SetDParam(0, this->supply_details);
+		static uint height = GetStringBoundingBox(STR_SMALLMAP_SUPPLY_CAPTION).height;
+		DrawString(x, x + 2 * this->column_width - 1, y_org, STR_SMALLMAP_SUPPLY_CAPTION, TC_BLACK);
+		y_org += height;
+		uint y = y_org;
+		for (int i = 0; i < _smallmap_cargo_count; ++i) {
+			if (y + FONT_HEIGHT_SMALL - 1 >= bottom) {
+				/* Column break needed, continue at top, SD_LEGEND_COLUMN_WIDTH pixels
+				 * (one "row") to the right. */
+				x += this->column_width;
+				y = y_org;
+			}
+
+			const LegendAndColour &tbl = _legend_table[this->map_type][i];
+
+			CargoID c = tbl.type;
+			int supply = st->goods[c].supply * 30 / _settings_game.economy.moving_average_length / _settings_game.economy.moving_average_unit;;
+			if (supply > 0) {
+				TextColour textcol = TC_BLACK;
+				if (tbl.show_on_map) {
+					GfxFillRect(x, y + 1, x + LEGEND_BLOB_WIDTH, y + FONT_HEIGHT_SMALL - 1, 0); // outer border of the legend colour
+				} else {
+					textcol = TC_GREY;
+				}
+				SetDParam(0, c);
+				SetDParam(1, supply);
+				DrawString(x + LEGEND_BLOB_WIDTH + WD_FRAMERECT_LEFT, x + this->column_width - 1, y, STR_SMALLMAP_SUPPLY, textcol);
+				GfxFillRect(x + 1, y + 2, x + LEGEND_BLOB_WIDTH - 1, y + FONT_HEIGHT_SMALL - 2, tbl.colour); // legend colour
+				y += FONT_HEIGHT_SMALL;
+			}
 		}
 	}
 
@@ -598,11 +1321,8 @@ class SmallMapWindow : public Window {
 		const Town *t;
 		FOR_ALL_TOWNS(t) {
 			/* Remap the town coordinate */
-			Point pt = RemapCoords(
-					this->RemapX(TileX(t->xy)),
-					this->RemapY(TileY(t->xy)),
-					0);
-			int x = pt.x - this->subscroll - (t->sign.width_small >> 1);
+			Point pt = RemapTileCoords(t->xy);
+			int x = pt.x - (t->sign.width_small >> 1);
 			int y = pt.y;
 
 			/* Check if the town sign is within bounds */
@@ -651,15 +1371,11 @@ class SmallMapWindow : public Window {
 
 		Point pt = RemapCoords(this->scroll_x, this->scroll_y, 0);
 
-		int x = vp->virtual_left - pt.x;
-		int y = vp->virtual_top - pt.y;
-		int x2 = (x + vp->virtual_width) / TILE_SIZE;
-		int y2 = (y + vp->virtual_height) / TILE_SIZE;
-		x /= TILE_SIZE;
-		y /= TILE_SIZE;
-
-		x -= this->subscroll;
-		x2 -= this->subscroll;
+		/* UnScale everything separately to produce the same rounding errors as when drawing the background */
+		int x = UnScalePlainCoord(vp->virtual_left) - UnScalePlainCoord(pt.x);
+		int y = UnScalePlainCoord(vp->virtual_top) - UnScalePlainCoord(pt.y);
+		int x2 = x + UnScalePlainCoord(vp->virtual_width);
+		int y2 = y + UnScalePlainCoord(vp->virtual_height);
 
 		SmallMapWindow::DrawVertMapIndicator(x, y, y2);
 		SmallMapWindow::DrawVertMapIndicator(x2, y, y2);
@@ -687,9 +1403,6 @@ class SmallMapWindow : public Window {
 		old_dpi = _cur_dpi;
 		_cur_dpi = dpi;
 
-		/* Clear it */
-		GfxFillRect(dpi->left, dpi->top, dpi->left + dpi->width - 1, dpi->top + dpi->height - 1, 0);
-
 		/* Setup owner table */
 		if (this->map_type == SMT_OWNER) {
 			const Company *c;
@@ -706,10 +1419,10 @@ class SmallMapWindow : public Window {
 			}
 		}
 
-		int tile_x = this->scroll_x / TILE_SIZE;
-		int tile_y = this->scroll_y / TILE_SIZE;
+		int tile_x = UnScalePlainCoord(this->scroll_x);
+		int tile_y = UnScalePlainCoord(this->scroll_y);
 
-		int dx = dpi->left + this->subscroll;
+		int dx = dpi->left;
 		tile_x -= dx / 4;
 		tile_y += dx / 4;
 		dx &= 3;
@@ -718,61 +1431,91 @@ class SmallMapWindow : public Window {
 		tile_x += dy / 2;
 		tile_y += dy / 2;
 
+		/* prevent some artifacts when partially redrawing.
+		 * I have no idea how this works.
+		 */
+		dx += 1;
 		if (dy & 1) {
 			tile_x++;
 			dx += 2;
-			if (dx > 3) {
-				dx -= 4;
-				tile_x--;
-				tile_y++;
-			}
 		}
 
-		void *ptr = blitter->MoveTo(dpi->dst_ptr, -dx - 4, 0);
-		int x = - dx - 4;
+		/**
+		 * As we can resolve no less than 4 pixels of the smallmap at once we have to start drawing at an X position <= -4
+		 * otherwise we get artifacts when partially redrawing.
+		 * Make sure dx provides for that and update tile_x and tile_y accordingly.
+		 */
+		while(dx < SD_MAP_COLUMN_WIDTH) {
+			dx += SD_MAP_COLUMN_WIDTH;
+			tile_x++;
+			tile_y--;
+		}
+
+		/* The map background is off by a little less than one tile in y direction compared to vehicles and signs.
+		 * I have no idea why this is the case.
+		 * on zoom levels >= ZOOM_LVL_NORMAL this isn't visible as only full tiles can be shown. However, beginning
+		 * at ZOOM_LVL_OUT_2X it's again off by 1 pixel
+		 */
+		dy = 0;
+		if (this->zoom < ZOOM_LVL_NORMAL) {
+			dy = UnScaleByZoomLower(2, this->zoom) - 2;
+		} else if (this->zoom > ZOOM_LVL_NORMAL) {
+			dy = 1;
+		}
+
+		/* correct the various problems mentioned above by moving the initial drawing pointer a little */
+		void *ptr = blitter->MoveTo(dpi->dst_ptr, -dx, -dy);
+		int x = -dx;
 		int y = 0;
 
 		for (;;) {
-			uint32 mask = 0xFFFFFFFF;
-
 			/* Distance from left edge */
-			if (x >= -3) {
-				if (x < 0) {
-					/* Mask to use at the left edge */
-					mask = _smallmap_mask_left[x + 3];
-				}
+			if (x > -SD_MAP_COLUMN_WIDTH) {
 
 				/* Distance from right edge */
-				int t = dpi->width - x;
-				if (t < 4) {
-					if (t <= 0) break; // Exit loop
-					/* Mask to use at the right edge */
-					mask &= _smallmap_mask_right[t - 1];
-				}
+				if (dpi->width - x <= 0) break;
 
-				/* Number of lines */
-				int reps = (dpi->height - y + 1) / 2;
-				if (reps > 0) {
-					this->DrawSmallMapStuff(ptr, tile_x, tile_y, dpi->pitch * 2, reps, mask, blitter, _smallmap_draw_procs[this->map_type]);
-				}
+				int col_start = x < 0 ? -x : 0;
+				int col_end = x + SD_MAP_COLUMN_WIDTH > dpi->width ? dpi->width - x : SD_MAP_COLUMN_WIDTH;
+				int row_start = dy - y;
+				int row_end = dy + dpi->height - y;
+				this->DrawSmallMapStuff(ptr, tile_x, tile_y, col_start, col_end, row_start, row_end, blitter, _smallmap_draw_procs[this->map_type]);
 			}
 
 			if (y == 0) {
 				tile_y++;
 				y++;
-				ptr = blitter->MoveTo(ptr, 0, 1);
+				ptr = blitter->MoveTo(ptr, 0, SD_MAP_ROW_OFFSET / 2);
 			} else {
 				tile_x--;
 				y--;
-				ptr = blitter->MoveTo(ptr, 0, -1);
+				ptr = blitter->MoveTo(ptr, 0, -SD_MAP_ROW_OFFSET / 2);
 			}
-			ptr = blitter->MoveTo(ptr, 2, 0);
-			x += 2;
+			ptr = blitter->MoveTo(ptr, SD_MAP_COLUMN_WIDTH / 2, 0);
+			x += SD_MAP_COLUMN_WIDTH / 2;
 		}
 
 		/* Draw vehicles */
 		if (this->map_type == SMT_CONTOUR || this->map_type == SMT_VEHICLES) this->DrawVehicles(dpi, blitter);
 
+		if (this->map_type == SMT_LINKSTATS && _game_mode == GM_NORMAL) {
+			LinkLineDrawer lines;
+			this->link_details = lines.DrawLinks(this, true);
+
+			this->supply_details = DrawStationDots();
+
+			if (_legend_linkstats[_smallmap_cargo_count + STAT_TEXT].show_on_map) {
+				LinkTextDrawer text;
+				text.DrawLinks(this, false);
+			}
+			if (_legend_linkstats[_smallmap_cargo_count + STAT_GRAPH].show_on_map) {
+				LinkGraphDrawer graph;
+				graph.DrawLinks(this, false);
+			}
+		}
+
+		this->DrawIndustries(dpi);
+
 		/* Draw town names */
 		if (this->show_towns) this->DrawTowns(dpi);
 
@@ -782,17 +1525,111 @@ class SmallMapWindow : public Window {
 		_cur_dpi = old_dpi;
 	}
 
+	bool CheckStationSelected(Point *pt) const {
+		return abs(this->cursor.x - pt->x) < 7 && abs(this->cursor.y - pt->y) < 7;
+	}
+
+	bool CheckLinkSelected(Point * pta, Point * ptb) const {
+		if (this->cursor.x == -1 && this->cursor.y == -1) return false;
+		if (CheckStationSelected(pta) || CheckStationSelected(ptb)) return false;
+		if (pta->x > ptb->x) Swap(pta, ptb);
+		int minx = min(pta->x, ptb->x);
+		int maxx = max(pta->x, ptb->x);
+		int miny = min(pta->y, ptb->y);
+		int maxy = max(pta->y, ptb->y);
+		if (!IsInsideMM(cursor.x, minx - 3, maxx + 3) || !IsInsideMM(cursor.y, miny - 3, maxy + 3)) {
+			return false;
+		}
+
+		if (pta->x == ptb->x || ptb->y == pta->y) {
+			return true;
+		} else {
+			int incliney = (ptb->y - pta->y);
+			int inclinex = (ptb->x - pta->x);
+			int diff = (cursor.x - minx) * incliney / inclinex - (cursor.y - miny);
+			if (incliney < 0) {
+				diff += maxy - miny;
+			}
+			return abs(diff) < 4;
+		}
+	}
+
+	/**
+	 * Zoom in the map by one level.
+	 * @param cx horizontal coordinate of center point, relative to SM_WIDGET_MAP widget
+	 * @param cy vertical coordinate of center point, relative to SM_WIDGET_MAP widget
+	 */
+	void ZoomIn(int cx, int cy)
+	{
+		if (this->zoom > ZOOM_LVL_MIN) {
+			this->zoom--;
+			this->DoScroll(cx, cy);
+			this->SetWidgetDisabledState(SM_WIDGET_ZOOM_IN, this->zoom == ZOOM_LVL_MIN);
+			this->EnableWidget(SM_WIDGET_ZOOM_OUT);
+			this->SetDirty();
+		}
+	}
+
+	/**
+	 * Zoom out the map by one level.
+	 * @param cx horizontal coordinate of center point, relative to SM_WIDGET_MAP widget
+	 * @param cy vertical coordinate of center point, relative to SM_WIDGET_MAP widget
+	 */
+	void ZoomOut(int cx, int cy)
+	{
+		if (this->zoom < ZOOM_LVL_MAX) {
+			this->zoom++;
+			this->DoScroll(cx / -2, cy / -2);
+			this->EnableWidget(SM_WIDGET_ZOOM_IN);
+			this->SetWidgetDisabledState(SM_WIDGET_ZOOM_OUT, this->zoom == ZOOM_LVL_MAX);
+			this->SetDirty();
+		}
+	}
+
+	void RecalcVehiclePositions() {
+		this->vehicles_on_map.clear();
+		const Vehicle *v;
+		const NWidgetCore *wi = this->GetWidget<NWidgetCore>(SM_WIDGET_MAP);
+		int scale = GetVehicleScale();
+
+		FOR_ALL_VEHICLES(v) {
+			if (v->type == VEH_EFFECT) continue;
+			if (v->vehstatus & (VS_HIDDEN | VS_UNCLICKABLE)) continue;
+
+			/* Remap into flat coordinates. */
+			Point pos = RemapTileCoords(v->tile);
+
+			pos.x -= wi->pos_x;
+			pos.y -= wi->pos_y;
+			/* Check if rhombus is inside bounds */
+			if (IsInsideMM(pos.x, -2 * scale, wi->current_x + 2 * scale) &&
+				IsInsideMM(pos.y, -2 * scale, wi->current_y + 2 * scale)) {
+
+				this->vehicles_on_map.push_back(VehicleAndPosition(v));
+			}
+		}
+	}
+
 public:
-	SmallMapWindow(const WindowDesc *desc, int window_number) : Window(), refresh(FORCE_REFRESH_PERIOD)
+	SmallMapWindow(const WindowDesc *desc, int window_number) : Window(), zoom(ZOOM_LVL_NORMAL), supply_details(INVALID_STATION), refresh(FORCE_REFRESH_PERIOD)
 	{
+		this->cursor.x = -1;
+		this->cursor.y = -1;
 		this->InitNested(desc, window_number);
+		if (_smallmap_cargo_count == 0) {
+			this->DisableWidget(SM_WIDGET_LINKSTATS);
+			if (this->map_type == SMT_LINKSTATS) {
+				this->map_type = SMT_CONTOUR;
+			}
+		}
+
 		this->LowerWidget(this->map_type + SM_WIDGET_CONTOUR);
 
-		_smallmap_industry_show_heightmap = false;
-		this->SetWidgetLoweredState(SM_WIDGET_SHOW_HEIGHT, _smallmap_industry_show_heightmap);
+		_smallmap_show_heightmap = (this->map_type != SMT_INDUSTRY);
+		this->SetWidgetLoweredState(SM_WIDGET_SHOW_HEIGHT, _smallmap_show_heightmap);
 
 		this->SetWidgetLoweredState(SM_WIDGET_TOGGLETOWNNAME, this->show_towns);
-		this->GetWidget<NWidgetStacked>(SM_WIDGET_SELECTINDUSTRIES)->SetDisplayedPlane(this->map_type != SMT_INDUSTRY);
+		this->GetWidget<NWidgetStacked>(SM_WIDGET_SELECTINDUSTRIES)->SetDisplayedPlane(this->map_type != SMT_INDUSTRY && this->map_type != SMT_LINKSTATS);
 
 		this->SmallMapCenterOnCurrentPos();
 	}
@@ -802,8 +1639,7 @@ public:
 	 */
 	inline uint GetMaxLegendHeight() const
 	{
-		uint num_rows = max(this->min_number_of_fixed_rows, (_smallmap_industry_count + this->min_number_of_columns - 1) / this->min_number_of_columns);
-		return WD_FRAMERECT_TOP + WD_FRAMERECT_BOTTOM + num_rows * FONT_HEIGHT_SMALL;
+		return WD_FRAMERECT_TOP + WD_FRAMERECT_BOTTOM + this->GetMaxNumberRowsLegend(this->min_number_of_columns) * FONT_HEIGHT_SMALL;
 	}
 
 	/** Compute minimal required width of the legends.
@@ -828,8 +1664,7 @@ public:
 	uint GetLegendHeight(uint width) const
 	{
 		uint num_columns = this->GetNumberColumnsLegend(width);
-		uint num_rows = max(this->min_number_of_fixed_rows, (_smallmap_industry_count + num_columns - 1) / num_columns);
-		return WD_FRAMERECT_TOP + WD_FRAMERECT_BOTTOM + num_rows * FONT_HEIGHT_SMALL;
+		return WD_FRAMERECT_TOP + WD_FRAMERECT_BOTTOM + this->GetMaxNumberRowsLegend(num_columns) * FONT_HEIGHT_SMALL;
 	}
 
 	virtual void SetStringParameters(int widget) const
@@ -851,10 +1686,10 @@ public:
 			uint num_columns = 1;
 			for (const LegendAndColour *tbl = _legend_table[i]; !tbl->end; ++tbl) {
 				StringID str;
-				if (i == SMT_INDUSTRY) {
+				if (i == SMT_INDUSTRY || i == SMT_LINKSTATS) {
 					SetDParam(0, tbl->legend);
 					SetDParam(1, IndustryPool::MAX_SIZE);
-					str = STR_SMALLMAP_INDUSTRY;
+					str = (i == SMT_INDUSTRY) ? STR_SMALLMAP_INDUSTRY : STR_SMALLMAP_LINKSTATS_LEGEND;
 				} else {
 					if (tbl->col_break) {
 						this->min_number_of_fixed_rows = max(this->min_number_of_fixed_rows, height);
@@ -884,56 +1719,94 @@ public:
 			} break;
 
 			case SM_WIDGET_LEGEND: {
-				uint columns = this->GetNumberColumnsLegend(r.right - r.left + 1);
-				uint number_of_rows = max(this->map_type == SMT_INDUSTRY ? (_smallmap_industry_count + columns - 1) / columns : 0, this->min_number_of_fixed_rows);
-				bool rtl = _dynlang.text_dir == TD_RTL;
-				uint y_org = r.top + WD_FRAMERECT_TOP;
-				uint x = rtl ? r.right - this->column_width - WD_FRAMERECT_RIGHT : r.left + WD_FRAMERECT_LEFT;
-				uint y = y_org;
-				uint i = 0; // Row counter for industry legend.
-				uint row_height = FONT_HEIGHT_SMALL;
-
-				uint text_left  = rtl ? 0 : LEGEND_BLOB_WIDTH + WD_FRAMERECT_LEFT;
-				uint text_right = this->column_width - 1 - (rtl ? LEGEND_BLOB_WIDTH + WD_FRAMERECT_RIGHT : 0);
-				uint blob_left  = rtl ? this->column_width - 1 - LEGEND_BLOB_WIDTH : 0;
-				uint blob_right = rtl ? this->column_width - 1 : LEGEND_BLOB_WIDTH;
-
-				for (const LegendAndColour *tbl = _legend_table[this->map_type]; !tbl->end; ++tbl) {
-					if (tbl->col_break || (this->map_type == SMT_INDUSTRY && i++ >= number_of_rows)) {
-						/* Column break needed, continue at top, COLUMN_WIDTH pixels
-						 * (one "row") to the right. */
-						x += rtl ? -(int)this->column_width : this->column_width;
-						y = y_org;
-						i = 1;
-					}
+				DrawLegend(r);
+			} break;
+		}
+	}
 
-					if (this->map_type == SMT_INDUSTRY) {
+	uint GetNumberRowsLegend(uint columns) const {
+		uint number_of_rows = this->min_number_of_fixed_rows;
+		if (this->map_type == SMT_INDUSTRY) {
+			number_of_rows = max(number_of_rows, (_smallmap_industry_count + columns - 1) / columns);
+		} else if (this->map_type == SMT_LINKSTATS) {
+			number_of_rows = max(number_of_rows, (_smallmap_cargo_count + columns - 2) / (columns - 1));
+		}
+		return number_of_rows;
+	}
+
+	uint GetMaxNumberRowsLegend(uint columns) const {
+		uint number_of_rows = this->min_number_of_fixed_rows;
+		number_of_rows = max(number_of_rows, (_smallmap_industry_count + columns - 1) / columns);
+		number_of_rows = max(number_of_rows, (_smallmap_cargo_count + columns - 2) / (columns - 1));
+		return number_of_rows;
+	}
+
+	void DrawLegend(const Rect &r) const {
+		uint y_org = r.top + WD_FRAMERECT_TOP;
+		uint x = r.left + WD_FRAMERECT_LEFT;
+		if (this->supply_details != INVALID_STATION) {
+			this->DrawSupplyDetails(x, y_org, r.bottom - WD_FRAMERECT_BOTTOM);
+		} else if (!link_details.Empty()) {
+			this->DrawLinkDetails(x, y_org, r.right - WD_FRAMERECT_RIGHT, r.bottom - WD_FRAMERECT_BOTTOM);
+		} else {
+			uint columns = this->GetNumberColumnsLegend(r.right - r.left + 1);
+
+			uint number_of_rows = this->GetNumberRowsLegend(columns);
+
+			bool rtl = _dynlang.text_dir == TD_RTL;
+			uint y_org = r.top + WD_FRAMERECT_TOP;
+			uint x = rtl ? r.right - this->column_width - WD_FRAMERECT_RIGHT : r.left + WD_FRAMERECT_LEFT;
+			uint y = y_org;
+			uint i = 0; // Row counter for industry legend.
+			uint row_height = FONT_HEIGHT_SMALL;
+
+			uint text_left  = rtl ? 0 : LEGEND_BLOB_WIDTH + WD_FRAMERECT_LEFT;
+			uint text_right = this->column_width - 1 - (rtl ? LEGEND_BLOB_WIDTH + WD_FRAMERECT_RIGHT : 0);
+			uint blob_left  = rtl ? this->column_width - 1 - LEGEND_BLOB_WIDTH : 0;
+			uint blob_right = rtl ? this->column_width - 1 : LEGEND_BLOB_WIDTH;
+
+			StringID string = (this->map_type == SMT_INDUSTRY) ? STR_SMALLMAP_INDUSTRY : STR_SMALLMAP_LINKSTATS_LEGEND;
+
+			for (const LegendAndColour *tbl = _legend_table[this->map_type]; !tbl->end; ++tbl) {
+				if (tbl->col_break || ((this->map_type == SMT_INDUSTRY || this->map_type == SMT_LINKSTATS) && i++ >= number_of_rows)) {
+					/* Column break needed, continue at top, COLUMN_WIDTH pixels
+					 * (one "row") to the right. */
+					x += rtl ? -(int)this->column_width : this->column_width;
+					y = y_org;
+					i = 1;
+				}
+
+				switch(this->map_type) {
+					case SMT_INDUSTRY:
 						/* Industry name must be formatted, since it's not in tiny font in the specs.
 						 * So, draw with a parameter and use the STR_SMALLMAP_INDUSTRY string, which is tiny font */
-						SetDParam(0, tbl->legend);
 						assert(tbl->type < NUM_INDUSTRYTYPES);
 						SetDParam(1, _industry_counts[tbl->type]);
+					case SMT_LINKSTATS:
+						SetDParam(0, tbl->legend);
 						if (!tbl->show_on_map) {
 							/* Simply draw the string, not the black border of the legend colour.
 							 * This will enforce the idea of the disabled item */
-							DrawString(x + text_left, x + text_right, y, STR_SMALLMAP_INDUSTRY, TC_GREY);
+							DrawString(x + text_left, x + text_right, y, string, TC_GREY);
 						} else {
-							DrawString(x + text_left, x + text_right, y, STR_SMALLMAP_INDUSTRY, TC_BLACK);
-							GfxFillRect(x + blob_left, y + 1, x + blob_right, y + row_height - 1, 0); // Outer border of the legend colour
+							DrawString(x + text_left, x + text_right, y, string, TC_BLACK);
+							GfxFillRect(x + blob_left, y + 1, x + blob_right, y + row_height - 1, 0); // outer border of the legend colour
 						}
-					} else {
+						break;
+					default:
 						/* Anything that is not an industry is using normal process */
 						GfxFillRect(x + blob_left, y + 1, x + blob_right, y + row_height - 1, 0);
 						DrawString(x + text_left, x + text_right, y, tbl->legend);
-					}
-					GfxFillRect(x + blob_left + 1, y + 2, x + blob_right - 1, y + row_height - 2, tbl->colour); // Legend colour
-
-					y += row_height;
 				}
+				GfxFillRect(x + blob_left + 1, y + 2, x + blob_right - 1, y + row_height - 2, tbl->colour); // Legend colour
+
+				y += row_height;
 			}
 		}
 	}
 
+
+
 	virtual void OnPaint()
 	{
 		this->DrawWidgets();
@@ -956,15 +1829,32 @@ public:
 				Point pt = RemapCoords(this->scroll_x, this->scroll_y, 0);
 				Window *w = FindWindowById(WC_MAIN_WINDOW, 0);
 				w->viewport->follow_vehicle = INVALID_VEHICLE;
-				w->viewport->dest_scrollpos_x = pt.x + ((_cursor.pos.x - this->left + 2) << 4) - (w->viewport->virtual_width >> 1);
-				w->viewport->dest_scrollpos_y = pt.y + ((_cursor.pos.y - this->top - 16) << 4) - (w->viewport->virtual_height >> 1);
+				int scaled_x_off = ScaleByZoom((_cursor.pos.x - this->left - WD_FRAMERECT_LEFT) * TILE_SIZE, this->zoom);
+				int scaled_y_off = ScaleByZoom((_cursor.pos.y - this->top - WD_FRAMERECT_TOP - WD_CAPTION_HEIGHT) * TILE_SIZE, this->zoom);
+				w->viewport->dest_scrollpos_x = pt.x + scaled_x_off - w->viewport->virtual_width / 2;
+				w->viewport->dest_scrollpos_y = pt.y + scaled_y_off - w->viewport->virtual_height / 2;
 
 				this->SetDirty();
 			} break;
 
+			case SM_WIDGET_ZOOM_OUT: {
+				const NWidgetBase *wi = this->GetWidget<NWidgetBase>(SM_WIDGET_MAP);
+				this->ZoomOut(wi->current_x / 2, wi->current_y / 2);
+				this->HandleButtonClick(SM_WIDGET_ZOOM_OUT);
+				SndPlayFx(SND_15_BEEP);
+			} break;
+
+			case SM_WIDGET_ZOOM_IN: {
+				const NWidgetBase *wi = this->GetWidget<NWidgetBase>(SM_WIDGET_MAP);
+				this->ZoomIn(wi->current_x / 2, wi->current_y / 2);
+				this->HandleButtonClick(SM_WIDGET_ZOOM_IN);
+				SndPlayFx(SND_15_BEEP);
+			} break;
+
 			case SM_WIDGET_CONTOUR:    // Show land contours
 			case SM_WIDGET_VEHICLES:   // Show vehicles
 			case SM_WIDGET_INDUSTRIES: // Show industries
+			case SM_WIDGET_LINKSTATS:   // Show route map
 			case SM_WIDGET_ROUTES:     // Show transport routes
 			case SM_WIDGET_VEGETATION: // Show vegetation
 			case SM_WIDGET_OWNERS:     // Show land owners
@@ -973,7 +1863,7 @@ public:
 				this->LowerWidget(this->map_type + SM_WIDGET_CONTOUR);
 
 				/* Hide Enable all/Disable all buttons if is not industry type small map */
-				this->GetWidget<NWidgetStacked>(SM_WIDGET_SELECTINDUSTRIES)->SetDisplayedPlane(this->map_type != SMT_INDUSTRY);
+				this->GetWidget<NWidgetStacked>(SM_WIDGET_SELECTINDUSTRIES)->SetDisplayedPlane(this->map_type != SMT_INDUSTRY && this->map_type != SMT_LINKSTATS);
 
 				this->SetDirty();
 				SndPlayFx(SND_15_BEEP);
@@ -995,12 +1885,13 @@ public:
 
 			case SM_WIDGET_LEGEND: // Legend
 				/* If industry type small map*/
-				if (this->map_type == SMT_INDUSTRY) {
+				if (this->map_type == SMT_INDUSTRY || this->map_type == SMT_LINKSTATS) {
 					/* If click on industries label, find right industry type and enable/disable it */
 					const NWidgetBase *wi = this->GetWidget<NWidgetBase>(SM_WIDGET_LEGEND); // Label panel
 					uint line = (pt.y - wi->pos_y - WD_FRAMERECT_TOP) / FONT_HEIGHT_SMALL;
 					uint columns = this->GetNumberColumnsLegend(wi->current_x);
-					uint number_of_rows = max((_smallmap_industry_count + columns - 1) / columns, this->min_number_of_fixed_rows);
+					uint entry_count = (this->map_type == SMT_INDUSTRY) ? _smallmap_industry_count : _smallmap_cargo_count;
+					uint number_of_rows = max((entry_count + columns - 1) / columns, this->min_number_of_fixed_rows);
 					if (line >= number_of_rows) break;
 
 					bool rtl = _dynlang.text_dir == TD_RTL;
@@ -1009,46 +1900,101 @@ public:
 					uint column = (x - WD_FRAMERECT_LEFT) / this->column_width;
 
 					/* Check if click is on industry label*/
-					int industry_pos = (column * number_of_rows) + line;
-					if (industry_pos < _smallmap_industry_count) {
-						_legend_from_industries[industry_pos].show_on_map = !_legend_from_industries[industry_pos].show_on_map;
+					int click_pos = (column * number_of_rows) + line;
+					if (this->map_type == SMT_INDUSTRY) {
+						if (click_pos < _smallmap_industry_count) {
+							_legend_from_industries[click_pos].show_on_map = !_legend_from_industries[click_pos].show_on_map;
+						}
+					} else if (this->map_type == SMT_LINKSTATS) {
+						if (click_pos < _smallmap_cargo_count) {
+							_legend_linkstats[click_pos].show_on_map = !_legend_linkstats[click_pos].show_on_map;
+						} else {
+							uint stats_column = _smallmap_cargo_count / number_of_rows;
+							if (_smallmap_cargo_count % number_of_rows != 0) stats_column++;
+
+							if (column == stats_column && line < NUM_STATS) {
+								click_pos = _smallmap_cargo_count + line;
+								_legend_linkstats[click_pos].show_on_map = !_legend_linkstats[click_pos].show_on_map;
+							}
+						}
 					}
 
 					/* Raise the two buttons "all", as we have done a specific choice */
-					this->RaiseWidget(SM_WIDGET_ENABLEINDUSTRIES);
-					this->RaiseWidget(SM_WIDGET_DISABLEINDUSTRIES);
+					this->RaiseWidget(SM_WIDGET_ENABLE_ALL);
+					this->RaiseWidget(SM_WIDGET_DISABLE_ALL);
 					this->SetDirty();
 				}
 				break;
 
-			case SM_WIDGET_ENABLEINDUSTRIES: // Enable all industries
-				for (int i = 0; i != _smallmap_industry_count; i++) {
-					_legend_from_industries[i].show_on_map = true;
+			case SM_WIDGET_ENABLE_ALL: { // Enable all items
+				LegendAndColour *tbl = (this->map_type == SMT_INDUSTRY) ? _legend_from_industries : _legend_linkstats;
+				for (; !tbl->end; ++tbl) {
+					tbl->show_on_map = true;
 				}
 				/* Toggle appeareance indicating the choice */
-				this->LowerWidget(SM_WIDGET_ENABLEINDUSTRIES);
-				this->RaiseWidget(SM_WIDGET_DISABLEINDUSTRIES);
+				this->LowerWidget(SM_WIDGET_ENABLE_ALL);
+				this->RaiseWidget(SM_WIDGET_DISABLE_ALL);
 				this->SetDirty();
 				break;
+			}
 
-			case SM_WIDGET_DISABLEINDUSTRIES: // Disable all industries
-				for (int i = 0; i != _smallmap_industry_count; i++) {
-					_legend_from_industries[i].show_on_map = false;
+			case SM_WIDGET_DISABLE_ALL: { // Disable all items
+				LegendAndColour *tbl = (this->map_type == SMT_INDUSTRY) ? _legend_from_industries : _legend_linkstats;
+				for (; !tbl->end; ++tbl) {
+					tbl->show_on_map = false;
 				}
 				/* Toggle appeareance indicating the choice */
-				this->RaiseWidget(SM_WIDGET_ENABLEINDUSTRIES);
-				this->LowerWidget(SM_WIDGET_DISABLEINDUSTRIES);
+				this->RaiseWidget(SM_WIDGET_ENABLE_ALL);
+				this->LowerWidget(SM_WIDGET_DISABLE_ALL);
 				this->SetDirty();
 				break;
+			}
 
 			case SM_WIDGET_SHOW_HEIGHT: // Enable/disable showing of heightmap.
-				_smallmap_industry_show_heightmap = !_smallmap_industry_show_heightmap;
-				this->SetWidgetLoweredState(SM_WIDGET_SHOW_HEIGHT, _smallmap_industry_show_heightmap);
+				_smallmap_show_heightmap = !_smallmap_show_heightmap;
+				this->SetWidgetLoweredState(SM_WIDGET_SHOW_HEIGHT, _smallmap_show_heightmap);
 				this->SetDirty();
 				break;
 		}
 	}
 
+	virtual void OnMouseWheel(int wheel)
+	{
+		/* Cursor position relative to window */
+		int cx = _cursor.pos.x - this->left;
+		int cy = _cursor.pos.y - this->top;
+
+		const NWidgetBase *wi = this->GetWidget<NWidgetBase>(SM_WIDGET_MAP);
+		/* Is cursor over the map ? */
+		if (IsInsideMM(cx, wi->pos_x, wi->pos_x + wi->current_x + 1) &&
+				IsInsideMM(cy, wi->pos_y, wi->pos_y + wi->current_y + 1)) {
+			/* Cursor position relative to map */
+			cx -= wi->pos_x;
+			cy -= wi->pos_y;
+
+			if (wheel < 0) {
+				this->ZoomIn(cx, cy);
+			} else {
+				this->ZoomOut(cx, cy);
+			}
+		}
+	}
+
+	virtual void OnMouseOver(Point pt, int widget) {
+		static Point invalid = {-1, -1};
+		if (pt.x != cursor.x || pt.y != cursor.y) {
+			this->refresh = 1;
+			if (widget == SM_WIDGET_MAP) {
+				cursor = pt;
+				cursor.x -= WD_FRAMERECT_LEFT;
+				cursor.y -= WD_FRAMERECT_TOP + WD_CAPTION_HEIGHT;
+			} else {
+				cursor = invalid;
+			}
+		}
+	}
+
+
 	virtual void OnRightClick(Point pt, int widget)
 	{
 		if (widget == SM_WIDGET_MAP) {
@@ -1062,6 +2008,8 @@ public:
 		/* Update the window every now and then */
 		if (--this->refresh != 0) return;
 
+		this->RecalcVehiclePositions();
+
 		this->refresh = FORCE_REFRESH_PERIOD;
 		this->SetDirty();
 	}
@@ -1069,56 +2017,49 @@ public:
 	virtual void OnScroll(Point delta)
 	{
 		_cursor.fix_at = true;
+		DoScroll(delta.x, delta.y);
+		this->SetDirty();
+	}
 
-		int x = this->scroll_x;
-		int y = this->scroll_y;
-
-		int sub = this->subscroll + delta.x;
-
-		x -= (sub >> 2) << 4;
-		y += (sub >> 2) << 4;
-		sub &= 3;
-
-		x += (delta.y >> 1) << 4;
-		y += (delta.y >> 1) << 4;
-
-		if (delta.y & 1) {
-			x += TILE_SIZE;
-			sub += 2;
-			if (sub > 3) {
-				sub -= 4;
-				x -= TILE_SIZE;
-				y += TILE_SIZE;
-			}
-		}
-
-		const NWidgetBase *wi = this->GetWidget<NWidgetBase>(SM_WIDGET_MAP);
-		int hx = wi->current_x / 2;
-		int hy = wi->current_y / 2;
-		int hvx = hx * -4 + hy * 8;
-		int hvy = hx *  4 + hy * 8;
-		if (x < -hvx) {
-			x = -hvx;
-			sub = 0;
-		}
-		if (x > (int)MapMaxX() * TILE_SIZE - hvx) {
-			x = MapMaxX() * TILE_SIZE - hvx;
-			sub = 0;
-		}
-		if (y < -hvy) {
-			y = -hvy;
-			sub = 0;
+	/**
+	 * Do the actual scrolling, but don't fix the cursor or set the window dirty.
+	 * @param dx x offset to scroll in screen dimension
+	 * @param dy y offset to scroll in screen dimension
+	 */
+	void DoScroll(int dx, int dy)
+	{
+		/* divide as late as possible to avoid premature reduction to 0, which causes "jumpy" behaviour
+		 * at the same time make sure this is the exact reverse function of the drawing methods in order to
+		 * avoid map indicators shifting around:
+		 * 1. add/subtract
+		 * 2. * TILE_SIZE
+		 * 3. scale
+		 */
+		int x = dy * 2 - dx;
+		int y = dx + dy * 2;
+
+		/* round to next divisible by 4 to allow for smoother scrolling */
+		int rem_x = abs(x % 4);
+		int rem_y = abs(y % 4);
+		if (rem_x != 0) {
+			x += x > 0 ? 4 - rem_x : rem_x - 4;
 		}
-		if (y > (int)MapMaxY() * TILE_SIZE - hvy) {
-			y = MapMaxY() * TILE_SIZE - hvy;
-			sub = 0;
+		if (rem_y != 0) {
+			y += y > 0 ? 4 - rem_y : rem_y - 4;
 		}
 
-		this->scroll_x = x;
-		this->scroll_y = y;
-		this->subscroll = sub;
+		this->scroll_x += ScaleByZoomLower(x / 4 * TILE_SIZE, this->zoom);
+		this->scroll_y += ScaleByZoomLower(y / 4 * TILE_SIZE, this->zoom);
 
-		this->SetDirty();
+		/* enforce the screen limits */
+		const NWidgetBase *wi = this->GetWidget<NWidgetBase>(SM_WIDGET_MAP);
+		int hx = wi->current_x;
+		int hy = wi->current_y;
+		int hvx = ScaleByZoomLower(hy * 4 - hx * 2, this->zoom);
+		int hvy = ScaleByZoomLower(hx * 2 + hy * 4, this->zoom);
+		this->scroll_x = Clamp(this->scroll_x, -hvx, MapMaxX() * TILE_SIZE - hvx);
+		this->scroll_y = Clamp(this->scroll_y, -hvy, MapMaxY() * TILE_SIZE - hvy);
+		this->refresh = REFRESH_NEXT_TICK;
 	}
 
 	void SmallMapCenterOnCurrentPos()
@@ -1126,12 +2067,16 @@ public:
 		const ViewPort *vp = FindWindowById(WC_MAIN_WINDOW, 0)->viewport;
 		const NWidgetBase *wi = this->GetWidget<NWidgetBase>(SM_WIDGET_MAP);
 
-		int x = ((vp->virtual_width  - (int)wi->current_x * TILE_SIZE) / 2 + vp->virtual_left) / 4;
-		int y = ((vp->virtual_height - (int)wi->current_y * TILE_SIZE) / 2 + vp->virtual_top ) / 2 - TILE_SIZE * 2;
-		this->scroll_x = (y - x) & ~0xF;
-		this->scroll_y = (x + y) & ~0xF;
+		int zoomed_width = ScaleByZoom(wi->current_x * TILE_SIZE, this->zoom);
+		int zoomed_height = ScaleByZoom(wi->current_y * TILE_SIZE, this->zoom);
+		int x  = ((vp->virtual_width - zoomed_width) / 2 + vp->virtual_left);
+		int y  = ((vp->virtual_height - zoomed_height) / 2 + vp->virtual_top);
+		this->scroll_x = (y * 2 - x) / 4;
+		this->scroll_y = (x + y * 2) / 4;
 		this->SetDirty();
 	}
+
+	uint ColumnWidth() const {return column_width;}
 };
 
 SmallMapWindow::SmallMapType SmallMapWindow::map_type = SMT_CONTOUR;
@@ -1225,14 +2170,18 @@ static const NWidgetPart _nested_smallmap_bar[] = {
 			NWidget(NWID_VERTICAL),
 				/* Top button row. */
 				NWidget(NWID_HORIZONTAL, NC_EQUALSIZE),
+					NWidget(WWT_PUSHIMGBTN, COLOUR_BROWN, SM_WIDGET_ZOOM_IN), SetDataTip(SPR_IMG_ZOOMIN, STR_TOOLBAR_TOOLTIP_ZOOM_THE_VIEW_IN),
 					NWidget(WWT_PUSHIMGBTN, COLOUR_BROWN, SM_WIDGET_CENTERMAP), SetDataTip(SPR_IMG_SMALLMAP, STR_SMALLMAP_CENTER),
+					NWidget(WWT_IMGBTN, COLOUR_BROWN, SM_WIDGET_BLANK), SetMinimalSize(22, 22), SetDataTip(SPR_DOT_SMALL, STR_NULL),
 					NWidget(WWT_IMGBTN, COLOUR_BROWN, SM_WIDGET_CONTOUR), SetDataTip(SPR_IMG_SHOW_COUNTOURS, STR_SMALLMAP_TOOLTIP_SHOW_LAND_CONTOURS_ON_MAP),
 					NWidget(WWT_IMGBTN, COLOUR_BROWN, SM_WIDGET_VEHICLES), SetDataTip(SPR_IMG_SHOW_VEHICLES, STR_SMALLMAP_TOOLTIP_SHOW_VEHICLES_ON_MAP),
 					NWidget(WWT_IMGBTN, COLOUR_BROWN, SM_WIDGET_INDUSTRIES), SetDataTip(SPR_IMG_INDUSTRY, STR_SMALLMAP_TOOLTIP_SHOW_INDUSTRIES_ON_MAP),
 				EndContainer(),
 				/* Bottom button row. */
 				NWidget(NWID_HORIZONTAL, NC_EQUALSIZE),
+					NWidget(WWT_PUSHIMGBTN, COLOUR_BROWN, SM_WIDGET_ZOOM_OUT), SetDataTip(SPR_IMG_ZOOMOUT, STR_TOOLBAR_TOOLTIP_ZOOM_THE_VIEW_OUT),
 					NWidget(WWT_IMGBTN, COLOUR_BROWN, SM_WIDGET_TOGGLETOWNNAME), SetDataTip(SPR_IMG_TOWN, STR_SMALLMAP_TOOLTIP_TOGGLE_TOWN_NAMES_ON_OFF),
+					NWidget(WWT_IMGBTN, COLOUR_BROWN, SM_WIDGET_LINKSTATS), SetDataTip(SPR_IMG_GRAPHS, STR_SMALLMAP_TOOLTIP_SHOW_LINK_STATS_ON_MAP),
 					NWidget(WWT_IMGBTN, COLOUR_BROWN, SM_WIDGET_ROUTES), SetDataTip(SPR_IMG_SHOW_ROUTES, STR_SMALLMAP_TOOLTIP_SHOW_TRANSPORT_ROUTES_ON),
 					NWidget(WWT_IMGBTN, COLOUR_BROWN, SM_WIDGET_VEGETATION), SetDataTip(SPR_IMG_PLANTTREES, STR_SMALLMAP_TOOLTIP_SHOW_VEGETATION_ON_MAP),
 					NWidget(WWT_IMGBTN, COLOUR_BROWN, SM_WIDGET_OWNERS), SetDataTip(SPR_IMG_COMPANY_GENERAL, STR_SMALLMAP_TOOLTIP_SHOW_LAND_OWNERS_ON_MAP),
@@ -1267,8 +2216,8 @@ static const NWidgetPart _nested_smallmap_widgets[] = {
 			NWidget(NWID_HORIZONTAL),
 				NWidget(NWID_SELECTION, INVALID_COLOUR, SM_WIDGET_SELECTINDUSTRIES),
 					NWidget(NWID_HORIZONTAL, NC_EQUALSIZE),
-						NWidget(WWT_TEXTBTN, COLOUR_BROWN, SM_WIDGET_ENABLEINDUSTRIES), SetDataTip(STR_SMALLMAP_ENABLE_ALL, STR_SMALLMAP_TOOLTIP_ENABLE_ALL),
-						NWidget(WWT_TEXTBTN, COLOUR_BROWN, SM_WIDGET_DISABLEINDUSTRIES), SetDataTip(STR_SMALLMAP_DISABLE_ALL, STR_SMALLMAP_TOOLTIP_DISABLE_ALL),
+						NWidget(WWT_TEXTBTN, COLOUR_BROWN, SM_WIDGET_ENABLE_ALL), SetDataTip(STR_SMALLMAP_ENABLE_ALL, STR_SMALLMAP_TOOLTIP_ENABLE_ALL),
+						NWidget(WWT_TEXTBTN, COLOUR_BROWN, SM_WIDGET_DISABLE_ALL), SetDataTip(STR_SMALLMAP_DISABLE_ALL, STR_SMALLMAP_TOOLTIP_DISABLE_ALL),
 						NWidget(WWT_TEXTBTN, COLOUR_BROWN, SM_WIDGET_SHOW_HEIGHT), SetDataTip(STR_SMALLMAP_SHOW_HEIGHT, STR_SMALLMAP_TOOLTIP_SHOW_HEIGHT),
 					EndContainer(),
 					NWidget(NWID_SPACER), SetFill(1, 1),
@@ -1281,7 +2230,7 @@ static const NWidgetPart _nested_smallmap_widgets[] = {
 };
 
 static const WindowDesc _smallmap_desc(
-	WDP_AUTO, 446, 314,
+	WDP_AUTO, 488, 314,
 	WC_SMALLMAP, WC_NONE,
 	WDF_UNCLICK_BUTTONS,
 	_nested_smallmap_widgets, lengthof(_nested_smallmap_widgets)
diff --git a/src/sound.cpp b/src/sound.cpp
index 1fc5291..d756d84 100644
--- a/src/sound.cpp
+++ b/src/sound.cpp
@@ -180,7 +180,7 @@ static void StartSound(SoundID sound_id, int panning, uint volume)
 
 
 static const byte _vol_factor_by_zoom[] = {255, 190, 134, 87};
-assert_compile(lengthof(_vol_factor_by_zoom) == ZOOM_LVL_COUNT);
+assert_compile(lengthof(_vol_factor_by_zoom) == ZOOM_LVL_BLITTER_COUNT);
 
 static const byte _sound_base_vol[] = {
 	128,  90, 128, 128, 128, 128, 128, 128,
@@ -244,7 +244,7 @@ static void SndPlayScreenCoordFx(SoundID sound, int left, int right, int top, in
 			StartSound(
 				sound,
 				panning,
-				(msf.effect_vol * _vol_factor_by_zoom[vp->zoom - ZOOM_LVL_BEGIN]) / 256
+				(msf.effect_vol * _vol_factor_by_zoom[vp->zoom - ZOOM_LVL_BLITTER_MIN]) / 256
 			);
 			return;
 		}
diff --git a/src/station.cpp b/src/station.cpp
index 659ece3..030d7d9 100644
--- a/src/station.cpp
+++ b/src/station.cpp
@@ -69,6 +69,16 @@ Station::~Station()
 		if (a->targetairport == this->index) a->targetairport = INVALID_STATION;
 	}
 
+	Station * st;
+	FOR_ALL_STATIONS(st) {
+		for (CargoID c = CT_BEGIN; c != CT_END; ++c) {
+			GoodsEntry & ge = st->goods[c];
+			ge.link_stats.erase(this->index);
+			DeleteStaleFlows(st->index, c, this->index);
+			ge.cargo.RerouteStalePackets(this->index, this->index, &ge);
+		}
+	}
+	
 	Vehicle *v;
 	FOR_ALL_VEHICLES(v) {
 		/* Forget about this station if this station is removed */
diff --git a/src/station_base.h b/src/station_base.h
index cdf06c0..8f231d1 100644
--- a/src/station_base.h
+++ b/src/station_base.h
@@ -18,13 +18,106 @@
 #include "cargo_type.h"
 #include "industry_type.h"
 #include "core/geometry_type.hpp"
+#include "linkgraph/linkgraph_types.h"
 #include <list>
+#include <map>
+#include <set>
 
 typedef Pool<BaseStation, StationID, 32, 64000> StationPool;
 extern StationPool _station_pool;
 
 static const byte INITIAL_STATION_RATING = 175;
 
+class LinkStat {
+public:
+	uint capacity;
+	uint frozen;
+	uint usage;
+	LinkStat() : capacity(0), frozen(0), usage(0) {}
+
+	inline LinkStat & operator*=(uint factor) {
+		capacity *= factor;
+		usage *= factor;
+		return *this;
+	}
+
+	inline LinkStat & operator/=(uint divident) {
+		capacity /= divident;
+		if (capacity < frozen) {
+			capacity = frozen;
+		}
+		usage /= divident;
+		return *this;
+	}
+
+	inline LinkStat & operator+=(const LinkStat & other)
+	{
+		this->capacity += other.capacity;
+		this->usage += other.usage;
+		this->frozen += other.frozen;
+		return *this;
+	}
+
+	inline void Clear()
+	{
+		this->capacity = 0;
+		this->usage = 0;
+		this->frozen = 0;
+	}
+};
+
+class FlowStat {
+public:
+	FlowStat(StationID st = INVALID_STATION, uint p = 0, uint s = 0) :
+		planned(p), sent(s), via(st) {}
+	uint planned;
+	uint sent;
+	StationID via;
+	struct comp {
+		bool operator()(const FlowStat & x, const FlowStat & y) const {
+			int diff_x = (int)x.planned - (int)x.sent;
+			int diff_y = (int)y.planned - (int)y.sent;
+			if (diff_x != diff_y) {
+				return diff_x > diff_y;
+			} else {
+				return x.via > y.via;
+			}
+		}
+	};
+
+	inline FlowStat & operator*=(uint factor) {
+		planned *= factor;
+		sent *= factor;
+		return *this;
+	}
+
+	inline FlowStat & operator/=(uint divident) {
+		planned /= divident;
+		sent /= divident;
+		return *this;
+	}
+
+	inline FlowStat & operator+=(const FlowStat & other)
+	{
+		assert(this->via == INVALID_STATION || other.via == INVALID_STATION || this->via == other.via);
+		this->via = other.via;
+		this->planned += other.planned;
+		this->sent += other.sent;
+		return *this;
+	}
+
+	inline void Clear()
+	{
+		this->planned = 0;
+		this->sent = 0;
+		this->via = INVALID_STATION;
+	}
+};
+
+typedef std::set<FlowStat, FlowStat::comp> FlowStatSet; ///< percentage of flow to be sent via specified station (or consumed locally)
+typedef std::map<StationID, LinkStat> LinkStatMap;
+typedef std::map<StationID, FlowStatSet> FlowStatMap; ///< flow descriptions by origin stations
+
 struct GoodsEntry {
 	enum AcceptancePickup {
 		ACCEPTANCE,
@@ -36,7 +129,8 @@ struct GoodsEntry {
 		days_since_pickup(255),
 		rating(INITIAL_STATION_RATING),
 		last_speed(0),
-		last_age(255)
+		last_age(255),
+		last_component(0)
 	{}
 
 	byte acceptance_pickup;
@@ -45,6 +139,25 @@ struct GoodsEntry {
 	byte last_speed;
 	byte last_age;
 	StationCargoList cargo; ///< The cargo packets of cargo waiting in this station
+	uint supply;
+	FlowStatMap flows;      ///< The planned flows through this station
+	LinkStatMap link_stats; ///< capacities and usage statistics for outgoing links
+	LinkGraphComponentID last_component; ///< the component this station was last part of in this cargo's link graph
+	
+	FlowStat GetSumFlowVia(StationID via) const;
+
+	/**
+	 * update the flow stats for count cargo from source sent to next
+	 */
+	void UpdateFlowStats(StationID source, uint count, StationID next);
+	void UpdateFlowStats(FlowStatSet &flow_stats, uint count, StationID next);
+	void UpdateFlowStats(FlowStatSet &flow_stats, FlowStatSet::iterator flow_it, uint count);
+
+	/**
+	 * update the flow stats for count cargo that cannot be delivered here
+	 * return the direction where it is sent
+	 */
+	StationID UpdateFlowStatsTransfer(StationID source, uint count, StationID curr);
 };
 
 
diff --git a/src/station_cmd.cpp b/src/station_cmd.cpp
index e3cba4b..e273203 100644
--- a/src/station_cmd.cpp
+++ b/src/station_cmd.cpp
@@ -2903,6 +2903,169 @@ static void UpdateStationRating(Station *st)
 	}
 }
 
+void DeleteStaleFlows(StationID at, CargoID c_id, StationID to) {
+	FlowStatMap & flows = Station::Get(at)->goods[c_id].flows;
+	for (FlowStatMap::iterator f_it = flows.begin(); f_it != flows.end();) {
+		FlowStatSet & s_flows = f_it->second;
+		for (FlowStatSet::iterator s_it = s_flows.begin(); s_it != s_flows.end();) {
+			if (s_it->via == to) {
+				s_flows.erase(s_it++);
+			} else {
+				++s_it;
+			}
+		}
+		if (s_flows.empty()) {
+			flows.erase(f_it++);
+		} else {
+			++f_it;
+		}
+	}
+}
+
+static void UpdateStationStats(Station * st) {
+	uint length = _settings_game.economy.moving_average_length;
+	FlowStatSet new_flows;
+	for(int goods_index = CT_BEGIN; goods_index != CT_END; ++goods_index) {
+		GoodsEntry & good = st->goods[goods_index];
+		good.supply = DivideApprox(good.supply * length, length + 1);
+		LinkStatMap & links = good.link_stats;
+		for (LinkStatMap::iterator i = links.begin(); i != links.end();) {
+			StationID id = i->first;
+			if (!Station::IsValidID(id)) {
+				links.erase(i++);
+			} else {
+				LinkStat & ls = i->second;
+				ls *= length;
+				ls /= (length + 1);
+				if (ls.capacity == 0) {
+					DeleteStaleFlows(st->index, goods_index, id);
+					good.cargo.RerouteStalePackets(st->index, id, &good);
+					links.erase(i++);
+				} else {
+					++i;
+				}
+			}
+		}
+
+		FlowStatMap & flows = good.flows;
+		for (FlowStatMap::iterator i = flows.begin(); i != flows.end();) {
+			StationID source = i->first;
+			if (!Station::IsValidID(source)) {
+				flows.erase(i++);
+			} else {
+				FlowStatSet & flow_set = i->second;
+				for (FlowStatSet::iterator j = flow_set.begin(); j != flow_set.end(); ++j) {
+					StationID via = j->via;
+					if (Station::IsValidID(via)) {
+						new_flows.insert(FlowStat(via, j->planned, (j->sent * length) / (length + 1)));
+					}
+				}
+				flow_set.swap(new_flows);
+				new_flows.clear();
+				++i;
+			}
+		}
+	}
+}
+
+void UpdateFlows(Station * st, Vehicle *front, StationID next_station_id) {
+	if (next_station_id == INVALID_STATION) {
+		return;
+	} else {
+		for (Vehicle *v = front; v != NULL; v = v->Next()) {
+			GoodsEntry *ge = &st->goods[v->cargo_type];
+			const CargoPacketList * packets = v->cargo.Packets();
+			for(VehicleCargoList::ConstIterator i = packets->begin(); i != packets->end(); ++i) {
+				CargoPacket * p = *i;
+				ge->UpdateFlowStats(p->SourceStation(), p->Count(), next_station_id);
+			}
+		}
+	}
+}
+
+void IncreaseFrozen(Station *st, const Vehicle *front, StationID next_station_id) {
+	assert(st->index != next_station_id && next_station_id != INVALID_STATION);
+	for (const Vehicle *v = front; v != NULL; v = v->Next()) {
+		if (v->cargo_cap > 0) {
+			LinkStat & ls = st->goods[v->cargo_type].link_stats[next_station_id];
+			ls.frozen += v->cargo_cap;
+			ls.capacity = max(ls.capacity, ls.frozen);
+			assert(ls.capacity > 0);
+		}
+	}
+}
+
+void RecalcFrozenIfLoading(const Vehicle * v) {
+	if (v->current_order.IsType(OT_LOADING)) {
+		RecalcFrozen(Station::Get(v->last_station_visited));
+	}
+}
+
+void RecalcFrozen(Station * st) {
+	if (st->loading_vehicles.empty()) {
+		/* if no vehicles are there the frozen values are always correct */
+		return;
+	}
+
+	for(int goods_index = CT_BEGIN; goods_index != CT_END; ++goods_index) {
+		GoodsEntry & good = st->goods[goods_index];
+		LinkStatMap & links = good.link_stats;
+		for (LinkStatMap::iterator i = links.begin(); i != links.end(); ++i) {
+			i->second.frozen = 0;
+		}
+	}
+
+	std::list<Vehicle *>::iterator v_it = st->loading_vehicles.begin();
+	while(v_it != st->loading_vehicles.end()) {
+		const Vehicle * front = *v_it;
+		OrderList * orders = front->orders.list;
+		if (orders != NULL) {
+			StationID next_station_id = orders->GetNextStoppingStation(front->cur_order_index, front->type == VEH_ROAD || front->type == VEH_TRAIN);
+			if (next_station_id != INVALID_STATION && next_station_id != st->index) {
+				IncreaseFrozen(st, front, next_station_id);
+			}
+		}
+		++v_it;
+	}
+}
+
+void DecreaseFrozen(Station *st, const Vehicle *front, StationID next_station_id) {
+	assert(st->index != next_station_id && next_station_id != INVALID_STATION);
+	for (const Vehicle *v = front; v != NULL; v = v->Next()) {
+		if (v->cargo_cap > 0) {
+			LinkStatMap & link_stats = st->goods[v->cargo_type].link_stats;
+			LinkStatMap::iterator lstat_it = link_stats.find(next_station_id);
+			if (lstat_it == link_stats.end()) {
+				DEBUG(misc, 0, "frozen not in linkstat list.");
+				RecalcFrozen(st);
+				return;
+			} else {
+				LinkStat & link_stat = lstat_it->second;
+				if (link_stat.frozen < v->cargo_cap) {
+					DEBUG(misc, 0, "frozen is smaller than cargo cap.");
+					RecalcFrozen(st);
+					return;
+				} else {
+					link_stat.frozen -= v->cargo_cap;
+				}
+				assert(link_stat.capacity > 0);
+			}
+		}
+	}
+}
+
+void IncreaseStats(Station *st, const Vehicle *front, StationID next_station_id) {
+	assert(st->index != next_station_id && next_station_id != INVALID_STATION);
+	for (const Vehicle *v = front; v != NULL; v = v->Next()) {
+		if (v->cargo_cap > 0) {
+			LinkStat & link_stat = st->goods[v->cargo_type].link_stats[next_station_id];
+			link_stat.capacity += v->cargo_cap;
+			link_stat.usage += v->cargo.Count();
+			assert(link_stat.capacity > 0);
+		}
+	}
+}
+
 /* called for every station each tick */
 static void StationHandleSmallTick(BaseStation *st)
 {
@@ -2922,7 +3085,13 @@ void OnTick_Station()
 	BaseStation *st;
 	FOR_ALL_BASE_STATIONS(st) {
 		StationHandleSmallTick(st);
-
+		if (Station::IsExpected(st)) {
+			Station * real_st = Station::From(st);
+			// update the station statistics every <unit> days
+			if ((_tick_counter + real_st->index) % (DAY_TICKS * _settings_game.economy.moving_average_unit) == 0) {
+				UpdateStationStats(real_st);
+			}
+		}
 		/* Run 250 tick interval trigger for station animation.
 		 * Station index is included so that triggers are not all done
 		 * at the same time. */
@@ -2960,8 +3129,23 @@ void ModifyStationRatingAround(TileIndex tile, Owner owner, int amount, uint rad
 
 static void UpdateStationWaiting(Station *st, CargoID type, uint amount, SourceType source_type, SourceID source_id)
 {
-	st->goods[type].cargo.Append(new CargoPacket(st->index, st->xy, amount, source_type, source_id));
-	SetBit(st->goods[type].acceptance_pickup, GoodsEntry::PICKUP);
+	GoodsEntry & good = st->goods[type];
+	StationID id = st->index;
+	StationID next = INVALID_STATION;
+	FlowStatSet & flow_stats = good.flows[id];
+	FlowStatSet::iterator i = flow_stats.begin();
+	if (i != flow_stats.end()) {
+		StationID via = i->via;
+		uint planned = i->planned;
+		uint sent = i->sent + amount;
+		flow_stats.erase(i);
+		flow_stats.insert(FlowStat(via, planned, sent));
+		next = via;
+	}
+	CargoPacket * packet = new CargoPacket(st->index, st->xy, amount, source_type, source_id);
+	good.cargo.Append(next, packet);
+	SetBit(good.acceptance_pickup, GoodsEntry::PICKUP);
+	good.supply += amount;
 
 	StationAnimationTrigger(st, st->xy, STAT_ANIM_NEW_CARGO, type);
 
@@ -3317,6 +3501,67 @@ static CommandCost TerraformTile_Station(TileIndex tile, DoCommandFlag flags, ui
 	return DoCommand(tile, 0, 0, flags, CMD_LANDSCAPE_CLEAR);
 }
 
+void GoodsEntry::UpdateFlowStats(FlowStatSet &flow_stats, FlowStatSet::iterator flow_it, uint count)
+{
+	uint planned = flow_it->planned;
+	uint sent = flow_it->sent + count;
+	StationID via = flow_it->via;
+	flow_stats.erase(flow_it);
+	flow_stats.insert(FlowStat(via, planned, sent));
+}
+
+void GoodsEntry::UpdateFlowStats(FlowStatSet &flow_stats, uint count, StationID next)
+{
+	FlowStatSet::iterator flow_it = flow_stats.begin();
+	while (flow_it != flow_stats.end()) {
+		StationID via = flow_it->via;
+		if (via == next) { //usually the first one is the correct one
+			this->UpdateFlowStats(flow_stats, flow_it, count);
+			return;
+		} else {
+			++flow_it;
+		}
+	}
+}
+
+void GoodsEntry::UpdateFlowStats(StationID source, uint count, StationID next)
+{
+	if (source == INVALID_STATION || next == INVALID_STATION || flows.empty()) return;
+	FlowStatSet & flow_stats = flows[source];
+	this->UpdateFlowStats(flow_stats, count, next);
+}
+
+StationID GoodsEntry::UpdateFlowStatsTransfer(StationID source, uint count, StationID curr) {
+	if (source == INVALID_STATION || flows.empty()) return INVALID_STATION;
+	FlowStatSet & flow_stats = flows[source];
+	FlowStatSet::iterator flow_it = flow_stats.begin();
+	while (flow_it != flow_stats.end()) {
+		StationID via = flow_it->via;
+		if (via != curr) {
+			UpdateFlowStats(flow_stats, flow_it, count);
+			return via;
+		}
+		else {
+			++flow_it;
+		}
+	}
+	return INVALID_STATION;
+}
+
+FlowStat GoodsEntry::GetSumFlowVia(StationID via) const {
+	FlowStat ret(via);
+	for(FlowStatMap::const_iterator i = flows.begin(); i != flows.end(); ++i) {
+		const FlowStatSet & flow_set = i->second;
+		for (FlowStatSet::const_iterator j = flow_set.begin(); j != flow_set.end(); ++j) {
+			const FlowStat & flow = *j;
+			if (flow.via == via) {
+				ret.planned += flow.planned;
+				ret.sent += flow.sent;
+			}
+		}
+	}
+	return ret;
+}
 
 extern const TileTypeProcs _tile_type_station_procs = {
 	DrawTile_Station,           // draw_tile_proc
diff --git a/src/station_func.h b/src/station_func.h
index ecc794a..f6a1c36 100644
--- a/src/station_func.h
+++ b/src/station_func.h
@@ -50,4 +50,18 @@ bool IsStationTileElectrifiable(TileIndex tile);
 
 void UpdateAirportsNoise();
 
+void DecreaseFrozen(Station *st, const Vehicle *v, StationID next_station_id);
+
+void IncreaseFrozen(Station *st, const Vehicle *v, StationID next_station_id);
+
+void RecalcFrozen(Station * st);
+
+void RecalcFrozenIfLoading(const Vehicle * v);
+
+void IncreaseStats(Station *st, const Vehicle *v, StationID next_station_id);
+
+void UpdateFlows(Station * st, Vehicle *v, StationID next_station_id);
+
+void DeleteStaleFlows(StationID at, CargoID c_id, StationID to);
+
 #endif /* STATION_FUNC_H */
diff --git a/src/station_gui.cpp b/src/station_gui.cpp
index 45208d8..a1a1ca9 100644
--- a/src/station_gui.cpp
+++ b/src/station_gui.cpp
@@ -34,6 +34,8 @@
 #include "table/strings.h"
 #include "table/sprites.h"
 
+#include <vector>
+
 /**
  * Draw a (multi)line of cargos seperated by commas, and prefixed with a string.
  * @param cargo_mask Mask of cargos to include in the list.
@@ -824,10 +826,18 @@ static const NWidgetPart _nested_station_view_widgets[] = {
 		NWidget(WWT_STICKYBOX, COLOUR_GREY),
 	EndContainer(),
 	NWidget(NWID_HORIZONTAL),
-		NWidget(WWT_PANEL, COLOUR_GREY, SVW_WAITING), SetMinimalSize(237, 52), SetResize(1, 10), EndContainer(),
+		NWidget(WWT_PUSHTXTBTN, COLOUR_GREY, SVW_SORT_ORDER), SetMinimalSize(81, 12), SetFill(1, 1), SetDataTip(STR_BUTTON_SORT_BY, STR_TOOLTIP_SORT_ORDER),
+		NWidget(WWT_PUSHTXTBTN, COLOUR_GREY, SVW_SORT_BY), SetMinimalSize(168, 12), SetResize(1, 0), SetFill(0, 1), SetDataTip(0x0, STR_TOOLTIP_SORT_CRITERIAP),
+	EndContainer(),
+	NWidget(NWID_HORIZONTAL),
+		NWidget(WWT_DROPDOWN, COLOUR_GREY, SVW_MODE), SetMinimalSize(81, 12), SetFill(1, 1), SetDataTip(STR_STATION_VIEW_WAITING, STR_STATION_VIEW_TOGGLE_CARGO_VIEW),
+		NWidget(WWT_DROPDOWN, COLOUR_GREY, SVW_GROUP_BY), SetMinimalSize(168, 12), SetResize(1, 0), SetFill(0, 1), SetDataTip(0x0, STR_TOOLTIP_GROUP_ORDER),
+	EndContainer(),
+	NWidget(NWID_HORIZONTAL),
+		NWidget(WWT_PANEL, COLOUR_GREY, SVW_WAITING), SetMinimalSize(237, 44), SetResize(1, 10), EndContainer(),
 		NWidget(WWT_SCROLLBAR, COLOUR_GREY, SVW_SCROLLBAR),
 	EndContainer(),
-	NWidget(WWT_PANEL, COLOUR_GREY, SVW_ACCEPTLIST), SetMinimalSize(249, 32), SetResize(1, 0), EndContainer(),
+	NWidget(WWT_PANEL, COLOUR_GREY, SVW_ACCEPTLIST), SetMinimalSize(249, 23), SetResize(1, 0), EndContainer(),
 	NWidget(NWID_HORIZONTAL, NC_EQUALSIZE),
 		NWidget(WWT_PUSHTXTBTN, COLOUR_GREY, SVW_LOCATION), SetMinimalSize(60, 12), SetResize(1, 0), SetFill(1, 1),
 				SetDataTip(STR_BUTTON_LOCATION, STR_STATION_VIEW_CENTER_TOOLTIP),
@@ -860,33 +870,228 @@ static void DrawCargoIcons(CargoID i, uint waiting, int left, int right, int y)
 
 	SpriteID sprite = CargoSpec::Get(i)->GetCargoIcon();
 
-	int x = _dynlang.text_dir == TD_RTL ? right - num * 10 : left;
+	int x = _dynlang.text_dir == TD_RTL ? left : right - num * 10;
 	do {
 		DrawSprite(sprite, PAL_NONE, x, y);
 		x += 10;
 	} while (--num);
 }
 
-struct CargoData {
-	CargoID cargo;
-	StationID source;
-	uint count;
+CargoDataEntry::CargoDataEntry() :
+	parent(NULL),
+	station(INVALID_STATION),
+	size(0),
+	count(0),
+	subentries(new CargoDataSet(CargoSorter(ST_CARGO_ID)))
+{}
+
+CargoDataEntry::CargoDataEntry(CargoID car, uint c, CargoDataEntry * p) :
+	parent(p),
+	cargo(car),
+	size(0),
+	count(c),
+	subentries(new CargoDataSet)
+{}
+
+CargoDataEntry::CargoDataEntry(StationID st, uint c, CargoDataEntry * p) :
+	parent(p),
+	station(st),
+	size(0),
+	count(c),
+	subentries(new CargoDataSet)
+{}
+
+CargoDataEntry::CargoDataEntry(StationID st) :
+	parent(NULL),
+	station(st),
+	size(0),
+	count(0),
+	subentries(NULL)
+{}
+
+CargoDataEntry::CargoDataEntry(CargoID ca) :
+	parent(NULL),
+	cargo(ca),
+	size(0),
+	count(0),
+	subentries(NULL)
+{}
+
+CargoDataEntry::~CargoDataEntry() {
+	this->Clear();
+	delete subentries;
+}
 
-	CargoData(CargoID cargo, StationID source, uint count) :
-		cargo(cargo),
-		source(source),
-		count(count)
-	{ }
-};
+void CargoDataEntry::Clear() {
+	if (subentries != NULL) {
+		for (CargoDataSet::iterator i = subentries->begin(); i != subentries->end(); ++i) {
+			assert(*i != this);
+			delete *i;
+		}
+		subentries->clear();
+	}
+	if (parent != NULL) {
+		parent->count -= this->count;
+	}
+	this->count = 0;
+	this->size = 0;
+}
 
-typedef std::list<CargoData> CargoDataList;
+void CargoDataEntry::Remove(CargoDataEntry * comp) {
+	CargoDataSet::iterator i = subentries->find(comp);
+	if (i != subentries->end()) {
+		delete(*i);
+		subentries->erase(i);
+	}
+}
+
+template<class ID>
+CargoDataEntry * CargoDataEntry::InsertOrRetrieve(ID s) {
+	CargoDataEntry tmp(s);
+	CargoDataSet::iterator i = subentries->find(&tmp);
+	if (i == subentries->end()) {
+		IncrementSize();
+		return *(subentries->insert(new CargoDataEntry(s, 0, this)).first);
+	} else {
+		CargoDataEntry * ret = *i;
+		assert(subentries->value_comp().GetSortType() != ST_COUNT);
+		return ret;
+	}
+}
+
+void CargoDataEntry::Update(uint count) {
+	this->count += count;
+	if (parent != NULL) {
+		parent->Update(count);
+	}
+}
+
+void CargoDataEntry::IncrementSize() {
+	 ++size;
+	 if (parent != NULL) parent->IncrementSize();
+}
+
+void CargoDataEntry::Resort(CargoSortType type, SortOrder order) {
+	CargoDataSet * new_subs = new CargoDataSet(subentries->begin(), subentries->end(), CargoSorter(type, order));
+	delete subentries;
+	subentries = new_subs;
+}
+
+CargoDataEntry * CargoDataEntry::Retrieve(CargoDataSet::iterator i) const {
+	if (i == subentries->end()) {
+		return NULL;
+	} else {
+		assert(subentries->value_comp().GetSortType() != ST_COUNT);
+		return *i;
+	}
+}
+
+bool CargoSorter::operator()(const CargoDataEntry * cd1, const CargoDataEntry * cd2) const {
+	switch (type) {
+	case ST_STATION_ID:
+		return SortId<StationID>(cd1->GetStation(), cd2->GetStation());
+		break;
+	case ST_CARGO_ID:
+		return SortId<CargoID>(cd1->GetCargo(), cd2->GetCargo());
+		break;
+	case ST_COUNT:
+		return SortCount(cd1, cd2);
+		break;
+	case ST_STATION_STRING:
+		return SortStation(cd1->GetStation(), cd2->GetStation());
+		break;
+	default:
+		NOT_REACHED();
+	}
+	return false;
+}
+
+template<class ID>
+bool CargoSorter::SortId(ID st1, ID st2) const {
+	if (order == SO_ASCENDING) {
+		return st1 < st2;
+	} else {
+		return st2 < st1;
+	}
+}
+
+bool CargoSorter::SortCount(const CargoDataEntry *cd1, const CargoDataEntry *cd2) const {
+	uint c1 = cd1->GetCount();
+	uint c2 = cd2->GetCount();
+	if (c1 == c2) {
+		return SortStation(cd1->GetStation(), cd2->GetStation());
+	} else if (order == SO_ASCENDING) {
+		return c1 < c2;
+	} else {
+		return c2 < c1;
+	}
+}
+
+bool CargoSorter::SortStation(StationID st1, StationID st2) const {
+	static char buf1[64];
+	static char buf2[64];
+
+	if (!Station::IsValidID(st1)) {
+		if (!Station::IsValidID(st2)) {
+			return SortId(st1, st2);
+		} else {
+			return order == SO_ASCENDING;
+		}
+	} else if (!Station::IsValidID(st2)) {
+		return order == SO_DESCENDING;
+	}
+
+	SetDParam(0, st1);
+	GetString(buf1, STR_STATION_NAME, lastof(buf1));
+	SetDParam(0, st2);
+	GetString(buf2, STR_STATION_NAME, lastof(buf2));
+
+	int res = strcmp(buf1, buf2);
+	if (res == 0) {
+		return SortId(st1, st2);
+	} else if (res < 0) {
+		return order == SO_ASCENDING;
+	} else {
+		return order == SO_DESCENDING;
+	}
+}
 
 /**
  * The StationView window
  */
 struct StationViewWindow : public Window {
-	uint32 cargo;                 ///< Bitmask of cargo types to expand
-	uint16 cargo_rows[NUM_CARGO]; ///< Header row for each cargo type
+	struct RowDisplay {
+		RowDisplay(CargoDataEntry * f, StationID n) : filter(f), next_station(n) {}
+		RowDisplay(CargoDataEntry * f, CargoID n) : filter(f), next_cargo(n) {}
+		CargoDataEntry * filter;
+		union {
+			StationID next_station;
+			CargoID next_cargo;
+		};
+	};
+
+	typedef std::vector<RowDisplay> CargoDataVector;
+
+	static const int _num_columns = 4;
+
+	enum Invalidation {
+		INV_FLOWS = 0x100,
+		INV_CARGO = 0x200
+	};
+
+	enum Grouping {
+		GR_SOURCE,
+		GR_NEXT,
+		GR_DESTINATION,
+		GR_CARGO,
+	};
+
+	enum Mode {
+		WAITING,
+		PLANNED,
+		SENT
+	};
+	
 	uint expand_shrink_width;     ///< The width allocated to the expand/shrink 'button'
 
 	/** Height of the #SVW_ACCEPTLIST widget for different views. */
@@ -895,12 +1100,36 @@ struct StationViewWindow : public Window {
 		ALH_ACCEPTS = 3,  ///< Height of the accepted cargo view.
 	};
 
-	StationViewWindow(const WindowDesc *desc, WindowNumber window_number) : Window()
+	static const StringID _sort_names[];
+	static const StringID _group_names[];
+	static const StringID _mode_names[];
+
+	CargoSortType sortings[_num_columns];
+	SortOrder sort_orders[_num_columns];
+
+	int scroll_to_row;
+	int grouping_index;
+	Mode current_mode;
+	Grouping groupings[_num_columns];
+
+	CargoDataEntry expanded_rows;
+	CargoDataEntry cached_destinations;
+	CargoDataVector displayed_rows;
+
+	StationViewWindow(const WindowDesc *desc, WindowNumber window_number) : Window(),
+		scroll_to_row(INT_MAX), grouping_index(0)
 	{
 		this->CreateNestedTree(desc);
 		/* Nested widget tree creation is done in two steps to ensure that this->GetWidget<NWidgetCore>(SVW_ACCEPTS) exists in UpdateWidgetSize(). */
 		this->FinishInitNested(desc, window_number);
 
+		this->groupings[0] = GR_CARGO;
+		this->sortings[0] = ST_AS_GROUPING;
+		this->SelectGroupBy(_settings_client.gui.station_gui_group_order);
+		this->SelectSortBy((CargoSortType)_settings_client.gui.station_gui_sort_by);
+		this->sort_orders[0] = SO_ASCENDING;
+		this->SelectSortOrder((SortOrder)_settings_client.gui.station_gui_sort_order);
+		this->SelectMode(WAITING);
 		Owner owner = Station::Get(window_number)->owner;
 		if (owner != OWNER_NONE) this->owner = owner;
 	}
@@ -915,12 +1144,39 @@ struct StationViewWindow : public Window {
 		DeleteWindowById(WC_AIRCRAFT_LIST, wno | (VEH_AIRCRAFT << 11), false);
 	}
 
+	void ShowCargo(CargoDataEntry * data, CargoID cargo, StationID source, StationID next, StationID dest, uint count) {
+		if (count == 0) return;
+		const CargoDataEntry * expand = &expanded_rows;
+		for (int i = 0; i < _num_columns && expand != NULL; ++i) {
+			switch (groupings[i]) {
+			case GR_CARGO:
+				assert(i == 0);
+				data = data->InsertOrRetrieve(cargo);
+				expand = expand->Retrieve(cargo);
+				break;
+			case GR_SOURCE:
+				data = data->InsertOrRetrieve(source);
+				expand = expand->Retrieve(source);
+				break;
+			case GR_NEXT:
+				data = data->InsertOrRetrieve(next);
+				expand = expand->Retrieve(next);
+				break;
+			case GR_DESTINATION:
+				data = data->InsertOrRetrieve(dest);
+				expand = expand->Retrieve(dest);
+				break;
+			}
+		}
+		data->Update(count);
+	}
+
 	virtual void UpdateWidgetSize(int widget, Dimension *size, const Dimension &padding, Dimension *fill, Dimension *resize)
 	{
 		switch (widget) {
 			case SVW_WAITING:
 				resize->height = FONT_HEIGHT_NORMAL;
-				size->height = WD_FRAMERECT_TOP + 5 * resize->height + WD_FRAMERECT_BOTTOM;
+				size->height = WD_FRAMERECT_TOP + 4 * resize->height + WD_FRAMERECT_BOTTOM;
 				this->expand_shrink_width = max(GetStringBoundingBox("-").width, GetStringBoundingBox("+").width) + WD_FRAMERECT_LEFT + WD_FRAMERECT_RIGHT;
 				break;
 
@@ -932,26 +1188,37 @@ struct StationViewWindow : public Window {
 
 	virtual void OnPaint()
 	{
-		CargoDataList cargolist;
-		uint32 transfers = 0;
-		this->OrderWaitingCargo(&cargolist, &transfers);
+		const Station *st = Station::Get(this->window_number);
+		CargoDataEntry cargo;
+		BuildCargoList(&cargo, st);
 
-		this->vscroll.SetCount((int)cargolist.size() + 1); // update scrollbar
+		this->vscroll.SetCount(cargo.Size()); // update scrollbar
 
 		/* disable some buttons */
-		const Station *st = Station::Get(this->window_number);
 		this->SetWidgetDisabledState(SVW_RENAME,   st->owner != _local_company);
 		this->SetWidgetDisabledState(SVW_TRAINS,   !(st->facilities & FACIL_TRAIN));
 		this->SetWidgetDisabledState(SVW_ROADVEHS, !(st->facilities & FACIL_TRUCK_STOP) && !(st->facilities & FACIL_BUS_STOP));
 		this->SetWidgetDisabledState(SVW_PLANES,   !(st->facilities & FACIL_AIRPORT));
 		this->SetWidgetDisabledState(SVW_SHIPS,    !(st->facilities & FACIL_DOCK));
 
+		SetDParam(0, st->index);
+		SetDParam(1, st->facilities);
 		this->DrawWidgets();
 
+		/* draw arrow pointing up/down for ascending/descending sorting */
+		this->DrawSortButtonState(SVW_SORT_ORDER, sort_orders[1] == SO_ASCENDING ? SBS_UP : SBS_DOWN);
+
+		int pos = this->vscroll.GetPosition(); ///< = this->vscroll.pos
+
+		int maxrows = this->vscroll.GetCapacity();
+
+		displayed_rows.clear();
+
 		if (!this->IsShaded()) {
 			NWidgetBase *nwi = this->GetWidget<NWidgetBase>(SVW_WAITING);
 			Rect waiting_rect = {nwi->pos_x, nwi->pos_y, nwi->pos_x + nwi->current_x - 1, nwi->pos_y + nwi->current_y - 1};
-			this->DrawWaitingCargo(waiting_rect, cargolist, transfers);
+			this->DrawEntries(&cargo, waiting_rect, pos, maxrows, 0);
+			scroll_to_row = INT_MAX;
 		}
 	}
 
@@ -975,115 +1242,268 @@ struct StationViewWindow : public Window {
 		}
 	}
 
-	/** Order waiting cargo by type and destination.
-	 * @param cargolist [out] Ordered cargo.
-	 * @param transfers [out] Bitmask for cargoes being transfered.
-	 * @pre \c *cargolist must be empty.
-	 */
-	void OrderWaitingCargo(CargoDataList *cargolist, uint32 *transfers)
-	{
-		assert(cargolist->size() == 0);
-		*transfers = 0;
+	void RecalcDestinations(CargoID i) {
+		const Station *st = Station::Get(this->window_number);
+		CargoDataEntry *cargo_entry = cached_destinations.InsertOrRetrieve(i);
+		cargo_entry->Clear();
+
+		const FlowStatMap & flows = st->goods[i].flows;
+		for (FlowStatMap::const_iterator it = flows.begin(); it != flows.end(); ++it) {
+			StationID from = it->first;
+			CargoDataEntry *source_entry = cargo_entry->InsertOrRetrieve(from);
+			const FlowStatSet & flow_set = it->second;
+			for (FlowStatSet::const_iterator flow_it = flow_set.begin(); flow_it != flow_set.end(); ++flow_it) {
+				const FlowStat & stat = *flow_it;
+				CargoDataEntry * via_entry = source_entry->InsertOrRetrieve(stat.via);
+				if (stat.via == this->window_number) {
+					via_entry->InsertOrRetrieve(stat.via)->Update(stat.planned);
+				} else {
+					EstimateDestinations(i, from, stat.via, stat.planned, via_entry);
+				}
+			}
+		}
+	}
 
-		StationID station_id = this->window_number;
-		const Station *st = Station::Get(station_id);
+	void EstimateDestinations(CargoID cargo, StationID source, StationID next, uint count, CargoDataEntry *dest) {
+		if (Station::IsValidID(next) && Station::IsValidID(source)) {
+			CargoDataEntry tmp;
+			FlowStatMap & flowmap = Station::Get(next)->goods[cargo].flows;
+			FlowStatMap::iterator map_it = flowmap.find(source);
+			if (map_it != flowmap.end()) {
+				FlowStatSet & flows = map_it->second;
+				for (FlowStatSet::iterator i = flows.begin(); i != flows.end(); ++i) {
+					tmp.InsertOrRetrieve(i->via)->Update(i->planned);
+				}
+			}
 
-		/* count types of cargos waiting in station */
-		for (CargoID i = 0; i < NUM_CARGO; i++) {
-			if (st->goods[i].cargo.Empty()) {
-				this->cargo_rows[i] = 0;
+			if (tmp.GetCount() == 0) {
+				dest->InsertOrRetrieve(INVALID_STATION)->Update(count);
 			} else {
-				/* Add an entry for total amount of cargo of this type waiting. */
-				cargolist->push_back(CargoData(i, INVALID_STATION, st->goods[i].cargo.Count()));
-
-				/* Set the row for this cargo entry for the expand/hide button */
-				this->cargo_rows[i] = (uint16)cargolist->size();
-
-				/* Add an entry for each distinct cargo source. */
-				const StationCargoList::List *packets = st->goods[i].cargo.Packets();
-				for (StationCargoList::ConstIterator it(packets->begin()); it != packets->end(); it++) {
-					const CargoPacket *cp = *it;
-					if (cp->SourceStation() != station_id) {
-						bool added = false;
-
-						/* Enable the expand/hide button for this cargo type */
-						SetBit(*transfers, i);
-
-						/* Don't add cargo lines if not expanded */
-						if (!HasBit(this->cargo, i)) break;
-
-						/* Check if we already have this source in the list */
-						for (CargoDataList::iterator jt(cargolist->begin()); jt != cargolist->end(); jt++) {
-							CargoData *cd = &(*jt);
-							if (cd->cargo == i && cd->source == cp->SourceStation()) {
-								cd->count += cp->Count();
-								added = true;
-								break;
+				uint sum_estimated = 0;
+				while(sum_estimated < count) {
+					for(CargoDataSet::iterator i = tmp.Begin(); i != tmp.End() && sum_estimated < count; ++i) {
+						CargoDataEntry *child = *i;
+						uint estimate = DivideApprox(child->GetCount() * count, tmp.GetCount());
+						if (estimate == 0) estimate = 1;
+
+						sum_estimated += estimate;
+						if (sum_estimated > count) {
+							estimate -= sum_estimated - count;
+							sum_estimated = count;
+						}
+
+						if (estimate > 0) {
+							if (child->GetStation() == next) {
+								dest->InsertOrRetrieve(next)->Update(estimate);
+							} else {
+								EstimateDestinations(cargo, source, child->GetStation(), estimate, dest);
 							}
 						}
+					}
+
+				}
+			}
+		} else {
+			dest->InsertOrRetrieve(INVALID_STATION)->Update(count);
+		}
+	}
 
-						if (!added) cargolist->push_back(CargoData(i, cp->SourceStation(), cp->Count()));
+	void BuildFlowList(CargoID i, const FlowStatMap & flows, CargoDataEntry * cargo) {
+		uint scale = _settings_game.economy.moving_average_length * _settings_game.economy.moving_average_unit;
+		const CargoDataEntry *source_dest = cached_destinations.Retrieve(i);
+		for (FlowStatMap::const_iterator it = flows.begin(); it != flows.end(); ++it) {
+			StationID from = it->first;
+			const CargoDataEntry *source_entry = source_dest->Retrieve(from);
+			const FlowStatSet & flow_set = it->second;
+			for (FlowStatSet::const_iterator flow_it = flow_set.begin(); flow_it != flow_set.end(); ++flow_it) {
+				const FlowStat & stat = *flow_it;
+				const CargoDataEntry *via_entry = source_entry->Retrieve(stat.via);
+				for (CargoDataSet::iterator dest_it = via_entry->Begin(); dest_it != via_entry->End(); ++dest_it) {
+					CargoDataEntry *dest_entry = *dest_it;
+					uint val = dest_entry->GetCount() * 30;
+					if (this->current_mode == SENT) {
+						val *= stat.sent;
+						val = DivideApprox(val, via_entry->GetCount());
 					}
+					val = DivideApprox(val, scale);
+					ShowCargo(cargo, i, from, stat.via, dest_entry->GetStation(), val);
 				}
 			}
 		}
 	}
 
-	/** Draw waiting cargo.
-	 * @param r Rectangle of the widget.
-	 * @param cargolist Cargo, ordered by type and destination.
-	 * @param transfers Bitmask for cargoes that are transfered.
-	 */
-	void DrawWaitingCargo(const Rect &r, const CargoDataList &cargolist, uint32 transfers) const
-	{
-		int y = r.top + WD_FRAMERECT_TOP;
-		int pos = this->vscroll.GetPosition();
+	void BuildCargoList(CargoID i, const StationCargoList &packets, CargoDataEntry *cargo) {
+		const CargoDataEntry *source_dest = cached_destinations.Retrieve(i);
+		for (StationCargoList::ConstIterator it = packets.Packets()->begin(); it != packets.Packets()->end(); it++) {
+			const CargoPacket *cp = *it;
+			StationID next = it.GetKey();
 
-		const Station *st = Station::Get(this->window_number);
-		if (--pos < 0) {
-			StringID str = STR_JUST_NOTHING;
-			for (CargoID i = 0; i < NUM_CARGO; i++) {
-				if (!st->goods[i].cargo.Empty()) str = STR_EMPTY;
+			const CargoDataEntry *source_entry = source_dest->Retrieve(cp->SourceStation());
+			if (source_entry == NULL) {
+				ShowCargo(cargo, i, cp->SourceStation(), next, INVALID_STATION, cp->Count());
+				continue;
+			}
+
+			const CargoDataEntry *via_entry = source_entry->Retrieve(next);
+			if (via_entry == NULL) {
+				ShowCargo(cargo, i, cp->SourceStation(), next, INVALID_STATION, cp->Count());
+				continue;
+			}
+
+			for (CargoDataSet::iterator dest_it = via_entry->Begin(); dest_it != via_entry->End(); ++dest_it) {
+				CargoDataEntry * dest_entry = *dest_it;
+				uint val = DivideApprox(cp->Count() * dest_entry->GetCount(), via_entry->GetCount());
+				ShowCargo(cargo, i, cp->SourceStation(), next, dest_entry->GetStation(), val);
 			}
-			SetDParam(0, str);
-			DrawString(r.left + WD_FRAMERECT_LEFT, r.right - WD_FRAMERECT_RIGHT, y, STR_STATION_VIEW_WAITING_TITLE);
-			y += FONT_HEIGHT_NORMAL;
 		}
+	}
 
-		bool rtl = _dynlang.text_dir == TD_RTL;
-		int text_left    = rtl ? r.left + this->expand_shrink_width : r.left + WD_FRAMERECT_LEFT;
-		int text_right   = rtl ? r.right - WD_FRAMERECT_LEFT : r.right - this->expand_shrink_width;
-		int shrink_left  = rtl ? r.left + WD_FRAMERECT_LEFT : r.right - this->expand_shrink_width + WD_FRAMERECT_LEFT;
-		int shrink_right = rtl ? r.left + this->expand_shrink_width - WD_FRAMERECT_RIGHT : r.right - WD_FRAMERECT_RIGHT;
+	void BuildCargoList(CargoDataEntry * cargo, const Station * st) {
+		for (CargoID i = 0; i < NUM_CARGO; i++) {
 
+			if (this->cached_destinations.Retrieve(i) == NULL) {
+				this->RecalcDestinations(i);
+			}
 
-		int maxrows = this->vscroll.GetCapacity();
-		for (CargoDataList::const_iterator it = cargolist.begin(); it != cargolist.end() && pos > -maxrows; ++it) {
-			if (--pos < 0) {
-				const CargoData *cd = &(*it);
-				if (cd->source == INVALID_STATION) {
-					/* Heading */
-					DrawCargoIcons(cd->cargo, cd->count, r.left + WD_FRAMERECT_LEFT, r.right - WD_FRAMERECT_RIGHT, y);
-					SetDParam(0, cd->cargo);
-					SetDParam(1, cd->count);
-					if (HasBit(transfers, cd->cargo)) {
-						/* This cargo has transfers waiting so show the expand or shrink 'button' */
-						const char *sym = HasBit(this->cargo, cd->cargo) ? "-" : "+";
-						DrawString(text_left, text_right, y, STR_STATION_VIEW_WAITING_CARGO, TC_FROMSTRING, SA_RIGHT);
-						DrawString(shrink_left, shrink_right, y, sym, TC_YELLOW, SA_RIGHT);
-					} else {
-						DrawString(r.left + WD_FRAMERECT_LEFT, r.right - WD_FRAMERECT_RIGHT, y, STR_STATION_VIEW_WAITING_CARGO, TC_FROMSTRING, SA_RIGHT);
-					}
+			if (this->current_mode == WAITING) {
+				BuildCargoList(i, st->goods[i].cargo, cargo);
+			} else {
+				BuildFlowList(i, st->goods[i].flows, cargo);
+			}
+		}
+	}
+
+	void SetDisplayedRow(const CargoDataEntry * data) {
+		std::list<StationID> stations;
+		const CargoDataEntry * parent = data->GetParent();
+		if (parent->GetParent() == NULL) {
+			displayed_rows.push_back(RowDisplay(&expanded_rows, data->GetCargo()));
+			return;
+		}
+
+		StationID next = data->GetStation();
+		while(parent->GetParent()->GetParent() != NULL) {
+			stations.push_back(parent->GetStation());
+			parent = parent->GetParent();
+		}
+
+		CargoID cargo = parent->GetCargo();
+		CargoDataEntry * filter = expanded_rows.Retrieve(cargo);
+		while(!stations.empty()) {
+			filter = filter->Retrieve(stations.back());
+			stations.pop_back();
+		}
+
+		displayed_rows.push_back(RowDisplay(filter, next));
+	}
+
+	StringID GetEntryString(StationID station, StringID here, StringID other_station, StringID any) {
+		if (station == this->window_number) {
+			return here;
+		} else if (station != INVALID_STATION) {
+			SetDParam(2, station);
+			return other_station;
+		} else {
+			return any;
+		}
+	}
+
+	StringID SearchNonStop(CargoDataEntry * cd, StationID station, int column) {
+		CargoDataEntry * parent = cd->GetParent();
+		for (int i = column - 1; i > 0; --i) {
+			if (groupings[i] == GR_DESTINATION) {
+				if (parent->GetStation() == station) {
+					return STR_STATION_VIEW_NONSTOP;
+				} else {
+					return STR_STATION_VIEW_VIA;
+				}
+			}
+			parent = parent->GetParent();
+		}
+
+		if (groupings[column + 1] == GR_DESTINATION) {
+			CargoDataSet::iterator begin = cd->Begin();
+			CargoDataSet::iterator end = cd->End();
+			if (begin != end && ++(cd->Begin()) == end && (*(begin))->GetStation() == station) {
+				return STR_STATION_VIEW_NONSTOP;
+			} else {
+				return STR_STATION_VIEW_VIA;
+			}
+		}
+
+		return STR_STATION_VIEW_VIA;
+	}
+
+	int DrawEntries(CargoDataEntry * entry, Rect &r, int pos, int maxrows, int column, CargoID cargo = CT_INVALID) {
+		if (sortings[column] == ST_AS_GROUPING) {
+			if (groupings[column] != GR_CARGO) {
+				entry->Resort(ST_STATION_STRING, sort_orders[column]);
+			}
+		} else {
+			entry->Resort(ST_COUNT, sort_orders[column]);
+		}
+		for (CargoDataSet::iterator i = entry->Begin(); i != entry->End(); ++i) {
+			CargoDataEntry *cd = *i;
+
+			if (groupings[column] == GR_CARGO) {
+				cargo = cd->GetCargo();
+			}
+
+			if (pos > -maxrows && pos <= 0) {
+				StringID str = STR_EMPTY;
+				int y = r.top + WD_FRAMERECT_TOP - pos * FONT_HEIGHT_NORMAL;
+				SetDParam(0, cargo);
+				SetDParam(1, cd->GetCount());
+
+				if (groupings[column] == GR_CARGO) {
+					str = STR_STATION_VIEW_WAITING_CARGO;
+					DrawCargoIcons(cd->GetCargo(), cd->GetCount(), r.left + WD_FRAMERECT_LEFT + this->expand_shrink_width, r.right - WD_FRAMERECT_RIGHT - this->expand_shrink_width, y);
 				} else {
-					SetDParam(0, cd->cargo);
-					SetDParam(1, cd->count);
-					SetDParam(2, cd->source);
-					DrawString(r.left + WD_FRAMERECT_LEFT, r.right - WD_FRAMERECT_RIGHT, y, STR_STATION_VIEW_EN_ROUTE_FROM, TC_FROMSTRING, SA_RIGHT);
+					StationID station = cd->GetStation();
+
+					switch(groupings[column]) {
+					case GR_SOURCE:
+						str = GetEntryString(station, STR_STATION_VIEW_FROM_HERE, STR_STATION_VIEW_FROM, STR_STATION_VIEW_FROM_ANY);
+						break;
+					case GR_NEXT:
+						str = GetEntryString(station, STR_STATION_VIEW_VIA_HERE, STR_STATION_VIEW_VIA, STR_STATION_VIEW_VIA_ANY);
+						if (str == STR_STATION_VIEW_VIA) {
+							str = SearchNonStop(cd, station, column);
+						}
+						break;
+					case GR_DESTINATION:
+						str = GetEntryString(station, STR_STATION_VIEW_TO_HERE, STR_STATION_VIEW_TO, STR_STATION_VIEW_TO_ANY);
+						break;
+					default:
+						NOT_REACHED();
+					}
+					if (pos == -scroll_to_row && Station::IsValidID(station)) {
+						ScrollMainWindowToTile(Station::Get(station)->xy);
+					}
 				}
+				
+				bool rtl = _dynlang.text_dir == TD_RTL;
+				int text_left    = rtl ? r.left + this->expand_shrink_width : r.left + WD_FRAMERECT_LEFT + column * this->expand_shrink_width;
+				int text_right   = rtl ? r.right - WD_FRAMERECT_LEFT - column * this->expand_shrink_width : r.right - this->expand_shrink_width;
+				int shrink_left  = rtl ? r.left + WD_FRAMERECT_LEFT : r.right - this->expand_shrink_width + WD_FRAMERECT_LEFT;
+				int shrink_right = rtl ? r.left + this->expand_shrink_width - WD_FRAMERECT_RIGHT : r.right - WD_FRAMERECT_RIGHT;
 
-				y += FONT_HEIGHT_NORMAL;
+				DrawString(text_left, text_right, y, str, TC_FROMSTRING);
+
+				if (column < _num_columns - 1) {
+					const char *sym = cd->Size() > 0 ? "-" : "+";
+					DrawString(shrink_left, shrink_right, y, sym, TC_YELLOW);
+				}
+				SetDisplayedRow(cd);
 			}
+			pos = DrawEntries(cd, r, --pos, maxrows, column + 1, cargo);
 		}
+		return pos;
+	}
+
+	virtual void OnInvalidateData(int cargo) {
+		this->cached_destinations.Remove((CargoID)cargo);
+		this->SetDirty();
 	}
 
 	/** Draw accepted cargo in the #SVW_ACCEPTLIST widget.
@@ -1112,37 +1532,51 @@ struct StationViewWindow : public Window {
 		DrawString(r.left + WD_FRAMERECT_LEFT, r.right - WD_FRAMERECT_RIGHT, y, STR_STATION_VIEW_CARGO_RATINGS_TITLE);
 		y += FONT_HEIGHT_NORMAL;
 
+		uint scale = _settings_game.economy.moving_average_length * _settings_game.economy.moving_average_unit;
 		const CargoSpec *cs;
 		FOR_ALL_CARGOSPECS(cs) {
 			const GoodsEntry *ge = &st->goods[cs->Index()];
 			if (!HasBit(ge->acceptance_pickup, GoodsEntry::PICKUP)) continue;
 
 			SetDParam(0, cs->name);
-			SetDParam(2, ToPercent8(ge->rating));
-			SetDParam(1, STR_CARGO_RATING_APPALLING + (ge->rating >> 5));
-			DrawString(r.left + WD_FRAMERECT_LEFT + 6, r.right - WD_FRAMERECT_RIGHT - 6, y, STR_STATION_VIEW_CARGO_RATING);
+			SetDParam(1, DivideApprox(ge->supply * 30, scale));
+			SetDParam(3, ToPercent8(ge->rating));
+			SetDParam(2, STR_CARGO_RATING_APPALLING + (ge->rating >> 5));
+			DrawString(r.left + WD_FRAMERECT_LEFT + 6, r.right - WD_FRAMERECT_RIGHT - 6, y, STR_STATION_VIEW_CARGO_SUPPLY_RATING);
 			y += FONT_HEIGHT_NORMAL;
 		}
 	}
 
+	template<class ID>
+	void HandleCargoWaitingClick(CargoDataEntry * filter, ID next) {
+		if (filter->Retrieve(next) != NULL) {
+			filter->Remove(next);
+		} else {
+			filter->InsertOrRetrieve(next);
+		}
+	}
+
 	void HandleCargoWaitingClick(int row)
 	{
-		if (row == 0) return;
-
-		for (CargoID c = 0; c < NUM_CARGO; c++) {
-			if (this->cargo_rows[c] == row) {
-				ToggleBit(this->cargo, c);
-				this->SetWidgetDirty(SVW_WAITING);
-				break;
+		if (row < 0 || (uint)row >= displayed_rows.size()) return;
+		if (_ctrl_pressed) {
+			scroll_to_row = row;
+		} else {
+			RowDisplay & display = displayed_rows[row];
+			if (display.filter == &expanded_rows) {
+				HandleCargoWaitingClick<CargoID>(display.filter, display.next_cargo);
+			} else {
+				HandleCargoWaitingClick<StationID>(display.filter, display.next_station);
 			}
 		}
+		this->SetWidgetDirty(SVW_WAITING);
 	}
 
 	virtual void OnClick(Point pt, int widget)
 	{
 		switch (widget) {
 			case SVW_WAITING:
-				this->HandleCargoWaitingClick((pt.y - this->GetWidget<NWidgetBase>(SVW_WAITING)->pos_y - WD_FRAMERECT_TOP) / FONT_HEIGHT_NORMAL + this->vscroll.GetPosition());
+				this->HandleCargoWaitingClick((pt.y - this->GetWidget<NWidgetBase>(SVW_WAITING)->pos_y - WD_FRAMERECT_TOP) / FONT_HEIGHT_NORMAL);
 				break;
 
 			case SVW_LOCATION:
@@ -1201,6 +1635,100 @@ struct StationViewWindow : public Window {
 				ShowVehicleListWindow(owner, VEH_SHIP, (StationID)this->window_number);
 				break;
 			}
+
+			case SVW_MODE: {
+				ShowDropDownMenu(this, _mode_names, this->current_mode, SVW_MODE, 0, 0);
+				break;
+			}
+
+			case SVW_SORT_BY: {
+				CargoSortType sorting = (sortings[1] == ST_AS_GROUPING ? ST_COUNT : ST_AS_GROUPING);
+				SelectSortBy(sorting);
+				this->flags4 |= WF_TIMEOUT_BEGIN;
+				this->LowerWidget(SVW_SORT_BY);
+				break;
+			}
+
+			case SVW_GROUP_BY: {
+				ShowDropDownMenu(this, _group_names, this->grouping_index, SVW_GROUP_BY, 0, 0);
+				break;
+			}
+
+			case SVW_SORT_ORDER: { // flip sorting method asc/desc
+				SortOrder order = (sort_orders[1] == SO_ASCENDING ? SO_DESCENDING : SO_ASCENDING);
+				SelectSortOrder(order);
+				this->flags4 |= WF_TIMEOUT_BEGIN;
+				this->LowerWidget(SVW_SORT_ORDER);
+				break;
+			}
+		}
+	}
+
+	void SelectSortBy(CargoSortType sorting) {
+		_settings_client.gui.station_gui_sort_by = sorting;
+		sortings[1] = sortings[2] = sortings[3] = sorting;
+		/* Display the current sort variant */
+		this->GetWidget<NWidgetCore>(SVW_SORT_BY)->widget_data = this->_sort_names[sorting];
+		this->SetDirty();
+	}
+
+	void SelectSortOrder(SortOrder order) {
+		sort_orders[1] = sort_orders[2] = sort_orders[3] = order;
+		_settings_client.gui.station_gui_sort_order = sort_orders[1];
+		this->SetDirty();
+	}
+
+	void SelectMode(int index) {
+		this->current_mode = (Mode)index;
+		this->GetWidget<NWidgetCore>(SVW_MODE)->widget_data = _mode_names[index];
+		this->SetDirty();
+	}
+
+	void SelectGroupBy(int index) {
+		this->grouping_index = index;
+		_settings_client.gui.station_gui_group_order = index;
+		this->GetWidget<NWidgetCore>(SVW_GROUP_BY)->widget_data = _group_names[index];
+		switch(_group_names[index]) {
+		case STR_STATION_VIEW_GROUP_S_V_D:
+			groupings[1] = GR_SOURCE;
+			groupings[2] = GR_NEXT;
+			groupings[3] = GR_DESTINATION;
+			break;
+		case STR_STATION_VIEW_GROUP_S_D_V:
+			groupings[1] = GR_SOURCE;
+			groupings[2] = GR_DESTINATION;
+			groupings[3] = GR_NEXT;
+			break;
+		case STR_STATION_VIEW_GROUP_V_S_D:
+			groupings[1] = GR_NEXT;
+			groupings[2] = GR_SOURCE;
+			groupings[3] = GR_DESTINATION;
+			break;
+		case STR_STATION_VIEW_GROUP_V_D_S:
+			groupings[1] = GR_NEXT;
+			groupings[2] = GR_DESTINATION;
+			groupings[3] = GR_SOURCE;
+			break;
+		case STR_STATION_VIEW_GROUP_D_S_V:
+			groupings[1] = GR_DESTINATION;
+			groupings[2] = GR_SOURCE;
+			groupings[3] = GR_NEXT;
+			break;
+		case STR_STATION_VIEW_GROUP_D_V_S:
+			groupings[1] = GR_DESTINATION;
+			groupings[2] = GR_NEXT;
+			groupings[3] = GR_SOURCE;
+			break;
+		}
+		this->SetDirty();
+	}
+
+	virtual void OnDropdownSelect(int widget, int index)
+	{
+		if (widget == SVW_MODE) {
+			SelectMode(index);
+		} else {
+			SelectGroupBy(index);
 		}
 	}
 
@@ -1218,8 +1746,31 @@ struct StationViewWindow : public Window {
 };
 
 
+const StringID StationViewWindow::_sort_names[] = {
+	STR_SORT_BY_STATION,
+	STR_SORT_BY_AMOUNT,
+	INVALID_STRING_ID
+};
+
+const StringID StationViewWindow::_mode_names[] = {
+	STR_STATION_VIEW_WAITING,
+	STR_STATION_VIEW_PLANNED,
+	STR_STATION_VIEW_SENT,
+	INVALID_STRING_ID
+};
+
+const StringID StationViewWindow::_group_names[] = {
+	STR_STATION_VIEW_GROUP_S_V_D,
+	STR_STATION_VIEW_GROUP_S_D_V,
+	STR_STATION_VIEW_GROUP_V_S_D,
+	STR_STATION_VIEW_GROUP_V_D_S,
+	STR_STATION_VIEW_GROUP_D_S_V,
+	STR_STATION_VIEW_GROUP_D_V_S,
+	INVALID_STRING_ID
+};
+
 static const WindowDesc _station_view_desc(
-	WDP_AUTO, 249, 110,
+	WDP_AUTO, 249, 117,
 	WC_STATION_VIEW, WC_NONE,
 	WDF_UNCLICK_BUTTONS,
 	_nested_station_view_widgets, lengthof(_nested_station_view_widgets)
diff --git a/src/station_gui.h b/src/station_gui.h
index efd6ad1..4f682ec 100644
--- a/src/station_gui.h
+++ b/src/station_gui.h
@@ -14,19 +14,24 @@
 
 #include "command_type.h"
 #include "station_type.h"
+#include <set>
 
 /** Enum for StationView, referring to _station_view_widgets and _station_view_expanded_widgets */
 enum StationViewWidgets {
 	SVW_CAPTION    =  0, ///< Caption of the window
-	SVW_WAITING    =  1, ///< List of waiting cargo
-	SVW_SCROLLBAR  =  2, ///< Scrollbar
-	SVW_ACCEPTLIST =  3, ///< List of accepted cargos
-	SVW_RATINGLIST =  3, ///< Ratings of cargos
-	SVW_LOCATION   =  4, ///< 'Location' button
-	SVW_RATINGS    =  5, ///< 'Ratings' button
-	SVW_ACCEPTS    =  5, ///< 'Accepts' button
-	SVW_RENAME     =  6, ///< 'Rename' button
-	SVW_TRAINS     =  7, ///< List of scheduled trains button
+	SVW_SORT_ORDER =  1, ///< 'Sort order' button
+	SVW_SORT_BY    =  2, ///< 'Sort by' button
+	SVW_MODE       =  3, ///< button for toggling planned and real flows
+	SVW_GROUP_BY   =  4, ///< 'Group by' button
+	SVW_WAITING    =  5, ///< List of waiting cargo
+	SVW_SCROLLBAR  =  6, ///< Scrollbar
+	SVW_ACCEPTLIST =  7, ///< List of accepted cargos
+	SVW_RATINGLIST =  7, ///< Ratings of cargos
+	SVW_LOCATION   =  8, ///< 'Location' button
+	SVW_RATINGS    =  9, ///< 'Ratings' button
+	SVW_ACCEPTS    =  9, ///< 'Accepts' button
+	SVW_RENAME     = 10, ///< 'Rename' button
+	SVW_TRAINS     = 11, ///< List of scheduled trains button
 	SVW_ROADVEHS,        ///< List of scheduled road vehs button
 	SVW_PLANES,          ///< List of scheduled planes button
 	SVW_SHIPS,           ///< List of scheduled ships button
@@ -45,4 +50,85 @@ void CheckRedrawStationCoverage(const Window *w);
 void ShowSelectStationIfNeeded(CommandContainer cmd, TileArea ta);
 void ShowSelectWaypointIfNeeded(CommandContainer cmd, TileArea ta);
 
+enum SortOrder {
+	SO_DESCENDING,
+	SO_ASCENDING
+};
+
+class CargoDataEntry;
+
+enum CargoSortType {
+	ST_AS_GROUPING,    ///< by the same principle the entries are being grouped
+	ST_COUNT,          ///< by amount of cargo
+	ST_STATION_STRING, ///< by station name
+	ST_STATION_ID,     ///< by station id
+	ST_CARGO_ID,       ///< by cargo id
+};
+
+class CargoSorter {
+public:
+	CargoSorter(CargoSortType t = ST_STATION_ID, SortOrder o = SO_ASCENDING) : type(t), order(o) {}
+	CargoSortType GetSortType() {return type;}
+	bool operator()(const CargoDataEntry * cd1, const CargoDataEntry * cd2) const;
+
+private:
+	CargoSortType type;
+	SortOrder order;
+
+	template<class ID>
+	bool SortId(ID st1, ID st2) const;
+	bool SortCount(const CargoDataEntry *cd1, const CargoDataEntry *cd2) const;
+	bool SortStation (StationID st1, StationID st2) const;
+};
+
+typedef std::set<CargoDataEntry *, CargoSorter> CargoDataSet;
+
+class CargoDataEntry {
+public:
+	CargoDataEntry();
+	~CargoDataEntry();
+
+	CargoDataEntry * InsertOrRetrieve(StationID s) {return InsertOrRetrieve<StationID>(s);}
+	CargoDataEntry * InsertOrRetrieve(CargoID car) {return InsertOrRetrieve<CargoID>(car);}
+	void Update(uint count);
+
+	void Remove(StationID s) {CargoDataEntry t(s); Remove(&t);}
+	void Remove(CargoID c) {CargoDataEntry t(c); Remove(&t);}
+
+	CargoDataEntry * Retrieve(StationID s) const {CargoDataEntry t(s); return Retrieve(subentries->find(&t));}
+	CargoDataEntry * Retrieve(CargoID c) const {CargoDataEntry t(c);return Retrieve(subentries->find(&t));}
+
+	void Resort(CargoSortType type, SortOrder order);
+
+	StationID GetStation() const {return station;}
+	CargoID GetCargo() const {return cargo;}
+	uint GetCount() const {return count;}
+	CargoDataEntry * GetParent() const {return parent;}
+	uint Size() const {return size;}
+
+	CargoDataSet::iterator Begin() const {return subentries->begin();}
+	CargoDataSet::iterator End() const {return subentries->end();}
+
+	void Clear();
+private:
+
+	CargoDataEntry(StationID st, uint c, CargoDataEntry * p);
+	CargoDataEntry(CargoID car, uint c, CargoDataEntry * p);
+	CargoDataEntry(StationID s);
+	CargoDataEntry(CargoID c);
+	CargoDataEntry * Retrieve(CargoDataSet::iterator i) const;
+	template<class ID>
+	CargoDataEntry * InsertOrRetrieve(ID s);
+	void Remove(CargoDataEntry * comp);
+	void IncrementSize();
+	CargoDataEntry * parent;
+	const union {
+		StationID station;
+		CargoID cargo;
+	};
+	uint size;
+	uint count;
+	CargoDataSet * subentries;
+};
+
 #endif /* STATION_GUI_H */
diff --git a/src/table/settings.h b/src/table/settings.h
index 7b7971c..914b3d0 100644
--- a/src/table/settings.h
+++ b/src/table/settings.h
@@ -34,6 +34,10 @@ static bool CheckFreeformEdges(int32 p1);
 static bool ChangeDynamicEngines(int32 p1);
 static bool StationCatchmentChanged(int32 p1);
 static bool InvalidateVehTimetableWindow(int32 p1);
+static bool CheckSharingRail(int32 p1);
+static bool CheckSharingRoad(int32 p1);
+static bool CheckSharingWater(int32 p1);
+static bool CheckSharingAir(int32 p1);
 
 #ifdef ENABLE_NETWORK
 static bool UpdateClientName(int32 p1);
@@ -289,7 +293,7 @@ static const char *_locale_currencies = "GBP|USD|EUR|YEN|ATS|BEF|CHF|CZK|DEM|DKK
 static const char *_locale_units = "imperial|metric|si";
 static const char *_town_names = "english|french|german|american|latin|silly|swedish|dutch|finnish|polish|slovak|norwegian|hungarian|austrian|romanian|czech|swiss|danish|turkish|italian|catalan";
 static const char *_climates = "temperate|arctic|tropic|toyland";
-static const char *_autosave_interval = "off|monthly|quarterly|half year|yearly";
+static const char *_autosave_interval = "off|daily|weekly|monthly|quarterly|half year|yearly";
 static const char *_roadsides = "left|right";
 static const char *_savegame_date = "long|short|iso";
 #ifdef ENABLE_NETWORK
@@ -317,7 +321,7 @@ static const SettingDesc _gameopt_settings[] = {
 	    SDT_OMANY(GameSettings, game_creation.landscape,  SLE_UINT8,                     0, 0, 0, 3, _climates, STR_NULL, NULL, ConvertLandscape),
 	      SDT_VAR(GameSettings, game_creation.snow_line,  SLE_UINT8,                     0, 0, DEF_SNOWLINE_HEIGHT * TILE_HEIGHT, MIN_SNOWLINE_HEIGHT * TILE_HEIGHT, DEF_SNOWLINE_HEIGHT * TILE_HEIGHT, 0, STR_NULL, NULL),
 	 SDT_CONDNULL(                                                1,  0, 22),
- SDTC_CONDOMANY(              gui.autosave,             SLE_UINT8, 23, SL_MAX_VERSION, S, 0, 1, 4, _autosave_interval, STR_NULL, NULL),
+ SDTC_CONDOMANY(              gui.autosave,             SLE_UINT8, 23, SL_MAX_VERSION, S, 0, 3, 6, _autosave_interval, STR_NULL, NULL),
 	    SDT_OMANY(GameSettings, vehicle.road_side,        SLE_UINT8,                     0, 0, 1, 1, _roadsides, STR_NULL, NULL, NULL),
 	    SDT_END()
 };
@@ -369,7 +373,9 @@ const SettingDesc _settings[] = {
 	    SDT_BOOL(GameSettings, station.never_expire_airports,                                       0,NN, false,                    STR_CONFIG_SETTING_NEVER_EXPIRE_AIRPORTS,  NULL),
 	 SDT_CONDVAR(GameSettings, economy.town_layout,                  SLE_UINT8, 59, SL_MAX_VERSION, 0,MS,TL_ORIGINAL,TL_BEGIN,NUM_TLS - 1, 1, STR_CONFIG_SETTING_TOWN_LAYOUT,  TownFoundingChanged),
 	SDT_CONDBOOL(GameSettings, economy.allow_town_roads,                       113, SL_MAX_VERSION, 0, 0,  true,                    STR_CONFIG_SETTING_ALLOW_TOWN_ROADS,       NULL),
+	SDTG_CONDVAR("daylength_factor",                                 SLE_UINT8, 0, 0, _date_daylength_factor, 1,     1,     255, 1, STR_CONFIG_SETTING_DAYLENGTH_FACTOR,       NULL,                               133, SL_MAX_VERSION),
 	 SDT_CONDVAR(GameSettings, economy.found_town,                   SLE_UINT8,128, SL_MAX_VERSION, 0,MS,TF_FORBIDDEN,TF_BEGIN,TF_END - 1, 1, STR_CONFIG_SETTING_TOWN_FOUNDING, TownFoundingChanged),
+	 SDT_CONDVAR(GameSettings, economy.town_cargo_factor,            SLE_INT8, 133, SL_MAX_VERSION, 0, 0,     0,   -16,      +8, 1,       STR_CONFIG_SETTING_TOWN_CARGO_FACTOR,      NULL),
 
 	     SDT_VAR(GameSettings, vehicle.train_acceleration_model,     SLE_UINT8,                     0,MS,     0,     0,       1, 1, STR_CONFIG_SETTING_TRAIN_ACCELERATION_MODEL, TrainAccelerationModelChanged),
 	 SDT_CONDVAR(GameSettings, vehicle.train_slope_steepness,        SLE_UINT8,133, SL_MAX_VERSION, 0, 0,     3,     0,      10, 1, STR_CONFIG_SETTING_TRAIN_SLOPE_STEEPNESS,  NULL),
@@ -448,11 +454,34 @@ const SettingDesc _settings[] = {
 	    SDT_BOOL(GameSettings, ai.ai_disable_veh_ship,                                              0, 0, false,                    STR_CONFIG_SETTING_AI_BUILDS_SHIPS,        NULL),
 	 SDT_CONDVAR(GameSettings, ai.ai_max_opcode_till_suspend,       SLE_UINT32,107, SL_MAX_VERSION, 0, NG, 10000, 5000,250000,2500, STR_CONFIG_SETTING_AI_MAX_OPCODES,         NULL),
 
+	SDT_CONDBOOL(GameSettings, sharing.sharing_rail,                           133, SL_MAX_VERSION, 0, 0, false,                    STR_CONFIG_SETTING_SHARING_RAIL,           CheckSharingRail),
+	SDT_CONDBOOL(GameSettings, sharing.sharing_road,                           133, SL_MAX_VERSION, 0, 0, false,                    STR_CONFIG_SETTING_SHARING_ROAD,           CheckSharingRoad),
+	SDT_CONDBOOL(GameSettings, sharing.sharing_water,                          133, SL_MAX_VERSION, 0, 0, false,                    STR_CONFIG_SETTING_SHARING_WATER,          CheckSharingWater),
+	SDT_CONDBOOL(GameSettings, sharing.sharing_air,                            133, SL_MAX_VERSION, 0, 0, false,                    STR_CONFIG_SETTING_SHARING_AIR,            CheckSharingAir),
+	 SDT_CONDVAR(GameSettings, sharing.fee_rail,                      SLE_UINT,133, SL_MAX_VERSION, 0,CR,   100,     0, 1000000, 10,STR_CONFIG_SETTING_SHARING_FEE_RAIL,       NULL),
+	 SDT_CONDVAR(GameSettings, sharing.fee_road,                      SLE_UINT,133, SL_MAX_VERSION, 0,CR,   100,     0, 1000000, 10,STR_CONFIG_SETTING_SHARING_FEE_ROAD,       NULL),
+	 SDT_CONDVAR(GameSettings, sharing.fee_water,                     SLE_UINT,133, SL_MAX_VERSION, 0,CR,   100,     0, 1000000, 10,STR_CONFIG_SETTING_SHARING_FEE_WATER,      NULL),
+	 SDT_CONDVAR(GameSettings, sharing.fee_air,                       SLE_UINT,133, SL_MAX_VERSION, 0,CR,   100,     0, 1000000, 10,STR_CONFIG_SETTING_SHARING_FEE_AIR,        NULL),
+	SDT_CONDBOOL(GameSettings, sharing.payment_in_debt,                        133, SL_MAX_VERSION, 0, 0, false,                    STR_CONFIG_SETTING_SHARING_PAYMENT_IN_DEBT,NULL),
+
 	     SDT_VAR(GameSettings, vehicle.extend_vehicle_life,          SLE_UINT8,                     0, 0,     0,     0,     100, 0, STR_NULL,                                  NULL),
 	     SDT_VAR(GameSettings, economy.dist_local_authority,         SLE_UINT8,                     0, 0,    20,     5,      60, 0, STR_NULL,                                  NULL),
 	     SDT_VAR(GameSettings, pf.wait_oneway_signal,                SLE_UINT8,                     0, 0,    15,     2,     255, 0, STR_NULL,                                  NULL),
 	     SDT_VAR(GameSettings, pf.wait_twoway_signal,                SLE_UINT8,                     0, 0,    41,     2,     255, 0, STR_NULL,                                  NULL),
 	SDT_CONDLISTO(GameSettings, economy.town_noise_population, 3,   SLE_UINT16, 96, SL_MAX_VERSION, 0,D0, "800,2000,4000",          STR_NULL,                                  NULL, CheckNoiseToleranceLevel),
+	 SDT_CONDVAR(GameSettings, economy.moving_average_unit,         SLE_UINT16, CAPACITIES_SV, SL_MAX_VERSION, 0, 0, 1, 1, 4096, 1,	STR_CONFIG_SETTING_AVERAGE_UNIT,           NULL),
+	 SDT_CONDVAR(GameSettings, economy.moving_average_length,       SLE_UINT16, CAPACITIES_SV, SL_MAX_VERSION, 0, 0, 128, 1, 4096, 4, STR_CONFIG_SETTING_AVERAGE_LENGTH,       NULL),
+
+	 SDT_CONDVAR(GameSettings, linkgraph.recalc_interval,           SLE_UINT16, LINKGRAPH_SV, SL_MAX_VERSION,  0, 0,16, 1, 4096, 1, STR_CONFIG_SETTING_LINKGRAPH_INTERVAL,     NULL),
+	 SDT_CONDVAR(GameSettings, linkgraph.demand_pax,                 SLE_UINT8,DEMANDS_SV, SL_MAX_VERSION,0,MS,DT_SYMMETRIC,DT_BEGIN,DT_NUM-1,1,STR_CONFIG_SETTING_DEMAND_PAX, NULL),
+	 SDT_CONDVAR(GameSettings, linkgraph.demand_mail,                SLE_UINT8,DEMANDS_SV,SL_MAX_VERSION,0,MS,DT_SYMMETRIC,DT_BEGIN,DT_NUM-1,1,STR_CONFIG_SETTING_DEMAND_MAIL, NULL),
+	 SDT_CONDVAR(GameSettings, linkgraph.demand_express,      SLE_UINT8,DEMANDS_SV,SL_MAX_VERSION,0,MS,DT_ANTISYMMETRIC,DT_BEGIN,DT_NUM-1,1,STR_CONFIG_SETTING_DEMAND_EXPRESS, NULL),
+	 SDT_CONDVAR(GameSettings, linkgraph.demand_armoured,        SLE_UINT8,DEMANDS_SV,SL_MAX_VERSION,0,MS,DT_SYMMETRIC,DT_BEGIN,DT_NUM-1,1,STR_CONFIG_SETTING_DEMAND_ARMOURED, NULL),
+	 SDT_CONDVAR(GameSettings, linkgraph.demand_default,      SLE_UINT8,DEMANDS_SV,SL_MAX_VERSION,0,MS,DT_ANTISYMMETRIC,DT_BEGIN,DT_NUM-1,1,STR_CONFIG_SETTING_DEMAND_DEFAULT, NULL),
+	 SDT_CONDVAR(GameSettings, linkgraph.accuracy,                   SLE_UINT8,DEMANDS_SV, SL_MAX_VERSION, 0, 0,16,  2,      64, 1, STR_CONFIG_SETTING_LINKGRAPH_ACCURACY,     NULL),
+	 SDT_CONDVAR(GameSettings, linkgraph.demand_distance,            SLE_UINT8,DEMANDS_SV, SL_MAX_VERSION, 0, 0,100, 0,     255, 5, STR_CONFIG_SETTING_DEMAND_DISTANCE,        NULL),
+	 SDT_CONDVAR(GameSettings, linkgraph.demand_size,                SLE_UINT8,DEMANDS_SV, SL_MAX_VERSION, 0, 0,100, 0,     100, 5, STR_CONFIG_SETTING_DEMAND_SIZE,            NULL),
+	 SDT_CONDVAR(GameSettings, linkgraph.short_path_saturation,      SLE_UINT8,  MCF_SV, SL_MAX_VERSION, 0, 0,80,    0,     250, 5, STR_CONFIG_SETTING_SHORT_PATH_SATURATION,  NULL),
 
 	 SDT_CONDVAR(GameSettings, pf.wait_for_pbs_path,                 SLE_UINT8,100, SL_MAX_VERSION, 0, 0,    30,     2,     255, 0, STR_NULL,                                  NULL),
 	SDT_CONDBOOL(GameSettings, pf.reserve_paths,                               100, SL_MAX_VERSION, 0, 0, false,                    STR_NULL,                                  NULL),
@@ -537,7 +566,7 @@ const SettingDesc _settings[] = {
 
 	/***************************************************************************/
 	/* Unsaved setting variables. */
-	SDTC_OMANY(gui.autosave,                  SLE_UINT8, S,  0, 1, 4, _autosave_interval,     STR_NULL,                                       NULL),
+	SDTC_OMANY(gui.autosave,                  SLE_UINT8, S,  0, 3, 6, _autosave_interval,     STR_NULL,                                       NULL),
 	 SDTC_BOOL(gui.threaded_saves,                       S,  0,  true,                        STR_NULL,                                       NULL),
 	SDTC_OMANY(gui.date_format_in_default_names,SLE_UINT8,S,MS, 0, 2, _savegame_date,         STR_CONFIG_SETTING_DATE_FORMAT_IN_SAVE_NAMES,   NULL),
 	 SDTC_BOOL(gui.vehicle_speed,                        S,  0,  true,                        STR_CONFIG_SETTING_VEHICLESPEED,                NULL),
@@ -599,6 +628,9 @@ const SettingDesc _settings[] = {
 
 	  SDTC_VAR(gui.console_backlog_timeout,  SLE_UINT16, S,  0,   100,       10,    65500, 0, STR_NULL,                                       NULL),
 	  SDTC_VAR(gui.console_backlog_length,   SLE_UINT16, S,  0,   100,       10,    65500, 0, STR_NULL,                                       NULL),
+	  SDTC_VAR(gui.station_gui_group_order,   SLE_UINT8, S,  0,     0,        0,        5, 1, STR_NULL,                                       NULL),
+	  SDTC_VAR(gui.station_gui_sort_by,       SLE_UINT8, S,  0,     0,        0,        1, 1, STR_NULL,                                       NULL),
+	  SDTC_VAR(gui.station_gui_sort_order,    SLE_UINT8, S,  0,     0,        0,        1, 1, STR_NULL,                                       NULL),
 #ifdef ENABLE_NETWORK
 	  SDTC_VAR(gui.network_chat_box_width,   SLE_UINT16, S,  0,   700,      200,    65535, 0, STR_NULL,                                       NULL),
 	  SDTC_VAR(gui.network_chat_box_height,   SLE_UINT8, S,  0,    25,        5,      255, 0, STR_NULL,                                       NULL),
diff --git a/src/timetable_cmd.cpp b/src/timetable_cmd.cpp
index 4cde4b0..71f123a 100644
--- a/src/timetable_cmd.cpp
+++ b/src/timetable_cmd.cpp
@@ -267,7 +267,7 @@ void UpdateVehicleTimetable(Vehicle *v, bool travelling)
 			/* Round the time taken up to the nearest day, as this will avoid
 			 * confusion for people who are timetabling in days, and can be
 			 * adjusted later by people who aren't. */
-			time_taken = (((time_taken - 1) / DAY_TICKS) + 1) * DAY_TICKS;
+			time_taken = (((time_taken - 1) / ORIG_DAY_TICKS) + 1) * ORIG_DAY_TICKS;
 
 			ChangeTimetable(v, v->cur_order_index, time_taken, travelling);
 		}
diff --git a/src/town.h b/src/town.h
index 08d376f..04ecdce 100644
--- a/src/town.h
+++ b/src/town.h
@@ -173,7 +173,7 @@ enum TownRatingCheckType {
 /** This is the number of ticks between towns being processed for building new
  * houses or roads. This value originally came from the size of the town array
  * in TTD. */
-static const byte TOWN_GROWTH_FREQUENCY = 70;
+#define TOWN_GROWTH_FREQUENCY (70 * _date_daylength_factor)
 
 /** This enum is used in conjonction with town->flags.
  * IT simply states what bit is used for.
diff --git a/src/town_cmd.cpp b/src/town_cmd.cpp
index 0003a51..f046792 100644
--- a/src/town_cmd.cpp
+++ b/src/town_cmd.cpp
@@ -433,6 +433,61 @@ static void MakeTownHouseBigger(TileIndex tile)
 }
 
 /**
+ * Generate cargo for a town (house).
+ *
+ * The amount of cargo should be and will be greater than zero.
+ *
+ * @param t current town
+ * @param ct type of cargo to generate, usually CT_PASSENGERS or CT_MAIL
+ * @param amount how many units of cargo
+ * @param stations available stations for this house
+ */
+static void TownGenerateCargo (Town *t, CargoID ct, uint amount, StationFinder &stations)
+{
+	// custom cargo generation factor
+	int cf = _settings_game.economy.town_cargo_factor;
+
+	// when the economy flunctuates, everyone wants to stay at home
+	if (_economy.fluct <= 0)
+		amount = (amount + 1) >> 1; 
+	
+	// apply custom factor?
+	if (cf < 0)
+		// approx (amount / 2^cf)
+		// adjust with a constant offset of {(2 ^ cf) - 1} (i.e. add cf * 1-bits) before dividing to ensure that it doesn't become zero
+		// this skews the curve a little so that isn't entirely exponential, but will still decrease
+		amount = (amount + ((1 << -cf) - 1)) >> -cf;
+
+	else if (cf > 0)
+		// approx (amount * 2^cf)
+		// XXX: overflow?
+		amount = amount << cf;
+	
+	// with the adjustments above, this should never happen
+	assert(amount > 0);
+	
+	// send cargo to station
+	uint moved = MoveGoodsToStation(ct, amount, ST_TOWN, t->index, stations.GetStations());
+	
+	// calculate for town stats
+	const CargoSpec *cs = CargoSpec::Get(ct);
+	switch (cs->town_effect) {
+		case TE_PASSENGERS:
+			t->new_max_pass += amount;
+			t->new_act_pass += moved;
+			break;
+
+		case TE_MAIL:
+			t->new_max_mail += amount;
+			t->new_act_mail += moved;
+			break;
+
+		default:
+			break;
+	}
+}
+
+/**
  * Tile callback function.
  *
  * Periodic tic handler for houses and town
@@ -479,39 +534,20 @@ static void TileLoop_Town(TileIndex tile)
 			uint amt = GB(callback, 0, 8);
 			if (amt == 0) continue;
 
-			uint moved = MoveGoodsToStation(cargo, amt, ST_TOWN, t->index, stations.GetStations());
-
-			const CargoSpec *cs = CargoSpec::Get(cargo);
-			switch (cs->town_effect) {
-				case TE_PASSENGERS:
-					t->new_max_pass += amt;
-					t->new_act_pass += moved;
-					break;
-
-				case TE_MAIL:
-					t->new_max_mail += amt;
-					t->new_act_mail += moved;
-					break;
-
-				default:
-					break;
-			}
+			// XXX: no economy flunctuation for GRF cargos?
+			TownGenerateCargo(t, cargo, amt, stations);
 		}
 	} else {
 		if (GB(r, 0, 8) < hs->population) {
 			uint amt = GB(r, 0, 8) / 8 + 1;
-
-			if (_economy.fluct <= 0) amt = (amt + 1) >> 1;
-			t->new_max_pass += amt;
-			t->new_act_pass += MoveGoodsToStation(CT_PASSENGERS, amt, ST_TOWN, t->index, stations.GetStations());
+			
+			TownGenerateCargo(t, CT_PASSENGERS, amt, stations);
 		}
 
 		if (GB(r, 8, 8) < hs->mail_generation) {
 			uint amt = GB(r, 8, 8) / 8 + 1;
-
-			if (_economy.fluct <= 0) amt = (amt + 1) >> 1;
-			t->new_max_mail += amt;
-			t->new_act_mail += MoveGoodsToStation(CT_MAIL, amt, ST_TOWN, t->index, stations.GetStations());
+			
+			TownGenerateCargo(t, CT_MAIL, amt, stations);
 		}
 	}
 
diff --git a/src/train.h b/src/train.h
index ba8a4dc..5e858c7 100644
--- a/src/train.h
+++ b/src/train.h
@@ -55,6 +55,8 @@ void CheckTrainsLengths();
 void FreeTrainTrackReservation(const Train *v, TileIndex origin = INVALID_TILE, Trackdir orig_td = INVALID_TRACKDIR);
 bool TryPathReserve(Train *v, bool mark_as_stuck = false, bool first_tile_okay = false);
 
+void DeleteVisibleTrain(Train *v);
+
 int GetTrainStopLocation(StationID station_id, TileIndex tile, const Train *v, int *station_ahead, int *station_length);
 
 void TrainConsistChanged(Train *v, bool same_length);
diff --git a/src/train_cmd.cpp b/src/train_cmd.cpp
index 17dad7a..d2dddc3 100644
--- a/src/train_cmd.cpp
+++ b/src/train_cmd.cpp
@@ -38,6 +38,7 @@
 #include "gamelog.h"
 #include "network/network.h"
 #include "spritecache.h"
+#include "infrastructure_func.h"
 
 #include "table/strings.h"
 #include "table/train_cmd.h"
@@ -846,7 +847,7 @@ CommandCost CmdBuildRailVehicle(TileIndex tile, DoCommandFlag flags, uint32 p1,
 	/* Check if the train is actually being built in a depot belonging
 	 * to the company. Doesn't matter if only the cost is queried */
 	if (!IsRailDepotTile(tile)) return CMD_ERROR;
-	if (!IsTileOwner(tile, _current_company)) return CMD_ERROR;
+	if (!CheckInfraUsageAllowed(GetTileOwner(tile), VEH_TRAIN)) return CMD_ERROR;
 
 	if (rvi->railveh_type == RAILVEH_WAGON) return CmdBuildRailWagon(p1, tile, flags);
 
@@ -1964,7 +1965,7 @@ static void ReverseTrainDirection(Train *v)
 CommandCost CmdReverseTrainDirection(TileIndex tile, DoCommandFlag flags, uint32 p1, uint32 p2, const char *text)
 {
 	Train *v = Train::GetIfValid(p1);
-	if (v == NULL || !CheckOwnership(v->owner)) return CMD_ERROR;
+	if (v == NULL || !CheckVehicleControlAllowed(v)) return CMD_ERROR;
 
 	if (p2 != 0) {
 		/* turn a single unit around */
@@ -2031,7 +2032,7 @@ CommandCost CmdReverseTrainDirection(TileIndex tile, DoCommandFlag flags, uint32
 CommandCost CmdForceTrainProceed(TileIndex tile, DoCommandFlag flags, uint32 p1, uint32 p2, const char *text)
 {
 	Train *t = Train::GetIfValid(p1);
-	if (t == NULL || !CheckOwnership(t->owner)) return CMD_ERROR;
+	if (t == NULL || !CheckVehicleControlAllowed(t)) return CMD_ERROR;
 
 	if (flags & DC_EXEC) {
 		/* If we are forced to proceed, cancel that order.
@@ -3015,6 +3016,7 @@ static int UpdateTrainSpeed(Train *v)
 
 static void TrainEnterStation(Train *v, StationID station)
 {
+	StationID previous_station = v->last_station_visited;
 	v->last_station_visited = station;
 
 	/* check if a train ever visited this station before */
@@ -3031,7 +3033,7 @@ static void TrainEnterStation(Train *v, StationID station)
 		AI::NewEvent(v->owner, new AIEventStationFirstVehicle(st->index, v->index));
 	}
 
-	v->BeginLoading();
+	v->BeginLoading(previous_station);
 
 	StationAnimationTrigger(st, v->tile, STAT_ANIM_TRAIN_ARRIVES);
 }
@@ -3070,7 +3072,7 @@ static byte AfterSetTrainPos(Train *v, bool new_tile)
 static inline bool CheckCompatibleRail(const Train *v, TileIndex tile)
 {
 	return
-		IsTileOwner(tile, v->owner) && (
+		IsInfraTileUsageAllowed(tile, v->owner, VEH_TRAIN) && (
 			!v->IsFrontEngine() ||
 			HasBit(v->compatible_railtypes, GetRailType(tile))
 		);
@@ -4184,6 +4186,9 @@ void Train::OnNewDay()
 			/* running costs */
 			CommandCost cost(EXPENSES_TRAIN_RUN, this->GetRunningCost() * this->running_ticks / (DAYS_IN_YEAR  * DAY_TICKS));
 
+			/* sharing fee */
+			PayDailyTrackSharingFee(this);
+
 			this->profit_this_year -= cost.GetCost();
 			this->running_ticks = 0;
 
@@ -4214,3 +4219,47 @@ Trackdir Train::GetVehicleTrackdir() const
 
 	return TrackDirectionToTrackdir(FindFirstTrack(this->track), this->direction);
 }
+
+/**
+ * Delete a train while it is visible.
+ * This happens when a company bankrupts when infrastructure sharing is enabled.
+ * @param v The train to delete.
+ */
+void DeleteVisibleTrain(Train *v)
+{
+	FreeTrainTrackReservation(v);
+	TileIndex crossing = TrainApproachingCrossingTile(v);
+
+	/* delete train from back to front */
+	Train *u;
+	Train *prev = v->Last();
+	do {
+		u = prev;
+		prev = u->Previous();
+		if (prev != NULL) prev->SetNext(NULL);
+
+		/* 'u' shouldn't be accessed after it has been deleted */
+		TileIndex tile = u->tile;
+		TrackBits trackbits = u->track;
+
+		delete u;
+
+		if (trackbits == TRACK_BIT_WORMHOLE) {
+			/* Vehicle is inside a wormhole, u->track contains no useful value then. */
+			trackbits = DiagDirToDiagTrackBits(GetTunnelBridgeDirection(tile));
+		}
+
+		Track track = TrackBitsToTrack(trackbits);
+		if (HasReservedTracks(tile, trackbits)) UnreserveRailTrack(tile, track);
+		if (IsLevelCrossingTile(tile)) UpdateLevelCrossing(tile);
+
+		/* Update signals */
+		if (IsTileType(tile, MP_TUNNELBRIDGE) || IsRailDepotTile(tile)) {
+			UpdateSignalsOnSegment(tile, INVALID_DIAGDIR, GetTileOwner(tile));
+		} else {
+			SetSignalsOnBothDir(tile, track, GetTileOwner(tile));
+		}
+	} while (prev != NULL);
+
+	if (crossing != INVALID_TILE) UpdateLevelCrossing(crossing);
+}
diff --git a/src/vehicle.cpp b/src/vehicle.cpp
index 6b625f7..c2ccedf 100644
--- a/src/vehicle.cpp
+++ b/src/vehicle.cpp
@@ -47,6 +47,7 @@
 #include "economy_base.h"
 #include "articulated_vehicles.h"
 #include "roadstop_base.h"
+#include "infrastructure_func.h"
 
 #include "table/sprites.h"
 #include "table/strings.h"
@@ -550,10 +551,11 @@ void Vehicle::PreDestructor()
 	if (CleaningPool()) return;
 
 	if (Station::IsValidID(this->last_station_visited)) {
-		Station::Get(this->last_station_visited)->loading_vehicles.remove(this);
+		Station *st = Station::Get(this->last_station_visited);
+		st->loading_vehicles.remove(this);
 
 		HideFillingPercent(&this->fill_percent_te_id);
-
+		this->CancelReservation(INVALID_STATION, st);
 		delete this->cargo_payment;
 	}
 
@@ -1516,7 +1518,7 @@ uint GetVehicleCapacity(const Vehicle *v, uint16 *mail_capacity)
 }
 
 
-void Vehicle::BeginLoading()
+void Vehicle::BeginLoading(StationID last_station_id)
 {
 	assert(IsTileType(tile, MP_STATION) || type == VEH_SHIP);
 
@@ -1536,9 +1538,25 @@ void Vehicle::BeginLoading()
 		current_order.MakeLoading(false);
 	}
 
-	Station::Get(this->last_station_visited)->loading_vehicles.push_back(this);
+	StationID curr_station_id = this->last_station_visited;
+	Station * curr_station = Station::Get(curr_station_id);
+	curr_station->loading_vehicles.push_back(this);
+
+	StationID next_station_id = INVALID_STATION;
+	OrderList * orders = this->orders.list;
+	if (orders != NULL) {
+		next_station_id = orders->GetNextStoppingStation(this->cur_order_index, this->type == VEH_ROAD || this->type == VEH_TRAIN);
+	}
+
+	if (last_station_id != INVALID_STATION && last_station_id != curr_station_id) {
+		IncreaseStats(Station::Get(last_station_id), this, curr_station_id);
+	}
+
+	if (next_station_id != INVALID_STATION && next_station_id != curr_station_id) {
+		IncreaseFrozen(curr_station, this, next_station_id);
+	}
 
-	PrepareUnload(this);
+	PrepareUnload(curr_station, this, next_station_id);
 
 	SetWindowDirty(GetWindowClassForVehicleType(this->type), this->owner);
 	SetWindowWidgetDirty(WC_VEHICLE_VIEW, this->index, VVW_WIDGET_START_STOP_VEH);
@@ -1550,19 +1568,49 @@ void Vehicle::BeginLoading()
 	this->MarkDirty();
 }
 
+/**
+ * return all reserved cargo packets to the station
+ * @param st the station where the reserved packets should go.
+ */
+void Vehicle::CancelReservation(StationID next, Station *st) {
+	for(Vehicle *v = this; v != NULL; v = v->next) {
+		VehicleCargoList &cargo = v->cargo;
+		if (cargo.ReservedCount() > 0) {
+			DEBUG(misc, 1, "cancelling cargo reservation");
+			GoodsEntry &ge = st->goods[v->cargo_type];
+			cargo.Unreserve(next, &ge.cargo);
+			SetBit(ge.acceptance_pickup, GoodsEntry::PICKUP);
+		}
+	}
+}
+
+
 void Vehicle::LeaveStation()
 {
 	assert(current_order.IsType(OT_LOADING));
-
+	Station *st = Station::Get(this->last_station_visited);
 	delete this->cargo_payment;
 
 	/* Only update the timetable if the vehicle was supposed to stop here. */
 	if (current_order.GetNonStopType() != ONSF_STOP_EVERYWHERE) UpdateVehicleTimetable(this, false);
 
 	current_order.MakeLeaveStation();
-	Station *st = Station::Get(this->last_station_visited);
 	st->loading_vehicles.remove(this);
 
+
+	OrderList * orders = this->orders.list;
+	if (orders != NULL) {
+		StationID next_station_id = orders->GetNextStoppingStation(this->cur_order_index, this->type == VEH_ROAD || this->type == VEH_TRAIN);
+		this->CancelReservation(next_station_id, st);
+		if (next_station_id != INVALID_STATION && next_station_id != this->last_station_visited) {
+			DecreaseFrozen(st, this, next_station_id);
+		}
+	} else {
+		this->CancelReservation(INVALID_STATION, st);
+		DEBUG(misc, 0, "orders are NULL");
+		RecalcFrozen(st);
+	}
+
 	HideFillingPercent(&this->fill_percent_te_id);
 
 	if (this->type == VEH_TRAIN && !(this->vehstatus & VS_CRASHED)) {
@@ -1585,6 +1633,9 @@ void Vehicle::HandleLoading(bool mode)
 		case OT_LOADING: {
 			uint wait_time = max(this->current_order.wait_time - this->lateness_counter, 0);
 
+			/* Pay the loading fee for using someone else's station, if appropriate */
+			if (!mode && this->type != VEH_TRAIN) PayStationSharingFee(this, Station::Get(this->last_station_visited));
+
 			/* Not the first call for this tick, or still loading */
 			if (mode || !HasBit(this->vehicle_flags, VF_LOADING_FINISHED) ||
 					(_settings_game.order.timetabling && this->current_order_time < wait_time)) return;
diff --git a/src/vehicle_base.h b/src/vehicle_base.h
index b032b44..72224c3 100644
--- a/src/vehicle_base.h
+++ b/src/vehicle_base.h
@@ -201,7 +201,8 @@ public:
 	/** We want to 'destruct' the right class. */
 	virtual ~Vehicle();
 
-	void BeginLoading();
+	void BeginLoading(StationID last_station_id);
+	void CancelReservation(StationID next, Station *st);
 	void LeaveStation();
 
 	/**
diff --git a/src/vehicle_cmd.cpp b/src/vehicle_cmd.cpp
index a284366..d2bbaa8 100644
--- a/src/vehicle_cmd.cpp
+++ b/src/vehicle_cmd.cpp
@@ -27,6 +27,7 @@
 #include "string_func.h"
 #include "depot_map.h"
 #include "vehiclelist.h"
+#include "infrastructure_func.h"
 
 #include "table/strings.h"
 
@@ -74,7 +75,7 @@ CommandCost CmdStartStopVehicle(TileIndex tile, DoCommandFlag flags, uint32 p1,
 	if ((flags & DC_AUTOREPLACE) == 0) SetBit(p2, 0);
 
 	Vehicle *v = Vehicle::GetIfValid(p1);
-	if (v == NULL || !CheckOwnership(v->owner) || !v->IsPrimaryVehicle()) return CMD_ERROR;
+	if (v == NULL || !CheckVehicleControlAllowed(v) || !v->IsPrimaryVehicle()) return CMD_ERROR;
 
 	switch (v->type) {
 		case VEH_TRAIN:
@@ -217,7 +218,7 @@ CommandCost CmdDepotMassAutoReplace(TileIndex tile, DoCommandFlag flags, uint32
 	VehicleType vehicle_type = (VehicleType)GB(p1, 0, 8);
 	bool all_or_nothing = HasBit(p2, 0);
 
-	if (!IsDepotTile(tile) || !IsTileOwner(tile, _current_company)) return CMD_ERROR;
+	if (!IsDepotTile(tile) || !CheckInfraUsageAllowed(GetTileOwner(tile), vehicle_type)) return CMD_ERROR;
 
 	/* Get the list of vehicles in the depot */
 	BuildDepotVehicleList(vehicle_type, tile, &list, &list, true);
diff --git a/src/vehicle_gui.cpp b/src/vehicle_gui.cpp
index 2ebf02b..7305c04 100644
--- a/src/vehicle_gui.cpp
+++ b/src/vehicle_gui.cpp
@@ -40,6 +40,7 @@
 #include "articulated_vehicles.h"
 #include "cargotype.h"
 #include "spritecache.h"
+#include "infrastructure_func.h"
 
 #include "table/sprites.h"
 #include "table/strings.h"
@@ -1953,6 +1954,7 @@ public:
 	{
 		const Vehicle *v = Vehicle::Get(this->window_number);
 		bool is_localcompany = v->owner == _local_company;
+		bool can_control = IsVehicleControlAllowed(v, _local_company);
 		bool refitable_and_stopped_in_depot = IsVehicleRefitable(v);
 
 		this->SetWidgetDisabledState(VVW_WIDGET_GOTO_DEPOT, !is_localcompany);
@@ -1961,8 +1963,8 @@ public:
 
 		if (v->type == VEH_TRAIN) {
 			this->SetWidgetLoweredState(VVW_WIDGET_FORCE_PROCEED, Train::From(v)->force_proceed == 2);
-			this->SetWidgetDisabledState(VVW_WIDGET_FORCE_PROCEED, !is_localcompany);
-			this->SetWidgetDisabledState(VVW_WIDGET_TURN_AROUND, !is_localcompany);
+			this->SetWidgetDisabledState(VVW_WIDGET_FORCE_PROCEED, !can_control);
+			this->SetWidgetDisabledState(VVW_WIDGET_TURN_AROUND, !can_control);
 		}
 
 		this->DrawWidgets();
diff --git a/src/vehicle_type.h b/src/vehicle_type.h
index 0ccc37e..fcd57e3 100644
--- a/src/vehicle_type.h
+++ b/src/vehicle_type.h
@@ -65,7 +65,7 @@ enum DepotCommand {
 
 enum {
 	MAX_LENGTH_VEHICLE_NAME_BYTES  =  31, ///< The maximum length of a vehicle name in bytes including '\0'
-	MAX_LENGTH_VEHICLE_NAME_PIXELS = 150, ///< The maximum length of a vehicle name in pixels
+	MAX_LENGTH_VEHICLE_NAME_PIXELS = 250, ///< The maximum length of a vehicle name in pixels
 };
 
 enum TrainAccelerationModel {
diff --git a/src/viewport.cpp b/src/viewport.cpp
index 7b417f9..9f5f810 100644
--- a/src/viewport.cpp
+++ b/src/viewport.cpp
@@ -459,10 +459,10 @@ Point GetTileZoomCenterWindow(bool in, Window * w)
  * @param widget_zoom_out widget index for window with zoom-out button */
 void HandleZoomMessage(Window *w, const ViewPort *vp, byte widget_zoom_in, byte widget_zoom_out)
 {
-	w->SetWidgetDisabledState(widget_zoom_in, vp->zoom == ZOOM_LVL_MIN);
+	w->SetWidgetDisabledState(widget_zoom_in, vp->zoom == ZOOM_LVL_BLITTER_MIN);
 	w->SetWidgetDirty(widget_zoom_in);
 
-	w->SetWidgetDisabledState(widget_zoom_out, vp->zoom == ZOOM_LVL_MAX);
+	w->SetWidgetDisabledState(widget_zoom_out, vp->zoom == ZOOM_LVL_BLITTER_MAX);
 	w->SetWidgetDirty(widget_zoom_out);
 }
 
@@ -1203,15 +1203,15 @@ void ViewportSign::UpdatePosition(int center, int top, StringID str)
  */
 void ViewportSign::MarkDirty() const
 {
-	/* We use ZOOM_LVL_MAX here, as every viewport can have another zoom,
+	/* We use ZOOM_LVL_BLITTER_MAX here, as every viewport can have another zoom,
 	 *  and there is no way for us to know which is the biggest. So make the
 	 *  biggest area dirty, and we are safe for sure.
 	 * We also add 1 to make sure the whole thing is redrawn. */
 	MarkAllViewportsDirty(
-		this->center - ScaleByZoom(this->width_normal / 2 + 1, ZOOM_LVL_MAX),
-		this->top    - ScaleByZoom(1, ZOOM_LVL_MAX),
-		this->center + ScaleByZoom(this->width_normal / 2 + 1, ZOOM_LVL_MAX),
-		this->top    + ScaleByZoom(VPSM_TOP + FONT_HEIGHT_NORMAL + VPSM_BOTTOM + 1, ZOOM_LVL_MAX));
+		this->center - ScaleByZoom(this->width_normal / 2 + 1, ZOOM_LVL_BLITTER_MAX),
+		this->top    - ScaleByZoom(1, ZOOM_LVL_BLITTER_MAX),
+		this->center + ScaleByZoom(this->width_normal / 2 + 1, ZOOM_LVL_BLITTER_MAX),
+		this->top    + ScaleByZoom(VPSM_TOP + FONT_HEIGHT_NORMAL + VPSM_BOTTOM + 1, ZOOM_LVL_BLITTER_MAX));
 }
 
 static void ViewportDrawTileSprites(const TileSpriteToDrawVector *tstdv)
diff --git a/src/waypoint_gui.cpp b/src/waypoint_gui.cpp
index b48c3a9..85d130e 100644
--- a/src/waypoint_gui.cpp
+++ b/src/waypoint_gui.cpp
@@ -56,7 +56,7 @@ public:
 
 		this->flags4 |= WF_DISABLE_VP_SCROLL;
 		NWidgetViewport *nvp = this->GetWidget<NWidgetViewport>(WAYPVW_VIEWPORT);
-		nvp->InitializeViewport(this, this->wp->xy, ZOOM_LVL_MIN);
+		nvp->InitializeViewport(this, this->wp->xy, ZOOM_LVL_BLITTER_MIN);
 
 		this->OnInvalidateData(0);
 	}
diff --git a/src/zoom_type.h b/src/zoom_type.h
index e2698ae..e2211a3 100644
--- a/src/zoom_type.h
+++ b/src/zoom_type.h
@@ -16,15 +16,13 @@
 
 enum ZoomLevel {
 	/* Our possible zoom-levels */
-	ZOOM_LVL_BEGIN  = 0,
-	ZOOM_LVL_NORMAL = 0,
+	ZOOM_LVL_IN_8X,
+	ZOOM_LVL_IN_4X,
+	ZOOM_LVL_IN_2X,
+	ZOOM_LVL_NORMAL,
 	ZOOM_LVL_OUT_2X,
 	ZOOM_LVL_OUT_4X,
 	ZOOM_LVL_OUT_8X,
-	ZOOM_LVL_END,
-
-	/* Number of zoom levels */
-	ZOOM_LVL_COUNT = ZOOM_LVL_END - ZOOM_LVL_BEGIN,
 
 	/* Here we define in which zoom viewports are */
 	ZOOM_LVL_VIEWPORT = ZOOM_LVL_NORMAL,
@@ -37,10 +35,22 @@ enum ZoomLevel {
 	ZOOM_LVL_ROADVEH  = ZOOM_LVL_NORMAL,
 	ZOOM_LVL_WORLD_SCREENSHOT = ZOOM_LVL_NORMAL,
 
-	ZOOM_LVL_DETAIL   = ZOOM_LVL_OUT_2X, ///< All zoomlevels below or equal to this, will result in details on the screen, like road-work, ...
+	ZOOM_LVL_DETAIL   = ZOOM_LVL_OUT_2X, ///< All zoomlevels with higher resolution or equal to this, will result in details on the screen, like road-work, ...
 
-	ZOOM_LVL_MIN      = ZOOM_LVL_NORMAL,
+	/* min/max for all zoom levels */
+	ZOOM_LVL_MIN      = ZOOM_LVL_IN_8X,
 	ZOOM_LVL_MAX      = ZOOM_LVL_OUT_8X,
+	ZOOM_LVL_COUNT    = ZOOM_LVL_MAX + 1 - ZOOM_LVL_MIN,
+
+	/* min/max for zoom levels the blitter can handle
+	 *
+	 * This distinction makes it possible to introduce more zoom levels for other windows.
+	 * For example the smallmap is drawn independently from the main viewport and thus
+	 * could support different zoom levels.
+	 */
+	ZOOM_LVL_BLITTER_MIN   = ZOOM_LVL_NORMAL,
+	ZOOM_LVL_BLITTER_MAX   = ZOOM_LVL_OUT_8X,
+	ZOOM_LVL_BLITTER_COUNT = ZOOM_LVL_BLITTER_MAX + 1 - ZOOM_LVL_BLITTER_MIN,
 };
 DECLARE_POSTFIX_INCREMENT(ZoomLevel)
 
