Beispiel #1
0
int OTTDStringCompare(const char *s1, const char *s2)
{
	typedef int (WINAPI *PFNCOMPARESTRINGEX)(LPCWSTR, DWORD, LPCWCH, int, LPCWCH, int, LPVOID, LPVOID, LPARAM);
	static PFNCOMPARESTRINGEX _CompareStringEx = NULL;
	static bool first_time = true;

#ifndef SORT_DIGITSASNUMBERS
#	define SORT_DIGITSASNUMBERS 0x00000008  // use digits as numbers sort method
#endif
#ifndef LINGUISTIC_IGNORECASE
#	define LINGUISTIC_IGNORECASE 0x00000010 // linguistically appropriate 'ignore case'
#endif

	if (first_time) {
		_CompareStringEx = (PFNCOMPARESTRINGEX)GetProcAddress(GetModuleHandle(_T("Kernel32")), "CompareStringEx");
		first_time = false;
	}

	if (_CompareStringEx != NULL) {
		/* CompareStringEx takes UTF-16 strings, even in ANSI-builds. */
		int len_s1 = MultiByteToWideChar(CP_UTF8, 0, s1, -1, NULL, 0);
		int len_s2 = MultiByteToWideChar(CP_UTF8, 0, s2, -1, NULL, 0);

		if (len_s1 != 0 && len_s2 != 0) {
			LPWSTR str_s1 = AllocaM(WCHAR, len_s1);
			LPWSTR str_s2 = AllocaM(WCHAR, len_s2);

			MultiByteToWideChar(CP_UTF8, 0, s1, -1, str_s1, len_s1);
			MultiByteToWideChar(CP_UTF8, 0, s2, -1, str_s2, len_s2);

			int result = _CompareStringEx(_cur_iso_locale, LINGUISTIC_IGNORECASE | SORT_DIGITSASNUMBERS, str_s1, -1, str_s2, -1, NULL, NULL, 0);
			if (result != 0) return result;
		}
	}

	TCHAR s1_buf[512], s2_buf[512];
	convert_to_fs(s1, s1_buf, lengthof(s1_buf));
	convert_to_fs(s2, s2_buf, lengthof(s2_buf));

	return CompareString(MAKELCID(_current_language->winlangid, SORT_DEFAULT), NORM_IGNORECASE, s1_buf, -1, s2_buf, -1);
}
Beispiel #2
0
/**
 * Convert from OpenTTD's encoding to that of the environment in
 * UNICODE. OpenTTD encoding is UTF8, local is wide
 * @param name pointer to a valid string that will be converted
 * @param system_buf pointer to a valid wide-char buffer that will receive the
 * converted string
 * @param buflen length in wide characters of the receiving buffer
 * @param console_cp convert to the console encoding instead of the normal system encoding.
 * @return pointer to system_buf. If conversion fails the string is of zero-length
 */
TCHAR *convert_to_fs(const char *name, TCHAR *system_buf, size_t buflen, bool console_cp)
{
#if defined(UNICODE)
	int len = MultiByteToWideChar(CP_UTF8, 0, name, -1, system_buf, (int)buflen);
	if (len == 0) system_buf[0] = '\0';
#else
	int len = MultiByteToWideChar(CP_UTF8, 0, name, -1, NULL, 0);
	if (len == 0) {
		system_buf[0] = '\0';
		return system_buf;
	}

	WCHAR *wide_buf = AllocaM(WCHAR, len);
	MultiByteToWideChar(CP_UTF8, 0, name, -1, wide_buf, len);

	len = WideCharToMultiByte(console_cp ? CP_OEMCP : CP_ACP, 0, wide_buf, len, system_buf, (int)buflen, NULL, NULL);
	if (len == 0) system_buf[0] = '\0';
#endif

	return system_buf;
}
Beispiel #3
0
/**
 * Convert to OpenTTD's encoding from that of the environment in
 * UNICODE. OpenTTD encoding is UTF8, local is wide
 * @param name pointer to a valid string that will be converted
 * @param utf8_buf pointer to a valid buffer that will receive the converted string
 * @param buflen length in characters of the receiving buffer
 * @return pointer to utf8_buf. If conversion fails the string is of zero-length
 */
char *convert_from_fs(const TCHAR *name, char *utf8_buf, size_t buflen)
{
#if defined(UNICODE)
	const WCHAR *wide_buf = name;
#else
	/* Convert string from the local codepage to UTF-16. */
	int wide_len = MultiByteToWideChar(CP_ACP, 0, name, -1, NULL, 0);
	if (wide_len == 0) {
		utf8_buf[0] = '\0';
		return utf8_buf;
	}

	WCHAR *wide_buf = AllocaM(WCHAR, wide_len);
	MultiByteToWideChar(CP_ACP, 0, name, -1, wide_buf, wide_len);
#endif

	/* Convert UTF-16 string to UTF-8. */
	int len = WideCharToMultiByte(CP_UTF8, 0, wide_buf, -1, utf8_buf, (int)buflen, NULL, NULL);
	if (len == 0) utf8_buf[0] = '\0';

	return utf8_buf;
}
Beispiel #4
0
bool SpriteLoaderGrf::LoadSprite(SpriteLoader::Sprite *sprite, uint8 file_slot, size_t file_pos, SpriteType sprite_type)
{
	/* Open the right file and go to the correct position */
	FioSeekToFile(file_slot, file_pos);

	/* Read the size and type */
	int num = FioReadWord();
	byte type = FioReadByte();

	/* Type 0xFF indicates either a colourmap or some other non-sprite info; we do not handle them here */
	if (type == 0xFF) return false;

	sprite->height = FioReadByte();
	sprite->width  = FioReadWord();
	sprite->x_offs = FioReadWord();
	sprite->y_offs = FioReadWord();

	/* 0x02 indicates it is a compressed sprite, so we can't rely on 'num' to be valid.
	 *  In case it is uncompressed, the size is 'num' - 8 (header-size). */
	num = (type & 0x02) ? sprite->width * sprite->height : num - 8;

	byte *dest_orig = AllocaM(byte, num);
	byte *dest = dest_orig;
	const int dest_size = num;

	/* Read the file, which has some kind of compression */
	while (num > 0) {
		int8 code = FioReadByte();

		if (code >= 0) {
			/* Plain bytes to read */
			int size = (code == 0) ? 0x80 : code;
			num -= size;
			if (num < 0) return WarnCorruptSprite(file_slot, file_pos, __LINE__);
			for (; size > 0; size--) {
				*dest = FioReadByte();
				dest++;
			}
		} else {
			/* Copy bytes from earlier in the sprite */
			const uint data_offset = ((code & 7) << 8) | FioReadByte();
			if (dest - data_offset < dest_orig) return WarnCorruptSprite(file_slot, file_pos, __LINE__);
			int size = -(code >> 3);
			num -= size;
			if (num < 0) return WarnCorruptSprite(file_slot, file_pos, __LINE__);
			for (; size > 0; size--) {
				*dest = *(dest - data_offset);
				dest++;
			}
		}
	}

	if (num != 0) return WarnCorruptSprite(file_slot, file_pos, __LINE__);

	sprite->AllocateData(sprite->width * sprite->height * ZOOM_LVL_BASE * ZOOM_LVL_BASE);

	/* When there are transparency pixels, this format has another trick.. decode it */
	if (type & 0x08) {
		for (int y = 0; y < sprite->height; y++) {
			bool last_item = false;
			/* Look up in the header-table where the real data is stored for this row */
			int offset = (dest_orig[y * 2 + 1] << 8) | dest_orig[y * 2];

			/* Go to that row */
			dest = dest_orig + offset;

			do {
				if (dest + 2 > dest_orig + dest_size) {
					return WarnCorruptSprite(file_slot, file_pos, __LINE__);
				}

				SpriteLoader::CommonPixel *data;
				/* Read the header:
				 *  0 .. 14  - length
				 *  15       - last_item
				 *  16 .. 31 - transparency bytes */
				last_item  = ((*dest) & 0x80) != 0;
				int length =  (*dest++) & 0x7F;
				int skip   =   *dest++;

				data = &sprite->data[y * sprite->width + skip];

				if (skip + length > sprite->width || dest + length > dest_orig + dest_size) {
					return WarnCorruptSprite(file_slot, file_pos, __LINE__);
				}

				for (int x = 0; x < length; x++) {
					switch (sprite_type) {
						case ST_NORMAL: data->m = _palette_remap_grf[file_slot] ? _palmap_w2d[*dest] : *dest; break;
						case ST_FONT:   data->m = min(*dest, 2u); break;
						default:        data->m = *dest; break;
					}
					dest++;
					data++;
				}
			} while (!last_item);
		}
	} else {
		if (dest_size < sprite->width * sprite->height) {
			return WarnCorruptSprite(file_slot, file_pos, __LINE__);
		}

		if (dest_size > sprite->width * sprite->height) {
			static byte warning_level = 0;
			DEBUG(sprite, warning_level, "Ignoring %i unused extra bytes from the sprite from %s at position %i", dest_size - sprite->width * sprite->height, FioGetFilename(file_slot), (int)file_pos);
			warning_level = 6;
		}

		dest = dest_orig;

		for (int i = 0; i < sprite->width * sprite->height; i++) {
			switch (sprite_type) {
				case ST_NORMAL: sprite->data[i].m = _palette_remap_grf[file_slot] ? _palmap_w2d[dest[i]] : dest[i]; break;
				case ST_FONT:   sprite->data[i].m = min(dest[i], 2u); break;
				default:        sprite->data[i].m = dest[i]; break;
			}
		}
	}

	if (ZOOM_LVL_BASE != 1 && sprite_type == ST_NORMAL) {
		/* Simple scaling, back-to-front so that no intermediate buffers are needed. */
		int width  = sprite->width  * ZOOM_LVL_BASE;
		int height = sprite->height * ZOOM_LVL_BASE;
		for (int y = height - 1; y >= 0; y--) {
			for (int x = width - 1; x >= 0; x--) {
				sprite->data[y * width + x] = sprite->data[y / ZOOM_LVL_BASE * sprite->width + x / ZOOM_LVL_BASE];
			}
		}

		sprite->width  *= ZOOM_LVL_BASE;
		sprite->height *= ZOOM_LVL_BASE;
		sprite->x_offs *= ZOOM_LVL_BASE;
		sprite->y_offs *= ZOOM_LVL_BASE;
	}

	/* Make sure to mark all transparent pixels transparent on the alpha channel too */
	for (int i = 0; i < sprite->width * sprite->height; i++) {
		if (sprite->data[i].m != 0) sprite->data[i].a = 0xFF;
	}

	return true;
}
	virtual void DrawWidget(const Rect &r, int widget) const
	{
		const Vehicle *v = this->vehicle;
		int selected = this->sel_index;

		switch (widget) {
			case WID_VT_TIMETABLE_PANEL: {
				int y = r.top + WD_FRAMERECT_TOP;
				int i = this->vscroll->GetPosition();
				VehicleOrderID order_id = (i + 1) / 2;
				bool final_order = false;

				bool rtl = _current_text_dir == TD_RTL;
				SetDParamMaxValue(0, v->GetNumOrders(), 2);
				int index_column_width = GetStringBoundingBox(STR_ORDER_INDEX).width + 2 * GetSpriteSize(rtl ? SPR_ARROW_RIGHT : SPR_ARROW_LEFT).width + 3;
				int middle = rtl ? r.right - WD_FRAMERECT_RIGHT - index_column_width : r.left + WD_FRAMERECT_LEFT + index_column_width;

				const Order *order = v->GetOrder(order_id);
				while (order != NULL) {
					/* Don't draw anything if it extends past the end of the window. */
					if (!this->vscroll->IsVisible(i)) break;

					if (i % 2 == 0) {
						DrawOrderString(v, order, order_id, y, i == selected, true, r.left + WD_FRAMERECT_LEFT, middle, r.right - WD_FRAMERECT_RIGHT);

						order_id++;

						if (order_id >= v->GetNumOrders()) {
							order = v->GetOrder(0);
							final_order = true;
						} else {
							order = order->next;
						}
					} else {
						StringID string;
						TextColour colour = (i == selected) ? TC_WHITE : TC_BLACK;
						if (order->IsType(OT_CONDITIONAL)) {
							string = STR_TIMETABLE_NO_TRAVEL;
						} else if (order->IsType(OT_IMPLICIT)) {
							string = STR_TIMETABLE_NOT_TIMETABLEABLE;
							colour = ((i == selected) ? TC_SILVER : TC_GREY) | TC_NO_SHADE;
						} else if (order->travel_time == 0) {
							string = order->max_speed != UINT16_MAX ? STR_TIMETABLE_TRAVEL_NOT_TIMETABLED_SPEED : STR_TIMETABLE_TRAVEL_NOT_TIMETABLED;
						} else {
							SetTimetableParams(0, 1, order->travel_time);
							string = order->max_speed != UINT16_MAX ? STR_TIMETABLE_TRAVEL_FOR_SPEED : STR_TIMETABLE_TRAVEL_FOR;
						}
						SetDParam(2, order->max_speed);

						DrawString(rtl ? r.left + WD_FRAMERECT_LEFT : middle, rtl ? middle : r.right - WD_FRAMERECT_LEFT, y, string, colour);

						if (final_order) break;
					}

					i++;
					y += FONT_HEIGHT_NORMAL;
				}
				break;
			}

			case WID_VT_ARRIVAL_DEPARTURE_PANEL: {
				/* Arrival and departure times are handled in an all-or-nothing approach,
				 * i.e. are only shown if we can calculate all times.
				 * Excluding order lists with only one order makes some things easier.
				 */
				Ticks total_time = v->orders.list != NULL ? v->orders.list->GetTimetableDurationIncomplete() : 0;
				if (total_time <= 0 || v->GetNumOrders() <= 1 || !HasBit(v->vehicle_flags, VF_TIMETABLE_STARTED)) break;

				TimetableArrivalDeparture *arr_dep = AllocaM(TimetableArrivalDeparture, v->GetNumOrders());
				const VehicleOrderID cur_order = v->cur_real_order_index % v->GetNumOrders();

				VehicleOrderID earlyID = BuildArrivalDepartureList(v, arr_dep) ? cur_order : (VehicleOrderID)INVALID_VEH_ORDER_ID;

				int y = r.top + WD_FRAMERECT_TOP;

				bool show_late = this->show_expected && v->lateness_counter > DAY_TICKS;
				Ticks offset = show_late ? 0 : -v->lateness_counter;

				bool rtl = _current_text_dir == TD_RTL;
				int abbr_left  = rtl ? r.right - WD_FRAMERECT_RIGHT - this->deparr_abbr_width : r.left + WD_FRAMERECT_LEFT;
				int abbr_right = rtl ? r.right - WD_FRAMERECT_RIGHT : r.left + WD_FRAMERECT_LEFT + this->deparr_abbr_width;
				int time_left  = rtl ? r.left + WD_FRAMERECT_LEFT : r.right - WD_FRAMERECT_RIGHT - this->deparr_time_width;
				int time_right = rtl ? r.left + WD_FRAMERECT_LEFT + this->deparr_time_width : r.right - WD_FRAMERECT_RIGHT;

				for (int i = this->vscroll->GetPosition(); i / 2 < v->GetNumOrders(); ++i) { // note: i is also incremented in the loop
					/* Don't draw anything if it extends past the end of the window. */
					if (!this->vscroll->IsVisible(i)) break;

					if (i % 2 == 0) {
						if (arr_dep[i / 2].arrival != INVALID_TICKS) {
							DrawString(abbr_left, abbr_right, y, STR_TIMETABLE_ARRIVAL_ABBREVIATION, i == selected ? TC_WHITE : TC_BLACK);
							if (this->show_expected && i / 2 == earlyID) {
								SetArrivalDepartParams(0, 1, arr_dep[i / 2].arrival);
								DrawString(time_left, time_right, y, STR_GREEN_STRING, i == selected ? TC_WHITE : TC_BLACK);
							} else {
								SetArrivalDepartParams(0, 1, arr_dep[i / 2].arrival + offset);
								DrawString(time_left, time_right, y, show_late ? STR_RED_STRING : STR_JUST_STRING, i == selected ? TC_WHITE : TC_BLACK);
							}
						}
					} else {
						if (arr_dep[i / 2].departure != INVALID_TICKS) {
							DrawString(abbr_left, abbr_right, y, STR_TIMETABLE_DEPARTURE_ABBREVIATION, i == selected ? TC_WHITE : TC_BLACK);
							SetArrivalDepartParams(0, 1, arr_dep[i/2].departure + offset);
							DrawString(time_left, time_right, y, show_late ? STR_RED_STRING : STR_JUST_STRING, i == selected ? TC_WHITE : TC_BLACK);
						}
					}
					y += FONT_HEIGHT_NORMAL;
				}
				break;
			}

			case WID_VT_SUMMARY_PANEL: {
				int y = r.top + WD_FRAMERECT_TOP;

				Ticks total_time = v->orders.list != NULL ? v->orders.list->GetTimetableDurationIncomplete() : 0;
				if (total_time != 0) {
					SetTimetableParams(0, 1, total_time);
					DrawString(r.left + WD_FRAMERECT_LEFT, r.right - WD_FRAMERECT_RIGHT, y, v->orders.list->IsCompleteTimetable() ? STR_TIMETABLE_TOTAL_TIME : STR_TIMETABLE_TOTAL_TIME_INCOMPLETE);
				}
				y += FONT_HEIGHT_NORMAL;

				if (v->timetable_start != 0) {
					/* We are running towards the first station so we can start the
					 * timetable at the given time. */
					SetDParam(0, STR_JUST_DATE_TINY);
					SetDParam(1, v->timetable_start);
					DrawString(r.left + WD_FRAMERECT_LEFT, r.right - WD_FRAMERECT_RIGHT, y, STR_TIMETABLE_STATUS_START_AT);
				} else if (!HasBit(v->vehicle_flags, VF_TIMETABLE_STARTED)) {
					/* We aren't running on a timetable yet, so how can we be "on time"
					 * when we aren't even "on service"/"on duty"? */
					DrawString(r.left + WD_FRAMERECT_LEFT, r.right - WD_FRAMERECT_RIGHT, y, STR_TIMETABLE_STATUS_NOT_STARTED);
				} else if (v->lateness_counter == 0 || (!_settings_client.gui.timetable_in_ticks && v->lateness_counter / DAY_TICKS == 0)) {
					DrawString(r.left + WD_FRAMERECT_LEFT, r.right - WD_FRAMERECT_RIGHT, y, STR_TIMETABLE_STATUS_ON_TIME);
				} else {
					SetTimetableParams(0, 1, abs(v->lateness_counter));
					DrawString(r.left + WD_FRAMERECT_LEFT, r.right - WD_FRAMERECT_RIGHT, y, v->lateness_counter < 0 ? STR_TIMETABLE_STATUS_EARLY : STR_TIMETABLE_STATUS_LATE);
				}
				break;
			}
		}
	}
Beispiel #6
0
/**
 * Convert existing rail to waypoint. Eg build a waypoint station over
 * piece of rail
 * @param start_tile northern most tile where waypoint will be built
 * @param flags type of operation
 * @param p1 various bitstuffed elements
 * - p1 = (bit  4)    - orientation (Axis)
 * - p1 = (bit  8-15) - width of waypoint
 * - p1 = (bit 16-23) - height of waypoint
 * - p1 = (bit 24)    - allow waypoints directly adjacent to other waypoints.
 * @param p2 various bitstuffed elements
 * - p2 = (bit  0- 7) - custom station class
 * - p2 = (bit  8-15) - custom station id
 * @param text unused
 * @return the cost of this operation or an error
 */
CommandCost CmdBuildRailWaypoint(TileIndex start_tile, DoCommandFlag flags, uint32 p1, uint32 p2, const char *text)
{
	/* Unpack parameters */
	Axis axis      = Extract<Axis, 4, 1>(p1);
	byte width     = GB(p1,  8, 8);
	byte height    = GB(p1, 16, 8);
	bool adjacent  = HasBit(p1, 24);

	StationClassID spec_class = Extract<StationClassID, 0, 8>(p2);
	byte spec_index           = GB(p2, 8, 8);
	StationID station_to_join = GB(p2, 16, 16);

	/* Check if the given station class is valid */
	if (spec_class != STAT_CLASS_WAYP) return CMD_ERROR;
	if (spec_index >= StationClass::Get(spec_class)->GetSpecCount()) return CMD_ERROR;

	/* The number of parts to build */
	byte count = axis == AXIS_X ? height : width;

	if ((axis == AXIS_X ? width : height) != 1) return CMD_ERROR;
	if (count == 0 || count > _settings_game.station.station_spread) return CMD_ERROR;

	bool reuse = (station_to_join != NEW_STATION);
	if (!reuse) station_to_join = INVALID_STATION;
	bool distant_join = (station_to_join != INVALID_STATION);

	if (distant_join && (!_settings_game.station.distant_join_stations || !Waypoint::IsValidID(station_to_join))) return CMD_ERROR;

	/* Make sure the area below consists of clear tiles. (OR tiles belonging to a certain rail station) */
	StationID est = INVALID_STATION;

	/* Check whether the tiles we're building on are valid rail or not. */
	TileIndexDiff offset = TileOffsByDiagDir(AxisToDiagDir(OtherAxis(axis)));
	for (int i = 0; i < count; i++) {
		TileIndex tile = start_tile + i * offset;
		CommandCost ret = IsValidTileForWaypoint(tile, axis, &est);
		if (ret.Failed()) return ret;
	}

	Waypoint *wp = NULL;
	TileArea new_location(TileArea(start_tile, width, height));
	CommandCost ret = FindJoiningWaypoint(est, station_to_join, adjacent, new_location, &wp);
	if (ret.Failed()) return ret;

	/* Check if there is an already existing, deleted, waypoint close to us that we can reuse. */
	TileIndex center_tile = start_tile + (count / 2) * offset;
	if (wp == NULL && reuse) wp = FindDeletedWaypointCloseTo(center_tile, STR_SV_STNAME_WAYPOINT, _current_company);

	if (wp != NULL) {
		/* Reuse an existing waypoint. */
		if (wp->owner != _current_company) return_cmd_error(STR_ERROR_TOO_CLOSE_TO_ANOTHER_WAYPOINT);

		/* check if we want to expand an already existing waypoint? */
		if (wp->train_station.tile != INVALID_TILE) {
			CommandCost ret = CanExpandRailStation(wp, new_location, axis);
			if (ret.Failed()) return ret;
		}

		CommandCost ret = wp->rect.BeforeAddRect(start_tile, width, height, StationRect::ADD_TEST);
		if (ret.Failed()) return ret;
	} else {
		/* allocate and initialize new waypoint */
		if (!Waypoint::CanAllocateItem()) return_cmd_error(STR_ERROR_TOO_MANY_STATIONS_LOADING);
	}

	if (flags & DC_EXEC) {
		if (wp == NULL) {
			wp = new Waypoint(start_tile);
		} else if (!wp->IsInUse()) {
			/* Move existing (recently deleted) waypoint to the new location */
			wp->xy = start_tile;
		}
		wp->owner = GetTileOwner(start_tile);

		wp->rect.BeforeAddRect(start_tile, width, height, StationRect::ADD_TRY);

		wp->delete_ctr = 0;
		wp->facilities |= FACIL_TRAIN;
		wp->build_date = _date;
		wp->string_id = STR_SV_STNAME_WAYPOINT;
		wp->train_station = new_location;

		if (wp->town == NULL) MakeDefaultName(wp);

		wp->UpdateVirtCoord();

		const StationSpec *spec = StationClass::Get(spec_class)->GetSpec(spec_index);
		byte *layout_ptr = AllocaM(byte, count);
		if (spec == NULL) {
			/* The layout must be 0 for the 'normal' waypoints by design. */
			memset(layout_ptr, 0, count);
		} else {
			/* But for NewGRF waypoints we like to have their style. */
			GetStationLayout(layout_ptr, count, 1, spec);
		}
		byte map_spec_index = AllocateSpecToStation(spec, wp, true);

		Company *c = Company::Get(wp->owner);
		for (int i = 0; i < count; i++) {
			TileIndex tile = start_tile + i * offset;
			byte old_specindex = HasStationTileRail(tile) ? GetCustomStationSpecIndex(tile) : 0;
			if (!HasStationTileRail(tile)) c->infrastructure.station++;
			bool reserved = IsTileType(tile, MP_RAILWAY) ?
					HasBit(GetRailReservationTrackBits(tile), AxisToTrack(axis)) :
					HasStationReservation(tile);
			MakeRailWaypoint(tile, wp->owner, wp->index, axis, layout_ptr[i], GetRailType(tile));
			SetCustomStationSpecIndex(tile, map_spec_index);
			SetRailStationReservation(tile, reserved);
			MarkTileDirtyByTile(tile);

			DeallocateSpecFromStation(wp, old_specindex);
			YapfNotifyTrackLayoutChange(tile, AxisToTrack(axis));
		}
		DirtyCompanyInfrastructureWindows(wp->owner);
	}

	return CommandCost(EXPENSES_CONSTRUCTION, count * _price[PR_BUILD_WAYPOINT_RAIL]);
}
Beispiel #7
0
/**
 * Generic .BMP writer
 * @param name file name including extension
 * @param callb callback used for gathering rendered image
 * @param userdata parameters forwarded to \a callb
 * @param w width in pixels
 * @param h height in pixels
 * @param pixelformat bits per pixel
 * @param palette colour palette (for 8bpp mode)
 * @return was everything ok?
 * @see ScreenshotHandlerProc
 */
static bool MakeBMPImage(const char *name, ScreenshotCallback *callb, void *userdata, uint w, uint h, int pixelformat, const Colour *palette)
{
	uint bpp; // bytes per pixel
	switch (pixelformat) {
		case 8:  bpp = 1; break;
		/* 32bpp mode is saved as 24bpp BMP */
		case 32: bpp = 3; break;
		/* Only implemented for 8bit and 32bit images so far */
		default: return false;
	}

	FILE *f = fopen(name, "wb");
	if (f == NULL) return false;

	/* Each scanline must be aligned on a 32bit boundary */
	uint bytewidth = Align(w * bpp, 4); // bytes per line in file

	/* Size of palette. Only present for 8bpp mode */
	uint pal_size = pixelformat == 8 ? sizeof(RgbQuad) * 256 : 0;

	/* Setup the file header */
	BitmapFileHeader bfh;
	bfh.type = TO_LE16('MB');
	bfh.size = TO_LE32(sizeof(BitmapFileHeader) + sizeof(BitmapInfoHeader) + pal_size + bytewidth * h);
	bfh.reserved = 0;
	bfh.off_bits = TO_LE32(sizeof(BitmapFileHeader) + sizeof(BitmapInfoHeader) + pal_size);

	/* Setup the info header */
	BitmapInfoHeader bih;
	bih.size = TO_LE32(sizeof(BitmapInfoHeader));
	bih.width = TO_LE32(w);
	bih.height = TO_LE32(h);
	bih.planes = TO_LE16(1);
	bih.bitcount = TO_LE16(bpp * 8);
	bih.compression = 0;
	bih.sizeimage = 0;
	bih.xpels = 0;
	bih.ypels = 0;
	bih.clrused = 0;
	bih.clrimp = 0;

	/* Write file header and info header */
	if (fwrite(&bfh, sizeof(bfh), 1, f) != 1 || fwrite(&bih, sizeof(bih), 1, f) != 1) {
		fclose(f);
		return false;
	}

	if (pixelformat == 8) {
		/* Convert the palette to the windows format */
		RgbQuad rq[256];
		for (uint i = 0; i < 256; i++) {
			rq[i].red   = palette[i].r;
			rq[i].green = palette[i].g;
			rq[i].blue  = palette[i].b;
			rq[i].reserved = 0;
		}
		/* Write the palette */
		if (fwrite(rq, sizeof(rq), 1, f) != 1) {
			fclose(f);
			return false;
		}
	}

	/* Try to use 64k of memory, store between 16 and 128 lines */
	uint maxlines = Clamp(65536 / (w * pixelformat / 8), 16, 128); // number of lines per iteration

	uint8 *buff = MallocT<uint8>(maxlines * w * pixelformat / 8); // buffer which is rendered to
	uint8 *line = AllocaM(uint8, bytewidth); // one line, stored to file
	memset(line, 0, bytewidth);

	/* Start at the bottom, since bitmaps are stored bottom up */
	do {
		uint n = min(h, maxlines);
		h -= n;

		/* Render the pixels */
		callb(userdata, buff, h, w, n);

		/* Write each line */
		while (n-- != 0) {
			if (pixelformat == 8) {
				/* Move to 'line', leave last few pixels in line zeroed */
				memcpy(line, buff + n * w, w);
			} else {
				/* Convert from 'native' 32bpp to BMP-like 24bpp.
				 * Works for both big and little endian machines */
				Colour *src = ((Colour *)buff) + n * w;
				byte *dst = line;
				for (uint i = 0; i < w; i++) {
					dst[i * 3    ] = src[i].b;
					dst[i * 3 + 1] = src[i].g;
					dst[i * 3 + 2] = src[i].r;
				}
			}
			/* Write to file */
			if (fwrite(line, bytewidth, 1, f) != 1) {
				free(buff);
				fclose(f);
				return false;
			}
		}
	} while (h != 0);

	free(buff);
	fclose(f);

	return true;
}
Beispiel #8
0
FT_Error GetFontByFaceName(const char *font_name, FT_Face *face)
{
	FT_Error err = FT_Err_Cannot_Open_Resource;
	HKEY hKey;
	LONG ret;
	TCHAR vbuffer[MAX_PATH], dbuffer[256];
	TCHAR *pathbuf;
	const char *font_path;
	uint index;
	size_t path_len;

	/* On windows NT (2000, NT3.5, XP, etc.) the fonts are stored in the
	 * "Windows NT" key, on Windows 9x in the Windows key. To save us having
	 * to retrieve the windows version, we'll just query both */
	ret = RegOpenKeyEx(HKEY_LOCAL_MACHINE, _T(FONT_DIR_NT), 0, KEY_READ, &hKey);
	if (ret != ERROR_SUCCESS) ret = RegOpenKeyEx(HKEY_LOCAL_MACHINE, _T(FONT_DIR_9X), 0, KEY_READ, &hKey);

	if (ret != ERROR_SUCCESS) {
		DEBUG(freetype, 0, "Cannot open registry key HKLM\\SOFTWARE\\Microsoft\\Windows (NT)\\CurrentVersion\\Fonts");
		return err;
	}

	/* Convert font name to file system encoding. */
	TCHAR *font_namep = _tcsdup(OTTD2FS(font_name));

	for (index = 0;; index++) {
		TCHAR *s;
		DWORD vbuflen = lengthof(vbuffer);
		DWORD dbuflen = lengthof(dbuffer);

		ret = RegEnumValue(hKey, index, vbuffer, &vbuflen, NULL, NULL, (byte*)dbuffer, &dbuflen);
		if (ret != ERROR_SUCCESS) goto registry_no_font_found;

		/* The font names in the registry are of the following 3 forms:
		 * - ADMUI3.fon
		 * - Book Antiqua Bold (TrueType)
		 * - Batang & BatangChe & Gungsuh & GungsuhChe (TrueType)
		 * We will strip the font-type '()' if any and work with the font name
		 * itself, which must match exactly; if...
		 * TTC files, font files which contain more than one font are separated
		 * by '&'. Our best bet will be to do substr match for the fontname
		 * and then let FreeType figure out which index to load */
		s = _tcschr(vbuffer, _T('('));
		if (s != NULL) s[-1] = '\0';

		if (_tcschr(vbuffer, _T('&')) == NULL) {
			if (_tcsicmp(vbuffer, font_namep) == 0) break;
		} else {
			if (_tcsstr(vbuffer, font_namep) != NULL) break;
		}
	}

	if (!SUCCEEDED(OTTDSHGetFolderPath(NULL, CSIDL_FONTS, NULL, SHGFP_TYPE_CURRENT, vbuffer))) {
		DEBUG(freetype, 0, "SHGetFolderPath cannot return fonts directory");
		goto folder_error;
	}

	/* Some fonts are contained in .ttc files, TrueType Collection fonts. These
	 * contain multiple fonts inside this single file. GetFontData however
	 * returns the whole file, so we need to check each font inside to get the
	 * proper font. */
	path_len = _tcslen(vbuffer) + _tcslen(dbuffer) + 2; // '\' and terminating nul.
	pathbuf = AllocaM(TCHAR, path_len);
	_sntprintf(pathbuf, path_len, _T("%s\\%s"), vbuffer, dbuffer);

	/* Convert the path into something that FreeType understands. */
	font_path = GetShortPath(pathbuf);

	index = 0;
	do {
		err = FT_New_Face(_library, font_path, index, face);
		if (err != FT_Err_Ok) break;

		if (strncasecmp(font_name, (*face)->family_name, strlen((*face)->family_name)) == 0) break;
		/* Try english name if font name failed */
		if (strncasecmp(font_name + strlen(font_name) + 1, (*face)->family_name, strlen((*face)->family_name)) == 0) break;
		err = FT_Err_Cannot_Open_Resource;

	} while ((FT_Long)++index != (*face)->num_faces);


folder_error:
registry_no_font_found:
	free(font_namep);
	RegCloseKey(hKey);
	return err;
}
Beispiel #9
0
/**
 * Additional map variety is provided by applying different curve maps
 * to different parts of the map. A randomized low resolution grid contains
 * which curve map to use on each part of the make. This filtered non-linearly
 * to smooth out transitions between curves, so each tile could have between
 * 100% of one map applied or 25% of four maps.
 *
 * The curve maps define different land styles, i.e. lakes, low-lands, hills
 * and mountain ranges, although these are dependent on the landscape style
 * chosen as well.
 *
 * The level parameter dictates the resolution of the grid. A low resolution
 * grid will result in larger continuous areas of a land style, a higher
 * resolution grid splits the style into smaller areas.
 * @param level Rough indication of the size of the grid sections to style. Small level means large grid sections.
 */
static void HeightMapCurves(uint level)
{
	height_t mh = TGPGetMaxHeight() - I2H(1); // height levels above sea level only

	/** Basically scale height X to height Y. Everything in between is interpolated. */
	struct control_point_t {
		height_t x; ///< The height to scale from.
		height_t y; ///< The height to scale to.
	};
	/* Scaled curve maps; value is in height_ts. */
#define F(fraction) ((height_t)(fraction * mh))
	const control_point_t curve_map_1[] = { { F(0.0), F(0.0) },                       { F(0.8), F(0.13) },                       { F(1.0), F(0.4)  } };
	const control_point_t curve_map_2[] = { { F(0.0), F(0.0) }, { F(0.53), F(0.13) }, { F(0.8), F(0.27) },                       { F(1.0), F(0.6)  } };
	const control_point_t curve_map_3[] = { { F(0.0), F(0.0) }, { F(0.53), F(0.27) }, { F(0.8), F(0.57) },                       { F(1.0), F(0.8)  } };
	const control_point_t curve_map_4[] = { { F(0.0), F(0.0) }, { F(0.4),  F(0.3)  }, { F(0.7), F(0.8)  }, { F(0.92), F(0.99) }, { F(1.0), F(0.99) } };
#undef F

	/** Helper structure to index the different curve maps. */
	struct control_point_list_t {
		size_t length;               ///< The length of the curve map.
		const control_point_t *list; ///< The actual curve map.
	};
	const control_point_list_t curve_maps[] = {
		{ lengthof(curve_map_1), curve_map_1 },
		{ lengthof(curve_map_2), curve_map_2 },
		{ lengthof(curve_map_3), curve_map_3 },
		{ lengthof(curve_map_4), curve_map_4 },
	};

	height_t ht[lengthof(curve_maps)];
	MemSetT(ht, 0, lengthof(ht));

	/* Set up a grid to choose curve maps based on location; attempt to get a somewhat square grid */
	float factor = sqrt((float)_height_map.size_x / (float)_height_map.size_y);
	uint sx = Clamp((int)(((1 << level) * factor) + 0.5), 1, 128);
	uint sy = Clamp((int)(((1 << level) / factor) + 0.5), 1, 128);
	byte *c = AllocaM(byte, sx * sy);

	for (uint i = 0; i < sx * sy; i++) {
		c[i] = Random() % lengthof(curve_maps);
	}

	/* Apply curves */
	for (int x = 0; x < _height_map.size_x; x++) {

		/* Get our X grid positions and bi-linear ratio */
		float fx = (float)(sx * x) / _height_map.size_x + 1.0f;
		uint x1 = (uint)fx;
		uint x2 = x1;
		float xr = 2.0f * (fx - x1) - 1.0f;
		xr = sin(xr * M_PI_2);
		xr = sin(xr * M_PI_2);
		xr = 0.5f * (xr + 1.0f);
		float xri = 1.0f - xr;

		if (x1 > 0) {
			x1--;
			if (x2 >= sx) x2--;
		}

		for (int y = 0; y < _height_map.size_y; y++) {

			/* Get our Y grid position and bi-linear ratio */
			float fy = (float)(sy * y) / _height_map.size_y + 1.0f;
			uint y1 = (uint)fy;
			uint y2 = y1;
			float yr = 2.0f * (fy - y1) - 1.0f;
			yr = sin(yr * M_PI_2);
			yr = sin(yr * M_PI_2);
			yr = 0.5f * (yr + 1.0f);
			float yri = 1.0f - yr;

			if (y1 > 0) {
				y1--;
				if (y2 >= sy) y2--;
			}

			uint corner_a = c[x1 + sx * y1];
			uint corner_b = c[x1 + sx * y2];
			uint corner_c = c[x2 + sx * y1];
			uint corner_d = c[x2 + sx * y2];

			/* Bitmask of which curve maps are chosen, so that we do not bother
			 * calculating a curve which won't be used. */
			uint corner_bits = 0;
			corner_bits |= 1 << corner_a;
			corner_bits |= 1 << corner_b;
			corner_bits |= 1 << corner_c;
			corner_bits |= 1 << corner_d;

			height_t *h = &_height_map.height(x, y);

			/* Do not touch sea level */
			if (*h < I2H(1)) continue;

			/* Only scale above sea level */
			*h -= I2H(1);

			/* Apply all curve maps that are used on this tile. */
			for (uint t = 0; t < lengthof(curve_maps); t++) {
				if (!HasBit(corner_bits, t)) continue;

				bool found = false;
				const control_point_t *cm = curve_maps[t].list;
				for (uint i = 0; i < curve_maps[t].length - 1; i++) {
					const control_point_t &p1 = cm[i];
					const control_point_t &p2 = cm[i + 1];

					if (*h >= p1.x && *h < p2.x) {
						ht[t] = p1.y + (*h - p1.x) * (p2.y - p1.y) / (p2.x - p1.x);
						found = true;
						break;
					}
				}
				assert(found);
			}

			/* Apply interpolation of curve map results. */
			*h = (height_t)((ht[corner_a] * yri + ht[corner_b] * yr) * xri + (ht[corner_c] * yri + ht[corner_d] * yr) * xr);

			/* Readd sea level */
			*h += I2H(1);
		}
	}
}
Beispiel #10
0
static void HeightMapCurves(uint level)
{
	height_t ht[lengthof(_curve_maps)];
	MemSetT(ht, 0, lengthof(ht));

	/* Set up a grid to choose curve maps based on location */
	uint sx = Clamp(1 << level, 2, 32);
	uint sy = Clamp(1 << level, 2, 32);
	byte *c = AllocaM(byte, sx * sy);

	for (uint i = 0; i < sx * sy; i++) {
		c[i] = Random() % lengthof(_curve_maps);
	}

	/* Apply curves */
	for (uint x = 0; x < _height_map.size_x; x++) {

		/* Get our X grid positions and bi-linear ratio */
		float fx = (float)(sx * x) / _height_map.size_x + 0.5f;
		uint x1 = (uint)fx;
		uint x2 = x1;
		float xr = 2.0f * (fx - x1) - 1.0f;
		xr = sin(xr * M_PI_2);
		xr = sin(xr * M_PI_2);
		xr = 0.5f * (xr + 1.0f);
		float xri = 1.0f - xr;

		if (x1 > 0) {
			x1--;
			if (x2 >= sx) x2--;
		}

		for (uint y = 0; y < _height_map.size_y; y++) {

			/* Get our Y grid position and bi-linear ratio */
			float fy = (float)(sy * y) / _height_map.size_y + 0.5f;
			uint y1 = (uint)fy;
			uint y2 = y1;
			float yr = 2.0f * (fy - y1) - 1.0f;
			yr = sin(yr * M_PI_2);
			yr = sin(yr * M_PI_2);
			yr = 0.5f * (yr + 1.0f);
			float yri = 1.0f - yr;

			if (y1 > 0) {
				y1--;
				if (y2 >= sy) y2--;
			}

			uint corner_a = c[x1 + sx * y1];
			uint corner_b = c[x1 + sx * y2];
			uint corner_c = c[x2 + sx * y1];
			uint corner_d = c[x2 + sx * y2];

			/* Bitmask of which curve maps are chosen, so that we do not bother
			 * calculating a curve which won't be used. */
			uint corner_bits = 0;
			corner_bits |= 1 << corner_a;
			corner_bits |= 1 << corner_b;
			corner_bits |= 1 << corner_c;
			corner_bits |= 1 << corner_d;

			height_t *h = &_height_map.height(x, y);

			/* Apply all curve maps that are used on this tile. */
			for (uint t = 0; t < lengthof(_curve_maps); t++) {
				if (!HasBit(corner_bits, t)) continue;

				const control_point_t *cm = _curve_maps[t].list;
				for (uint i = 0; i < _curve_maps[t].length - 1; i++) {
					const control_point_t &p1 = cm[i];
					const control_point_t &p2 = cm[i + 1];

					if (*h >= p1.x && *h < p2.x) {
						ht[t] = p1.y + (*h - p1.x) * (p2.y - p1.y) / (p2.x - p1.x);
						break;
					}
				}
			}

			/* Apply interpolation of curve map results. */
			*h = (height_t)((ht[corner_a] * yri + ht[corner_b] * yr) * xri + (ht[corner_c] * yri + ht[corner_d] * yr) * xr);
		}
	}
}
Beispiel #11
0
static bool LoadPNG(SpriteLoader::Sprite *sprite, const char *filename, uint32 id, volatile bool mask)
{
	png_byte header[8];
	png_structp png_ptr;
	png_infop info_ptr, end_info;
	uint bit_depth, colour_type;
	uint i, pixelsize;
	SpriteLoader::CommonPixel *dst;

	if (!OpenPNGFile(filename, id, mask)) return mask; // If mask is true, and file not found, continue true anyway, as it isn't a show-stopper

	/* Check the header */
	FioReadBlock(header, 8);
	if (png_sig_cmp(header, 0, 8) != 0) return false;

	/* Create the reader */
	png_ptr = png_create_read_struct(PNG_LIBPNG_VER_STRING, (png_voidp)NULL, png_my_error, png_my_warning);
	if (png_ptr == NULL) return false;

	/* Create initial stuff */
	info_ptr = png_create_info_struct(png_ptr);
	if (info_ptr == NULL) {
		png_destroy_read_struct(&png_ptr, (png_infopp)NULL, (png_infopp)NULL);
		return false;
	}
	end_info = png_create_info_struct(png_ptr);
	if (end_info == NULL) {
		png_destroy_read_struct(&png_ptr, &info_ptr, (png_infopp)NULL);
		return false;
	}

	/* Make sure that upon error, we can clean up graceful */
	if (setjmp(png_jmpbuf(png_ptr))) {
		png_destroy_read_struct(&png_ptr, &info_ptr, &end_info);
		return false;
	}

	/* Read the file */
	png_set_read_fn(png_ptr, NULL, png_my_read);
	png_set_sig_bytes(png_ptr, 8);

	png_read_info(png_ptr, info_ptr);

	if (!mask) {
		/* Read the text chunks */
		png_textp text_ptr;
		int num_text = 0;
		png_get_text(png_ptr, info_ptr, &text_ptr, &num_text);
		if (num_text == 0) DEBUG(misc, 0, "Warning: PNG Sprite '%s/%d.png' doesn't have x_offs and y_offs; expect graphical problems", filename, id);
		for (int i = 0; i < num_text; i++) {
			/* x_offs and y_offs are in the text-chunk of PNG */
			if (strcmp("x_offs", text_ptr[i].key) == 0) sprite->x_offs = strtol(text_ptr[i].text, NULL, 0);
			if (strcmp("y_offs", text_ptr[i].key) == 0) sprite->y_offs = strtol(text_ptr[i].text, NULL, 0);
		}

		sprite->height = png_get_image_height(png_ptr, info_ptr);
		sprite->width  = png_get_image_width(png_ptr, info_ptr);
		sprite->AllocateData(sprite->width * sprite->height);
	}

	bit_depth  = png_get_bit_depth(png_ptr, info_ptr);
	colour_type = png_get_color_type(png_ptr, info_ptr);

	if (mask && (bit_depth != 8 || colour_type != PNG_COLOR_TYPE_PALETTE)) {
		DEBUG(misc, 0, "Ignoring mask for SpriteID %d as it isn't a 8 bit palette image", id);
		return true;
	}

	if (!mask) {
		if (bit_depth == 16) png_set_strip_16(png_ptr);

		if (colour_type == PNG_COLOR_TYPE_PALETTE) {
			png_set_palette_to_rgb(png_ptr);
			colour_type = PNG_COLOR_TYPE_RGB;
		}
		if (colour_type == PNG_COLOR_TYPE_GRAY || colour_type == PNG_COLOR_TYPE_GRAY_ALPHA) {
			png_set_gray_to_rgb(png_ptr);
			colour_type = PNG_COLOR_TYPE_RGB;
		}

		if (colour_type == PNG_COLOR_TYPE_RGB) {
			png_set_filler(png_ptr, 0xff, PNG_FILLER_AFTER);
		}

		pixelsize = sizeof(uint32);
	} else {
		pixelsize = sizeof(uint8);
	}

	png_bytep row_pointer = AllocaM(png_byte, png_get_image_width(png_ptr, info_ptr) * pixelsize);

	for (i = 0; i < png_get_image_height(png_ptr, info_ptr); i++) {
		png_read_row(png_ptr, row_pointer, NULL);

		dst = sprite->data + i * png_get_image_width(png_ptr, info_ptr);

		for (uint x = 0; x < png_get_image_width(png_ptr, info_ptr); x++) {
			if (mask) {
				if (row_pointer[x * sizeof(uint8)] != 0) {
					dst[x].r = 0;
					dst[x].g = 0;
					dst[x].b = 0;
					/* Alpha channel is used from the original image (to allow transparency in remap colours) */
					dst[x].m = row_pointer[x * sizeof(uint8)];
				}
			} else {
				dst[x].r = row_pointer[x * sizeof(uint32) + 0];
				dst[x].g = row_pointer[x * sizeof(uint32) + 1];
				dst[x].b = row_pointer[x * sizeof(uint32) + 2];
				dst[x].a = row_pointer[x * sizeof(uint32) + 3];
				dst[x].m = 0;
			}
		}
	}

	png_destroy_read_struct(&png_ptr, &info_ptr, &end_info);

	return true;
}