Changeset - r9968:45fe01d99177
[Not reviewed]
master
0 1 0
frosch - 16 years ago 2008-08-21 20:42:45
frosch@openttd.org
(svn r14125) -Fix [FS#2237]: Segfault when autoreplace failed very early.
Note: Proper indenting in next commit.
1 file changed with 12 insertions and 9 deletions:
0 comments (0 inline, 0 general)
src/autoreplace_cmd.cpp
Show inline comments
 
@@ -346,55 +346,55 @@ static CommandCost ReplaceChain(Vehicle 
 
		int num_units = 0; ///< Number of units in the chain
 
		for (Vehicle *w = old_head; w != NULL; w = GetNextUnit(w)) num_units++;
 

	
 
		Vehicle **old_vehs = CallocT<Vehicle *>(num_units); ///< Will store vehicles of the old chain in their order
 
		Vehicle **new_vehs = CallocT<Vehicle *>(num_units); ///< New vehicles corresponding to old_vehs or NULL if no replacement
 
		Money *new_costs = MallocT<Money>(num_units);       ///< Costs for buying and refitting the new vehicles
 

	
 
		/* Collect vehicles and build replacements
 
		 * Note: The replacement vehicles can only successfully build as long as the old vehicles are still in their chain */
 
		int i;
 
		Vehicle *w;
 
		for (w = old_head, i = 0; w != NULL; w = GetNextUnit(w), i++) {
 
			assert(i < num_units);
 
			old_vehs[i] = w;
 

	
 
			CommandCost ret = BuildReplacementVehicle(old_vehs[i], &new_vehs[i]);
 
			cost.AddCost(ret);
 
			if (cost.Failed()) break;
 

	
 
			new_costs[i] = ret.GetCost();
 
			if (new_vehs[i] != NULL) *nothing_to_do = false;
 
		}
 
		Vehicle *new_head = (new_vehs[0] != NULL ? new_vehs[0] : old_vehs[0]);
 

	
 
		/* Note: When autoreplace has already failed here, old_vehs[] is not completely initialized. But it is also not needed. */
 
		if (cost.Succeeded()) {
 
		/* Separate the head, so we can start constructing the new chain */
 
		if (cost.Succeeded()) {
 
			Vehicle *second = GetNextUnit(old_head);
 
			if (second != NULL) cost.AddCost(MoveVehicle(second, NULL, DC_EXEC | DC_AUTOREPLACE, true));
 

	
 
			assert(GetNextUnit(new_head) == NULL);
 
		}
 

	
 
		/* Append engines to the new chain
 
		 * We do this from back to front, so that the head of the temporary vehicle chain does not change all the time.
 
		 * OTOH the vehicle attach callback is more expensive this way :s */
 
		Vehicle *last_engine = NULL; ///< Shall store the last engine unit after this step
 
		if (cost.Succeeded()) {
 
			for (int i = num_units - 1; i > 0; i--) {
 
				Vehicle *append = (new_vehs[i] != NULL ? new_vehs[i] : old_vehs[i]);
 

	
 
				if (RailVehInfo(append->engine_type)->railveh_type == RAILVEH_WAGON) continue;
 

	
 
				if (last_engine == NULL) last_engine = append;
 
				cost.AddCost(MoveVehicle(append, new_head, DC_EXEC, false));
 
				if (cost.Failed()) break;
 
			}
 
			if (last_engine == NULL) last_engine = new_head;
 
		}
 

	
 
		/* When wagon removal is enabled and the new engines without any wagons are already longer than the old, we have to fail */
 
		if (cost.Succeeded() && wagon_removal && new_head->u.rail.cached_total_length > old_total_length) cost = CommandCost(STR_TRAIN_TOO_LONG_AFTER_REPLACEMENT);
 

	
 
		/* Append/insert wagons into the new vehicle chain
 
		 * We do this from back to front, so we can stop when wagon removal or maximum train length (i.e. from mammoth-train setting) is triggered.
 
		 */
 
@@ -447,65 +447,68 @@ static CommandCost ReplaceChain(Vehicle 
 

	
 
		if (cost.Succeeded()) {
 
			/* Success ! */
 
			if ((flags & DC_EXEC) != 0 && new_head != old_head) {
 
				*chain = new_head;
 
			}
 

	
 
			/* Transfer cargo of old vehicles and sell them*/
 
			for (int i = 0; i < num_units; i++) {
 
				Vehicle *w = old_vehs[i];
 
				/* Is the vehicle again part of the new chain?
 
				 * Note: We cannot test 'new_vehs[i] != NULL' as wagon removal might cause to remove both */
 
				if (w->First() == new_head) continue;
 

	
 
				if ((flags & DC_EXEC) != 0) TransferCargo(w, new_head);
 

	
 
				cost.AddCost(DoCommand(0, w->index, 0, flags, GetCmdSellVeh(w)));
 
				if ((flags & DC_EXEC) != 0) {
 
					old_vehs[i] = NULL;
 
					if (i == 0) old_head = NULL;
 
				}
 
			}
 
		}
 

	
 
		/* If we are not in DC_EXEC undo everything */
 
		/* If we are not in DC_EXEC undo everything, i.e. rearrange old vehicles.
 
		 * We do this from back to front, so that the head of the temporary vehicle chain does not change all the time.
 
		 * Note: The vehicle attach callback is disabled here :) */
 
		if ((flags & DC_EXEC) == 0) {
 
			/* Separate the head, so we can reattach the old vehicles */
 
			Vehicle *second = GetNextUnit(old_head);
 
			if (second != NULL) MoveVehicle(second, NULL, DC_EXEC | DC_AUTOREPLACE, true);
 

	
 
			assert(GetNextUnit(old_head) == NULL);
 

	
 
			/* Rearrange old vehicles and sell new
 
			 * We do this from back to front, so that the head of the temporary vehicle chain does not change all the time.
 
			 * Note: The vehicle attach callback is disabled here :) */
 

	
 
			for (int i = num_units - 1; i >= 0; i--) {
 
				if (i > 0) {
 
			for (int i = num_units - 1; i > 0; i--) {
 
					CommandCost ret = MoveVehicle(old_vehs[i], old_head, DC_EXEC | DC_AUTOREPLACE, false);
 
					assert(ret.Succeeded());
 
				}
 
		}
 
		}
 

	
 
		/* Finally undo buying of new vehicles */
 
		if ((flags & DC_EXEC) == 0) {
 
			for (int i = num_units - 1; i >= 0; i--) {
 
				if (new_vehs[i] != NULL) {
 
					DoCommand(0, new_vehs[i]->index, 0, DC_EXEC, GetCmdSellVeh(new_vehs[i]));
 
					new_vehs[i] = NULL;
 
				}
 
			}
 
		}
 

	
 
		free(old_vehs);
 
		free(new_vehs);
 
		free(new_costs);
 
	} else {
 
		/* Build and refit replacement vehicle */
 
		Vehicle *new_head = NULL;
 
		cost.AddCost(BuildReplacementVehicle(old_head, &new_head));
 

	
 
		/* Was a new vehicle constructed? */
 
		if (cost.Succeeded() && new_head != NULL) {
 
			*nothing_to_do = false;
 

	
 
			/* The new vehicle is constructed, now take over orders and everything... */
 
			cost.AddCost(CopyHeadSpecificThings(old_head, new_head, flags));
 

	
 
			if (cost.Succeeded()) {
 
				/* The new vehicle is constructed, now take over cargo */
0 comments (0 inline, 0 general)