changeset 3668:4b443d0d50e5 draft

(svn r4583) - NewGRF: Revamp sprite group loading to support advanced varadjusts and variable size. These are not yet processed, however.
author peter1138 <peter1138@openttd.org>
date Wed, 26 Apr 2006 20:44:28 +0000
parents 813e5df222b7
children 815e93e58a75
files newgrf.c newgrf_engine.c newgrf_spritegroup.c newgrf_spritegroup.h newgrf_station.c sprite.c
diffstat 6 files changed, 254 insertions(+), 169 deletions(-) [+]
line wrap: on
line diff
--- a/newgrf.c
+++ b/newgrf.c
@@ -176,6 +176,17 @@
 	return val;
 }
 
+static uint32 grf_load_var(byte size, byte **buf)
+{
+	switch (size) {
+		case 1: return grf_load_byte(buf);
+		case 2: return grf_load_word(buf);
+		case 4: return grf_load_dword(buf);
+		default:
+			NOT_REACHED();
+			return 0;
+	}
+}
 
 static GRFFile *GetFileByGRFID(uint32 grfid)
 {
@@ -1247,12 +1258,12 @@
  * @param sprites The number of sprites per set.
  * @return A spritegroup representing the sprite number result.
  */
-static SpriteGroup* NewResultSpriteGroup(uint16 value, byte sprites)
+static SpriteGroup* NewResultSpriteGroup(SpriteID sprite, byte num_sprites)
 {
 	SpriteGroup *group = AllocateSpriteGroup();
 	group->type = SGT_RESULT;
-	group->g.result.result = value;
-	group->g.result.sprites = sprites;
+	group->g.result.sprite = sprite;
+	group->g.result.num_sprites = num_sprites;
 	return group;
 }
 
@@ -1384,42 +1395,78 @@
 		/* Deterministic Sprite Group */
 		case 0x81: // Self scope, byte
 		case 0x82: // Parent scope, byte
+		case 0x85: // Self scope, word
+		case 0x86: // Parent scope, word
+		case 0x89: // Self scope, dword
+		case 0x8A: // Parent scope, dword
 		{
-			DeterministicSpriteGroup *dg;
-			int i;
-
-			check_length(bufend - buf, 6, "NewSpriteGroup 0x81/0x82");
+			byte varadjust;
+			byte varsize;
+			uint i;
+
+			/* Check we can load the var size parameter */
+			check_length(bufend - buf, 1, "NewSpriteGroup (Deterministic) (1)");
 
 			group = AllocateSpriteGroup();
 			group->type = SGT_DETERMINISTIC;
-			dg = &group->g.determ;
-
-			dg->var_scope = type == 0x82 ? VSG_SCOPE_PARENT : VSG_SCOPE_SELF;
-			dg->variable = grf_load_byte(&buf);
-			/* Variables 0x60 - 0x7F include an extra parameter */
-			if (IS_BYTE_INSIDE(dg->variable, 0x60, 0x80))
-				dg->parameter = 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);
+			group->g.determ.var_scope = HASBIT(type, 1) ? VSG_SCOPE_PARENT : VSG_SCOPE_SELF;
+
+			switch (GB(type, 2, 2)) {
+				case 0: group->g.determ.size = DSG_SIZE_BYTE;  varsize = 1; break;
+				case 1: group->g.determ.size = DSG_SIZE_WORD;  varsize = 2; break;
+				case 2: group->g.determ.size = DSG_SIZE_DWORD; varsize = 4; break;
+				default: NOT_REACHED(); break;
 			}
 
-			/* (groupid & 0x8000) means this is callback result. */
-
-			dg->num_ranges = grf_load_byte(&buf);
-			dg->ranges = calloc(dg->num_ranges, sizeof(*dg->ranges));
-			for (i = 0; i < dg->num_ranges; i++) {
-				dg->ranges[i].group = GetGroupFromGroupID(setid, type, grf_load_word(&buf));
-				dg->ranges[i].low = grf_load_byte(&buf);
-				dg->ranges[i].high = grf_load_byte(&buf);
+			check_length(bufend - buf, 2 + (varsize * 3) + 2, "NewSpriteGroup (Deterministic) (2)");
+
+			/* Loop through the var adjusts. Unfortunately we don't know how many we have
+			 * from the outset, so we shall have to keep reallocing. */
+			do {
+				DeterministicSpriteGroupAdjust *adjust;
+
+				if (group->g.determ.num_adjusts > 0) {
+					check_length(bufend - buf, 2 + (varsize * 3) + 3, "NewSpriteGroup (Deterministic) (3)");
+				}
+
+				group->g.determ.num_adjusts++;
+				group->g.determ.adjusts = realloc(group->g.determ.adjusts, group->g.determ.num_adjusts * sizeof(*group->g.determ.adjusts));
+
+				adjust = &group->g.determ.adjusts[group->g.determ.num_adjusts - 1];
+
+				/* The first var adjust doesn't have an operation specified, so we set it to add. */
+				adjust->operation = group->g.determ.num_adjusts == 1 ? DSGA_OP_ADD : grf_load_byte(&buf);
+				adjust->variable  = grf_load_byte(&buf);
+				adjust->parameter = IS_BYTE_INSIDE(adjust->variable, 0x60, 0x80) ? grf_load_byte(&buf) : 0;
+
+				varadjust = grf_load_byte(&buf);
+				adjust->shift_num = GB(varadjust, 0, 5);
+				adjust->type      = GB(varadjust, 6, 2);
+				adjust->and_mask  = grf_load_var(varsize, &buf);
+
+				if (adjust->type != DSGA_TYPE_NONE) {
+					adjust->add_val    = grf_load_var(varsize, &buf);
+					adjust->divmod_val = grf_load_var(varsize, &buf);
+				} else {
+					adjust->add_val    = 0;
+					adjust->divmod_val = 0;
+				}
+
+				/* Continue reading var adjusts while bit 5 is set. */
+			} while (HASBIT(varadjust, 5));
+
+			group->g.determ.num_ranges = grf_load_byte(&buf);
+			group->g.determ.ranges = calloc(group->g.determ.num_ranges, sizeof(*group->g.determ.ranges));
+
+			check_length(bufend - buf, 2 + (2 + 2 * varsize) * group->g.determ.num_ranges, "NewSpriteGroup (Deterministic)");
+
+			for (i = 0; i < group->g.determ.num_ranges; i++) {
+				group->g.determ.ranges[i].group = GetGroupFromGroupID(setid, type, grf_load_word(&buf));
+				group->g.determ.ranges[i].low   = grf_load_var(varsize, &buf);
+				group->g.determ.ranges[i].high  = grf_load_var(varsize, &buf);
 			}
 
-			dg->default_group = GetGroupFromGroupID(setid, type, grf_load_word(&buf));
+			group->g.determ.default_group = GetGroupFromGroupID(setid, type, grf_load_word(&buf));
 			break;
 		}
 
@@ -1427,79 +1474,88 @@
 		case 0x80: // Self scope
 		case 0x83: // Parent scope
 		{
-			RandomizedSpriteGroup *rg;
-			int i;
-
-			/* This stuff is getting actually evaluated in
-			 * EvalRandomizedSpriteGroup(). */
-
-			check_length(bufend - buf, 6, "NewSpriteGroup 0x80/0x83");
+			byte triggers;
+			uint i;
+
+			check_length(bufend - buf, 7, "NewSpriteGroup (Randomized) (1)");
 
 			group = AllocateSpriteGroup();
 			group->type = SGT_RANDOMIZED;
-			rg = &group->g.random;
-
-			rg->var_scope = type == 0x83 ? VSG_SCOPE_PARENT : VSG_SCOPE_SELF;
-
-			rg->triggers = grf_load_byte(&buf);
-			rg->cmp_mode = rg->triggers & 0x80;
-			rg->triggers &= 0x7F;
-
-			rg->lowest_randbit = grf_load_byte(&buf);
-			rg->num_groups = grf_load_byte(&buf);
-
-			rg->groups = calloc(rg->num_groups, sizeof(*rg->groups));
-			for (i = 0; i < rg->num_groups; i++) {
-				rg->groups[i] = GetGroupFromGroupID(setid, type, grf_load_word(&buf));
+			group->g.random.var_scope = HASBIT(type, 1) ? VSG_SCOPE_PARENT : VSG_SCOPE_SELF;
+
+			triggers = grf_load_byte(&buf);
+			group->g.random.triggers       = GB(triggers, 0, 7);
+			group->g.random.cmp_mode       = HASBIT(triggers, 7) ? RSG_CMP_ALL : RSG_CMP_ANY;
+			group->g.random.lowest_randbit = grf_load_byte(&buf);
+			group->g.random.num_groups     = grf_load_byte(&buf);
+			group->g.random.groups = calloc(group->g.random.num_groups, sizeof(*group->g.random.groups));
+
+			check_length(bufend - buf, 2 * group->g.random.num_groups, "NewSpriteGroup (Randomized) (2)");
+
+			for (i = 0; i < group->g.random.num_groups; i++) {
+				group->g.random.groups[i] = GetGroupFromGroupID(setid, type, grf_load_word(&buf));
 			}
 
 			break;
 		}
 
+		/* Neither a variable or randomized sprite group... must be a real group */
 		default:
 		{
-			RealSpriteGroup *rg;
-			byte num_loaded  = type;
-			byte num_loading = grf_load_byte(&buf);
-			uint i;
-
-			if (_cur_grffile->spriteset_start == 0) {
-				grfmsg(GMS_ERROR, "NewSpriteGroup: No sprite set to work on! Skipping.");
-				return;
+			switch (feature) {
+				case GSF_TRAIN:
+				case GSF_ROAD:
+				case GSF_SHIP:
+				case GSF_AIRCRAFT:
+				case GSF_STATION:
+				{
+					byte sprites     = _cur_grffile->spriteset_numents;
+					byte num_loaded  = type;
+					byte num_loading = grf_load_byte(&buf);
+					uint i;
+
+					if (_cur_grffile->spriteset_start == 0) {
+						grfmsg(GMS_ERROR, "NewSpriteGroup: No sprite set to work on! Skipping.");
+						return;
+					}
+
+					if (_cur_grffile->first_spriteset == 0) {
+						DEBUG(grf, 6) ("NewSpriteGroup: Setting 0x%X as first Sprite ID", _cur_grffile->spriteset_start);
+						_cur_grffile->first_spriteset = _cur_grffile->spriteset_start;
+					}
+
+					check_length(bufend - buf, 2 * num_loaded + 2 * num_loading, "NewSpriteGroup (Real) (1)");
+
+					group = AllocateSpriteGroup();
+					group->type = SGT_REAL;
+
+					group->g.real.num_loaded  = num_loaded;
+					group->g.real.num_loading = num_loading;
+					if (num_loaded  > 0) group->g.real.loaded  = calloc(num_loaded,  sizeof(*group->g.real.loaded));
+					if (num_loading > 0) group->g.real.loading = calloc(num_loading, sizeof(*group->g.real.loading));
+
+					DEBUG(grf, 6) ("NewSpriteGroup: New SpriteGroup 0x%02X, %u views, %u loaded, %u loading",
+							setid, sprites, num_loaded, num_loading);
+
+					for (i = 0; i < num_loaded; i++) {
+						uint16 spriteid = grf_load_word(&buf);
+						group->g.real.loaded[i] = CreateGroupFromGroupID(setid, type, spriteid, sprites);
+						DEBUG(grf, 8) ("NewSpriteGroup: + rg->loaded[%i]  = subset %u", i, spriteid);
+					}
+
+					for (i = 0; i < num_loading; i++) {
+						uint16 spriteid = grf_load_word(&buf);
+						group->g.real.loading[i] = CreateGroupFromGroupID(setid, type, spriteid, sprites);
+						DEBUG(grf, 8) ("NewSpriteGroup: + rg->loading[%i] = subset %u", i, spriteid);
+					}
+
+					break;
+				}
+
+				/* Loading of Tile Layout and Production Callback groups would happen here */
+				default:
+					grfmsg(GMS_WARN, "NewSpriteGroup: Unsupported feature %d, skipping.", feature);
 			}
-
-			if (_cur_grffile->first_spriteset == 0)
-				_cur_grffile->first_spriteset = _cur_grffile->spriteset_start;
-
-			group = AllocateSpriteGroup();
-			group->type = SGT_REAL;
-			rg = &group->g.real;
-
-			rg->sprites_per_set = _cur_grffile->spriteset_numents;
-			rg->loaded_count  = num_loaded;
-			rg->loading_count = num_loading;
-
-			rg->loaded  = calloc(rg->loaded_count,  sizeof(*rg->loaded));
-			rg->loading = calloc(rg->loading_count, sizeof(*rg->loading));
-
-			DEBUG(grf, 6) ("NewSpriteGroup: New SpriteGroup 0x%02hhx, %u views, %u loaded, %u loading, sprites %u - %u",
-					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 * (num_loaded + num_loading)) - _cur_grffile->sprite_offset);
-
-			for (i = 0; i < num_loaded; i++) {
-				uint16 spriteset_id = grf_load_word(&buf);
-				rg->loaded[i] = CreateGroupFromGroupID(setid, type, spriteset_id, rg->sprites_per_set);
-				DEBUG(grf, 8) ("NewSpriteGroup: + rg->loaded[%i]  = %u (subset %u)", i, rg->loaded[i]->g.result.result, spriteset_id);
-			}
-
-			for (i = 0; i < num_loading; i++) {
-				uint16 spriteset_id = grf_load_word(&buf);
-				rg->loading[i] = CreateGroupFromGroupID(setid, type, spriteset_id, rg->sprites_per_set);
-				DEBUG(grf, 8) ("NewSpriteGroup: + rg->loading[%i] = %u (subset %u)", i, rg->loading[i]->g.result.result, spriteset_id);
-			}
-
-			break;
 		}
 	}
 
--- a/newgrf_engine.c
+++ b/newgrf_engine.c
@@ -208,16 +208,18 @@
 			const DeterministicSpriteGroup *dsg = &spritegroup->g.determ;
 			const SpriteGroup *target;
 			int value = -1;
+			// XXX Temporary support
+			byte variable = dsg->adjusts[0].variable;
 
-			//debug("[%p] Having fun resolving variable %x", veh, dsg->variable);
-			if (dsg->variable == 0x0C) {
+			//debug("[%p] Having fun resolving variable %x", veh, variable);
+			if (variable == 0x0C) {
 				/* Callback ID */
 				value = callback_info & 0xFF;
-			} else if (dsg->variable == 0x10) {
+			} else if (variable == 0x10) {
 				value = (callback_info >> 8) & 0xFF;
-			} else if ((dsg->variable >> 6) == 0) {
+			} else if ((variable >> 6) == 0) {
 				/* General property */
-				value = GetDeterministicSpriteValue(dsg->variable);
+				value = GetDeterministicSpriteValue(variable);
 			} else {
 				/* Vehicle-specific property. */
 
@@ -240,18 +242,18 @@
 						veh = GetFirstVehicleInChain(veh);
 				}
 
-				if (dsg->variable == 0x40 || dsg->variable == 0x41) {
+				if (variable == 0x40 || variable == 0x41) {
 					if (veh->type == VEH_Train) {
 						const Vehicle *u = GetFirstVehicleInChain(veh);
 						byte chain_before = 0, chain_after = 0;
 
 						while (u != veh) {
 							chain_before++;
-							if (dsg->variable == 0x41 && u->engine_type != veh->engine_type)
+							if (variable == 0x41 && u->engine_type != veh->engine_type)
 								chain_before = 0;
 							u = u->next;
 						}
-						while (u->next != NULL && (dsg->variable == 0x40 || u->next->engine_type == veh->engine_type)) {
+						while (u->next != NULL && (variable == 0x40 || u->next->engine_type == veh->engine_type)) {
 							chain_after++;
 							u = u->next;
 						};
@@ -265,7 +267,7 @@
 				} else {
 					// TTDPatch runs on little-endian arch;
 					// Variable is 0x80 + offset in TTD's vehicle structure
-					switch (dsg->variable - 0x80) {
+					switch (variable - 0x80) {
 #define veh_prop(id_, value_) case (id_): value = (value_); break
 						veh_prop(0x00, veh->type);
 						veh_prop(0x01, MapOldSubType(veh));
@@ -347,7 +349,7 @@
 #undef veh_prop
 
 						// Handle vehicle specific properties.
-						default: value = VehicleSpecificProperty(veh, dsg->variable - 0x80); break;
+						default: value = VehicleSpecificProperty(veh, variable - 0x80); break;
 					}
 				}
 			}
@@ -411,7 +413,6 @@
 	byte loaded = 0;
 	bool in_motion = 0;
 	int totalsets, spriteset;
-	int r;
 
 	if (v != NULL) {
 		int capacity = v->cargo_cap;
@@ -444,16 +445,11 @@
 	assert(group->type == SGT_REAL);
 	rsg = &group->g.real;
 
-	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;
-	}
+	// This group is empty. This function users should therefore
+	// look up the sprite number in _engine_original_sprites.
+	if (rsg->num_loaded == 0 || rsg->num_loading == 0) return 0;
 
-	assert(rsg->sprites_per_set <= 8);
-	direction %= rsg->sprites_per_set;
-
-	totalsets = in_motion ? rsg->loaded_count : rsg->loading_count;
+	totalsets = in_motion ? rsg->num_loaded : rsg->num_loading;
 
 	// My aim here is to make it possible to visually determine absolutely
 	// empty and totally full vehicles. --pasky
@@ -470,8 +466,10 @@
 			spriteset--;
 	}
 
-	r = (in_motion ? rsg->loaded[spriteset]->g.result.result : rsg->loading[spriteset]->g.result.result) + direction;
-	return r;
+	group = in_motion ? rsg->loaded[spriteset] : rsg->loading[spriteset];
+	if (group->type != SGT_RESULT) return 0;
+
+	return group->g.result.sprite + (direction % group->g.result.num_sprites);
 }
 
 /**
--- a/newgrf_spritegroup.c
+++ b/newgrf_spritegroup.c
@@ -29,6 +29,7 @@
 				break;
 
 			case SGT_DETERMINISTIC:
+				free(group->g.determ.adjusts);
 				free(group->g.determ.ranges);
 				break;
 
--- a/newgrf_spritegroup.h
+++ b/newgrf_spritegroup.h
@@ -6,9 +6,10 @@
 
 typedef struct SpriteGroup SpriteGroup;
 
+
+/* 'Real' sprite groups contain a list of other result or callback sprite
+ * groups. */
 typedef struct RealSpriteGroup {
-	byte sprites_per_set; // means number of directions - 4 or 8
-
 	// Loaded = in motion, loading = not moving
 	// Each group contains several spritesets, for various loading stages
 
@@ -16,53 +17,81 @@
 	// with small amount of cargo whilst loading is for stations with a lot
 	// of da stuff.
 
-	byte loaded_count;     ///< Number of loaded groups
+	byte num_loaded;       ///< Number of loaded groups
+	byte num_loading;      ///< Number of loading groups
 	SpriteGroup **loaded;  ///< List of loaded groups (can be SpriteIDs or Callback results)
-	byte loading_count;    ///< Number of loading groups
 	SpriteGroup **loading; ///< List of loading groups (can be SpriteIDs or Callback results)
 } RealSpriteGroup;
 
 /* Shared by deterministic and random groups. */
-typedef enum VarSpriteGroupScope {
+typedef enum VarSpriteGroupScopes {
 	VSG_SCOPE_SELF,
 	// Engine of consists for vehicles, city for stations.
 	VSG_SCOPE_PARENT,
 } VarSpriteGroupScope;
 
-typedef struct DeterministicSpriteGroupRanges DeterministicSpriteGroupRanges;
+typedef enum DeterministicSpriteGroupSizes {
+	DSG_SIZE_BYTE,
+	DSG_SIZE_WORD,
+	DSG_SIZE_DWORD,
+} DeterministicSpriteGroupSize;
+
+typedef enum DeterministicSpriteGroupAdjustTypes {
+	DSGA_TYPE_NONE,
+	DSGA_TYPE_DIV,
+	DSGA_TYPE_MOD,
+} DeterministicSpriteGroupAdjustType;
 
-typedef enum DeterministicSpriteGroupOperation {
-	DSG_OP_NONE,
-	DSG_OP_DIV,
-	DSG_OP_MOD,
-} DeterministicSpriteGroupOperation;
+typedef enum DeterministicSpriteGroupAdjustOperations {
+	DSGA_OP_ADD,  // a + b
+	DSGA_OP_SUB,  // a - b
+	DSGA_OP_SMIN, // (signed) min(a, b)
+	DSGA_OP_SMAX, // (signed) max(a, b)
+	DSGA_OP_UMIN, // (unsigned) min(a, b)
+	DSGA_OP_UMAX, // (unsigned) max(a, b)
+	DSGA_OP_SDIV, // (signed) a / b
+	DSGA_OP_SMOD, // (signed) a % b
+	DSGA_OP_UDIV, // (unsigned) a / b
+	DSGA_OP_UMOD, // (unsigned) a & b
+	DSGA_OP_MUL,  // a * b
+	DSGA_OP_AND,  // a & b
+	DSGA_OP_OR,   // a | b
+	DSGA_OP_XOR,  // a ^ b
+} DeterministicSpriteGroupAdjustOperation;
+
 
-typedef struct DeterministicSpriteGroupRange DeterministicSpriteGroupRange;
+typedef struct DeterministicSpriteGroupAdjust {
+	DeterministicSpriteGroupAdjustOperation operation;
+	DeterministicSpriteGroupAdjustType type;
+	byte variable;
+	byte parameter; ///< Used for variables between 0x60 and 0x7F inclusive.
+	byte shift_num;
+	uint32 and_mask;
+	uint32 add_val;
+	uint32 divmod_val;
+} DeterministicSpriteGroupAdjust;
+
+
+typedef struct DeterministicSpriteGroupRange {
+	SpriteGroup *group;
+	uint32 low;
+	uint32 high;
+} DeterministicSpriteGroupRange;
+
 
 typedef struct DeterministicSpriteGroup {
-	// Take this variable:
 	VarSpriteGroupScope var_scope;
-	byte variable;
-	byte parameter; ///< Used for variables between 0x60 and 0x7F inclusive.
-
-	// Do this with it:
-	byte shift_num;
-	byte and_mask;
-
-	// Then do this with it:
-	DeterministicSpriteGroupOperation operation;
-	byte add_val;
-	byte divmod_val;
-
-	// And apply it to this:
+	DeterministicSpriteGroupSize size;
+	byte num_adjusts;
 	byte num_ranges;
+	DeterministicSpriteGroupAdjust *adjusts;
 	DeterministicSpriteGroupRange *ranges; // Dynamically allocated
 
 	// Dynamically allocated, this is the sole owner
 	SpriteGroup *default_group;
 } DeterministicSpriteGroup;
 
-typedef enum RandomizedSpriteGroupCompareMode {
+typedef enum RandomizedSpriteGroupCompareModes {
 	RSG_CMP_ANY,
 	RSG_CMP_ALL,
 } RandomizedSpriteGroupCompareMode;
@@ -83,15 +112,22 @@
 	SpriteGroup **groups;
 } RandomizedSpriteGroup;
 
+
+/* This contains a callback result. A failed callback has a value of
+ * CALLBACK_FAILED */
 typedef struct CallbackResultSpriteGroup {
 	uint16 result;
 } CallbackResultSpriteGroup;
 
+
+/* A result sprite group returns the first SpriteID and the number of
+ * sprites in the set */
 typedef struct ResultSpriteGroup {
-	uint16 result;
-	byte sprites;
+	SpriteID sprite;
+	byte num_sprites;
 } ResultSpriteGroup;
 
+/* List of different sprite group types */
 typedef enum SpriteGroupType {
 	SGT_INVALID,
 	SGT_REAL,
@@ -101,6 +137,7 @@
 	SGT_RESULT,
 } SpriteGroupType;
 
+/* Common wrapper for all the different sprite group types */
 struct SpriteGroup {
 	SpriteGroupType type;
 
@@ -113,12 +150,6 @@
 	} g;
 };
 
-struct DeterministicSpriteGroupRange {
-	SpriteGroup *group;
-	byte low;
-	byte high;
-};
-
 
 SpriteGroup *AllocateSpriteGroup(void);
 void InitializeSpriteGroupPool(void);
--- a/newgrf_station.c
+++ b/newgrf_station.c
@@ -169,10 +169,11 @@
 			const DeterministicSpriteGroup *dsg = &spg->g.determ;
 			SpriteGroup *target;
 			int value = -1;
+			byte variable = dsg->adjusts[0].variable;
 
-			if ((dsg->variable >> 6) == 0) {
+			if ((variable >> 6) == 0) {
 				/* General property */
-				value = GetDeterministicSpriteValue(dsg->variable);
+				value = GetDeterministicSpriteValue(variable);
 			} else {
 				if (st == NULL) {
 					/* We are in a build dialog of something,
@@ -192,7 +193,7 @@
 					/* TODO: Town structure. */
 
 				} else /* VSG_SELF */ {
-					if (dsg->variable == 0x40 || dsg->variable == 0x41) {
+					if (variable == 0x40 || variable == 0x41) {
 						/* FIXME: This is ad hoc only
 						 * for waypoints. */
 						value = 0x01010000;
@@ -200,7 +201,7 @@
 						/* TODO: Only small fraction done. */
 						// TTDPatch runs on little-endian arch;
 						// Variable is 0x70 + offset in the TTD's station structure
-						switch (dsg->variable - 0x70) {
+						switch (variable - 0x70) {
 							case 0x80: value = st->facilities;             break;
 							case 0x81: value = st->airport_type;           break;
 							case 0x82: value = st->truck_stops->status;    break;
@@ -229,10 +230,8 @@
 	const RealSpriteGroup *rsg = ResolveStationSpriteGroup(spec->spritegroup[ctype], st);
 	if (rsg == NULL) return 0;
 
-	if (rsg->sprites_per_set != 0) {
-		if (rsg->loading_count != 0) return rsg->loading[0]->g.result.result;
-		if (rsg->loaded_count != 0) return rsg->loaded[0]->g.result.result;
-	}
+	if (rsg->num_loading != 0) return rsg->loading[0]->g.result.sprite;
+	if (rsg->num_loaded  != 0) return rsg->loaded[0]->g.result.sprite;
 
 	DEBUG(grf, 6)("Custom station 0x%08x::0x%02x has no sprites associated.",
 		spec->grfid, spec->localidx);
--- a/sprite.c
+++ b/sprite.c
@@ -11,27 +11,27 @@
 {
 	int i;
 
-	value >>= dsg->shift_num; // This should bring us to the byte range.
-	value &= dsg->and_mask;
+	value >>= dsg->adjusts[0].shift_num; // This should bring us to the byte range.
+	value &= dsg->adjusts[0].and_mask;
 
-	if (dsg->operation != DSG_OP_NONE)
-		value += (signed char) dsg->add_val;
+	if (dsg->adjusts[0].operation != DSGA_TYPE_NONE)
+		value += (signed char) dsg->adjusts[0].add_val;
 
-	switch (dsg->operation) {
-		case DSG_OP_DIV:
-			value /= (signed char) dsg->divmod_val;
+	switch (dsg->adjusts[0].type) {
+		case DSGA_TYPE_DIV:
+			value /= (signed char) dsg->adjusts[0].divmod_val;
 			break;
-		case DSG_OP_MOD:
-			value %= (signed char) dsg->divmod_val;
+		case DSGA_TYPE_MOD:
+			value %= (signed char) dsg->adjusts[0].divmod_val;
 			break;
-		case DSG_OP_NONE:
+		case DSGA_TYPE_NONE:
 			break;
 	}
 
 	for (i = 0; i < dsg->num_ranges; i++) {
 		DeterministicSpriteGroupRange *range = &dsg->ranges[i];
 
-		if (range->low <= value && value <= range->high)
+		if (range->low <= (uint32)value && (uint32)value <= range->high)
 			return range->group;
 	}