OpenTTD Source: src/newgrf/newgrf_act0_stations.cpp Source File

OpenTTD Source 15.0-RC2

newgrf_act0_stations.cpp
Go to the documentation of this file.
1/*
2 * This file is part of OpenTTD.
3 * OpenTTD is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, version 2.
4 * OpenTTD is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
5 * See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with OpenTTD. If not, see <https://www.gnu.org/licenses/old-licenses/gpl-2.0>.
6 */
7
10#include "../stdafx.h"
11#include "../debug.h"
12#include "../newgrf_station.h"
13#include "newgrf_bytereader.h"
14#include "newgrf_internal.h"
15#include "newgrf_stringmapping.h"
16
17#include "../safeguards.h"
18
20 static const uint NUM_STATIONS_PER_GRF = UINT16_MAX - 1;
21
30 static ChangeInfoResult StationChangeInfo(uint first, uint last, int prop, ByteReader &buf)
31{
32 ChangeInfoResult ret = CIR_SUCCESS;
33
34 if (last > NUM_STATIONS_PER_GRF) {
35 GrfMsg(1, "StationChangeInfo: Station {} is invalid, max {}, ignoring", last, NUM_STATIONS_PER_GRF);
36 return CIR_INVALID_ID;
37 }
38
39 /* Allocate station specs if necessary */
40 if (_cur_gps.grffile->stations.size() < last) _cur_gps.grffile->stations.resize(last);
41
42 for (uint id = first; id < last; ++id) {
43 auto &statspec = _cur_gps.grffile->stations[id];
44
45 /* Check that the station we are modifying is defined. */
46 if (statspec == nullptr && prop != 0x08) {
47 GrfMsg(2, "StationChangeInfo: Attempt to modify undefined station {}, ignoring", id);
48 return CIR_INVALID_ID;
49 }
50
51 switch (prop) {
52 case 0x08: { // Class ID
53 /* Property 0x08 is special; it is where the station is allocated */
54 if (statspec == nullptr) {
55 statspec = std::make_unique<StationSpec>();
56 }
57
58 /* Swap classid because we read it in BE meaning WAYP or DFLT */
59 uint32_t classid = buf.ReadDWord();
60 statspec->class_index = StationClass::Allocate(std::byteswap(classid));
61 break;
62 }
63
64 case 0x09: { // Define sprite layout
65 uint16_t tiles = buf.ReadExtendedByte();
66 statspec->renderdata.clear(); // delete earlier loaded stuff
67 statspec->renderdata.reserve(tiles);
68
69 for (uint t = 0; t < tiles; t++) {
70 NewGRFSpriteLayout *dts = &statspec->renderdata.emplace_back();
71 dts->consistent_max_offset = UINT16_MAX; // Spritesets are unknown, so no limit.
72
73 if (buf.HasData(4) && buf.PeekDWord() == 0) {
74 buf.Skip(4);
75 extern const DrawTileSpriteSpan _station_display_datas_rail[8];
76 const DrawTileSpriteSpan &dtss = _station_display_datas_rail[t % 8];
77 dts->ground = dtss.ground;
78 dts->seq.insert(dts->seq.end(), dtss.GetSequence().begin(), dtss.GetSequence().end());
79 continue;
80 }
81
82 ReadSpriteLayoutSprite(buf, false, false, false, GSF_STATIONS, &dts->ground);
83 /* On error, bail out immediately. Temporary GRF data was already freed */
84 if (_cur_gps.skip_sprites < 0) return CIR_DISABLED;
85
86 std::vector<DrawTileSeqStruct> tmp_layout;
87 for (;;) {
88 uint8_t delta_x = buf.ReadByte();
89 if (delta_x == 0x80) break;
90
91 /* no relative bounding box support */
92 DrawTileSeqStruct &dtss = tmp_layout.emplace_back();
93 dtss.origin.x = delta_x;
94 dtss.origin.y = buf.ReadByte();
95 dtss.origin.z = buf.ReadByte();
96 dtss.extent.x = buf.ReadByte();
97 dtss.extent.y = buf.ReadByte();
98 dtss.extent.z = buf.ReadByte();
99
100 ReadSpriteLayoutSprite(buf, false, true, false, GSF_STATIONS, &dtss.image);
101 /* On error, bail out immediately. Temporary GRF data was already freed */
102 if (_cur_gps.skip_sprites < 0) return CIR_DISABLED;
103 }
104 dts->seq = std::move(tmp_layout);
105 }
106
107 /* Number of layouts must be even, alternating X and Y */
108 if (statspec->renderdata.size() & 1) {
109 GrfMsg(1, "StationChangeInfo: Station {} defines an odd number of sprite layouts, dropping the last item", id);
110 statspec->renderdata.pop_back();
111 }
112 break;
113 }
114
115 case 0x0A: { // Copy sprite layout
116 uint16_t srcid = buf.ReadExtendedByte();
117 const StationSpec *srcstatspec = srcid >= _cur_gps.grffile->stations.size() ? nullptr : _cur_gps.grffile->stations[srcid].get();
118
119 if (srcstatspec == nullptr) {
120 GrfMsg(1, "StationChangeInfo: Station {} is not defined, cannot copy sprite layout to {}.", srcid, id);
121 continue;
122 }
123
124 statspec->renderdata.clear(); // delete earlier loaded stuff
125 statspec->renderdata.reserve(srcstatspec->renderdata.size());
126
127 for (const auto &it : srcstatspec->renderdata) {
128 statspec->renderdata.emplace_back(it);
129 }
130 break;
131 }
132
133 case 0x0B: // Callback mask
134 statspec->callback_mask = static_cast<StationCallbackMasks>(buf.ReadByte());
135 break;
136
137 case 0x0C: // Disallowed number of platforms
138 statspec->disallowed_platforms = buf.ReadByte();
139 break;
140
141 case 0x0D: // Disallowed platform lengths
142 statspec->disallowed_lengths = buf.ReadByte();
143 break;
144
145 case 0x0E: // Define custom layout
146 while (buf.HasData()) {
147 uint8_t length = buf.ReadByte();
148 uint8_t number = buf.ReadByte();
149
150 if (length == 0 || number == 0) break;
151
152 const uint8_t *buf_layout = buf.ReadBytes(length * number);
153
154 /* Create entry in layouts and assign the layout to it. */
155 auto &layout = statspec->layouts[GetStationLayoutKey(number, length)];
156 layout.assign(buf_layout, buf_layout + length * number);
157
158 /* Ensure the first bit, axis, is zero. The rest of the value is validated during rendering, as we don't know the range yet. */
159 for (auto &tile : layout) {
160 if ((tile & ~1U) != tile) {
161 GrfMsg(1, "StationChangeInfo: Invalid tile {} in layout {}x{}", tile, length, number);
162 tile &= ~1U;
163 }
164 }
165 }
166 break;
167
168 case 0x0F: { // Copy custom layout
169 uint16_t srcid = buf.ReadExtendedByte();
170 const StationSpec *srcstatspec = srcid >= _cur_gps.grffile->stations.size() ? nullptr : _cur_gps.grffile->stations[srcid].get();
171
172 if (srcstatspec == nullptr) {
173 GrfMsg(1, "StationChangeInfo: Station {} is not defined, cannot copy tile layout to {}.", srcid, id);
174 continue;
175 }
176
177 statspec->layouts = srcstatspec->layouts;
178 break;
179 }
180
181 case 0x10: // Little/lots cargo threshold
182 statspec->cargo_threshold = buf.ReadWord();
183 break;
184
185 case 0x11: { // Pylon placement
186 uint8_t pylons = buf.ReadByte();
187 if (statspec->tileflags.size() < 8) statspec->tileflags.resize(8);
188 for (int j = 0; j < 8; ++j) {
189 if (HasBit(pylons, j)) {
190 statspec->tileflags[j].Set(StationSpec::TileFlag::Pylons);
191 } else {
192 statspec->tileflags[j].Reset(StationSpec::TileFlag::Pylons);
193 }
194 }
195 break;
196 }
197
198 case 0x12: // Cargo types for random triggers
199 if (_cur_gps.grffile->grf_version >= 7) {
200 statspec->cargo_triggers = TranslateRefitMask(buf.ReadDWord());
201 } else {
202 statspec->cargo_triggers = (CargoTypes)buf.ReadDWord();
203 }
204 break;
205
206 case 0x13: // General flags
207 statspec->flags = StationSpecFlags{buf.ReadByte()};
208 break;
209
210 case 0x14: { // Overhead wire placement
211 uint8_t wires = buf.ReadByte();
212 if (statspec->tileflags.size() < 8) statspec->tileflags.resize(8);
213 for (int j = 0; j < 8; ++j) {
214 if (HasBit(wires, j)) {
215 statspec->tileflags[j].Set(StationSpec::TileFlag::NoWires);
216 } else {
217 statspec->tileflags[j].Reset(StationSpec::TileFlag::NoWires);
218 }
219 }
220 break;
221 }
222
223 case 0x15: { // Blocked tiles
224 uint8_t blocked = buf.ReadByte();
225 if (statspec->tileflags.size() < 8) statspec->tileflags.resize(8);
226 for (int j = 0; j < 8; ++j) {
227 if (HasBit(blocked, j)) {
228 statspec->tileflags[j].Set(StationSpec::TileFlag::Blocked);
229 } else {
230 statspec->tileflags[j].Reset(StationSpec::TileFlag::Blocked);
231 }
232 }
233 break;
234 }
235
236 case 0x16: // Animation info
237 statspec->animation.frames = buf.ReadByte();
238 statspec->animation.status = static_cast<AnimationStatus>(buf.ReadByte());
239 break;
240
241 case 0x17: // Animation speed
242 statspec->animation.speed = buf.ReadByte();
243 break;
244
245 case 0x18: // Animation triggers
246 statspec->animation.triggers = static_cast<StationAnimationTriggers>(buf.ReadWord());
247 break;
248
249 /* 0x19 road routing (not implemented) */
250
251 case 0x1A: { // Advanced sprite layout
252 uint16_t tiles = buf.ReadExtendedByte();
253 statspec->renderdata.clear(); // delete earlier loaded stuff
254 statspec->renderdata.reserve(tiles);
255
256 for (uint t = 0; t < tiles; t++) {
257 NewGRFSpriteLayout *dts = &statspec->renderdata.emplace_back();
258 uint num_building_sprites = buf.ReadByte();
259 /* On error, bail out immediately. Temporary GRF data was already freed */
260 if (ReadSpriteLayout(buf, num_building_sprites, false, GSF_STATIONS, true, false, dts)) return CIR_DISABLED;
261 }
262
263 /* Number of layouts must be even, alternating X and Y */
264 if (statspec->renderdata.size() & 1) {
265 GrfMsg(1, "StationChangeInfo: Station {} defines an odd number of sprite layouts, dropping the last item", id);
266 statspec->renderdata.pop_back();
267 }
268 break;
269 }
270
271 case 0x1B: // Minimum bridge height (not implemented)
272 buf.ReadWord();
273 buf.ReadWord();
274 buf.ReadWord();
275 buf.ReadWord();
276 break;
277
278 case 0x1C: // Station Name
279 AddStringForMapping(GRFStringID{buf.ReadWord()}, &statspec->name);
280 break;
281
282 case 0x1D: // Station Class name
283 AddStringForMapping(GRFStringID{buf.ReadWord()}, [statspec = statspec.get()](StringID str) { StationClass::Get(statspec->class_index)->name = str; });
284 break;
285
286 case 0x1E: { // Extended tile flags (replaces prop 11, 14 and 15)
287 uint16_t tiles = buf.ReadExtendedByte();
288 auto flags = reinterpret_cast<const StationSpec::TileFlags *>(buf.ReadBytes(tiles));
289 statspec->tileflags.assign(flags, flags + tiles);
290 break;
291 }
292
293 case 0x1F: // Badge list
294 statspec->badges = ReadBadgeList(buf, GSF_STATIONS);
295 break;
296
297 case 0x20: { // Minimum bridge height (extended)
298 uint16_t tiles = buf.ReadExtendedByte();
299 if (statspec->bridgeable_info.size() < tiles) statspec->bridgeable_info.resize(tiles);
300 for (int j = 0; j != tiles; ++j) {
301 statspec->bridgeable_info[j].height = buf.ReadByte();
302 }
303 break;
304 }
305
306 case 0x21: { // Disallowed bridge pillars
307 uint16_t tiles = buf.ReadExtendedByte();
308 if (statspec->bridgeable_info.size() < tiles) statspec->bridgeable_info.resize(tiles);
309 for (int j = 0; j != tiles; ++j) {
310 statspec->bridgeable_info[j].disallowed_pillars = BridgePillarFlags{buf.ReadByte()};
311 }
312 break;
313 }
314
315 default:
316 ret = CIR_UNKNOWN;
317 break;
318 }
319 }
320
321 return ret;
322}
323
324template <> ChangeInfoResult GrfChangeInfoHandler<GSF_STATIONS>::Reserve(uint, uint, int, ByteReader &) { return CIR_UNHANDLED; }
325template <> ChangeInfoResult GrfChangeInfoHandler<GSF_STATIONS>::Activation(uint first, uint last, int prop, ByteReader &buf) { return StationChangeInfo(first, last, prop, buf); }
HasBit
constexpr bool HasBit(const T x, const uint8_t y)
Checks if a bit in a value is set.
Definition bitmath_func.hpp:103
ByteReader
Class to read from a NewGRF file.
Definition newgrf_bytereader.h:18
ByteReader::PeekDWord
uint32_t PeekDWord()
Read a single DWord (32 bits).
Definition newgrf_bytereader.h:78
ByteReader::ReadDWord
uint32_t ReadDWord()
Read a single DWord (32 bits).
Definition newgrf_bytereader.h:66
ByteReader::ReadWord
uint16_t ReadWord()
Read a single Word (16 bits).
Definition newgrf_bytereader.h:45
ByteReader::ReadExtendedByte
uint16_t ReadExtendedByte()
Read a single Extended Byte (8 or 16 bits).
Definition newgrf_bytereader.h:56
ByteReader::ReadByte
uint8_t ReadByte()
Read a single byte (8 bits).
Definition newgrf_bytereader.h:34
EnumBitSet< StationCallbackMask, uint16_t >
NewGRFClass::name
StringID name
Name of this class.
Definition newgrf_class.h:49
NewGRFClass::Get
static NewGRFClass * Get(Tindex class_index)
Get a particular class.
Definition newgrf_class_func.h:82
NewGRFClass::Allocate
static Tindex Allocate(uint32_t global_id)
Allocate a class with a given global class ID.
Definition newgrf_class_func.h:32
TranslateRefitMask
CargoTypes TranslateRefitMask(uint32_t refit_mask)
Translate the refit mask.
Definition newgrf.cpp:315
ReadBadgeList
std::vector< BadgeID > ReadBadgeList(ByteReader &buf, GrfSpecFeature feature)
Read a list of badges.
Definition newgrf_act0.cpp:86
StationChangeInfo
static ChangeInfoResult StationChangeInfo(uint first, uint last, int prop, ByteReader &buf)
Define properties for stations.
Definition newgrf_act0_stations.cpp:30
NUM_STATIONS_PER_GRF
static const uint NUM_STATIONS_PER_GRF
The maximum amount of stations a single GRF is allowed to add.
Definition newgrf_act0_stations.cpp:20
ReadSpriteLayoutSprite
TileLayoutFlags ReadSpriteLayoutSprite(ByteReader &buf, bool read_flags, bool invert_action1_flag, bool use_cur_spritesets, GrfSpecFeature feature, PalSpriteID *grf_sprite, uint16_t *max_sprite_offset, uint16_t *max_palette_offset)
Read a sprite and a palette from the GRF and convert them into a format suitable to OpenTTD.
Definition newgrf_act2.cpp:63
ReadSpriteLayout
bool ReadSpriteLayout(ByteReader &buf, uint num_building_sprites, bool use_cur_spritesets, GrfSpecFeature feature, bool allow_var10, bool no_z_position, NewGRFSpriteLayout *dts)
Read a spritelayout from the GRF.
Definition newgrf_act2.cpp:174
AnimationStatus
AnimationStatus
Definition newgrf_animation_type.h:13
newgrf_bytereader.h
NewGRF buffer reader definition.
newgrf_internal.h
NewGRF internal processing state.
ChangeInfoResult
ChangeInfoResult
Possible return values for the GrfChangeInfoHandler functions.
Definition newgrf_internal.h:20
CIR_INVALID_ID
@ CIR_INVALID_ID
Attempt to modify an invalid ID.
Definition newgrf_internal.h:25
CIR_DISABLED
@ CIR_DISABLED
GRF was disabled due to error.
Definition newgrf_internal.h:22
CIR_UNKNOWN
@ CIR_UNKNOWN
Variable is unknown.
Definition newgrf_internal.h:24
CIR_UNHANDLED
@ CIR_UNHANDLED
Variable was parsed but unread.
Definition newgrf_internal.h:23
CIR_SUCCESS
@ CIR_SUCCESS
Variable was parsed and read.
Definition newgrf_internal.h:21
GetStationLayoutKey
uint16_t GetStationLayoutKey(uint8_t platforms, uint8_t length)
Get the station layout key for a given station layout size.
Definition newgrf_station.h:194
AddStringForMapping
void AddStringForMapping(GRFStringID source, std::function< void(StringID)> &&func)
Record a static StringID for getting translated later.
Definition newgrf_stringmapping.cpp:42
newgrf_stringmapping.h
NewGRF string mapping definition.
StringID
uint32_t StringID
Numeric value that represents a string, independent of the selected language.
Definition strings_type.h:19
Coord3D::x
T x
X coordinate.
Definition geometry_type.hpp:44
Coord3D::y
T y
Y coordinate.
Definition geometry_type.hpp:45
Coord3D::z
T z
Z coordinate.
Definition geometry_type.hpp:46
DrawTileSeqStruct
A tile child sprite and palette to draw for stations etc, with 3D bounding box.
Definition sprite.h:33
DrawTileSpriteSpan
Ground palette sprite of a tile, together with its sprite layout.
Definition sprite.h:67
DrawTileSprites::ground
PalSpriteID ground
Palette and sprite for the ground.
Definition sprite.h:53
GrfChangeInfoHandler
GRF feature handler.
Definition newgrf_internal.h:30
GrfProcessingState::grffile
GRFFile * grffile
Currently processed GRF file.
Definition newgrf_internal.h:67
GrfProcessingState::skip_sprites
int skip_sprites
Number of pseudo sprites to skip before processing the next one. (-1 to skip to end of file)
Definition newgrf_internal.h:72
NewGRFSpriteLayout
NewGRF supplied spritelayout.
Definition newgrf_commons.h:113
NewGRFSpriteLayout::consistent_max_offset
uint consistent_max_offset
Number of sprites in all referenced spritesets.
Definition newgrf_commons.h:121
SpriteBounds::origin
Coord3D< int8_t > origin
Position of northern corner within tile.
Definition sprite.h:19
SpriteBounds::extent
Coord3D< uint8_t > extent
Size of bounding box.
Definition sprite.h:20
StationSpec
Station specification.
Definition newgrf_station.h:123
StationSpec::layouts
std::unordered_map< uint16_t, std::vector< uint8_t > > layouts
Custom platform layouts, keyed by platform and length combined.
Definition newgrf_station.h:178
StationSpec::renderdata
std::vector< NewGRFSpriteLayout > renderdata
Number of tile layouts.
Definition newgrf_station.h:152
StationSpec::TileFlag::NoWires
@ NoWires
Tile should NOT contain catenary wires.
StationSpec::TileFlag::Pylons
@ Pylons
Tile should contain catenary pylons.
StationSpec::TileFlag::Blocked
@ Blocked
Tile is blocked to vehicles.
StrongType::Typedef< uint32_t, struct GRFStringIDTag, StrongType::Compare, StrongType::Integer >