(svn r10266) -Codechange: keep track of the origin, time of travel and accumulated feeder share (transfers) of individual pieces of cargo. This means that cargo isn't thrown on a big pile when it's put in a station or unloaded at a station, however the GUI does not reflect these changes yet so you will not actually see it.
authorrubidium
Fri, 22 Jun 2007 11:58:59 +0000
changeset 7506 e52d89f5c7c1
parent 7505 94a7391e0377
child 7507 c287e5e93517
(svn r10266) -Codechange: keep track of the origin, time of travel and accumulated feeder share (transfers) of individual pieces of cargo. This means that cargo isn't thrown on a big pile when it's put in a station or unloaded at a station, however the GUI does not reflect these changes yet so you will not actually see it.
projects/openttd.vcproj
projects/openttd_vs80.vcproj
source.list
src/ai/trolly/trolly.cpp
src/aircraft_cmd.cpp
src/aircraft_gui.cpp
src/autoreplace_cmd.cpp
src/cargopacket.cpp
src/cargopacket.h
src/economy.cpp
src/misc.cpp
src/newgrf_engine.cpp
src/newgrf_station.cpp
src/oldloader.cpp
src/openttd.cpp
src/openttd.h
src/roadveh_cmd.cpp
src/roadveh_gui.cpp
src/saveload.cpp
src/saveload.h
src/ship_cmd.cpp
src/ship_gui.cpp
src/station.cpp
src/station.h
src/station_cmd.cpp
src/station_gui.cpp
src/train_cmd.cpp
src/train_gui.cpp
src/vehicle.cpp
src/vehicle.h
src/water_cmd.cpp
--- a/projects/openttd.vcproj	Fri Jun 22 10:57:53 2007 +0000
+++ b/projects/openttd.vcproj	Fri Jun 22 11:58:59 2007 +0000
@@ -182,6 +182,9 @@
 				RelativePath=".\..\src\cargotype.cpp">
 			</File>
 			<File
+				RelativePath=".\..\src\cargopacket.cpp">
+			</File>
+			<File
 				RelativePath=".\..\src\command.cpp">
 			</File>
 			<File
@@ -402,6 +405,9 @@
 				RelativePath=".\..\src\cargotype.h">
 			</File>
 			<File
+				RelativePath=".\..\src\cargopacket.h">
+			</File>
+			<File
 				RelativePath=".\..\src\command.h">
 			</File>
 			<File
--- a/projects/openttd_vs80.vcproj	Fri Jun 22 10:57:53 2007 +0000
+++ b/projects/openttd_vs80.vcproj	Fri Jun 22 11:58:59 2007 +0000
@@ -472,6 +472,10 @@
 				>
 			</File>
 			<File
+				RelativePath=".\..\src\cargopacket.cpp"
+				>
+			</File>
+			<File
 				RelativePath=".\..\src\command.cpp"
 				>
 			</File>
@@ -764,6 +768,10 @@
 				>
 			</File>
 			<File
+				RelativePath=".\..\src\cargopacket.h"
+				>
+			</File>
+			<File
 				RelativePath=".\..\src\command.h"
 				>
 			</File>
--- a/source.list	Fri Jun 22 10:57:53 2007 +0000
+++ b/source.list	Fri Jun 22 11:58:59 2007 +0000
@@ -6,6 +6,7 @@
 bmp.cpp
 callback_table.cpp
 cargotype.cpp
+cargopacket.cpp
 command.cpp
 console.cpp
 console_cmds.cpp
@@ -101,6 +102,7 @@
 aystar.h
 bmp.h
 cargotype.h
+cargopacket.h
 command.h
 console.h
 currency.h
--- a/src/ai/trolly/trolly.cpp	Fri Jun 22 10:57:53 2007 +0000
+++ b/src/ai/trolly/trolly.cpp	Fri Jun 22 11:58:59 2007 +0000
@@ -617,7 +617,7 @@
 			if (p->ainew.tbt == AI_BUS && (FACIL_BUS_STOP & st->facilities) == FACIL_BUS_STOP) {
 				if (st->town == town) {
 					// Check how much cargo there is left in the station
-					if ((st->goods[p->ainew.cargo].waiting_acceptance & 0xFFF) > RoadVehInfo(i)->capacity * AI_STATION_REUSE_MULTIPLER) {
+					if ((int)st->goods[p->ainew.cargo].cargo.Count() > RoadVehInfo(i)->capacity * AI_STATION_REUSE_MULTIPLER) {
 						if (AiNew_CheckVehicleStation(p, st)) {
 							// We did found a station that was good enough!
 							new_tile = st->xy;
@@ -1258,7 +1258,7 @@
 		if (v->profit_last_year + v->profit_this_year < AI_MINIMUM_ROUTE_PROFIT ||
 				(v->reliability * 100 >> 16) < 40) {
 			// There is a possibility that the route is fucked up...
-			if (v->cargo_days > AI_VEHICLE_LOST_DAYS) {
+			if (v->cargo.DaysInTransit() > AI_VEHICLE_LOST_DAYS) {
 				// The vehicle is lost.. check the route, or else, get the vehicle
 				//  back to a depot
 				// TODO: make this piece of code
--- a/src/aircraft_cmd.cpp	Fri Jun 22 10:57:53 2007 +0000
+++ b/src/aircraft_cmd.cpp	Fri Jun 22 11:58:59 2007 +0000
@@ -680,13 +680,8 @@
 		Vehicle *u = v->next;
 		uint mail = IsCargoInClass(new_cid, CC_PASSENGERS) ? avi->mail_capacity : 0;
 		u->cargo_cap = mail;
-		if (v->cargo_type == new_cid) {
-			v->cargo_count = min(pass, v->cargo_count);
-			u->cargo_count = min(mail, u->cargo_count);
-		} else {
-			v->cargo_count = 0;
-			u->cargo_count = 0;
-		}
+		v->cargo.Truncate(v->cargo_type == new_cid ? pass : 0);
+		u->cargo.Truncate(v->cargo_type == new_cid ? mail : 0);
 		v->cargo_type = new_cid;
 		v->cargo_subtype = new_subtype;
 		InvalidateWindow(WC_VEHICLE_DETAILS, v->index);
@@ -774,7 +769,7 @@
 	if (_age_cargo_skip_counter != 0) return;
 
 	do {
-		if (v->cargo_days != 0xFF) v->cargo_days++;
+		v->cargo.AgeCargo();
 		v = v->next;
 	} while (v != NULL);
 }
@@ -1429,11 +1424,11 @@
 	InvalidateWindow(WC_VEHICLE_VIEW, v->index);
 
 	uint amt = 2;
-	if (IsCargoInClass(v->cargo_type, CC_PASSENGERS)) amt += v->cargo_count;
+	if (IsCargoInClass(v->cargo_type, CC_PASSENGERS)) amt += v->cargo.Count();
 	SetDParam(0, amt);
 
-	v->cargo_count = 0;
-	v->next->cargo_count = 0;
+	v->cargo.Truncate(0);
+	v->next->cargo.Truncate(0);
 	const Station *st = GetStation(v->u.air.targetairport);
 	StringID newsitem;
 	if (st->airport_tile == 0) {
@@ -1469,7 +1464,7 @@
 	/* Crash the airplane. Remove all goods stored at the station. */
 	for (CargoID i = 0; i < NUM_CARGO; i++) {
 		st->goods[i].rating = 1;
-		SB(st->goods[i].waiting_acceptance, 0, 12, 0);
+		st->goods[i].cargo.Truncate(0);
 	}
 
 	CrashAirplane(v);
--- a/src/aircraft_gui.cpp	Fri Jun 22 10:57:53 2007 +0000
+++ b/src/aircraft_gui.cpp	Fri Jun 22 11:58:59 2007 +0000
@@ -125,7 +125,7 @@
 
 		/* Draw Transfer credits text */
 		{
-			SetDParam(0, v->cargo_feeder_share);
+			SetDParam(0, v->cargo.FeederShare());
 			DrawString(60, 101, STR_FEEDER_CARGO_VALUE, 0);
 		}
 
@@ -152,12 +152,13 @@
 					y += 14;
 				}
 
-				if (v->cargo_count != 0) {
+				uint cargo_count = v->cargo.Count();
+				if (cargo_count != 0) {
 
 					/* Cargo names (fix pluralness) */
 					SetDParam(0, v->cargo_type);
-					SetDParam(1, v->cargo_count);
-					SetDParam(2, v->cargo_source);
+					SetDParam(1, cargo_count);
+					SetDParam(2, v->cargo.Source());
 					DrawString(60, y, STR_8813_FROM, 0);
 
 					y += 10;
--- a/src/autoreplace_cmd.cpp	Fri Jun 22 10:57:53 2007 +0000
+++ b/src/autoreplace_cmd.cpp	Fri Jun 22 11:58:59 2007 +0000
@@ -25,27 +25,23 @@
 static void MoveVehicleCargo(Vehicle *dest, Vehicle *source)
 {
 	Vehicle *v = dest;
-	int units_moved;
 
 	do {
 		do {
 			if (source->cargo_type != dest->cargo_type)
 				continue; // cargo not compatible
 
-			if (dest->cargo_count == dest->cargo_cap)
+			if (dest->cargo.Count() == dest->cargo_cap)
 				continue; // the destination vehicle is already full
 
-			units_moved = min(source->cargo_count, dest->cargo_cap - dest->cargo_count);
-			source->cargo_count -= units_moved;
-			dest->cargo_count   += units_moved;
-			dest->cargo_source   = source->cargo_source;
+			uint units_moved = min(source->cargo.Count(), dest->cargo_cap - dest->cargo.Count());
+			source->cargo.MoveTo(&dest->cargo, units_moved);
 
 			// copy the age of the cargo
-			dest->cargo_days   = source->cargo_days;
 			dest->day_counter  = source->day_counter;
 			dest->tick_counter = source->tick_counter;
 
-		} while (source->cargo_count > 0 && (dest = dest->next) != NULL);
+		} while (source->cargo.Count() > 0 && (dest = dest->next) != NULL);
 		dest = v;
 	} while ((source = source->next) != NULL);
 
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/src/cargopacket.cpp	Fri Jun 22 11:58:59 2007 +0000
@@ -0,0 +1,351 @@
+/* $Id$ */
+
+/** @file cargopacket.cpp Implementation of the cargo packets */
+
+#include "stdafx.h"
+#include "openttd.h"
+#include "station.h"
+#include "cargopacket.h"
+#include "saveload.h"
+
+/**
+ * Called if a new block is added to the station-pool
+ */
+static void CargoPacketPoolNewBlock(uint cpart_item)
+{
+	/* We don't use FOR_ALL here, because FOR_ALL skips invalid items.
+	 *  TODO - This is just a temporary stage, this will be removed. */
+	for (CargoPacket *cp = GetCargoPacket(cpart_item); cp != NULL; cp = (cp->index + 1U < GetCargoPacketPoolSize()) ? GetCargoPacket(cp->index + 1U) : NULL) cp->index = cpart_item++;
+}
+
+static void CargoPacketPoolCleanBlock(uint cpart_item, uint end_item)
+{
+	for (uint i = cpart_item; i <= end_item; i++) {
+		CargoPacket *cp = GetCargoPacket(i);
+		if (cp->IsValid()) cp->~CargoPacket();
+	}
+}
+
+/* Initialize the cargopacket-pool */
+DEFINE_OLD_POOL(CargoPacket, CargoPacket, CargoPacketPoolNewBlock, CargoPacketPoolCleanBlock)
+
+void InitializeCargoPackets()
+{
+	/* Clean the cargo packet pool and create 1 block in it */
+	CleanPool(&_CargoPacket_pool);
+	AddBlockToPool(&_CargoPacket_pool);
+
+	/* Check whether our &cargolist == &cargolist.packets "hack" works */
+	CargoList::AssertOnWrongPacketOffset();
+}
+
+CargoPacket::CargoPacket(StationID source, uint16 count)
+{
+	if (source != INVALID_STATION) assert(count != 0);
+
+	this->source          = source;
+	this->source_xy       = (source != INVALID_STATION) ? GetStation(source)->xy : 0;
+	this->loaded_at_xy    = this->source_xy;
+
+	this->count           = count;
+	this->days_in_transit = 0;
+	this->feeder_share    = 0;
+	this->paid_for        = false;
+}
+
+CargoPacket::~CargoPacket()
+{
+	this->count = 0;
+}
+
+bool CargoPacket::SameSource(CargoPacket *cp)
+{
+	return this->source_xy == cp->source_xy && this->days_in_transit == cp->days_in_transit && this->paid_for == cp->paid_for;
+}
+
+void *CargoPacket::operator new(size_t size)
+{
+	CargoPacket *cp = AllocateRaw();
+	return cp;
+}
+
+void *CargoPacket::operator new(size_t size, CargoPacket::ID cp_idx)
+{
+	if (!AddBlockIfNeeded(&_CargoPacket_pool, cp_idx))
+		error("CargoPackets: failed loading savegame: too many cargo packets");
+
+	CargoPacket *cp = GetCargoPacket(cp_idx);
+	return cp;
+}
+
+void CargoPacket::operator delete(void *p)
+{
+}
+
+void CargoPacket::operator delete(void *p, CargoPacket::ID cp_idx)
+{
+}
+
+/*static*/ CargoPacket *CargoPacket::AllocateRaw()
+{
+	CargoPacket *cp = NULL;
+
+	/* We don't use FOR_ALL here, because FOR_ALL skips invalid items.
+	 * TODO - This is just a temporary stage, this will be removed. */
+	for (cp = GetCargoPacket(0); cp != NULL; cp = (cp->index + 1U < GetCargoPacketPoolSize()) ? GetCargoPacket(cp->index + 1U) : NULL) {
+		if (!cp->IsValid()) {
+			CargoPacket::ID index = cp->index;
+
+			memset(cp, 0, sizeof(CargoPacket));
+			cp->index = index;
+			return cp;
+		}
+	}
+
+	/* Check if we can add a block to the pool */
+	if (AddBlockToPool(&_CargoPacket_pool)) return AllocateRaw();
+
+	error("CargoPackets: too many cargo packets");
+}
+
+static const SaveLoad _cargopacket_desc[] = {
+	SLE_VAR(CargoPacket, source,          SLE_UINT16),
+	SLE_VAR(CargoPacket, source_xy,       SLE_UINT32),
+	SLE_VAR(CargoPacket, loaded_at_xy,    SLE_UINT32),
+	SLE_VAR(CargoPacket, count,           SLE_UINT16),
+	SLE_VAR(CargoPacket, days_in_transit, SLE_UINT8),
+	SLE_VAR(CargoPacket, feeder_share,    SLE_INT64),
+	SLE_VAR(CargoPacket, paid_for,        SLE_BOOL),
+
+	SLE_END()
+};
+
+static void Save_CAPA()
+{
+	CargoPacket *cp;
+
+	FOR_ALL_CARGOPACKETS(cp) {
+		SlSetArrayIndex(cp->index);
+		SlObject(cp, _cargopacket_desc);
+	}
+}
+
+static void Load_CAPA()
+{
+	int index;
+
+	while ((index = SlIterateArray()) != -1) {
+		if (!AddBlockIfNeeded(&_CargoPacket_pool, index)) {
+			error("CargoPackets: failed loading savegame: too many cargo packets");
+		}
+
+		CargoPacket *cp = GetCargoPacket(index);
+		SlObject(cp, _cargopacket_desc);
+	}
+}
+
+extern const ChunkHandler _cargopacket_chunk_handlers[] = {
+	{ 'CAPA', Save_CAPA, Load_CAPA, CH_ARRAY | CH_LAST},
+};
+
+/*
+ *
+ * Cargo list implementation
+ *
+ */
+
+/* static */ void CargoList::AssertOnWrongPacketOffset()
+{
+	CargoList cl;
+	if ((void*)&cl != (void*)cl.Packets()) NOT_REACHED();
+}
+
+
+CargoList::~CargoList()
+{
+	while (!packets.empty()) {
+		delete packets.front();
+		packets.pop_front();
+	}
+}
+
+const CargoList::List *CargoList::Packets() const
+{
+	return &packets;
+}
+
+void CargoList::AgeCargo()
+{
+	if (empty) return;
+
+	uint dit = 0;
+	for (List::const_iterator it = packets.begin(); it != packets.end(); it++) {
+		if ((*it)->days_in_transit != 0xFF) (*it)->days_in_transit++;
+		dit += (*it)->days_in_transit * (*it)->count;
+	}
+	days_in_transit = dit / count;
+}
+
+bool CargoList::Empty() const
+{
+	return empty;
+}
+
+uint CargoList::Count() const
+{
+	return count;
+}
+
+bool CargoList::UnpaidCargo() const
+{
+	return unpaid_cargo;
+}
+
+Money CargoList::FeederShare() const
+{
+	return feeder_share;
+}
+
+StationID CargoList::Source() const
+{
+	return source;
+}
+
+uint CargoList::DaysInTransit() const
+{
+	return days_in_transit;
+}
+
+void CargoList::Append(CargoPacket *cp)
+{
+	assert(cp != NULL);
+	assert(cp->IsValid());
+
+	for (List::iterator it = packets.begin(); it != packets.end(); it++) {
+		if ((*it)->SameSource(cp)) {
+			(*it)->count        += cp->count;
+			(*it)->feeder_share += cp->feeder_share;
+			delete cp;
+
+			InvalidateCache();
+			return;
+		}
+	}
+
+	/* The packet could not be merged with another one */
+	packets.push_back(cp);
+	InvalidateCache();
+}
+
+
+void CargoList::Truncate(uint count)
+{
+	for (List::iterator it = packets.begin(); it != packets.end(); it++) {
+		uint local_count = (*it)->count;
+		if (local_count <= count) {
+			count -= local_count;
+			continue;
+		}
+
+		(*it)->count = count;
+		count = 0;
+	}
+
+	while (!packets.empty()) {
+		CargoPacket *cp = packets.back();
+		if (cp->count != 0) break;
+		delete cp;
+		packets.pop_back();
+	}
+
+	InvalidateCache();
+}
+
+bool CargoList::MoveTo(CargoList *dest, uint count, CargoList::MoveToAction mta, uint data)
+{
+	assert(mta == MTA_FINAL_DELIVERY || dest != NULL);
+	CargoList tmp;
+
+	while (!packets.empty() && count > 0) {
+		CargoPacket *cp = *packets.begin();
+		if (cp->count <= count) {
+			/* Can move the complete packet */
+			packets.remove(cp);
+			switch (mta) {
+				case MTA_FINAL_DELIVERY:
+					if (cp->source == data) {
+						tmp.Append(cp);
+					} else {
+						count -= cp->count;
+						delete cp;
+					}
+					break;
+				case MTA_CARGO_LOAD:
+					cp->loaded_at_xy = data;
+					/* When cargo is moved into another vehicle you have *always* paid for it */
+					cp->paid_for     = false;
+					/* FALL THROUGH */
+				case MTA_OTHER:
+					count -= cp->count;
+					dest->packets.push_back(cp);
+					break;
+			}
+		} else {
+			/* Can move only part of the packet, so split it into two pieces */
+			if (mta != MTA_FINAL_DELIVERY) {
+				CargoPacket *cp_new = new CargoPacket();
+				cp_new->source          = cp->source;
+				cp_new->source_xy       = cp->source_xy;
+				cp_new->loaded_at_xy    = (mta == MTA_CARGO_LOAD) ? data : cp->loaded_at_xy;
+
+				cp_new->days_in_transit = cp->days_in_transit;
+				cp_new->feeder_share    = cp->feeder_share / count;
+				/* When cargo is moved into another vehicle you have *always* paid for it */
+				cp_new->paid_for        = (mta == MTA_CARGO_LOAD) ? false : cp->paid_for;
+
+				cp_new->count = count;
+				dest->packets.push_back(cp_new);
+
+				cp->feeder_share /= cp->count - count;
+			}
+			cp->count -= count;
+
+			count = 0;
+		}
+	}
+
+	bool remaining = !packets.empty();
+
+	if (mta == MTA_FINAL_DELIVERY && !tmp.Empty()) {
+		/* There are some packets that could not be delivered at the station, put them back */
+		tmp.MoveTo(this, MAX_UVALUE(uint));
+		tmp.packets.clear();
+	}
+
+	if (dest != NULL) dest->InvalidateCache();
+	InvalidateCache();
+
+	return remaining;
+}
+
+void CargoList::InvalidateCache()
+{
+	empty = packets.empty();
+	count = 0;
+	unpaid_cargo = false;
+	feeder_share = 0;
+	source = INVALID_STATION;
+	days_in_transit = 0;
+
+	if (empty) return;
+
+	uint dit = 0;
+	for (List::const_iterator it = packets.begin(); it != packets.end(); it++) {
+		count        += (*it)->count;
+		unpaid_cargo |= !(*it)->paid_for;
+		dit          += (*it)->days_in_transit * (*it)->count;
+		feeder_share += (*it)->feeder_share;
+	}
+	days_in_transit = dit / count;
+	source = (*packets.begin())->source;
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/src/cargopacket.h	Fri Jun 22 11:58:59 2007 +0000
@@ -0,0 +1,221 @@
+/* $Id$ */
+
+/** @file cargotype.h */
+
+#ifndef CARGOPACKET_H
+#define CARGOPACKET_H
+
+#include <list>
+
+/**
+ * Container for cargo from the same location and time
+ */
+struct CargoPacket {
+	bool touched;
+
+	typedef uint32 ID;      ///< Type for cargopacket identifiers
+
+	ID index;               ///< The unique index of this packet
+
+	StationID source;       ///< The station where the cargo came from first
+	TileIndex source_xy;    ///< The origin of the cargo (first station in feeder chain)
+	TileIndex loaded_at_xy; ///< Location where this cargo has been loaded into the vehicle
+
+	uint16 count;           ///< The amount of cargo in this packet
+	byte days_in_transit;   ///< Amount of days this packet has been in transit
+	Money feeder_share;     ///< Value of feeder pickup to be paid for on delivery of cargo
+	bool paid_for;          ///< Have we been paid for this cargo packet?
+
+	/**
+	 * Creates a new cargo packet
+	 * @param source the source of the packet
+	 * @param count  the number of cargo entities to put in this packet
+	 * @pre count != 0 || source == INVALID_STATION
+	 */
+	CargoPacket(StationID source = INVALID_STATION, uint16 count = 0);
+
+	/** Destroy the packet */
+	~CargoPacket();
+
+
+	/**
+	 * Is this a valid cargo packet ?
+	 * @return true if and only it is valid
+	 */
+	bool IsValid() const { return this->count != 0; }
+
+	/**
+	 * Checks whether the cargo packet is from (exactly) the same source
+	 * in time and location.
+	 * @param cp the cargo packet to compare to
+	 * @return true if and only if days_in_transit and source_xy are equal
+	 */
+	bool SameSource(CargoPacket *cp);
+
+
+	/* normal new/delete operators. Used when building/removing station */
+	void* operator new (size_t size);
+	void operator delete(void *p);
+
+	/* new/delete operators accepting station index. Used when loading station from savegame. */
+	void* operator new (size_t size, CargoPacket::ID cp_idx);
+	void operator delete(void *p, CargoPacket::ID cp_idx);
+
+private:
+	/**
+	 * Allocate the raw memory for this cargo packet
+	 * @return the allocated memory
+	 */
+	static CargoPacket *AllocateRaw();
+};
+
+/** We want to use a pool */
+DECLARE_OLD_POOL(CargoPacket, CargoPacket, 10, 1000)
+
+/**
+ * Iterate over all _valid_ cargo packets from the given start
+ * @param cp    the variable used as "iterator"
+ * @param start the cargo packet ID of the first packet to iterate over
+ */
+#define FOR_ALL_CARGOPACKETS_FROM(cp, start) for (cp = GetCargoPacket(start); cp != NULL; cp = (cp->index + 1U < GetCargoPacketPoolSize()) ? GetCargoPacket(cp->index + 1U) : NULL) if (cp->IsValid())
+
+/**
+ * Iterate over all _valid_ cargo packets from the begin of the pool
+ * @param cp    the variable used as "iterator"
+ */
+#define FOR_ALL_CARGOPACKETS(cp) FOR_ALL_CARGOPACKETS_FROM(cp, 0)
+
+/**
+ * Simple collection class for a list of cargo packets
+ */
+class CargoList {
+public:
+	/** List of cargo packets */
+	typedef std::list<CargoPacket *> List;
+
+	/** 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_OTHER           ///< "Just" move the packet to another cargo list
+	};
+
+private:
+	List packets;         ///< The cargo packets in this list
+
+	bool empty;           ///< Cache for whether this list is empty or not
+	uint count;           ///< Cache for the number of cargo entities
+	bool unpaid_cargo;    ///< Cache for the unpaid cargo
+	Money feeder_share;   ///< Cache for the feeder share
+	StationID source;     ///< Cache for the source of the packet
+	uint days_in_transit; ///< Cache for the number of days in transit
+
+public:
+	/**
+	 * Needed for an ugly hack:
+	 *  - vehicles and stations need to store cargo lists, so they use CargoList as container
+	 *  - this internals of the container should be protected, e.g. private (or protected) by C++
+	 *  - for saving/loading we need to pass pointer to objects
+	 *  -> so *if* the pointer to the cargo list is the same as the pointer to the packet list
+	 *     encapsulated in the CargoList, we can just pass the CargoList as "offset".
+	 *     Normally we would then just add the offset of the packets variable within the cargo list
+	 *     but that is not possible because the variable is private. Furthermore we are not sure
+	 *     that this works on all platforms, we need to check whether the offset is actually 0.
+	 *     This cannot be done compile time, because the variable is private. So we need to write
+	 *     a function that does actually check the offset runtime and call it somewhere where it
+	 *     is always called but it should not be called often.
+	 */
+	static void AssertOnWrongPacketOffset();
+
+	/** Create the cargo list */
+	CargoList() { this->InvalidateCache(); }
+	/** And destroy it ("frees" all cargo packets) */
+	~CargoList();
+
+	/**
+	 * Returns a pointer to the cargo packet list (so you can iterate over it etc).
+	 * @return pointer to the packet list
+	 */
+	const CargoList::List *Packets() const;
+
+	/**
+	 * Ages the all cargo in this list
+	 */
+	void AgeCargo();
+
+	/**
+	 * Checks whether this list is empty
+	 * @return true if and only if the list is empty
+	 */
+	bool Empty() const;
+
+	/**
+	 * Returns the number of cargo entities in this list
+	 * @return the before mentioned number
+	 */
+	uint Count() const;
+
+	/**
+	 * Is there some cargo that has not been paid for?
+	 * @return true if and only if there is such a cargo
+	 */
+	bool UnpaidCargo() const;
+
+	/**
+	 * Returns total sum of the feeder share for all packets
+	 * @return the before mentioned number
+	 */
+	Money FeederShare() const;
+
+	/**
+	 * Returns source of the first cargo packet in this list
+	 * @return the before mentioned source
+	 */
+	StationID Source() const;
+
+	/**
+	 * Returns average number of days in transit for a cargo entity
+	 * @return the before mentioned number
+	 */
+	uint DaysInTransit() const;
+
+
+	/**
+	 * 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.
+	 * @param count the maximum amount of entities to be in the list after the command
+	 */
+	void Truncate(uint count);
+
+	/**
+	 * 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_OTHER:          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_OTHER          - unused
+	 * @param mta == MTA_FINAL_DELIVERY || dest != NULL
+	 * @return true if there are still packets that might be moved from this cargo list
+	 */
+	bool MoveTo(CargoList *dest, uint count, CargoList::MoveToAction mta = MTA_OTHER, uint data = 0);
+
+	/** Invalidates the cached data and rebuild it */
+	void InvalidateCache();
+};
+
+#endif /* CARGOPACKET_H */
--- a/src/economy.cpp	Fri Jun 22 10:57:53 2007 +0000
+++ b/src/economy.cpp	Fri Jun 22 11:58:59 2007 +0000
@@ -1371,14 +1371,9 @@
 {
 	int result = 0;
 
-	Money profit = 0;
-	Money total_veh_profit = 0;         // accumulates the profit across the vehicle chain (used by trains)
-	Money route_profit = 0;             // the grand total amount for the route. A-D of transfer chain A-B-C-D
-	Money virtual_profit = 0;           // virtual profit of one vehicle element for feeder systems
-	Money virtual_profit_total = 0;     // virtual profit for entire vehicle chain
-	Money total_cargo_feeder_share = 0; // the feeder cash amount for the goods being loaded/unloaded in this load step
-
-	Money all_vehicles_cargo_feeder_share = front_v->cargo_feeder_share; // used to hold transfer value of complete vehicle chain - used by trains
+	Money vehicle_profit = 0; // Money paid to the train
+	Money route_profit   = 0; // The grand total amount for the route. A-D of transfer chain A-B-C-D
+	Money virtual_profit = 0; // The virtual profit for entire vehicle chain
 
 	StationID last_visited = front_v->last_station_visited;
 	Station *st = GetStation(last_visited);
@@ -1395,75 +1390,70 @@
 
 	for (Vehicle *v = front_v; v != NULL; v = v->next) {
 		/* No cargo to unload */
-		if (v->cargo_cap == 0 || v->cargo_count == 0) continue;
+		if (v->cargo_cap == 0 || v->cargo.Empty()) continue;
 
 		/* All cargo has already been paid for, no need to pay again */
-		if (v->cargo_count == v->cargo_paid_for) {
+		if (!v->cargo.UnpaidCargo()) {
 			SETBIT(v->vehicle_flags, VF_CARGO_UNLOADING);
 			continue;
 		}
 
 		GoodsEntry *ge = &st->goods[v->cargo_type];
-
-		if (v->cargo_source != last_visited &&
-				HASBIT(ge->waiting_acceptance, 15) &&
-				(front_v->current_order.flags & OF_TRANSFER) == 0) {
-			/* Deliver goods to the station */
-			st->time_since_unload = 0;
-
-			/* handle end of route payment */
-			profit += DeliverGoods(v->cargo_count - v->cargo_paid_for, v->cargo_type, v->cargo_source, last_visited, v->cargo_source_xy, v->cargo_days);
-			v->cargo_paid_for        = v->cargo_count;
-			route_profit             = profit;                                   // display amount paid for final route delivery, A-D of a chain A-B-C-D
-			total_veh_profit         = profit - all_vehicles_cargo_feeder_share; // whole vehicle is not payed for transfers picked up earlier
-			total_cargo_feeder_share = -all_vehicles_cargo_feeder_share;         // total of transfer fees in vehicle chain needs to be zero at end of unload
-
-			v->cargo_feeder_share = 0;   // clear transfer cost per vehicle
-			result |= 1;
+		const CargoList::List *cargos = v->cargo.Packets();
 
-			SETBIT(v->vehicle_flags, VF_CARGO_UNLOADING);
-		} else if (front_v->current_order.flags & (OF_UNLOAD | OF_TRANSFER)) {
-			if ((front_v->current_order.flags & OF_TRANSFER) != 0) {
-				virtual_profit = GetTransportedGoodsIncome(
-					v->cargo_count - v->cargo_paid_for,
-					/* pay transfer vehicle for only the part of transfer it has done: ie. cargo_loaded_at_xy to here */
-					DistanceManhattan(v->cargo_loaded_at_xy, GetStation(last_visited)->xy),
-					v->cargo_days,
-					v->cargo_type);
+		for (CargoList::List::const_iterator it = cargos->begin(); it != cargos->end(); it++) {
+			CargoPacket *cp = *it;
+			if (!cp->paid_for &&
+					cp->source != last_visited &&
+					ge->acceptance &&
+					(front_v->current_order.flags & OF_TRANSFER) == 0) {
+				/* Deliver goods to the station */
+				st->time_since_unload = 0;
 
-				front_v->profit_this_year += virtual_profit;
-				ge->feeder_profit         += v->cargo_feeder_share + virtual_profit; // transfer cargo transfer fees to station
-				total_cargo_feeder_share  -= v->cargo_feeder_share; // accumulate deduction of feeder shares
-				v->cargo_feeder_share      = 0;                     // clear transfer cost
+				/* handle end of route payment */
+				Money profit = DeliverGoods(cp->count, v->cargo_type, cp->source, last_visited, cp->source_xy, cp->days_in_transit);
+				cp->paid_for = true;
+				route_profit   += profit - cp->feeder_share; // display amount paid for final route delivery, A-D of a chain A-B-C-D
+				vehicle_profit += profit;                    // whole vehicle is not payed for transfers picked up earlier
 
-				/* keep total of cargo unloaded (pending) for accurate cargoshare calculation on load */
-				SB(ge->unload_pending, 0, 12, GB(ge->unload_pending, 0, 12) + v->cargo_count);
+				result |= 1;
 
-				virtual_profit_total += virtual_profit;   // accumulate transfer profits for whole vehicle
-				v->cargo_paid_for = v->cargo_count;       // record how much of the cargo has been paid for to eliminate double counting
+				SETBIT(v->vehicle_flags, VF_CARGO_UNLOADING);
+			} else if (front_v->current_order.flags & (OF_UNLOAD | OF_TRANSFER)) {
+				if (!cp->paid_for && (front_v->current_order.flags & OF_TRANSFER) != 0) {
+					Money profit = GetTransportedGoodsIncome(
+						cp->count,
+						/* pay transfer vehicle for only the part of transfer it has done: ie. cargo_loaded_at_xy to here */
+						DistanceManhattan(cp->loaded_at_xy, GetStation(last_visited)->xy),
+						cp->days_in_transit,
+						v->cargo_type);
+
+					front_v->profit_this_year += profit;
+					virtual_profit   += profit; // accumulate transfer profits for whole vehicle
+					cp->feeder_share += profit; // account for the (virtual) profit already made for the cargo packet
+					cp->paid_for      = true;   // record that the cargo has been paid for to eliminate double counting
+				}
+				result |= 2;
+
+				SETBIT(v->vehicle_flags, VF_CARGO_UNLOADING);
 			}
-			result |= 2;
-
-			SETBIT(v->vehicle_flags, VF_CARGO_UNLOADING);
 		}
+		v->cargo.InvalidateCache();
 	}
 
-	/* Ensure a negative total is only applied to the vehicle if there is value to reduce. */
-	front_v->cargo_feeder_share = max(front_v->cargo_feeder_share + total_cargo_feeder_share, 0LL);
-
-	if (virtual_profit_total > 0) {
-		ShowFeederIncomeAnimation(front_v->x_pos, front_v->y_pos, front_v->z_pos, virtual_profit_total);
+	if (virtual_profit > 0) {
+		ShowFeederIncomeAnimation(front_v->x_pos, front_v->y_pos, front_v->z_pos, virtual_profit);
 	}
 
 	if (route_profit != 0) {
-		front_v->profit_this_year += total_veh_profit;
+		front_v->profit_this_year += vehicle_profit;
 		SubtractMoneyFromPlayer(-route_profit);
 
 		if (IsLocalPlayer() && !PlayVehicleSound(front_v, VSE_LOAD_UNLOAD)) {
 			SndPlayVehicleFx(SND_14_CASHTILL, front_v);
 		}
 
-		ShowCostOrIncomeAnimation(front_v->x_pos, front_v->y_pos, front_v->z_pos, -total_veh_profit);
+		ShowCostOrIncomeAnimation(front_v->x_pos, front_v->y_pos, front_v->z_pos, -vehicle_profit);
 	}
 
 	_current_player = old_player;
@@ -1486,7 +1476,7 @@
 		if (_patches.improved_load && HASBIT(v->current_order.flags, OFB_FULL_LOAD)) {
 			/* 'Reserve' this cargo for this vehicle, because we were first. */
 			for (; v != NULL; v = v->next) {
-				if (v->cargo_cap != 0) cargo_left[v->cargo_type] -= v->cargo_cap - v->cargo_count;
+				if (v->cargo_cap != 0) cargo_left[v->cargo_type] -= v->cargo_cap - v->cargo.Count();
 			}
 		}
 		return;
@@ -1502,14 +1492,13 @@
 	int unloading_time = 0;
 	Vehicle *u = v;
 	int result = 0;
-	int cap;
+	uint cap;
 
 	bool completely_empty  = true;
 	bool anything_unloaded = false;
 	bool anything_loaded   = false;
 	uint32 cargo_not_full  = 0;
 	uint32 cargo_full      = 0;
-	Money total_cargo_feeder_share = 0; // the feeder cash amount for the goods being loaded/unloaded in this load step
 
 	v->cur_speed = 0;
 
@@ -1526,36 +1515,19 @@
 		}
 
 		GoodsEntry *ge = &st->goods[v->cargo_type];
-		int count = GB(ge->waiting_acceptance, 0, 12);
 
 		if (HASBIT(v->vehicle_flags, VF_CARGO_UNLOADING)) {
-			uint16 amount_unloaded = _patches.gradual_loading ? min(v->cargo_count, load_amount) : v->cargo_count;
+			uint cargo_count = v->cargo.Count();
+			uint amount_unloaded = _patches.gradual_loading ? min(cargo_count, load_amount) : cargo_count;
+			bool remaining; // Are there cargo entities in this vehicle that can still be unloaded here?
 
-			if (v->cargo_source != last_visited && ge->waiting_acceptance & 0x8000 && !(u->current_order.flags & OF_TRANSFER)) {
+			if (ge->acceptance && !(u->current_order.flags & OF_TRANSFER)) {
+				/* The cargo has reached it's final destination, the packets may now be destroyed */
+				remaining = v->cargo.MoveTo(NULL, amount_unloaded, CargoList::MTA_FINAL_DELIVERY, last_visited);
+
 				result |= 1;
 			} else if (u->current_order.flags & (OF_UNLOAD | OF_TRANSFER)) {
-				if (count == 0) {
-					/* No goods waiting at station */
-					ge->enroute_time    = v->cargo_days;
-					ge->enroute_from    = v->cargo_source;
-					ge->enroute_from_xy = v->cargo_source_xy;
-				} else {
-					/* Goods already waiting at station. Set counters to the worst value. */
-					if (v->cargo_days >= ge->enroute_time) ge->enroute_time = v->cargo_days;
-
-					if (last_visited != ge->enroute_from) {
-						ge->enroute_from    = v->cargo_source;
-						ge->enroute_from_xy = v->cargo_source_xy;
-					}
-				}
-				/* Update amount of waiting cargo. There is, however, no sense in
-				 * updating the count variable because this vehicle will not be
-				 * able to take the cargo. */
-				SB(ge->waiting_acceptance, 0, 12, min(amount_unloaded + count, 0xFFF));
-
-				/* if there is not enough to unload from pending, ensure it does not go -ve
-				 * else deduct amount actually unloaded from unload_pending */
-				SB(ge->unload_pending, 0, 12, max(GB(ge->unload_pending, 0, 12) - amount_unloaded, 0U));
+				remaining = v->cargo.MoveTo(&ge->cargo, amount_unloaded);
 
 				result |= 2;
 			} else {
@@ -1570,11 +1542,8 @@
 
 			unloading_time += amount_unloaded;
 
-			v->cargo_count -= amount_unloaded;
-			v->cargo_paid_for -= min(amount_unloaded, v->cargo_paid_for);
-
 			anything_unloaded = true;
-			if (_patches.gradual_loading && v->cargo_count != 0) {
+			if (_patches.gradual_loading && remaining) {
 				completely_empty = false;
 			} else {
 				/* We have finished unloading (cargo count == 0) */
@@ -1584,9 +1553,6 @@
 			continue;
 		}
 
-		/* We cannot have paid for more cargo than there is on board. */
-		assert(v->cargo_paid_for <= v->cargo_count);
-
 		/* Do not pick up goods that we unloaded */
 		if (u->current_order.flags & OF_UNLOAD) continue;
 
@@ -1605,8 +1571,9 @@
 
 		/* If there's goods waiting at the station, and the vehicle
 		 * has capacity for it, load it on the vehicle. */
-		if (count != 0 &&
-				(cap = v->cargo_cap - v->cargo_count) != 0) {
+		if (!ge->cargo.Empty() &&
+				(cap = v->cargo_cap - v->cargo.Count()) != 0) {
+			uint count = ge->cargo.Count();
 
 			/* Skip loading this vehicle if another train/vehicle is already handling
 			 * the same cargo type at this station */
@@ -1623,7 +1590,7 @@
 				cargo_left[v->cargo_type] -= cap;
 			}
 
-			if (v->cargo_count == 0) TriggerVehicle(v, VEHICLE_TRIGGER_NEW_CARGO);
+			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
@@ -1635,32 +1602,16 @@
 			completely_empty = false;
 			anything_loaded = true;
 
-			/* cargoshare is proportioned by the amount due to unload
-			 * Otherwise, with gradual loading, 100% of credits would be taken immediately,
-			 * even if the cargo volume represents a tiny percent of the whole.
-			 * ge->unload_pending holds the amount that has been credited, but has not yet been unloaded.
-			 */
-			int cargoshare = cap * 10000 / (ge->waiting_acceptance + ge->unload_pending);
-			Money feeder_profit_share = ge->feeder_profit * cargoshare / 10000;
-			v->cargo_count += cap;
-			ge->waiting_acceptance -= cap;
+			ge->cargo.MoveTo(&v->cargo, cap, CargoList::MTA_CARGO_LOAD, st->xy);
 
-			total_cargo_feeder_share += feeder_profit_share;    // store cost for later payment when cargo unloaded
-			v->cargo_loaded_at_xy = st->xy;                     // retains location of where the cargo was picked up for intermediate payments
-
-			ge->feeder_profit -= feeder_profit_share;
 			unloading_time += cap;
 			st->time_since_load = 0;
+			st->last_vehicle_type = v->type;
 
-			/* And record the source of the cargo, and the days in travel. */
-			v->cargo_source = ge->enroute_from;
-			v->cargo_source_xy = ge->enroute_from_xy;
-			v->cargo_days = ge->enroute_time;
 			result |= 2;
-			st->last_vehicle_type = v->type;
 		}
 
-		if (v->cargo_count == v->cargo_cap) {
+		if (v->cargo.Count() == v->cargo_cap) {
 			SETBIT(cargo_full, v->cargo_type);
 		} else {
 			SETBIT(cargo_not_full, v->cargo_type);
@@ -1674,14 +1625,12 @@
 	if (_patches.improved_load && HASBIT(u->current_order.flags, OFB_FULL_LOAD)) {
 		/* Update left cargo */
 		for (v = u; v != NULL; v = v->next) {
-			if (v->cargo_cap != 0) cargo_left[v->cargo_type] -= v->cargo_cap - v->cargo_count;
+			if (v->cargo_cap != 0) cargo_left[v->cargo_type] -= v->cargo_cap - v->cargo.Count();
 		}
 	}
 
 	v = u;
 
-	v->cargo_feeder_share += total_cargo_feeder_share;
-
 	if (anything_loaded || anything_unloaded) {
 		if (_patches.gradual_loading) {
 			/* The time it takes to load one 'slice' of cargo or passengers depends
@@ -1696,7 +1645,7 @@
 			if (_patches.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.Count()) ||
 						(cargo_not_full && (cargo_full & ~cargo_not_full) == 0)) { // There are stull non-full cargos
 					finished_loading = false;
 				}
@@ -1754,7 +1703,7 @@
 {
 	int cargo_left[NUM_CARGO];
 
-	for (uint i = 0; i < NUM_CARGO; i++) cargo_left[i] = GB(st->goods[i].waiting_acceptance, 0, 12);
+	for (uint i = 0; i < NUM_CARGO; i++) cargo_left[i] = st->goods[i].cargo.Count();
 
 	std::list<Vehicle *>::iterator iter;
 	for (iter = st->loading_vehicles.begin(); iter != st->loading_vehicles.end(); ++iter) {
--- a/src/misc.cpp	Fri Jun 22 10:57:53 2007 +0000
+++ b/src/misc.cpp	Fri Jun 22 11:58:59 2007 +0000
@@ -93,6 +93,7 @@
 void InitializeTrees();
 void InitializeSigns();
 void InitializeStations();
+void InitializeCargoPackets();
 static void InitializeNameMgr();
 void InitializePlayers();
 static void InitializeCheats();
@@ -134,6 +135,7 @@
 	InitializeTrees();
 	InitializeSigns();
 	InitializeStations();
+	InitializeCargoPackets();
 	InitializeIndustries();
 	InitializeBuildingCounts();
 	InitializeMainGui();
--- a/src/newgrf_engine.cpp	Fri Jun 22 10:57:53 2007 +0000
+++ b/src/newgrf_engine.cpp	Fri Jun 22 11:58:59 2007 +0000
@@ -705,10 +705,10 @@
 		case 0x39: return v->cargo_type;
 		case 0x3A: return v->cargo_cap;
 		case 0x3B: return GB(v->cargo_cap, 8, 8);
-		case 0x3C: return v->cargo_count;
-		case 0x3D: return GB(v->cargo_count, 8, 8);
-		case 0x3E: return v->cargo_source;
-		case 0x3F: return v->cargo_days;
+		case 0x3C: return v->cargo.Count();
+		case 0x3D: return GB(v->cargo.Count(), 8, 8);
+		case 0x3E: return v->cargo.Source();
+		case 0x3F: return v->cargo.DaysInTransit();
 		case 0x40: return v->age;
 		case 0x41: return GB(v->age, 8, 8);
 		case 0x42: return v->max_age;
@@ -811,12 +811,12 @@
 
 	totalsets = in_motion ? group->g.real.num_loaded : group->g.real.num_loading;
 
-	if (v->cargo_count == v->cargo_cap || totalsets == 1) {
+	if (v->cargo.Count() == v->cargo_cap || totalsets == 1) {
 		set = totalsets - 1;
-	} else if (v->cargo_count == 0 || totalsets == 2) {
+	} else if (v->cargo.Empty() || totalsets == 2) {
 		set = 0;
 	} else {
-		set = v->cargo_count * (totalsets - 2) / max((uint16)1, v->cargo_cap) + 1;
+		set = v->cargo.Count() * (totalsets - 2) / max((uint16)1, v->cargo_cap) + 1;
 	}
 
 	return in_motion ? group->g.real.loaded[set] : group->g.real.loading[set];
--- a/src/newgrf_station.cpp	Fri Jun 22 10:57:53 2007 +0000
+++ b/src/newgrf_station.cpp	Fri Jun 22 11:58:59 2007 +0000
@@ -400,7 +400,7 @@
 			uint32 value = 0;
 
 			for (cargo_type = 0; cargo_type < NUM_CARGO; cargo_type++) {
-				if (HASBIT(st->goods[cargo_type].waiting_acceptance, 15)) SETBIT(value, cargo_type);
+				if (st->goods[cargo_type].acceptance) SETBIT(value, cargo_type);
 			}
 			return value;
 		}
@@ -431,12 +431,12 @@
 		const GoodsEntry *ge = &st->goods[c];
 
 		switch (variable) {
-			case 0x60: return GB(ge->waiting_acceptance, 0, 12);
+			case 0x60: return min(ge->cargo.Count(), 4095);
 			case 0x61: return ge->days_since_pickup;
 			case 0x62: return ge->rating;
-			case 0x63: return ge->enroute_time;
+			case 0x63: return ge->cargo.DaysInTransit();
 			case 0x64: return ge->last_speed | (ge->last_age << 8);
-			case 0x65: return GB(ge->waiting_acceptance, 12, 4);
+			case 0x65: return ge->acceptance << 3;
 		}
 	}
 
@@ -444,12 +444,12 @@
 	if (variable >= 0x8C && variable <= 0xEC) {
 		const GoodsEntry *g = &st->goods[GB(variable - 0x8C, 3, 4)];
 		switch (GB(variable - 0x8C, 0, 3)) {
-			case 0: return g->waiting_acceptance;
-			case 1: return GB(g->waiting_acceptance, 8, 8);
+			case 0: return g->cargo.Count();
+			case 1: return GB(min(g->cargo.Count(), 4095), 0, 4) | (g->acceptance << 7);
 			case 2: return g->days_since_pickup;
 			case 3: return g->rating;
-			case 4: return g->enroute_from;
-			case 5: return g->enroute_time;
+			case 4: return g->cargo.Source();
+			case 5: return g->cargo.DaysInTransit();
 			case 6: return g->last_speed;
 			case 7: return g->last_age;
 		}
@@ -484,12 +484,12 @@
 
 		case CT_DEFAULT:
 			for (cargo_type = 0; cargo_type < NUM_CARGO; cargo_type++) {
-				cargo += GB(st->goods[cargo_type].waiting_acceptance, 0, 12);
+				cargo += st->goods[cargo_type].cargo.Count();
 			}
 			break;
 
 		default:
-			cargo = GB(st->goods[cargo_type].waiting_acceptance, 0, 12);
+			cargo = st->goods[cargo_type].cargo.Count();
 			break;
 	}
 
@@ -545,7 +545,7 @@
 		for (CargoID cargo = 0; cargo < NUM_CARGO; cargo++) {
 			const CargoSpec *cs = GetCargo(cargo);
 			if (cs->IsValid() && object->u.station.statspec->spritegroup[cargo] != NULL &&
-					GB(object->u.station.st->goods[cargo].waiting_acceptance, 0, 12) != 0) {
+					!object->u.station.st->goods[cargo].cargo.Empty()) {
 				ctype = cargo;
 				break;
 			}
--- a/src/oldloader.cpp	Fri Jun 22 10:57:53 2007 +0000
+++ b/src/oldloader.cpp	Fri Jun 22 11:58:59 2007 +0000
@@ -565,15 +565,18 @@
 	return true;
 }
 
-static uint8 _old_platforms;
-static uint _current_station_id;
+static uint8  _old_platforms;
+static uint   _current_station_id;
+static uint16 _waiting_acceptance;
+static uint8  _cargo_source;
+static uint8  _cargo_days;
 
 static const OldChunks goods_chunk[] = {
-	OCL_SVAR( OC_UINT16, GoodsEntry, waiting_acceptance ),
+	OCL_VAR ( OC_UINT16, 1,          &_waiting_acceptance ),
 	OCL_SVAR(  OC_UINT8, GoodsEntry, days_since_pickup ),
 	OCL_SVAR(  OC_UINT8, GoodsEntry, rating ),
-	OCL_SVAR(  OC_FILE_U8 | OC_VAR_U16, GoodsEntry, enroute_from ),
-	OCL_SVAR(  OC_UINT8, GoodsEntry, enroute_time ),
+	OCL_VAR (  OC_UINT8, 1,          &_cargo_source ),
+	OCL_VAR (  OC_UINT8, 1,          &_cargo_days ),
 	OCL_SVAR(  OC_UINT8, GoodsEntry, last_speed ),
 	OCL_SVAR(  OC_UINT8, GoodsEntry, last_age ),
 
@@ -583,7 +586,17 @@
 static bool LoadOldGood(LoadgameState *ls, int num)
 {
 	Station *st = GetStation(_current_station_id);
-	return LoadChunk(ls, &st->goods[num], goods_chunk);
+	GoodsEntry *ge = &st->goods[num];
+	bool ret = LoadChunk(ls, ge, goods_chunk);
+	if (ret && GB(_waiting_acceptance, 0, 12) != 0) {
+		CargoPacket *cp = new CargoPacket();
+		cp->source          = (_cargo_source == 0xFF) ? INVALID_STATION : _cargo_source;
+		cp->count           = GB(_waiting_acceptance, 0, 12);
+		cp->days_in_transit = _cargo_days;
+		ge->acceptance      = HASBIT(_waiting_acceptance, 15);
+		ge->cargo.Append(cp);
+	}
+	return ret;
 }
 
 static const OldChunks station_chunk[] = {
@@ -1091,6 +1104,8 @@
 	return res;
 }
 
+static uint16 _cargo_count;
+
 static const OldChunks vehicle_chunk[] = {
 	OCL_SVAR(  OC_UINT8, Vehicle, type ),
 	OCL_SVAR(  OC_UINT8, Vehicle, subtype ),
@@ -1133,9 +1148,9 @@
 
 	OCL_SVAR(  OC_UINT8, Vehicle, cargo_type ),
 	OCL_SVAR( OC_UINT16, Vehicle, cargo_cap ),
-	OCL_SVAR( OC_UINT16, Vehicle, cargo_count ),
-	OCL_SVAR( OC_FILE_U8 | OC_VAR_U16, Vehicle, cargo_source ),
-	OCL_SVAR(  OC_UINT8, Vehicle, cargo_days ),
+	OCL_VAR ( OC_UINT16, 1,       &_cargo_count ),
+	OCL_VAR (  OC_UINT8, 1,       &_cargo_source ),
+	OCL_VAR (  OC_UINT8, 1,       &_cargo_days ),
 
 	OCL_SVAR( OC_FILE_U16 | OC_VAR_U32, Vehicle, age ),
 	OCL_SVAR( OC_FILE_U16 | OC_VAR_U32, Vehicle, max_age ),
@@ -1213,6 +1228,12 @@
 
 		/* Vehicle-subtype is different in TTD(Patch) */
 		if (v->type == VEH_SPECIAL) v->subtype = v->subtype >> 1;
+
+		if (_cargo_count != 0) {
+			CargoPacket *cp = new CargoPacket((_cargo_source == 0xFF) ? INVALID_STATION : _cargo_source, _cargo_count);
+			cp->days_in_transit = _cargo_days;
+			v->cargo.Append(cp);
+		}
 	}
 
 	return true;
--- a/src/openttd.cpp	Fri Jun 22 10:57:53 2007 +0000
+++ b/src/openttd.cpp	Fri Jun 22 11:58:59 2007 +0000
@@ -303,6 +303,7 @@
 	CleanPool(&_Sign_pool);
 	CleanPool(&_Order_pool);
 	CleanPool(&_Group_pool);
+	CleanPool(&_CargoPacket_pool);
 
 	free((void*)_town_sort);
 	free((void*)_industry_sort);
@@ -930,6 +931,31 @@
 	}
 }
 
+#include "cargopacket.h"
+void CheckCargoPacketLeaks()
+{
+	CargoPacket *cp;
+	FOR_ALL_CARGOPACKETS(cp) cp->touched = false;
+
+	Vehicle *v;
+	FOR_ALL_VEHICLES(v) {
+		const CargoList::List *packets = v->cargo.Packets();
+		for (CargoList::List::const_iterator it = packets->begin(); it != packets->end(); it++) (*it)->touched = true;
+	}
+
+	Station *st;
+	FOR_ALL_STATIONS(st) {
+		for (CargoID c = 0; c < NUM_CARGO; c++) {
+			GoodsEntry *ge = &st->goods[c];
+
+			const CargoList::List *packets = ge->cargo.Packets();
+			for (CargoList::List::const_iterator it = packets->begin(); it != packets->end(); it++) (*it)->touched = true;
+		}
+	}
+
+	FOR_ALL_CARGOPACKETS(cp) assert(cp->touched);
+}
+
 
 /* State controlling game loop.
  * The state must not be changed from anywhere
@@ -964,6 +990,7 @@
 		CallWindowTickEvent();
 		NewsLoop();
 		_current_player = p;
+		CheckCargoPacketLeaks();
 	}
 }
 
@@ -1896,13 +1923,19 @@
 	if (CheckSavegameVersion(44)) {
 		Vehicle *v;
 		/* If we remove a station while cargo from it is still enroute, payment calculation will assume
-		 * 0, 0 to be the origin of the cargo, resulting in very high payments usually. v->cargo_source_xy
+		 * 0, 0 to be the source of the cargo, resulting in very high payments usually. v->source_xy
 		 * stores the coordinates, preserving them even if the station is removed. However, if a game is loaded
-		 * where this situation exists, the cargo-source information is lost. in this case, we set the origin
+		 * where this situation exists, the cargo-source information is lost. in this case, we set the source
 		 * to the current tile of the vehicle to prevent excessive profits
 		 */
 		FOR_ALL_VEHICLES(v) {
-			v->cargo_source_xy = IsValidStationID(v->cargo_source) ? GetStation(v->cargo_source)->xy : v->tile;
+			const CargoList::List *packets = v->cargo.Packets();
+			for (CargoList::List::const_iterator it = packets->begin(); it != packets->end(); it++) {
+				CargoPacket *cp = *it;
+				cp->source_xy = IsValidStationID(cp->source) ? GetStation(cp->source)->xy : v->tile;
+				cp->loaded_at_xy = cp->source_xy;
+			}
+			v->cargo.InvalidateCache();
 		}
 
 		/* Store position of the station where the goods come from, so there
@@ -1915,12 +1948,12 @@
 			for (CargoID c = 0; c < NUM_CARGO; c++) {
 				GoodsEntry *ge = &st->goods[c];
 
-				/* In old versions, enroute_from used 0xFF as INVALID_STATION */
-				if (CheckSavegameVersion(7) && ge->enroute_from == 0xFF) {
-					ge->enroute_from = INVALID_STATION;
+				const CargoList::List *packets = ge->cargo.Packets();
+				for (CargoList::List::const_iterator it = packets->begin(); it != packets->end(); it++) {
+					CargoPacket *cp = *it;
+					cp->source_xy = IsValidStationID(cp->source) ? GetStation(cp->source)->xy : st->xy;
+					cp->loaded_at_xy = cp->source_xy;
 				}
-
-				ge->enroute_from_xy = IsValidStationID(ge->enroute_from) ? GetStation(ge->enroute_from)->xy : st->xy;
 			}
 		}
 	}
@@ -1933,12 +1966,13 @@
 		 * loading again, even if it didn't actually load anything, so now the
 		 * amount of cargo that has been paid for is stored. */
 		FOR_ALL_VEHICLES(v) {
-			if (HASBIT(v->vehicle_flags, 2)) {
-				v->cargo_paid_for = v->cargo_count;
-				CLRBIT(v->vehicle_flags, 2);
-			} else {
-				v->cargo_paid_for = 0;
+			const CargoList::List *packets = v->cargo.Packets();
+			for (CargoList::List::const_iterator it = packets->begin(); it != packets->end(); it++) {
+				CargoPacket *cp = *it;
+				cp->paid_for = HASBIT(v->vehicle_flags, 2);
 			}
+			CLRBIT(v->vehicle_flags, 2);
+			v->cargo.InvalidateCache();
 		}
 	}
 
--- a/src/openttd.h	Fri Jun 22 10:57:53 2007 +0000
+++ b/src/openttd.h	Fri Jun 22 11:58:59 2007 +0000
@@ -59,6 +59,7 @@
 /* IDs used in Pools */
 typedef uint16 VehicleID;
 typedef uint16 StationID;
+static const StationID INVALID_STATION = 0xFFFF;
 typedef uint16 RoadStopID;
 typedef uint16 TownID;
 typedef uint16 IndustryID;
--- a/src/roadveh_cmd.cpp	Fri Jun 22 10:57:53 2007 +0000
+++ b/src/roadveh_cmd.cpp	Fri Jun 22 11:58:59 2007 +0000
@@ -97,7 +97,7 @@
 	}
 
 	image = direction + _roadveh_images[img];
-	if (v->cargo_count >= v->cargo_cap / 2) image += _roadveh_full_adder[img];
+	if (v->cargo.Count() >= v->cargo_cap / 2) image += _roadveh_full_adder[img];
 	return image;
 }
 
@@ -677,7 +677,7 @@
 	v->u.road.crashed_ctr++;
 
 	for (Vehicle *u = v; u != NULL; u = u->next) {
-		if (IsCargoInClass(u->cargo_type, CC_PASSENGERS)) pass += u->cargo_count;
+		if (IsCargoInClass(u->cargo_type, CC_PASSENGERS)) pass += u->cargo.Count();
 
 		u->vehstatus |= VS_CRASHED;
 
@@ -1826,7 +1826,7 @@
 static void AgeRoadVehCargo(Vehicle *v)
 {
 	if (_age_cargo_skip_counter != 0) return;
-	if (v->cargo_days != 255) v->cargo_days++;
+	v->cargo.AgeCargo();
 }
 
 void RoadVeh_Tick(Vehicle *v)
@@ -2069,7 +2069,7 @@
 
 	if (flags & DC_EXEC) {
 		v->cargo_cap = capacity;
-		v->cargo_count = (v->cargo_type == new_cid) ? min(capacity, v->cargo_count) : 0;
+		v->cargo.Truncate((v->cargo_type == new_cid) ? capacity : 0);
 		v->cargo_type = new_cid;
 		v->cargo_subtype = new_subtype;
 		InvalidateWindow(WC_VEHICLE_DETAILS, v->index);
--- a/src/roadveh_gui.cpp	Fri Jun 22 10:57:53 2007 +0000
+++ b/src/roadveh_gui.cpp	Fri Jun 22 11:58:59 2007 +0000
@@ -154,10 +154,10 @@
 
 			for (const Vehicle *u = v; u != NULL; u = u->next) {
 				str = STR_8812_EMPTY;
-				if (u->cargo_count != 0) {
+				if (!u->cargo.Empty()) {
 					SetDParam(0, u->cargo_type);
-					SetDParam(1, u->cargo_count);
-					SetDParam(2, u->cargo_source);
+					SetDParam(1, u->cargo.Count());
+					SetDParam(2, u->cargo.Source());
 					str = STR_8813_FROM;
 				}
 				DrawString(34, 78 + y_offset, str, 0);
@@ -172,17 +172,17 @@
 			DrawString(34, 67 + y_offset, STR_9012_CAPACITY, 0);
 
 			str = STR_8812_EMPTY;
-			if (v->cargo_count != 0) {
+			if (!v->cargo.Empty()) {
 				SetDParam(0, v->cargo_type);
-				SetDParam(1, v->cargo_count);
-				SetDParam(2, v->cargo_source);
+				SetDParam(1, v->cargo.Count());
+				SetDParam(2, v->cargo.Source());
 				str = STR_8813_FROM;
 			}
 			DrawString(34, 78 + y_offset, str, 0);
 		}
 
 		/* Draw Transfer credits text */
-		SetDParam(0, v->cargo_feeder_share);
+		SetDParam(0, v->cargo.FeederShare());
 		DrawString(34, 90 + y_offset, STR_FEEDER_CARGO_VALUE, 0);
 
 		/* Draw service interval text */
--- a/src/saveload.cpp	Fri Jun 22 10:57:53 2007 +0000
+++ b/src/saveload.cpp	Fri Jun 22 11:58:59 2007 +0000
@@ -29,7 +29,7 @@
 #include <setjmp.h>
 #include <list>
 
-extern const uint16 SAVEGAME_VERSION = 67;
+extern const uint16 SAVEGAME_VERSION = 68;
 uint16 _sl_version;       ///< the major savegame version identifier
 byte   _sl_minor_version; ///< the minor savegame version, DO NOT USE!
 
@@ -820,7 +820,7 @@
 	}
 
 	for (; sld->cmd != SL_END; sld++) {
-		void *ptr = GetVariableAddress(object, sld);
+		void *ptr = sld->global ? sld->address : GetVariableAddress(object, sld);
 		SlObjectMember(ptr, sld);
 	}
 }
@@ -831,14 +831,7 @@
  */
 void SlGlobList(const SaveLoadGlobVarList *sldg)
 {
-	if (_sl.need_length != NL_NONE) {
-		SlSetLength(SlCalcObjLength(NULL, (const SaveLoad*)sldg));
-		if (_sl.need_length == NL_CALCLENGTH) return;
-	}
-
-	for (; sldg->cmd != SL_END; sldg++) {
-		SlObjectMember(sldg->address, (const SaveLoad*)sldg);
-	}
+	SlObject(NULL, (const SaveLoad*)sldg);
 }
 
 /**
@@ -1258,6 +1251,7 @@
 extern const ChunkHandler _animated_tile_chunk_handlers[];
 extern const ChunkHandler _newgrf_chunk_handlers[];
 extern const ChunkHandler _group_chunk_handlers[];
+extern const ChunkHandler _cargopacket_chunk_handlers[];
 
 static const ChunkHandler * const _chunk_handlers[] = {
 	_misc_chunk_handlers,
@@ -1276,6 +1270,7 @@
 	_animated_tile_chunk_handlers,
 	_newgrf_chunk_handlers,
 	_group_chunk_handlers,
+	_cargopacket_chunk_handlers,
 	NULL,
 };
 
@@ -1307,6 +1302,7 @@
 		case REF_ORDER:     return ((const    Order*)obj)->index + 1;
 		case REF_ROADSTOPS: return ((const RoadStop*)obj)->index + 1;
 		case REF_ENGINE_RENEWS: return ((const EngineRenew*)obj)->index + 1;
+		case REF_CARGO_PACKET:  return ((const CargoPacket*)obj)->index + 1;
 		default: NOT_REACHED();
 	}
 
@@ -1367,6 +1363,11 @@
 				error("EngineRenews: failed loading savegame: too many EngineRenews");
 			return GetEngineRenew(index);
 		}
+		case REF_CARGO_PACKET: {
+			if (!AddBlockIfNeeded(&_CargoPacket_pool, index))
+				error("CargoPackets: failed loading savegame: too many Cargo packets");
+			return GetCargoPacket(index);
+		}
 
 		case REF_VEHICLE_OLD: {
 			/* Old vehicles were saved differently:
--- a/src/saveload.h	Fri Jun 22 10:57:53 2007 +0000
+++ b/src/saveload.h	Fri Jun 22 11:58:59 2007 +0000
@@ -55,6 +55,7 @@
 	REF_VEHICLE_OLD   = 4,
 	REF_ROADSTOPS     = 5,
 	REF_ENGINE_RENEWS = 6,
+	REF_CARGO_PACKET  = 7,
 };
 
 #define SL_MAX_VERSION 255
@@ -172,6 +173,7 @@
 
 /** SaveLoad type struct. Do NOT use this directly but use the SLE_ macros defined just below! */
 struct SaveLoad {
+	bool global;         ///< should we load a global variable or a non-global one
 	SaveLoadType cmd;    ///< the action to take with the saved/loaded type, All types need different action
 	VarType conv;        ///< type of the variable to be saved, int
 	uint16 length;       ///< (conditional) length of the variable (eg. arrays) (max array size is 65536 elements)
@@ -180,7 +182,7 @@
 	/* NOTE: This element either denotes the address of the variable for a global
 	 * variable, or the offset within a struct which is then bound to a variable
 	 * during runtime. Decision on which one to use is controlled by the function
-	 * that is called to save it. address: SlGlobList, offset: SlObject */
+	 * that is called to save it. address: global=true, offset: global=false */
 	void *address;       ///< address of variable OR offset of variable in the struct (max offset is 65536)
 };
 
@@ -188,7 +190,7 @@
 typedef SaveLoad SaveLoadGlobVarList;
 
 /* Simple variables, references (pointers) and arrays */
-#define SLE_GENERAL(cmd, base, variable, type, length, from, to) {cmd, type, length, from, to, (void*)cpp_offsetof(base, variable)}
+#define SLE_GENERAL(cmd, base, variable, type, length, from, to) {false, cmd, type, length, from, to, (void*)cpp_offsetof(base, variable)}
 #define SLE_CONDVAR(base, variable, type, from, to) SLE_GENERAL(SL_VAR, base, variable, type, 0, from, to)
 #define SLE_CONDREF(base, variable, type, from, to) SLE_GENERAL(SL_REF, base, variable, type, 0, from, to)
 #define SLE_CONDARR(base, variable, type, length, from, to) SLE_GENERAL(SL_ARR, base, variable, type, length, from, to)
@@ -209,7 +211,7 @@
 #define SLE_INCLUDE(base, variable, include_index) SLE_GENERAL(SL_INCLUDE, base, variable, 0, 0, include_index, 0)
 
 /* The same as the ones at the top, only the offset is given directly; used for unions */
-#define SLE_GENERALX(cmd, offset, type, param1, param2) {cmd, type, 0, param1, param2, (void*)(offset)}
+#define SLE_GENERALX(cmd, offset, type, param1, param2) {false, cmd, type, 0, param1, param2, (void*)(offset)}
 #define SLE_CONDVARX(offset, type, from, to) SLE_GENERALX(SL_VAR, offset, type, from, to)
 #define SLE_CONDREFX(offset, type, from, to) SLE_GENERALX(SL_REF, offset, type, from, to)
 
@@ -220,10 +222,10 @@
 #define SLE_INCLUDEX(offset, type) SLE_GENERALX(SL_INCLUDE, offset, type, 0, SL_MAX_VERSION)
 
 /* End marker */
-#define SLE_END() {SL_END, 0, 0, 0, 0, NULL}
+#define SLE_END() {false, SL_END, 0, 0, 0, 0, NULL}
 
 /* Simple variables, references (pointers) and arrays, but for global variables */
-#define SLEG_GENERAL(cmd, variable, type, length, from, to) {cmd, type, length, from, to, (void*)&variable}
+#define SLEG_GENERAL(cmd, variable, type, length, from, to) {true, cmd, type, length, from, to, (void*)&variable}
 
 #define SLEG_CONDVAR(variable, type, from, to) SLEG_GENERAL(SL_VAR, variable, type, 0, from, to)
 #define SLEG_CONDREF(variable, type, from, to) SLEG_GENERAL(SL_REF, variable, type, 0, from, to)
@@ -237,9 +239,9 @@
 #define SLEG_STR(variable, type) SLEG_CONDSTR(variable, type, lengthof(variable), 0, SL_MAX_VERSION)
 #define SLEG_LST(variable, type) SLEG_CONDLST(variable, type, 0, SL_MAX_VERSION)
 
-#define SLEG_CONDNULL(length, from, to) {SL_ARR, SLE_FILE_U8 | SLE_VAR_NULL | SLF_CONFIG_NO, length, from, to, (void*)NULL}
+#define SLEG_CONDNULL(length, from, to) {true, SL_ARR, SLE_FILE_U8 | SLE_VAR_NULL | SLF_CONFIG_NO, length, from, to, (void*)NULL}
 
-#define SLEG_END() {SL_END, 0, 0, 0, 0, NULL}
+#define SLEG_END() {true, SL_END, 0, 0, 0, 0, NULL}
 
 /** Checks if the savegame is below major.minor.
  */
--- a/src/ship_cmd.cpp	Fri Jun 22 10:57:53 2007 +0000
+++ b/src/ship_cmd.cpp	Fri Jun 22 11:58:59 2007 +0000
@@ -784,7 +784,7 @@
 static void AgeShipCargo(Vehicle *v)
 {
 	if (_age_cargo_skip_counter != 0) return;
-	if (v->cargo_days != 255) v->cargo_days++;
+	v->cargo.AgeCargo();
 }
 
 void Ship_Tick(Vehicle *v)
@@ -1110,7 +1110,7 @@
 
 	if (flags & DC_EXEC) {
 		v->cargo_cap = capacity;
-		v->cargo_count = (v->cargo_type == new_cid) ? min(v->cargo_cap, v->cargo_count) : 0;
+		v->cargo.Truncate((v->cargo_type == new_cid) ? capacity : 0);
 		v->cargo_type = new_cid;
 		v->cargo_subtype = new_subtype;
 		InvalidateWindow(WC_VEHICLE_DETAILS, v->index);
--- a/src/ship_gui.cpp	Fri Jun 22 10:57:53 2007 +0000
+++ b/src/ship_gui.cpp	Fri Jun 22 11:58:59 2007 +0000
@@ -93,16 +93,16 @@
 		DrawString(74, 67, STR_9817_CAPACITY, 0);
 
 		str = STR_8812_EMPTY;
-		if (v->cargo_count != 0) {
+		if (!v->cargo.Empty()) {
 			SetDParam(0, v->cargo_type);
-			SetDParam(1, v->cargo_count);
-			SetDParam(2, v->cargo_source);
+			SetDParam(1, v->cargo.Count());
+			SetDParam(2, v->cargo.Source());
 			str = STR_8813_FROM;
 		}
 		DrawString(74, 78, str, 0);
 
 		/* Draw Transfer credits text */
-		SetDParam(0, v->cargo_feeder_share);
+		SetDParam(0, v->cargo.FeederShare());
 		DrawString(74, 89, STR_FEEDER_CARGO_VALUE, 0);
 
 	} break;
--- a/src/station.cpp	Fri Jun 22 10:57:53 2007 +0000
+++ b/src/station.cpp	Fri Jun 22 11:58:59 2007 +0000
@@ -79,6 +79,10 @@
 
 	free(speclist);
 	xy = 0;
+
+	for (CargoID c = 0; c < NUM_CARGO; c++) {
+		goods[c].cargo.Truncate(0);
+	}
 }
 
 void* Station::operator new(size_t size)
--- a/src/station.h	Fri Jun 22 10:57:53 2007 +0000
+++ b/src/station.h	Fri Jun 22 11:58:59 2007 +0000
@@ -12,34 +12,26 @@
 #include "tile.h"
 #include "road.h"
 #include "newgrf_station.h"
+#include "cargopacket.h"
 #include <list>
 
-static const StationID INVALID_STATION = 0xFFFF;
 static const byte INITIAL_STATION_RATING = 175;
 
 struct GoodsEntry {
 	GoodsEntry() :
-		waiting_acceptance(0),
-		unload_pending(0),
+		acceptance(false),
 		days_since_pickup(0),
 		rating(INITIAL_STATION_RATING),
-		enroute_from(INVALID_STATION),
-		enroute_from_xy(INVALID_TILE),
 		last_speed(0),
-		last_age(255),
-		feeder_profit(0)
+		last_age(255)
 	{}
 
-	uint16 waiting_acceptance;
-	uint16 unload_pending;        ///< records how much cargo is awaiting transfer during gradual loading to allow correct fee calc
+	bool acceptance;
 	byte days_since_pickup;
 	byte rating;
-	StationID enroute_from;
-	TileIndex enroute_from_xy;
-	byte enroute_time;
 	byte last_speed;
 	byte last_age;
-	Money feeder_profit;
+	CargoList cargo; ///< The cargo packets of cargo waiting in this station
 };
 
 /** A Stop for a Road Vehicle */
--- a/src/station_cmd.cpp	Fri Jun 22 10:57:53 2007 +0000
+++ b/src/station_cmd.cpp	Fri Jun 22 11:58:59 2007 +0000
@@ -355,7 +355,7 @@
 	uint mask = 0;
 
 	for (CargoID i = 0; i < NUM_CARGO; i++) {
-		if (st->goods[i].waiting_acceptance & 0x8000) mask |= 1 << i;
+		if (st->goods[i].acceptance) mask |= 1 << i;
 	}
 	return mask;
 }
@@ -536,7 +536,7 @@
 				(is_passengers && !(st->facilities & (byte)~FACIL_TRUCK_STOP)))
 			amt = 0;
 
-		SB(st->goods[i].waiting_acceptance, 12, 4, amt);
+		st->goods[i].acceptance = (amt >= 8);
 	}
 
 	// Only show a message in case the acceptance was actually changed.
@@ -2347,11 +2347,10 @@
 		/* Slowly increase the rating back to his original level in the case we
 		 *  didn't deliver cargo yet to this station. This happens when a bribe
 		 *  failed while you didn't moved that cargo yet to a station. */
-		if (ge->enroute_from == INVALID_STATION && ge->rating < INITIAL_STATION_RATING)
+		if (ge->days_since_pickup == 255 && ge->rating < INITIAL_STATION_RATING)
 			ge->rating++;
 		/* Only change the rating if we are moving this cargo */
-		if (ge->enroute_from != INVALID_STATION) {
-			byte_inc_sat(&ge->enroute_time);
+		if (ge->last_speed != 0) {
 			byte_inc_sat(&ge->days_since_pickup);
 
 			int rating = 0;
@@ -2383,7 +2382,7 @@
 				(rating += 35, true);
 			}
 
-			int waiting = GB(ge->waiting_acceptance, 0, 12);
+			uint waiting = ge->cargo.Count();
 			(rating -= 90, waiting > 1500) ||
 			(rating += 55, waiting > 1000) ||
 			(rating += 35, waiting > 600) ||
@@ -2409,12 +2408,13 @@
 				if (rating <= 127 && waiting != 0) {
 					uint32 r = Random();
 					if (rating <= (int)GB(r, 0, 7)) {
-						waiting = max(waiting - (int)GB(r, 8, 2) - 1, 0);
+						/* Need to have int, otherwise it will just overflow etc. */
+						waiting = max((int)waiting - (int)GB(r, 8, 2) - 1, 0);
 						waiting_changed = true;
 					}
 				}
 
-				if (waiting_changed) SB(ge->waiting_acceptance, 0, 12, waiting);
+				if (waiting_changed) ge->cargo.Truncate(waiting);
 			}
 		}
 	} while (++ge != endof(st->goods));
@@ -2467,7 +2467,7 @@
 			for (CargoID i = 0; i < NUM_CARGO; i++) {
 				GoodsEntry* ge = &st->goods[i];
 
-				if (ge->enroute_from != INVALID_STATION) {
+				if (ge->days_since_pickup != 255) {
 					ge->rating = clamp(ge->rating + amount, 0, 255);
 				}
 			}
@@ -2477,13 +2477,8 @@
 
 static void UpdateStationWaiting(Station *st, CargoID type, uint amount)
 {
-	SB(st->goods[type].waiting_acceptance, 0, 12,
-		min(0xFFF, GB(st->goods[type].waiting_acceptance, 0, 12) + amount)
-	);
-
-	st->goods[type].enroute_time = 0;
-	st->goods[type].enroute_from = st->index;
-	st->goods[type].enroute_from_xy = st->xy;
+	st->goods[type].cargo.Append(new CargoPacket(st->index, amount));
+
 	InvalidateWindow(WC_STATION_VIEW, st->index);
 	st->MarkTilesDirty(true);
 }
@@ -2553,7 +2548,7 @@
 			if (around[i] == NULL) {
 				if (!st->IsBuoy() &&
 						(st->town->exclusive_counter == 0 || st->town->exclusivity == st->owner) && // check exclusive transport rights
-						st->goods[type].rating != 0 &&
+						st->goods[type].rating != 0 && st->goods[type].days_since_pickup != 255 && // we actually service the station
 						(!_patches.selectgoods || st->goods[type].last_speed > 0) && // if last_speed is 0, no vehicle has been there.
 						((st->facilities & ~FACIL_BUS_STOP)   != 0 || IsCargoInClass(type, CC_PASSENGERS)) && // if we have other fac. than a bus stop, or the cargo is passengers
 						((st->facilities & ~FACIL_TRUCK_STOP) != 0 || !IsCargoInClass(type, CC_PASSENGERS))) { // if we have other fac. than a cargo bay or the cargo is not passengers
@@ -2683,10 +2678,8 @@
 	st->build_date = _date;
 
 	for (CargoID j = 0; j < NUM_CARGO; j++) {
-		st->goods[j].waiting_acceptance = 0;
-		st->goods[j].days_since_pickup = 0;
-		st->goods[j].enroute_from = INVALID_STATION;
-		st->goods[j].enroute_from_xy = INVALID_TILE;
+		st->goods[j].acceptance = false;
+		st->goods[j].days_since_pickup = 255;
 		st->goods[j].rating = INITIAL_STATION_RATING;
 		st->goods[j].last_speed = 0;
 		st->goods[j].last_age = 255;
@@ -2810,6 +2803,8 @@
 
 			st->speclist[i].spec = GetCustomStationSpecByGrf(st->speclist[i].grfid, st->speclist[i].localidx);
 		}
+
+		for (CargoID c = 0; c < NUM_CARGO; c++) st->goods[c].cargo.InvalidateCache();
 	}
 }
 
@@ -2906,19 +2901,27 @@
 	SLE_END()
 };
 
+static uint16 _waiting_acceptance;
+static uint16 _cargo_source;
+static uint32 _cargo_source_xy;
+static uint16 _cargo_days;
+static Money  _cargo_feeder_share;
+
 static const SaveLoad _goods_desc[] = {
-	    SLE_VAR(GoodsEntry, waiting_acceptance, SLE_UINT16),
-	SLE_CONDVAR(GoodsEntry, unload_pending,     SLE_UINT16,                51, SL_MAX_VERSION),
-	    SLE_VAR(GoodsEntry, days_since_pickup,  SLE_UINT8),
-	    SLE_VAR(GoodsEntry, rating,             SLE_UINT8),
-	SLE_CONDVAR(GoodsEntry, enroute_from,       SLE_FILE_U8 | SLE_VAR_U16,  0, 6),
-	SLE_CONDVAR(GoodsEntry, enroute_from,       SLE_UINT16,                 7, SL_MAX_VERSION),
-	SLE_CONDVAR(GoodsEntry, enroute_from_xy,    SLE_UINT32,                44, SL_MAX_VERSION),
-	    SLE_VAR(GoodsEntry, enroute_time,       SLE_UINT8),
-	    SLE_VAR(GoodsEntry, last_speed,         SLE_UINT8),
-	    SLE_VAR(GoodsEntry, last_age,           SLE_UINT8),
-	SLE_CONDVAR(GoodsEntry, feeder_profit,      SLE_FILE_I32 | SLE_VAR_I64,14, 64),
-	SLE_CONDVAR(GoodsEntry, feeder_profit,      SLE_INT64,                 65, SL_MAX_VERSION),
+	SLEG_CONDVAR(            _waiting_acceptance, SLE_UINT16,                  0, 67),
+	 SLE_CONDVAR(GoodsEntry, acceptance,          SLE_BOOL,                   68, SL_MAX_VERSION),
+	SLE_CONDNULL(2,                                                           51, 67),
+	     SLE_VAR(GoodsEntry, days_since_pickup,   SLE_UINT8),
+	     SLE_VAR(GoodsEntry, rating,              SLE_UINT8),
+	SLEG_CONDVAR(            _cargo_source,       SLE_FILE_U8 | SLE_VAR_U16,   0, 6),
+	SLEG_CONDVAR(            _cargo_source,       SLE_UINT16,                  7, 67),
+	SLEG_CONDVAR(            _cargo_source_xy,    SLE_UINT32,                 44, 67),
+	SLEG_CONDVAR(            _cargo_days,         SLE_UINT8,                   0, 67),
+	     SLE_VAR(GoodsEntry, last_speed,          SLE_UINT8),
+	     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,               REF_CARGO_PACKET,           68, SL_MAX_VERSION),
 
 	SLE_END()
 };
@@ -2935,9 +2938,28 @@
 {
 	SlObject(st, _station_desc);
 
+	_waiting_acceptance = 0;
+
 	uint num_cargo = CheckSavegameVersion(55) ? 12 : NUM_CARGO;
 	for (CargoID i = 0; i < num_cargo; i++) {
-		SlObject(&st->goods[i], _goods_desc);
+		GoodsEntry *ge = &st->goods[i];
+		SlObject(ge, _goods_desc);
+		if (_waiting_acceptance != 0) {
+			ge->acceptance = HASBIT(_waiting_acceptance, 15);
+			if (GB(_waiting_acceptance, 0, 12) != 0) {
+				/* Don't construct the packet with station here, because that'll fail with old savegames */
+				CargoPacket *cp = new CargoPacket();
+				/* In old versions, enroute_from used 0xFF as INVALID_STATION */
+				cp->source          = (CheckSavegameVersion(7) && _cargo_source == 0xFF) ? INVALID_STATION : _cargo_source;
+				cp->count           = GB(_waiting_acceptance, 0, 12);
+				cp->days_in_transit = _cargo_days;
+				cp->feeder_share    = _cargo_feeder_share;
+				cp->source_xy       = _cargo_source_xy;
+				cp->days_in_transit = _cargo_days;
+				cp->feeder_share    = _cargo_feeder_share;
+				ge->cargo.Append(cp);
+			}
+		}
 	}
 
 	if (st->num_specs != 0) {
--- a/src/station_gui.cpp	Fri Jun 22 10:57:53 2007 +0000
+++ b/src/station_gui.cpp	Fri Jun 22 11:58:59 2007 +0000
@@ -134,8 +134,8 @@
 	Money sum1 = 0, sum2 = 0;
 
 	for (CargoID j = 0; j < NUM_CARGO; j++) {
-		if (st1->goods[j].waiting_acceptance & 0xfff) sum1 += GetTransportedGoodsIncome(st1->goods[j].waiting_acceptance & 0xfff, 20, 50, j);
-		if (st2->goods[j].waiting_acceptance & 0xfff) sum2 += GetTransportedGoodsIncome(st2->goods[j].waiting_acceptance & 0xfff, 20, 50, j);
+		if (!st1->goods[j].cargo.Empty()) sum1 += GetTransportedGoodsIncome(st1->goods[j].cargo.Count(), 20, 50, j);
+		if (!st2->goods[j].cargo.Empty()) sum2 += GetTransportedGoodsIncome(st2->goods[j].cargo.Count(), 20, 50, j);
 	}
 
 	return (_internal_sort_order & 1) ? ClampToI32(sum2 - sum1) : ClampToI32(sum1 - sum2);
@@ -157,8 +157,8 @@
 	byte maxr2 = 0;
 
 	for (CargoID j = 0; j < NUM_CARGO; j++) {
-		if (st1->goods[j].enroute_from != INVALID_STATION) maxr1 = max(maxr1, st1->goods[j].rating);
-		if (st2->goods[j].enroute_from != INVALID_STATION) maxr2 = max(maxr2, st2->goods[j].rating);
+		if (st1->goods[j].days_since_pickup != 255) maxr1 = max(maxr1, st1->goods[j].rating);
+		if (st2->goods[j].days_since_pickup != 255) maxr2 = max(maxr2, st2->goods[j].rating);
 	}
 
 	return (_internal_sort_order & 1) ? maxr2 - maxr1 : maxr1 - maxr2;
@@ -225,7 +225,7 @@
 			if (facilities & st->facilities) { //only stations with selected facilities
 				int num_waiting_cargo = 0;
 				for (CargoID j = 0; j < NUM_CARGO; j++) {
-					if (st->goods[j].waiting_acceptance & 0xFFF) {
+					if (!st->goods[j].cargo.Empty()) {
 						num_waiting_cargo++; //count number of waiting cargo
 						if (HASBIT(cargo_filter, j)) {
 							station_sort[n++] = st;
@@ -368,9 +368,8 @@
 
 				/* show cargo waiting and station ratings */
 				for (CargoID j = 0; j < NUM_CARGO; j++) {
-					uint amount = GB(st->goods[j].waiting_acceptance, 0, 12);
-					if (amount != 0) {
-						StationsWndShowStationRating(x, y, j, amount, st->goods[j].rating);
+					if (!st->goods[j].cargo.Empty()) {
+						StationsWndShowStationRating(x, y, j, st->goods[j].cargo.Count(), st->goods[j].rating);
 						x += 20;
 					}
 				}
@@ -705,9 +704,9 @@
 
 	num = 1;
 	for (CargoID i = 0; i < NUM_CARGO; i++) {
-		if (GB(st->goods[i].waiting_acceptance, 0, 12) != 0) {
+		if (!st->goods[i].cargo.Empty()) {
 			num++;
-			if (st->goods[i].enroute_from != station_id) num++;
+			if (st->goods[i].cargo.Source() != station_id) num++;
 		}
 	}
 	SetVScrollCount(w, num);
@@ -729,7 +728,7 @@
 	if (--pos < 0) {
 		str = STR_00D0_NOTHING;
 		for (CargoID i = 0; i < NUM_CARGO; i++) {
-			if (GB(st->goods[i].waiting_acceptance, 0, 12) != 0) str = STR_EMPTY;
+			if (!st->goods[i].cargo.Empty()) str = STR_EMPTY;
 		}
 		SetDParam(0, str);
 		DrawString(x, y, STR_0008_WAITING, 0);
@@ -737,10 +736,10 @@
 	}
 
 	for (CargoID i = 0; i < NUM_CARGO && pos > -5; i++) {
-		uint waiting = GB(st->goods[i].waiting_acceptance, 0, 12);
+		uint waiting = st->goods[i].cargo.Count();
 		if (waiting == 0) continue;
 
-		if (st->goods[i].enroute_from == station_id) {
+		if (st->goods[i].cargo.Source() == station_id) {
 			if (--pos < 0) {
 				DrawCargoIcons(i, waiting, x, y);
 				SetDParam(1, waiting);
@@ -759,7 +758,7 @@
 			}
 
 			if (pos > -5 && --pos < 0) {
-				SetDParam(0, st->goods[i].enroute_from);
+				SetDParam(0, st->goods[i].cargo.Source());
 				DrawStringRightAligned(x + 234, y, STR_000B, 0);
 				y += 10;
 			}
@@ -774,7 +773,7 @@
 
 		for (CargoID i = 0; i < NUM_CARGO; i++) {
 			if (b >= endof(_userstring) - 5 - 1) break;
-			if (st->goods[i].waiting_acceptance & 0x8000) {
+			if (st->goods[i].acceptance) {
 				if (first) {
 					first = false;
 				} else {
@@ -800,7 +799,7 @@
 			if (!cs->IsValid()) continue;
 
 			const GoodsEntry *ge = &st->goods[i];
-			if (ge->enroute_from == INVALID_STATION) continue;
+			if (ge->days_since_pickup == 255) continue;
 
 			SetDParam(0, cs->name);
 			SetDParam(2, ge->rating * 101 >> 8);
--- a/src/train_cmd.cpp	Fri Jun 22 10:57:53 2007 +0000
+++ b/src/train_cmd.cpp	Fri Jun 22 11:58:59 2007 +0000
@@ -112,7 +112,7 @@
 	uint32 weight = 0;
 
 	for (Vehicle *u = v; u != NULL; u = u->next) {
-		uint32 vweight = GetCargo(u->cargo_type)->weight * u->cargo_count * FreightWagonMult(u->cargo_type) / 16;
+		uint32 vweight = GetCargo(u->cargo_type)->weight * u->cargo.Count() * FreightWagonMult(u->cargo_type) / 16;
 
 		/* Vehicle weight is not added for articulated parts. */
 		if (!IsArticulatedPart(u)) {
@@ -463,7 +463,7 @@
 
 	base = _engine_sprite_base[img] + ((direction + _engine_sprite_add[img]) & _engine_sprite_and[img]);
 
-	if (v->cargo_count >= v->cargo_cap / 2) base += _wagon_full_adder[img];
+	if (v->cargo.Count() >= v->cargo_cap / 2) base += _wagon_full_adder[img];
 	return base;
 }
 
@@ -1761,7 +1761,7 @@
 
 				num += amount;
 				if (flags & DC_EXEC) {
-					v->cargo_count = (v->cargo_type == new_cid) ? min(amount, v->cargo_count) : 0;
+					v->cargo.Truncate((v->cargo_type == new_cid) ? amount : 0);
 					v->cargo_type = new_cid;
 					v->cargo_cap = amount;
 					v->cargo_subtype = new_subtype;
@@ -2681,7 +2681,7 @@
 {
 	uint num = 0;
 	BEGIN_ENUM_WAGONS(v)
-		if (IsCargoInClass(v->cargo_type, CC_PASSENGERS)) num += v->cargo_count;
+		if (IsCargoInClass(v->cargo_type, CC_PASSENGERS)) num += v->cargo.Count();
 	END_ENUM_WAGONS(v)
 	return num;
 }
@@ -3294,8 +3294,7 @@
 
 void Train_Tick(Vehicle *v)
 {
-	if (_age_cargo_skip_counter == 0 && v->cargo_days != 0xff)
-		v->cargo_days++;
+	if (_age_cargo_skip_counter == 0) v->cargo.AgeCargo();
 
 	v->tick_counter++;
 
--- a/src/train_gui.cpp	Fri Jun 22 10:57:53 2007 +0000
+++ b/src/train_gui.cpp	Fri Jun 22 11:58:59 2007 +0000
@@ -327,13 +327,12 @@
 static void TrainDetailsCargoTab(const Vehicle *v, int x, int y)
 {
 	if (v->cargo_cap != 0) {
-		uint num = v->cargo_count;
 		StringID str = STR_8812_EMPTY;
 
-		if (num != 0) {
+		if (!v->cargo.Empty()) {
 			SetDParam(0, v->cargo_type);
-			SetDParam(1, num);
-			SetDParam(2, v->cargo_source);
+			SetDParam(1, v->cargo.Count());
+			SetDParam(2, v->cargo.Source());
 			SetDParam(3, _patches.freight_trains);
 			str = FreightWagonMult(v->cargo_type) > 1 ? STR_FROM_MULT : STR_8813_FROM;
 		}
@@ -387,7 +386,7 @@
 		}
 
 		do {
-			act_cargo[u->cargo_type] += u->cargo_count;
+			act_cargo[u->cargo_type] += u->cargo.Count();
 			max_cargo[u->cargo_type] += u->cargo_cap;
 		} while ((u = u->next) != NULL);
 
@@ -504,7 +503,7 @@
 				DrawString(x, y + 2, FreightWagonMult(i) > 1 ? STR_TOTAL_CAPACITY_MULT : STR_013F_TOTAL_CAPACITY, 0);
 			}
 		}
-		SetDParam(0, v->cargo_feeder_share);
+		SetDParam(0, v->cargo.FeederShare());
 		DrawString(x, y + 15, STR_FEEDER_CARGO_VALUE, 0);
 	}
 }
--- a/src/vehicle.cpp	Fri Jun 22 10:57:53 2007 +0000
+++ b/src/vehicle.cpp	Fri Jun 22 11:58:59 2007 +0000
@@ -233,6 +233,8 @@
 		v->first = NULL;
 		if (v->type == VEH_TRAIN) v->u.rail.first_engine = INVALID_ENGINE;
 		if (v->type == VEH_ROAD)  v->u.road.first_engine = INVALID_ENGINE;
+
+		v->cargo.InvalidateCache();
 	}
 
 	FOR_ALL_VEHICLES(v) {
@@ -690,6 +692,7 @@
 		InvalidateWindowData(WC_VEHICLE_DEPOT, v->tile);
 	}
 
+	v->cargo.Truncate(0);
 	UpdateVehiclePosHash(v, INVALID_COORD, 0);
 	v->next_hash = NULL;
 	v->next_new_hash = NULL;
@@ -2277,7 +2280,7 @@
 
 	/* Count up max and used */
 	for (; v != NULL; v = v->next) {
-		count += v->cargo_count;
+		count += v->cargo.Count();
 		max += v->cargo_cap;
 	}
 
@@ -2748,6 +2751,14 @@
 	return GetEngineColourMap(v->engine_type, v->owner, INVALID_ENGINE, v);
 }
 
+static uint8  _cargo_days;
+static uint16 _cargo_source;
+static uint32 _cargo_source_xy;
+static uint16 _cargo_count;
+static uint16 _cargo_paid_for;
+static Money  _cargo_feeder_share;
+static uint32 _cargo_loaded_at_xy;
+
 /** Save and load of vehicles */
 extern const SaveLoad _common_veh_desc[] = {
 	    SLE_VAR(Vehicle, subtype,              SLE_UINT8),
@@ -2784,14 +2795,15 @@
 	SLE_CONDVAR(Vehicle, last_station_visited, SLE_FILE_U8  | SLE_VAR_U16,  0, 4),
 	SLE_CONDVAR(Vehicle, last_station_visited, SLE_UINT16,                  5, SL_MAX_VERSION),
 
-	    SLE_VAR(Vehicle, cargo_type,           SLE_UINT8),
-	SLE_CONDVAR(Vehicle, cargo_subtype,        SLE_UINT8,                  35, SL_MAX_VERSION),
-	    SLE_VAR(Vehicle, cargo_days,           SLE_UINT8),
-	SLE_CONDVAR(Vehicle, cargo_source,         SLE_FILE_U8  | SLE_VAR_U16,  0, 6),
-	SLE_CONDVAR(Vehicle, cargo_source,         SLE_UINT16,                  7, SL_MAX_VERSION),
-	SLE_CONDVAR(Vehicle, cargo_source_xy,      SLE_UINT32,                 44, SL_MAX_VERSION),
-	    SLE_VAR(Vehicle, cargo_cap,            SLE_UINT16),
-	    SLE_VAR(Vehicle, cargo_count,          SLE_UINT16),
+	     SLE_VAR(Vehicle, cargo_type,           SLE_UINT8),
+	 SLE_CONDVAR(Vehicle, cargo_subtype,        SLE_UINT8,                  35, SL_MAX_VERSION),
+	SLEG_CONDVAR(         _cargo_days,          SLE_UINT8,                   0, 67),
+	SLEG_CONDVAR(         _cargo_source,        SLE_FILE_U8  | SLE_VAR_U16,  0, 6),
+	SLEG_CONDVAR(         _cargo_source,        SLE_UINT16,                  7, 67),
+	SLEG_CONDVAR(         _cargo_source_xy,     SLE_UINT32,                 44, 67),
+	     SLE_VAR(Vehicle, cargo_cap,            SLE_UINT16),
+	SLEG_CONDVAR(         _cargo_count,         SLE_UINT16,                  0, 67),
+	 SLE_CONDLST(Vehicle, cargo,                REF_CARGO_PACKET,           68, SL_MAX_VERSION),
 
 	    SLE_VAR(Vehicle, day_counter,          SLE_UINT8),
 	    SLE_VAR(Vehicle, tick_counter,         SLE_UINT8),
@@ -2837,19 +2849,19 @@
 	SLE_CONDVAR(Vehicle, build_year,           SLE_FILE_U8 | SLE_VAR_I32,  0, 30),
 	SLE_CONDVAR(Vehicle, build_year,           SLE_INT32,                 31, SL_MAX_VERSION),
 
-	    SLE_VAR(Vehicle, load_unload_time_rem, SLE_UINT16),
-	SLE_CONDVAR(Vehicle, cargo_paid_for,       SLE_UINT16,                45, SL_MAX_VERSION),
-	SLE_CONDVAR(Vehicle, vehicle_flags,        SLE_UINT8,                 40, SL_MAX_VERSION),
-
-	SLE_CONDVAR(Vehicle, profit_this_year,     SLE_FILE_I32 | SLE_VAR_I64, 0, 64),
-	SLE_CONDVAR(Vehicle, profit_this_year,     SLE_INT64,                 65, SL_MAX_VERSION),
-	SLE_CONDVAR(Vehicle, profit_last_year,     SLE_FILE_I32 | SLE_VAR_I64, 0, 64),
-	SLE_CONDVAR(Vehicle, profit_last_year,     SLE_INT64,                 65, SL_MAX_VERSION),
-	SLE_CONDVAR(Vehicle, cargo_feeder_share,   SLE_FILE_I32 | SLE_VAR_I64,51, 64),
-	SLE_CONDVAR(Vehicle, cargo_feeder_share,   SLE_INT64,                 65, SL_MAX_VERSION),
-	SLE_CONDVAR(Vehicle, cargo_loaded_at_xy,   SLE_UINT32,                51, SL_MAX_VERSION),
-	SLE_CONDVAR(Vehicle, value,                SLE_FILE_I32 | SLE_VAR_I64, 0, 64),
-	SLE_CONDVAR(Vehicle, value,                SLE_INT64,                 65, SL_MAX_VERSION),
+	     SLE_VAR(Vehicle, load_unload_time_rem, SLE_UINT16),
+	SLEG_CONDVAR(         _cargo_paid_for,      SLE_UINT16,                45, SL_MAX_VERSION),
+	 SLE_CONDVAR(Vehicle, vehicle_flags,        SLE_UINT8,                 40, SL_MAX_VERSION),
+
+	 SLE_CONDVAR(Vehicle, profit_this_year,     SLE_FILE_I32 | SLE_VAR_I64, 0, 64),
+	 SLE_CONDVAR(Vehicle, profit_this_year,     SLE_INT64,                 65, SL_MAX_VERSION),
+	 SLE_CONDVAR(Vehicle, profit_last_year,     SLE_FILE_I32 | SLE_VAR_I64, 0, 64),
+	 SLE_CONDVAR(Vehicle, profit_last_year,     SLE_INT64,                 65, SL_MAX_VERSION),
+	SLEG_CONDVAR(         _cargo_feeder_share,  SLE_FILE_I32 | SLE_VAR_I64,51, 64),
+	SLEG_CONDVAR(         _cargo_feeder_share,  SLE_INT64,                 65, 67),
+	SLEG_CONDVAR(         _cargo_loaded_at_xy,  SLE_UINT32,                51, 67),
+	 SLE_CONDVAR(Vehicle, value,                SLE_FILE_I32 | SLE_VAR_I64, 0, 64),
+	 SLE_CONDVAR(Vehicle, value,                SLE_INT64,                 65, SL_MAX_VERSION),
 
 	    SLE_VAR(Vehicle, random_bits,          SLE_UINT8),
 	    SLE_VAR(Vehicle, waiting_triggers,     SLE_UINT8),
@@ -3030,6 +3042,8 @@
 	int index;
 	Vehicle *v;
 
+	_cargo_count = 0;
+
 	while ((index = SlIterateArray()) != -1) {
 		Vehicle *v;
 
@@ -3038,9 +3052,8 @@
 
 		v = GetVehicle(index);
 		VehicleType vtype = (VehicleType)SlReadByte();
-		SlObject(v, (SaveLoad*)_veh_descs[vtype]);
-
-		switch (v->type) {
+
+		switch (vtype) {
 			case VEH_TRAIN:    v = new (v) Train();           break;
 			case VEH_ROAD:     v = new (v) RoadVehicle();     break;
 			case VEH_SHIP:     v = new (v) Ship();            break;
@@ -3051,6 +3064,20 @@
 			default: NOT_REACHED();
 		}
 
+		SlObject(v, (SaveLoad*)_veh_descs[vtype]);
+
+		if (_cargo_count != 0 && IsPlayerBuildableVehicleType(v)) {
+			/* Don't construct the packet with station here, because that'll fail with old savegames */
+			CargoPacket *cp = new CargoPacket();
+			cp->source          = _cargo_source;
+			cp->source_xy       = _cargo_source_xy;
+			cp->count           = _cargo_count;
+			cp->days_in_transit = _cargo_days;
+			cp->feeder_share    = _cargo_feeder_share;
+			cp->loaded_at_xy    = _cargo_loaded_at_xy;
+			v->cargo.Append(cp);
+		}
+
 		/* Old savegames used 'last_station_visited = 0xFF' */
 		if (CheckSavegameVersion(5) && v->last_station_visited == 0xFF)
 			v->last_station_visited = INVALID_STATION;
--- a/src/vehicle.h	Fri Jun 22 10:57:53 2007 +0000
+++ b/src/vehicle.h	Fri Jun 22 11:58:59 2007 +0000
@@ -9,6 +9,7 @@
 #include "order.h"
 #include "rail.h"
 #include "road.h"
+#include "cargopacket.h"
 #include "texteff.hpp"
 
 /** The returned bits of VehicleEnterTile. */
@@ -265,12 +266,10 @@
 	StationID last_station_visited;
 
 	CargoID cargo_type;      // type of cargo this vehicle is carrying
-	byte cargo_days;         // how many days have the pieces been in transit
-	StationID cargo_source;  // source of cargo
-	TileIndex cargo_source_xy; //< stores the Tile where the source station is located, in case it is removed
 	uint16 cargo_cap;        // total capacity
-	uint16 cargo_count;      // how many pieces are used
 	byte cargo_subtype;      ///< Used for livery refits (NewGRF variations)
+	CargoList cargo;         ///< The cargo this vehicle is carrying
+
 
 	byte day_counter;        // increased by one for each day
 	byte tick_counter;       // increased by one for each tick
@@ -312,13 +311,10 @@
 	bool leave_depot_instantly; // NOSAVE: stores if the vehicle needs to leave the depot it just entered. Used by autoreplace
 
 	uint16 load_unload_time_rem;
-	uint16 cargo_paid_for;      // How much of the cargo currently on board has been paid for.
 	byte vehicle_flags;         // Used for gradual loading and other miscellaneous things (@see VehicleFlags enum)
 
 	Money profit_this_year;
 	Money profit_last_year;
-	Money cargo_feeder_share;      ///< value of feeder pickup to be paid for on delivery of cargo
-	TileIndex cargo_loaded_at_xy;  ///< tile index where feeder cargo was loaded
 	Money value;
 
 	GroupID group_id;              ///< Index of group Pool array
--- a/src/water_cmd.cpp	Fri Jun 22 10:57:53 2007 +0000
+++ b/src/water_cmd.cpp	Fri Jun 22 11:58:59 2007 +0000
@@ -670,7 +670,7 @@
 
 			/* crash all wagons, and count passengers */
 			BEGIN_ENUM_WAGONS(v)
-				if (IsCargoInClass(v->cargo_type, CC_PASSENGERS)) pass += v->cargo_count;
+				if (IsCargoInClass(v->cargo_type, CC_PASSENGERS)) pass += v->cargo.Count();
 				v->vehstatus |= VS_CRASHED;
 				MarkAllViewportsDirty(v->left_coord, v->top_coord, v->right_coord + 1, v->bottom_coord + 1);
 			END_ENUM_WAGONS(v)