OpenTTD Source 20250331-master-g3c15e0c889
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 <http://www.gnu.org/licenses/>.
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"
16
17#include "../safeguards.h"
18
20static const uint NUM_STATIONS_PER_GRF = UINT16_MAX - 1;
21
30static ChangeInfoResult StationChangeInfo(uint first, uint last, int prop, ByteReader &buf)
31{
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.grffile->stations.size() < last) _cur.grffile->stations.resize(last);
41
42 for (uint id = first; id < last; ++id) {
43 auto &statspec = _cur.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.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.delta_x = delta_x;
94 dtss.delta_y = buf.ReadByte();
95 dtss.delta_z = buf.ReadByte();
96 dtss.size_x = buf.ReadByte();
97 dtss.size_y = buf.ReadByte();
98 dtss.size_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.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.grffile->stations.size() ? nullptr : _cur.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.grffile->stations.size() ? nullptr : _cur.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.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 = 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 = 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 default:
298 ret = CIR_UNKNOWN;
299 break;
300 }
301 }
302
303 return ret;
304}
305
307template <> ChangeInfoResult GrfChangeInfoHandler<GSF_STATIONS>::Activation(uint first, uint last, int prop, ByteReader &buf) { return StationChangeInfo(first, last, prop, buf); }
debug_inline constexpr bool HasBit(const T x, const uint8_t y)
Checks if a bit in a value is set.
Class to read from a NewGRF file.
uint32_t PeekDWord()
Read a single DWord (32 bits).
uint32_t ReadDWord()
Read a single DWord (32 bits).
uint16_t ReadWord()
Read a single Word (16 bits).
uint16_t ReadExtendedByte()
Read a single Extended Byte (8 or 16 bits).
uint8_t ReadByte()
Read a single byte (8 bits).
StringID name
Name of this class.
static NewGRFClass * Get(Tindex class_index)
Get a particular class.
static Tindex Allocate(uint32_t global_id)
Allocate a class with a given global class ID.
CargoTypes TranslateRefitMask(uint32_t refit_mask)
Translate the refit mask.
Definition newgrf.cpp:307
std::vector< BadgeID > ReadBadgeList(ByteReader &buf, GrfSpecFeature feature)
Read a list of badges.
static ChangeInfoResult StationChangeInfo(uint first, uint last, int prop, ByteReader &buf)
Define properties for stations.
static const uint NUM_STATIONS_PER_GRF
The maximum amount of stations a single GRF is allowed to add.
bool ReadSpriteLayout(ByteReader &buf, uint num_building_sprites, bool use_cur_spritesets, uint8_t feature, bool allow_var10, bool no_z_position, NewGRFSpriteLayout *dts)
Read a spritelayout from the GRF.
TileLayoutFlags ReadSpriteLayoutSprite(ByteReader &buf, bool read_flags, bool invert_action1_flag, bool use_cur_spritesets, int 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.
NewGRF buffer reader definition.
NewGRF internal processing state.
ChangeInfoResult
Possible return values for the GrfChangeInfoHandler functions.
@ CIR_INVALID_ID
Attempt to modify an invalid ID.
@ CIR_DISABLED
GRF was disabled due to error.
@ CIR_UNKNOWN
Variable is unknown.
@ CIR_UNHANDLED
Variable was parsed but unread.
@ CIR_SUCCESS
Variable was parsed and read.
uint16_t GetStationLayoutKey(uint8_t platforms, uint8_t length)
Get the station layout key for a given station layout size.
void AddStringForMapping(GRFStringID source, std::function< void(StringID)> &&func)
Record a static StringID for getting translated later.
NewGRF string mapping definition.
uint32_t StringID
Numeric value that represents a string, independent of the selected language.
A tile child sprite and palette to draw for stations etc, with 3D bounding box.
Definition sprite.h:25
int8_t delta_z
0x80 identifies child sprites
Definition sprite.h:28
Ground palette sprite of a tile, together with its sprite layout.
Definition sprite.h:61
PalSpriteID ground
Palette and sprite for the ground.
Definition sprite.h:47
GRF feature handler.
GRFFile * grffile
Currently processed GRF file.
int skip_sprites
Number of pseudo sprites to skip before processing the next one. (-1 to skip to end of file)
NewGRF supplied spritelayout.
uint consistent_max_offset
Number of sprites in all referenced spritesets.
Station specification.
std::unordered_map< uint16_t, std::vector< uint8_t > > layouts
Custom platform layouts, keyed by platform and length combined.
std::vector< NewGRFSpriteLayout > renderdata
Number of tile layouts.
@ NoWires
Tile should NOT contain catenary wires.
@ Pylons
Tile should contain catenary pylons.
@ Blocked
Tile is blocked to vehicles.