Index: station_cmd.c
===================================================================
--- station_cmd.c	(revision 603)
+++ station_cmd.c	(working copy)
@@ -977,6 +977,9 @@
 			if (_waypoint_data[i].grfid == spec->grfid
 			    && _waypoint_data[i].localidx == local_stid + 1) {
 				stid = i;
+				/* FIXME: Release original SpriteGroup to
+				 * prevent leaks. But first we need to
+				 * refcount the SpriteGroup. --pasky */
 				break;
 			}
 		}
@@ -1003,25 +1006,30 @@
 	return &_waypoint_data[stid];
 }
 
-uint32 GetCustomStationRelocation(struct StationSpec *spec, byte ctype)
+uint32 GetCustomStationRelocation(struct StationSpec *spec, struct Station *stat, byte ctype)
 {
+	struct RealSpriteGroup *rsg;
+
 	assert(spec->classid == 'WAYP');
 
 	/* In the future, variational spritegroups will kick in through this
-	 * accessor.  */
+	 * accessor, using @stat.  */
+	rsg = TriviallyGetRSG(&spec->spritegroup[ctype]);
 
-	if (spec->relocation[ctype].loading_count != 0) {
-		return spec->relocation[ctype].loading[0];
-	} else if (spec->relocation[ctype].loading_count != 0) {
-		return spec->relocation[ctype].loaded[0];
-	} else {
-		error("Custom station 0x%08x::0x%02x has no sprites associated.",
-		       spec->grfid, spec->localidx);
-		/* This is what gets subscribed of dtss->image in grfspecial.c,
-		 * so it's probably kinda "default offset". Try to use it as
-		 * emergency measure. */
-		return 0x42D;
+	if (rsg->sprites_per_set != 0) {
+		if (rsg->loading_count != 0) {
+			return rsg->loading[0];
+		} else if (rsg->loading_count != 0) {
+			return rsg->loaded[0];
+		}
 	}
+
+	error("Custom station 0x%08x::0x%02x has no sprites associated.",
+	       spec->grfid, spec->localidx);
+	/* This is what gets subscribed of dtss->image in grfspecial.c,
+	 * so it's probably kinda "default offset". Try to use it as
+	 * emergency measure. */
+	return 0x42D;
 }
 
 int GetCustomStationsCount(uint32 classid)
Index: engine.c
===================================================================
--- engine.c	(revision 603)
+++ engine.c	(working copy)
@@ -196,6 +196,9 @@
 	                         wos->overrides_count * sizeof(struct WagonOverride));
 
 	wo = &wos->overrides[wos->overrides_count - 1];
+	/* FIXME: If we are replacing an override, release original SpriteGroup
+	 * to prevent leaks. But first we need to refcount the SpriteGroup.
+	 * --pasky */
 	wo->group = *group;
 	wo->trains = trains;
 	wo->train_id = malloc(trains);
@@ -207,8 +210,10 @@
 	struct WagonOverrides *wos = &_engine_wagon_overrides[engine];
 	int i;
 
-	// XXX: This could turn out to be a timesink on profiles. We could always just
-	// dedicate 65535 bytes for an [engine][train] trampoline.
+	// XXX: This could turn out to be a timesink on profiles. We could
+	// always just dedicate 65535 bytes for an [engine][train] trampoline
+	// for O(1). Or O(logMlogN) and searching binary tree or smt. like
+	// that. --pasky
 
 	for (i = 0; i < wos->overrides_count; i++) {
 		struct WagonOverride *wo = &wos->overrides[i];
@@ -232,7 +237,9 @@
 
 void SetCustomEngineSprites(byte engine, byte cargo, struct SpriteGroup *group)
 {
-	assert(group->sprites_per_set == 4 || group->sprites_per_set == 8);
+	/* FIXME: If we are replacing an override, release original SpriteGroup
+	 * to prevent leaks. But first we need to refcount the SpriteGroup.
+	 * --pasky */
 	_engine_custom_sprites[engine][cargo] = *group;
 }
 
@@ -240,6 +247,7 @@
                           byte loaded, byte in_motion, byte direction)
 {
 	struct SpriteGroup *group = &_engine_custom_sprites[engine][cargo];
+	struct RealSpriteGroup *rsg;
 	int totalsets, spriteset;
 	int r;
 
@@ -250,22 +258,26 @@
 		if (overset) group = overset;
 	}
 
-	if (!group->sprites_per_set && cargo != 29) {
+	/* TODO: Resolve surreal groups properly. --pasky */
+	rsg = TriviallyGetRSG(group);
+
+	if (!rsg->sprites_per_set && cargo != 29) {
 		// This group is empty but perhaps there'll be a default one.
-		group = &_engine_custom_sprites[engine][29];
+		/* TODO: Resolve surreal groups properly. --pasky */
+		rsg = TriviallyGetRSG(&_engine_custom_sprites[engine][29]);
 	}
 
-	if (!group->sprites_per_set) {
+	if (!rsg->sprites_per_set) {
 		// This group is empty. This function users should therefore
 		// look up the sprite number in _engine_original_sprites.
 		return 0;
 	}
 
 	direction %= 8;
-	if (group->sprites_per_set == 4)
+	if (rsg->sprites_per_set == 4)
 		direction %= 4;
 
-	totalsets = in_motion ? group->loaded_count : group->loading_count;
+	totalsets = in_motion ? rsg->loaded_count : rsg->loading_count;
 
 	// My aim here is to make it possible to visually determine absolutely
 	// empty and totally full vehicles. --pasky
@@ -282,7 +294,7 @@
 			spriteset--;
 	}
 
-	r = (in_motion ? group->loaded[spriteset] : group->loading[spriteset]) + direction;
+	r = (in_motion ? rsg->loaded[spriteset] : rsg->loading[spriteset]) + direction;
 	return r;
 }
 
Index: sprite.h
===================================================================
--- sprite.h	(revision 603)
+++ sprite.h	(working copy)
@@ -7,7 +7,7 @@
  * depots or stations): */
 
 typedef struct DrawTileSeqStruct {
-	int8 delta_x;
+	int8 delta_x; // 0x80 is sequence terminator
 	int8 delta_y;
 	int8 delta_z;
 	byte width,height;
@@ -20,12 +20,16 @@
 	DrawTileSeqStruct const *seq;
 } DrawTileSprites;
 
+// Iterate through all DrawTileSeqStructs in DrawTileSprites.
 #define foreach_draw_tile_seq(idx, list) for (idx = list; ((byte) idx->delta_x) != 0x80; idx++)
 
 
 /* This is for custom sprites: */
 
-struct SpriteGroup {
+
+struct SpriteGroup;
+
+struct RealSpriteGroup {
 	// XXX: Would anyone ever need more than 16 spritesets? Maybe we should
 	// use even less, now we take whole 8kb for custom sprites table, oh my!
 	byte sprites_per_set; // means number of directions - 4 or 8
@@ -43,4 +47,74 @@
 	uint16 loading[16]; // sprite ids
 };
 
+/* Shared by deterministic and random groups. */
+enum VarSpriteGroupScope {
+	VSG_SCOPE_SELF,
+	// Engine of consists for vehicles, city for stations.
+	VSG_SCOPE_PARENT,
+};
+
+struct DeterministicSpriteGroupRanges;
+
+struct DeterministicSpriteGroup {
+	// Take this variable:
+	enum VarSpriteGroupScope var_scope;
+	byte variable;
+
+	// Do this with it:
+	byte shift_num;
+	byte and_mask;
+
+	// Then do this with it:
+	enum DeterministicSpriteGroupOperation {
+		DSG_OP_NONE,
+		DSG_OP_DIV,
+		DSG_OP_MOD,
+	} operation;
+	byte add_val;
+	byte divmod_val;
+	
+	// And apply it to this:
+	byte num_ranges;
+	struct DeterministicSpriteGroupRanges *ranges; // Dynamically allocated
+
+	// Dynamically allocated, this is the sole owner
+	struct SpriteGroup *default_group;
+};
+
+struct SpriteGroup {
+	enum SpriteGroupType {
+		SGT_REAL,
+		SGT_DETERMINISTIC,
+		SGT_RANDOM, /* TODO */
+	} type;
+
+	union {
+		struct RealSpriteGroup real;
+		struct DeterministicSpriteGroup determ;
+	} g;
+};
+
+struct DeterministicSpriteGroupRanges {
+	struct SpriteGroup group;
+	byte range_low;
+	byte range_high;
+};
+
+/* This is a temporary helper for SpriteGroup users not supporting variational
+ * sprite groups yet - it just traverses those cowardly, always taking the
+ * default choice until it hits a real sprite group, returning it. */
+static struct RealSpriteGroup *TriviallyGetRSG(struct SpriteGroup *sg);
+
+
+
+/**** Inline functions ****/
+
+static inline struct RealSpriteGroup *TriviallyGetRSG(struct SpriteGroup *sg)
+{
+	if (sg->type == SGT_REAL)
+		return &sg->g.real;
+	return TriviallyGetRSG(sg->g.determ.default_group);
+}
+
 #endif
Index: rail_cmd.c
===================================================================
--- rail_cmd.c	(revision 603)
+++ rail_cmd.c	(working copy)
@@ -1551,7 +1551,8 @@
 				DrawTileSeqStruct const *seq;
 				// emulate station tile - open with building
 				DrawTileSprites *cust = &stat->renderdata[2 + (m5 & 0x1)];
-				uint32 relocation = GetCustomStationRelocation(stat, 0);
+				/* FIXME: NULL Station! --pasky */
+				uint32 relocation = GetCustomStationRelocation(stat, NULL, 0);
 
 				image = cust->ground_sprite;
 				if (image & 0x8000) image = (image & 0x7FFF) + tracktype_offs;
@@ -1633,7 +1634,7 @@
 
 	assert(stat);
 
-	relocation = GetCustomStationRelocation(stat, 1);
+	relocation = GetCustomStationRelocation(stat, NULL, 1);
 	// emulate station tile - open with building
 	// add 1 to get the other direction
 	cust = &stat->renderdata[2];
Index: station.h
===================================================================
--- station.h	(revision 603)
+++ station.h	(working copy)
@@ -102,9 +102,9 @@
 	byte tiles;
 	DrawTileSprites renderdata[8];
 
-	/* Sprite offsets for renderdata->seq->image. relocation[0] is default
-	 * whilst relocation[1] is "CID_PURCHASE". */
-	struct SpriteGroup relocation[2];
+	/* Sprite offsets for renderdata->seq->image. spritegroup[0] is default
+	 * whilst spritegroup[1] is "CID_PURCHASE". */
+	struct SpriteGroup spritegroup[2];
 };
 
 /* Here, @stid is local per-GRFFile station index. If spec->localidx is not yet
@@ -115,7 +115,10 @@
 /* Here, @stid is global station index (in continous range 0..GetCustomStationsCount())
  * (lookup is therefore very fast as we do this very frequently). */
 struct StationSpec *GetCustomStation(uint32 classid, byte stid);
-uint32 GetCustomStationRelocation(struct StationSpec *spec, byte ctype);
+/* Get sprite offset for a given custom station and station structure (may be
+ * NULL if ctype is set - that means we are in a build dialog). The station
+ * structure is used for variational sprite groups. */
+uint32 GetCustomStationRelocation(struct StationSpec *spec, struct Station *stat, byte ctype);
 int GetCustomStationsCount(uint32 classid);
 
 #endif /* STATION_H */
Index: grfspecial.c
===================================================================
--- grfspecial.c	(revision 603)
+++ grfspecial.c	(working copy)
@@ -1055,6 +1055,7 @@
 	uint8 numloaded;
 	uint8 numloading;
 	struct SpriteGroup *group;
+	struct RealSpriteGroup *rg;
 	byte *loaded_ptr;
 	byte *loading_ptr;
 	int i;
@@ -1065,27 +1066,59 @@
 	numloaded = buf[3];
 	numloading = buf[4];
 
-	if (numloaded == 0x81) {
-		/* XXX: This just goes for the default superset for now,
-		 * straight and safe. --pasky */
-		uint8 var = buf[4];
-		//uint8 shiftnum = buf[5];
-		//uint8 andmask = buf[6];
-		uint8 nvar = buf[7];
-		//uint32 val;
-		uint16 def;
+	if (numloaded == 0x81 || numloaded == 0x82) {
+		struct DeterministicSpriteGroup *dg;
+		int i;
 
-		grfmsg(GMS_WARN, "NewSpriteGroup(0x81): Unsupported variable %x. Using default cid.", var);
+		// Ok, this is gonna get a little wild, so hold your breath...
 
-		//val = (0xff << shiftnum) & andmask;
+		/* This stuff is getting actually evaluated in
+		 * EvalDeterministicSpriteGroup(). */
 
+		buf += 4; len -= 4;
+		check_length(len, 6, "NewSpriteGroup 0x81/0x82");
+
 		if (setid >= _cur_grffile->spritegroups_count) {
 			_cur_grffile->spritegroups_count = setid + 1;
 			_cur_grffile->spritegroups = realloc(_cur_grffile->spritegroups, _cur_grffile->spritegroups_count * sizeof(struct SpriteGroup));
 		}
-		buf += 8 + nvar * 4;
-		def = grf_load_word(&buf);
-		_cur_grffile->spritegroups[setid] = _cur_grffile->spritegroups[def];
+
+		group = &_cur_grffile->spritegroups[setid];
+		memset(group, 0, sizeof(struct SpriteGroup));
+		group->type = SGT_DETERMINISTIC;
+		dg = &group->g.determ;
+
+		/* XXX: We don't free() anything, assuming that if there was
+		 * some action here before, it got associated by action 3.
+		 * We should perhaps keep some refcount? --pasky */
+
+		dg->var_scope = numloaded == 0x82 ? VSG_SCOPE_PARENT : VSG_SCOPE_SELF;
+		dg->variable = grf_load_byte(&buf);
+
+		dg->shift_num = grf_load_byte(&buf);
+		dg->and_mask = grf_load_byte(&buf);
+		dg->operation = dg->shift_num >> 6; /* w00t */
+		dg->shift_num &= 0x3F;
+		if (dg->operation != DSG_OP_NONE) {
+			dg->add_val = grf_load_byte(&buf);
+			dg->divmod_val = grf_load_byte(&buf);
+		}
+
+		dg->num_ranges = grf_load_byte(&buf);
+		dg->ranges = calloc(dg->num_ranges, sizeof(*dg->ranges));
+		for (i = 0; i < dg->num_ranges; i++) {
+			uint16 setid = grf_load_word(&buf);
+
+			/* XXX: If multiple surreal sets attach a surreal
+			 * set this way, we are in trouble. */
+			dg->ranges[i].group = _cur_grffile->spritegroups[setid];
+			dg->ranges[i].range_low = grf_load_byte(&buf);
+			dg->ranges[i].range_high = grf_load_byte(&buf);
+		}
+
+		dg->default_group = malloc(sizeof(*dg->default_group));
+		memcpy(dg->default_group, &_cur_grffile->spritegroups[grf_load_word(&buf)], sizeof(*dg->default_group));
+
 		return;
 
 	} else if (numloaded & 0x80) {
@@ -1124,25 +1157,28 @@
 	}
 	group = &_cur_grffile->spritegroups[setid];
 	memset(group, 0, sizeof(struct SpriteGroup));
-	group->sprites_per_set = _cur_grffile->spriteset_numents;
-	group->loaded_count  = numloaded;
-	group->loading_count = numloading;
+	group->type = SGT_REAL;
+	rg = &group->g.real;
 
+	rg->sprites_per_set = _cur_grffile->spriteset_numents;
+	rg->loaded_count  = numloaded;
+	rg->loading_count = numloading;
+
 	DEBUG(grf, 6) ("NewSpriteGroup: New SpriteGroup 0x%02hhx, %u views, %u loaded, %u loading, sprites %u - %u",
-			setid, group->sprites_per_set, group->loaded_count, group->loading_count,
+			setid, rg->sprites_per_set, rg->loaded_count, rg->loading_count,
 			_cur_grffile->spriteset_start - _cur_grffile->sprite_offset,
 			_cur_grffile->spriteset_start + (_cur_grffile->spriteset_numents * (numloaded + numloading)) - _cur_grffile->sprite_offset);
 
 	for (i = 0; i < numloaded; i++) {
 		uint16 spriteset_id = grf_load_word(&loaded_ptr);
-		group->loaded[i] = _cur_grffile->spriteset_start + spriteset_id * _cur_grffile->spriteset_numents;
-		DEBUG(grf, 8) ("NewSpriteGroup: + group->loaded[%i]  = %u (subset %u)", i, group->loaded[i], spriteset_id);
+		rg->loaded[i] = _cur_grffile->spriteset_start + spriteset_id * _cur_grffile->spriteset_numents;
+		DEBUG(grf, 8) ("NewSpriteGroup: + rg->loaded[%i]  = %u (subset %u)", i, rg->loaded[i], spriteset_id);
 	}
 
 	for (i = 0; i < numloading; i++) {
 		uint16 spriteset_id = grf_load_word(&loading_ptr);
-		group->loading[i] = _cur_grffile->spriteset_start + spriteset_id * _cur_grffile->spriteset_numents;
-		DEBUG(grf, 8) ("NewSpriteGroup: + group->loading[%i] = %u (subset %u)", i, group->loading[i], spriteset_id);
+		rg->loading[i] = _cur_grffile->spriteset_start + spriteset_id * _cur_grffile->spriteset_numents;
+		DEBUG(grf, 8) ("NewSpriteGroup: + rg->loading[%i] = %u (subset %u)", i, rg->loading[i], spriteset_id);
 	}
 }
 
@@ -1214,7 +1250,7 @@
 					continue;
 				}
 
-				stat->relocation[1] = _cur_grffile->spritegroups[groupid];
+				stat->spritegroup[1] = _cur_grffile->spritegroups[groupid];
 			}
 		}
 
@@ -1232,7 +1268,7 @@
 				uint8 stid = buf[3 + i];
 				struct StationSpec *stat = &_cur_grffile->stations[stid];
 
-				stat->relocation[0] = _cur_grffile->spritegroups[groupid];
+				stat->spritegroup[0] = _cur_grffile->spritegroups[groupid];
 				stat->grfid = _cur_grffile->grfid;
 				SetCustomStation(stid, stat);
 				stat->classid = 0;
