Changeset - r20058:431b83660079
[Not reviewed]
master
0 1 0
rubidium - 11 years ago 2013-02-17 20:16:49
rubidium@openttd.org
(svn r25016) -Fix [FS#5476]: integer overflow in UpdateStationRating (fonsinchen)
1 file changed with 3 insertions and 1 deletions:
0 comments (0 inline, 0 general)
src/station_cmd.cpp
Show inline comments
 
@@ -3214,193 +3214,195 @@ static void UpdateStationRating(Station 
 
		/* Only change the rating if we are moving this cargo */
 
		if (HasBit(ge->acceptance_pickup, GoodsEntry::GES_PICKUP)) {
 
			byte_inc_sat(&ge->time_since_pickup);
 

	
 
			bool skip = false;
 
			int rating = 0;
 
			uint waiting = ge->cargo.Count();
 

	
 
			if (HasBit(cs->callback_mask, CBM_CARGO_STATION_RATING_CALC)) {
 
				/* Perform custom station rating. If it succeeds the speed, days in transit and
 
				 * waiting cargo ratings must not be executed. */
 

	
 
				/* NewGRFs expect last speed to be 0xFF when no vehicle has arrived yet. */
 
				uint last_speed = ge->HasVehicleEverTriedLoading() ? ge->last_speed : 0xFF;
 

	
 
				uint32 var18 = min(ge->time_since_pickup, 0xFF) | (min(waiting, 0xFFFF) << 8) | (min(last_speed, 0xFF) << 24);
 
				/* Convert to the 'old' vehicle types */
 
				uint32 var10 = (st->last_vehicle_type == VEH_INVALID) ? 0x0 : (st->last_vehicle_type + 0x10);
 
				uint16 callback = GetCargoCallback(CBID_CARGO_STATION_RATING_CALC, var10, var18, cs);
 
				if (callback != CALLBACK_FAILED) {
 
					skip = true;
 
					rating = GB(callback, 0, 14);
 

	
 
					/* Simulate a 15 bit signed value */
 
					if (HasBit(callback, 14)) rating -= 0x4000;
 
				}
 
			}
 

	
 
			if (!skip) {
 
				int b = ge->last_speed - 85;
 
				if (b >= 0) rating += b >> 2;
 

	
 
				byte waittime = ge->time_since_pickup;
 
				if (st->last_vehicle_type == VEH_SHIP) waittime >>= 2;
 
				(waittime > 21) ||
 
				(rating += 25, waittime > 12) ||
 
				(rating += 25, waittime > 6) ||
 
				(rating += 45, waittime > 3) ||
 
				(rating += 35, true);
 

	
 
				(rating -= 90, waiting > 1500) ||
 
				(rating += 55, waiting > 1000) ||
 
				(rating += 35, waiting > 600) ||
 
				(rating += 10, waiting > 300) ||
 
				(rating += 20, waiting > 100) ||
 
				(rating += 10, true);
 
			}
 

	
 
			if (Company::IsValidID(st->owner) && HasBit(st->town->statues, st->owner)) rating += 26;
 

	
 
			byte age = ge->last_age;
 
			(age >= 3) ||
 
			(rating += 10, age >= 2) ||
 
			(rating += 10, age >= 1) ||
 
			(rating += 13, true);
 

	
 
			{
 
				int or_ = ge->rating; // old rating
 

	
 
				/* only modify rating in steps of -2, -1, 0, 1 or 2 */
 
				ge->rating = rating = or_ + Clamp(Clamp(rating, 0, 255) - or_, -2, 2);
 

	
 
				/* if rating is <= 64 and more than 200 items waiting,
 
				 * remove some random amount of goods from the station */
 
				if (rating <= 64 && waiting >= 200) {
 
					int dec = Random() & 0x1F;
 
					if (waiting < 400) dec &= 7;
 
					waiting -= dec + 1;
 
					waiting_changed = true;
 
				}
 

	
 
				/* if rating is <= 127 and there are any items waiting, maybe remove some goods. */
 
				if (rating <= 127 && waiting != 0) {
 
					uint32 r = Random();
 
					if (rating <= (int)GB(r, 0, 7)) {
 
						/* Need to have int, otherwise it will just overflow etc. */
 
						waiting = max((int)waiting - (int)GB(r, 8, 2) - 1, 0);
 
						waiting_changed = true;
 
					}
 
				}
 

	
 
				/* At some point we really must cap the cargo. Previously this
 
				 * was a strict 4095, but now we'll have a less strict, but
 
				 * increasingly aggressive truncation of the amount of cargo. */
 
				static const uint WAITING_CARGO_THRESHOLD  = 1 << 12;
 
				static const uint WAITING_CARGO_CUT_FACTOR = 1 <<  6;
 
				static const uint MAX_WAITING_CARGO        = 1 << 15;
 

	
 
				if (waiting > WAITING_CARGO_THRESHOLD) {
 
					uint difference = waiting - WAITING_CARGO_THRESHOLD;
 
					waiting -= (difference / WAITING_CARGO_CUT_FACTOR);
 

	
 
					waiting = min(waiting, MAX_WAITING_CARGO);
 
					waiting_changed = true;
 
				}
 

	
 
				if (waiting_changed) ge->cargo.Truncate(ge->cargo.Count() - waiting);
 
				if (waiting_changed && waiting < ge->cargo.Count()) {
 
					ge->cargo.Truncate(ge->cargo.Count() - waiting);
 
				}
 
			}
 
		}
 
	}
 

	
 
	StationID index = st->index;
 
	if (waiting_changed) {
 
		SetWindowDirty(WC_STATION_VIEW, index); // update whole window
 
	} else {
 
		SetWindowWidgetDirty(WC_STATION_VIEW, index, WID_SV_ACCEPT_RATING_LIST); // update only ratings list
 
	}
 
}
 

	
 
/* called for every station each tick */
 
static void StationHandleSmallTick(BaseStation *st)
 
{
 
	if ((st->facilities & FACIL_WAYPOINT) != 0 || !st->IsInUse()) return;
 

	
 
	byte b = st->delete_ctr + 1;
 
	if (b >= STATION_RATING_TICKS) b = 0;
 
	st->delete_ctr = b;
 

	
 
	if (b == 0) UpdateStationRating(Station::From(st));
 
}
 

	
 
void OnTick_Station()
 
{
 
	if (_game_mode == GM_EDITOR) return;
 

	
 
	BaseStation *st;
 
	FOR_ALL_BASE_STATIONS(st) {
 
		StationHandleSmallTick(st);
 

	
 
		/* Run STATION_ACCEPTANCE_TICKS = 250 tick interval trigger for station animation.
 
		 * Station index is included so that triggers are not all done
 
		 * at the same time. */
 
		if ((_tick_counter + st->index) % STATION_ACCEPTANCE_TICKS == 0) {
 
			/* Stop processing this station if it was deleted */
 
			if (!StationHandleBigTick(st)) continue;
 
			TriggerStationAnimation(st, st->xy, SAT_250_TICKS);
 
			if (Station::IsExpected(st)) AirportAnimationTrigger(Station::From(st), AAT_STATION_250_TICKS);
 
		}
 
	}
 
}
 

	
 
/** Monthly loop for stations. */
 
void StationMonthlyLoop()
 
{
 
	Station *st;
 

	
 
	FOR_ALL_STATIONS(st) {
 
		for (CargoID i = 0; i < NUM_CARGO; i++) {
 
			GoodsEntry *ge = &st->goods[i];
 
			SB(ge->acceptance_pickup, GoodsEntry::GES_LAST_MONTH, 1, GB(ge->acceptance_pickup, GoodsEntry::GES_CURRENT_MONTH, 1));
 
			ClrBit(ge->acceptance_pickup, GoodsEntry::GES_CURRENT_MONTH);
 
		}
 
	}
 
}
 

	
 

	
 
void ModifyStationRatingAround(TileIndex tile, Owner owner, int amount, uint radius)
 
{
 
	Station *st;
 

	
 
	FOR_ALL_STATIONS(st) {
 
		if (st->owner == owner &&
 
				DistanceManhattan(tile, st->xy) <= radius) {
 
			for (CargoID i = 0; i < NUM_CARGO; i++) {
 
				GoodsEntry *ge = &st->goods[i];
 

	
 
				if (ge->acceptance_pickup != 0) {
 
					ge->rating = Clamp(ge->rating + amount, 0, 255);
 
				}
 
			}
 
		}
 
	}
 
}
 

	
 
static uint UpdateStationWaiting(Station *st, CargoID type, uint amount, SourceType source_type, SourceID source_id)
 
{
 
	/* We can't allocate a CargoPacket? Then don't do anything
 
	 * at all; i.e. just discard the incoming cargo. */
 
	if (!CargoPacket::CanAllocateItem()) return 0;
 

	
 
	GoodsEntry &ge = st->goods[type];
 
	amount += ge.amount_fract;
 
	ge.amount_fract = GB(amount, 0, 8);
 

	
 
	amount >>= 8;
 
	/* No new "real" cargo item yet. */
 
	if (amount == 0) return 0;
 

	
 
	ge.cargo.Append(new CargoPacket(st->index, st->xy, amount, source_type, source_id));
 

	
 
	if (!HasBit(ge.acceptance_pickup, GoodsEntry::GES_PICKUP)) {
 
		InvalidateWindowData(WC_STATION_LIST, st->index);
 
		SetBit(ge.acceptance_pickup, GoodsEntry::GES_PICKUP);
0 comments (0 inline, 0 general)