OpenTTD Source 20250331-master-g3c15e0c889
newgrf_act14.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_text.h"
13#include "newgrf_bytereader.h"
14#include "newgrf_internal.h"
15
16#include "../safeguards.h"
17
19static bool ChangeGRFName(uint8_t langid, std::string_view str)
20{
21 AddGRFTextToList(_cur.grfconfig->name, langid, _cur.grfconfig->ident.grfid, false, str);
22 return true;
23}
24
26static bool ChangeGRFDescription(uint8_t langid, std::string_view str)
27{
28 AddGRFTextToList(_cur.grfconfig->info, langid, _cur.grfconfig->ident.grfid, true, str);
29 return true;
30}
31
33static bool ChangeGRFURL(uint8_t langid, std::string_view str)
34{
35 AddGRFTextToList(_cur.grfconfig->url, langid, _cur.grfconfig->ident.grfid, false, str);
36 return true;
37}
38
40static bool ChangeGRFNumUsedParams(size_t len, ByteReader &buf)
41{
42 if (len != 1) {
43 GrfMsg(2, "StaticGRFInfo: expected only 1 byte for 'INFO'->'NPAR' but got {}, ignoring this field", len);
44 buf.Skip(len);
45 } else {
46 _cur.grfconfig->num_valid_params = std::min(buf.ReadByte(), GRFConfig::MAX_NUM_PARAMS);
47 }
48 return true;
49}
50
52static bool ChangeGRFPalette(size_t len, ByteReader &buf)
53{
54 if (len != 1) {
55 GrfMsg(2, "StaticGRFInfo: expected only 1 byte for 'INFO'->'PALS' but got {}, ignoring this field", len);
56 buf.Skip(len);
57 } else {
58 char data = buf.ReadByte();
60 switch (data) {
61 case '*':
62 case 'A': pal = GRFP_GRF_ANY; break;
63 case 'W': pal = GRFP_GRF_WINDOWS; break;
64 case 'D': pal = GRFP_GRF_DOS; break;
65 default:
66 GrfMsg(2, "StaticGRFInfo: unexpected value '{:02X}' for 'INFO'->'PALS', ignoring this field", data);
67 break;
68 }
69 if (pal != GRFP_GRF_UNSET) {
70 _cur.grfconfig->palette &= ~GRFP_GRF_MASK;
71 _cur.grfconfig->palette |= pal;
72 }
73 }
74 return true;
75}
76
78static bool ChangeGRFBlitter(size_t len, ByteReader &buf)
79{
80 if (len != 1) {
81 GrfMsg(2, "StaticGRFInfo: expected only 1 byte for 'INFO'->'BLTR' but got {}, ignoring this field", len);
82 buf.Skip(len);
83 } else {
84 char data = buf.ReadByte();
86 switch (data) {
87 case '8': pal = GRFP_BLT_UNSET; break;
88 case '3': pal = GRFP_BLT_32BPP; break;
89 default:
90 GrfMsg(2, "StaticGRFInfo: unexpected value '{:02X}' for 'INFO'->'BLTR', ignoring this field", data);
91 return true;
92 }
93 _cur.grfconfig->palette &= ~GRFP_BLT_MASK;
94 _cur.grfconfig->palette |= pal;
95 }
96 return true;
97}
98
100static bool ChangeGRFVersion(size_t len, ByteReader &buf)
101{
102 if (len != 4) {
103 GrfMsg(2, "StaticGRFInfo: expected 4 bytes for 'INFO'->'VRSN' but got {}, ignoring this field", len);
104 buf.Skip(len);
105 } else {
106 /* Set min_loadable_version as well (default to minimal compatibility) */
108 }
109 return true;
110}
111
113static bool ChangeGRFMinVersion(size_t len, ByteReader &buf)
114{
115 if (len != 4) {
116 GrfMsg(2, "StaticGRFInfo: expected 4 bytes for 'INFO'->'MINV' but got {}, ignoring this field", len);
117 buf.Skip(len);
118 } else {
120 if (_cur.grfconfig->version == 0) {
121 GrfMsg(2, "StaticGRFInfo: 'MINV' defined before 'VRSN' or 'VRSN' set to 0, ignoring this field");
123 }
125 GrfMsg(2, "StaticGRFInfo: 'MINV' defined as {}, limiting it to 'VRSN'", _cur.grfconfig->min_loadable_version);
127 }
128 }
129 return true;
130}
131
133
135static bool ChangeGRFParamName(uint8_t langid, std::string_view str)
136{
137 AddGRFTextToList(_cur_parameter->name, langid, _cur.grfconfig->ident.grfid, false, str);
138 return true;
139}
140
142static bool ChangeGRFParamDescription(uint8_t langid, std::string_view str)
143{
144 AddGRFTextToList(_cur_parameter->desc, langid, _cur.grfconfig->ident.grfid, true, str);
145 return true;
146}
147
149static bool ChangeGRFParamType(size_t len, ByteReader &buf)
150{
151 if (len != 1) {
152 GrfMsg(2, "StaticGRFInfo: expected 1 byte for 'INFO'->'PARA'->'TYPE' but got {}, ignoring this field", len);
153 buf.Skip(len);
154 } else {
156 if (type < PTYPE_END) {
157 _cur_parameter->type = type;
158 } else {
159 GrfMsg(3, "StaticGRFInfo: unknown parameter type {}, ignoring this field", type);
160 }
161 }
162 return true;
163}
164
166static bool ChangeGRFParamLimits(size_t len, ByteReader &buf)
167{
169 GrfMsg(2, "StaticGRFInfo: 'INFO'->'PARA'->'LIMI' is only valid for parameters with type uint/enum, ignoring this field");
170 buf.Skip(len);
171 } else if (len != 8) {
172 GrfMsg(2, "StaticGRFInfo: expected 8 bytes for 'INFO'->'PARA'->'LIMI' but got {}, ignoring this field", len);
173 buf.Skip(len);
174 } else {
175 uint32_t min_value = buf.ReadDWord();
176 uint32_t max_value = buf.ReadDWord();
177 if (min_value <= max_value) {
178 _cur_parameter->min_value = min_value;
179 _cur_parameter->max_value = max_value;
180 } else {
181 GrfMsg(2, "StaticGRFInfo: 'INFO'->'PARA'->'LIMI' values are incoherent, ignoring this field");
182 }
183 }
184 return true;
185}
186
188static bool ChangeGRFParamMask(size_t len, ByteReader &buf)
189{
190 if (len < 1 || len > 3) {
191 GrfMsg(2, "StaticGRFInfo: expected 1 to 3 bytes for 'INFO'->'PARA'->'MASK' but got {}, ignoring this field", len);
192 buf.Skip(len);
193 } else {
194 uint8_t param_nr = buf.ReadByte();
195 if (param_nr >= GRFConfig::MAX_NUM_PARAMS) {
196 GrfMsg(2, "StaticGRFInfo: invalid parameter number in 'INFO'->'PARA'->'MASK', param {}, ignoring this field", param_nr);
197 buf.Skip(len - 1);
198 } else {
199 _cur_parameter->param_nr = param_nr;
200 if (len >= 2) _cur_parameter->first_bit = std::min<uint8_t>(buf.ReadByte(), 31);
201 if (len >= 3) _cur_parameter->num_bit = std::min<uint8_t>(buf.ReadByte(), 32 - _cur_parameter->first_bit);
202 }
203 }
204
205 return true;
206}
207
209static bool ChangeGRFParamDefault(size_t len, ByteReader &buf)
210{
211 if (len != 4) {
212 GrfMsg(2, "StaticGRFInfo: expected 4 bytes for 'INFO'->'PARA'->'DEFA' but got {}, ignoring this field", len);
213 buf.Skip(len);
214 } else {
216 }
217 _cur.grfconfig->has_param_defaults = true;
218 return true;
219}
220
221typedef bool (*DataHandler)(size_t, ByteReader &);
222typedef bool (*TextHandler)(uint8_t, std::string_view str);
223typedef bool (*BranchHandler)(ByteReader &);
224
234 using Span = std::pair<const AllowedSubtags *, const AllowedSubtags *>;
235
236 uint32_t id;
237 std::variant<DataHandler, TextHandler, BranchHandler, Span> handler;
238};
239
240static bool SkipUnknownInfo(ByteReader &buf, uint8_t type);
241static bool HandleNodes(ByteReader &buf, std::span<const AllowedSubtags> tags);
242
250{
251 uint8_t type = buf.ReadByte();
252 while (type != 0) {
253 uint32_t id = buf.ReadDWord();
254 if (type != 'T' || id > _cur_parameter->max_value) {
255 GrfMsg(2, "StaticGRFInfo: all child nodes of 'INFO'->'PARA'->param_num->'VALU' should have type 't' and the value/bit number as id");
256 if (!SkipUnknownInfo(buf, type)) return false;
257 type = buf.ReadByte();
258 continue;
259 }
260
261 uint8_t langid = buf.ReadByte();
262 std::string_view name_string = buf.ReadString();
263
264 auto it = std::ranges::lower_bound(_cur_parameter->value_names, id, std::less{}, &GRFParameterInfo::ValueName::first);
265 if (it == std::end(_cur_parameter->value_names) || it->first != id) {
266 it = _cur_parameter->value_names.emplace(it, id, GRFTextList{});
267 }
268 AddGRFTextToList(it->second, langid, _cur.grfconfig->ident.grfid, false, name_string);
269
270 type = buf.ReadByte();
271 }
272 return true;
273}
274
285
293{
294 uint8_t type = buf.ReadByte();
295 while (type != 0) {
296 uint32_t id = buf.ReadDWord();
297 if (type != 'C' || id >= _cur.grfconfig->num_valid_params) {
298 GrfMsg(2, "StaticGRFInfo: all child nodes of 'INFO'->'PARA' should have type 'C' and their parameter number as id");
299 if (!SkipUnknownInfo(buf, type)) return false;
300 type = buf.ReadByte();
301 continue;
302 }
303
304 if (id >= _cur.grfconfig->param_info.size()) {
305 _cur.grfconfig->param_info.resize(id + 1);
306 }
307 if (!_cur.grfconfig->param_info[id].has_value()) {
308 _cur.grfconfig->param_info[id] = GRFParameterInfo(id);
309 }
310 _cur_parameter = &_cur.grfconfig->param_info[id].value();
311 /* Read all parameter-data and process each node. */
312 if (!HandleNodes(buf, _tags_parameters)) return false;
313 type = buf.ReadByte();
314 }
315 return true;
316}
317
330
332static constexpr AllowedSubtags _tags_root[] = {
333 AllowedSubtags{'INFO', std::make_pair(std::begin(_tags_info), std::end(_tags_info))},
334};
335
336
343static bool SkipUnknownInfo(ByteReader &buf, uint8_t type)
344{
345 /* type and id are already read */
346 switch (type) {
347 case 'C': {
348 uint8_t new_type = buf.ReadByte();
349 while (new_type != 0) {
350 buf.ReadDWord(); // skip the id
351 if (!SkipUnknownInfo(buf, new_type)) return false;
352 new_type = buf.ReadByte();
353 }
354 break;
355 }
356
357 case 'T':
358 buf.ReadByte(); // lang
359 buf.ReadString(); // actual text
360 break;
361
362 case 'B': {
363 uint16_t size = buf.ReadWord();
364 buf.Skip(size);
365 break;
366 }
367
368 default:
369 return false;
370 }
371
372 return true;
373}
374
383static bool HandleNode(uint8_t type, uint32_t id, ByteReader &buf, std::span<const AllowedSubtags> subtags)
384{
385 /* Visitor to get a subtag handler's type. */
386 struct type_visitor {
387 char operator()(const DataHandler &) { return 'B'; }
388 char operator()(const TextHandler &) { return 'T'; }
389 char operator()(const BranchHandler &) { return 'C'; }
390 char operator()(const AllowedSubtags::Span &) { return 'C'; }
391 };
392
393 /* Visitor to evaluate a subtag handler. */
394 struct evaluate_visitor {
395 ByteReader &buf;
396
397 bool operator()(const DataHandler &handler)
398 {
399 size_t len = buf.ReadWord();
400 if (buf.Remaining() < len) return false;
401 return handler(len, buf);
402 }
403
404 bool operator()(const TextHandler &handler)
405 {
406 uint8_t langid = buf.ReadByte();
407 return handler(langid, buf.ReadString());
408 }
409
410 bool operator()(const BranchHandler &handler)
411 {
412 return handler(buf);
413 }
414
415 bool operator()(const AllowedSubtags::Span &subtags)
416 {
417 return HandleNodes(buf, {subtags.first, subtags.second});
418 }
419 };
420
421 for (const auto &tag : subtags) {
422 if (tag.id != std::byteswap(id) || std::visit(type_visitor{}, tag.handler) != type) continue;
423 return std::visit(evaluate_visitor{buf}, tag.handler);
424 }
425
426 GrfMsg(2, "StaticGRFInfo: unknown type/id combination found, type={:c}, id={:x}", type, id);
427 return SkipUnknownInfo(buf, type);
428}
429
436static bool HandleNodes(ByteReader &buf, std::span<const AllowedSubtags> subtags)
437{
438 uint8_t type = buf.ReadByte();
439 while (type != 0) {
440 uint32_t id = buf.ReadDWord();
441 if (!HandleNode(type, id, buf, subtags)) return false;
442 type = buf.ReadByte();
443 }
444 return true;
445}
446
451static void StaticGRFInfo(ByteReader &buf)
452{
453 /* <14> <type> <id> <text/data...> */
455}
456
457template <> void GrfActionHandler<0x14>::FileScan(ByteReader &buf) { StaticGRFInfo(buf); }
459template <> void GrfActionHandler<0x14>::LabelScan(ByteReader &) { }
460template <> void GrfActionHandler<0x14>::Init(ByteReader &) { }
461template <> void GrfActionHandler<0x14>::Reserve(ByteReader &) { }
Class to read from a NewGRF file.
uint32_t ReadDWord()
Read a single DWord (32 bits).
uint16_t ReadWord()
Read a single Word (16 bits).
std::string_view ReadString()
Read a string.
uint8_t ReadByte()
Read a single byte (8 bits).
static bool ChangeGRFParamType(size_t len, ByteReader &buf)
Callback function for 'INFO'->'PARAM'->param_num->'TYPE' to set the typeof a parameter.
bool(* BranchHandler)(ByteReader &)
Type of callback function for branch nodes.
static bool ChangeGRFParamLimits(size_t len, ByteReader &buf)
Callback function for 'INFO'->'PARAM'->param_num->'LIMI' to set the min/max value of a parameter.
static bool ChangeGRFNumUsedParams(size_t len, ByteReader &buf)
Callback function for 'INFO'->'NPAR' to set the number of valid parameters.
static constexpr AllowedSubtags _tags_info[]
Action14 tags for the INFO node.
static bool ChangeGRFURL(uint8_t langid, std::string_view str)
Callback function for 'INFO'->'URL_' to set the newgrf url.
bool(* DataHandler)(size_t, ByteReader &)
Type of callback function for binary nodes.
static bool ChangeGRFParamDescription(uint8_t langid, std::string_view str)
Callback function for 'INFO'->'PARAM'->param_num->'DESC' to set the description of a parameter.
static bool ChangeGRFMinVersion(size_t len, ByteReader &buf)
Callback function for 'INFO'->'MINV' to the minimum compatible version of the NewGRF.
static bool ChangeGRFParamDefault(size_t len, ByteReader &buf)
Callback function for 'INFO'->'PARAM'->param_num->'DFLT' to set the default value.
static bool ChangeGRFParamValueNames(ByteReader &buf)
Callback function for 'INFO'->'PARA'->param_num->'VALU' to set the names of some parameter values (ty...
static bool HandleParameterInfo(ByteReader &buf)
Callback function for 'INFO'->'PARA' to set extra information about the parameters.
static GRFParameterInfo * _cur_parameter
The parameter which info is currently changed by the newgrf.
static bool HandleNodes(ByteReader &buf, std::span< const AllowedSubtags > tags)
Handle the contents of a 'C' choice of an Action14.
static bool ChangeGRFName(uint8_t langid, std::string_view str)
Callback function for 'INFO'->'NAME' to add a translation to the newgrf name.
bool(* TextHandler)(uint8_t, std::string_view str)
Type of callback function for text nodes.
static constexpr AllowedSubtags _tags_root[]
Action14 root tags.
static void StaticGRFInfo(ByteReader &buf)
Handle Action 0x14.
static bool ChangeGRFParamMask(size_t len, ByteReader &buf)
Callback function for 'INFO'->'PARAM'->param_num->'MASK' to set the parameter and bits to use.
static bool SkipUnknownInfo(ByteReader &buf, uint8_t type)
Try to skip the current node and all subnodes (if it's a branch node).
static bool ChangeGRFDescription(uint8_t langid, std::string_view str)
Callback function for 'INFO'->'DESC' to add a translation to the newgrf description.
static bool ChangeGRFPalette(size_t len, ByteReader &buf)
Callback function for 'INFO'->'PALS' to set the number of valid parameters.
static bool ChangeGRFVersion(size_t len, ByteReader &buf)
Callback function for 'INFO'->'VRSN' to the version of the NewGRF.
static bool ChangeGRFParamName(uint8_t langid, std::string_view str)
Callback function for 'INFO'->'PARAM'->param_num->'NAME' to set the name of a parameter.
static bool HandleNode(uint8_t type, uint32_t id, ByteReader &buf, std::span< const AllowedSubtags > subtags)
Handle the nodes of an Action14.
static bool ChangeGRFBlitter(size_t len, ByteReader &buf)
Callback function for 'INFO'->'BLTR' to set the blitter info.
static constexpr AllowedSubtags _tags_parameters[]
Action14 parameter tags.
NewGRF buffer reader definition.
GRFParameterType
The possible types of a newgrf parameter.
@ PTYPE_UINT_ENUM
The parameter allows a range of numbers, each of which can have a special name.
@ PTYPE_END
Invalid parameter type.
GRFPalette
Information that can/has to be stored about a GRF's palette.
@ GRFP_GRF_UNSET
The NewGRF provided no information.
@ GRFP_BLT_UNSET
The NewGRF provided no information or doesn't care about a 32 bpp blitter.
@ GRFP_GRF_WINDOWS
The NewGRF says the Windows palette can be used.
@ GRFP_GRF_DOS
The NewGRF says the DOS palette can be used.
@ GRFP_GRF_ANY
The NewGRF says any palette can be used.
@ GRFP_BLT_32BPP
The NewGRF prefers a 32 bpp blitter.
NewGRF internal processing state.
static void AddGRFTextToList(GRFTextList &list, uint8_t langid, std::string_view text_to_add)
Add a new text to a GRFText list.
std::vector< GRFText > GRFTextList
A GRF text with a list of translations.
Data structure to store the allowed id/type combinations for action 14.
uint32_t id
The identifier for this node.
std::variant< DataHandler, TextHandler, BranchHandler, Span > handler
The handler for this node.
std::pair< const AllowedSubtags *, const AllowedSubtags * > Span
Custom 'span' of subtags.
GRFTextWrapper url
NOSAVE: URL belonging to this GRF.
uint8_t palette
GRFPalette, bitset.
GRFTextWrapper info
NOSAVE: GRF info (author, copyright, ...) (Action 0x08)
std::vector< std::optional< GRFParameterInfo > > param_info
NOSAVE: extra information about the parameters.
uint32_t version
NOSAVE: Version a NewGRF can set so only the newest NewGRF is shown.
bool has_param_defaults
NOSAVE: did this newgrf specify any defaults for it's parameters.
GRFTextWrapper name
NOSAVE: GRF name (Action 0x08)
uint8_t num_valid_params
NOSAVE: Number of valid parameters (action 0x14)
GRFIdentifier ident
grfid and md5sum to uniquely identify newgrfs
uint32_t min_loadable_version
NOSAVE: Minimum compatible version a NewGRF can define.
uint32_t grfid
GRF ID (defined by Action 0x08)
Information about one grf parameter.
uint8_t param_nr
GRF parameter to store content in.
uint32_t min_value
The minimal value this parameter can have.
uint8_t num_bit
Number of bits to use for this parameter.
uint8_t first_bit
First bit to use in the GRF parameter.
uint32_t max_value
The maximal value of this parameter.
GRFParameterType type
The type of this parameter.
std::vector< ValueName > value_names
Names for each value.
uint32_t def_value
Default value of this parameter.
GRFTextList name
The name of this parameter.
GRFTextList desc
The description of this parameter.
GRF action handler.
GRFConfig * grfconfig
Config of the currently processed GRF file.