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

OpenTTD Source 15.3

newgrf_act0.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_engine.h"
13#include "../newgrf_badge.h"
14#include "../newgrf_badge_type.h"
15#include "../newgrf_cargo.h"
16#include "../timer/timer_game_calendar.h"
17#include "../error.h"
18#include "../vehicle_base.h"
19#include "newgrf_bytereader.h"
20#include "newgrf_internal.h"
21
22#include "table/strings.h"
23
24#include "../safeguards.h"
25
33 ChangeInfoResult CommonVehicleChangeInfo(EngineInfo *ei, int prop, ByteReader &buf)
34{
35 switch (prop) {
36 case 0x00: // Introduction date
37 ei->base_intro = CalendarTime::DAYS_TILL_ORIGINAL_BASE_YEAR + buf.ReadWord();
38 break;
39
40 case 0x02: // Decay speed
41 ei->decay_speed = buf.ReadByte();
42 break;
43
44 case 0x03: // Vehicle life
45 ei->lifelength = TimerGameCalendar::Year{buf.ReadByte()};
46 break;
47
48 case 0x04: // Model life
49 ei->base_life = TimerGameCalendar::Year{buf.ReadByte()};
50 break;
51
52 case 0x06: // Climates available
53 ei->climates = LandscapeTypes{buf.ReadByte()};
54 break;
55
56 case PROP_VEHICLE_LOAD_AMOUNT: // 0x07 Loading speed
57 /* Amount of cargo loaded during a vehicle's "loading tick" */
58 ei->load_amount = buf.ReadByte();
59 break;
60
61 default:
62 return CIR_UNKNOWN;
63 }
64
65 return CIR_SUCCESS;
66}
67
72 void SkipBadgeList(ByteReader &buf)
73{
74 uint16_t count = buf.ReadWord();
75 while (count-- > 0) {
76 buf.ReadWord();
77 }
78}
79
86std::vector<BadgeID> ReadBadgeList(ByteReader &buf, GrfSpecFeature feature)
87{
88 uint16_t count = buf.ReadWord();
89
90 std::vector<BadgeID> badges;
91 badges.reserve(count);
92
93 while (count-- > 0) {
94 uint16_t local_index = buf.ReadWord();
95 if (local_index >= std::size(_cur_gps.grffile->badge_list)) {
96 GrfMsg(1, "ReadBadgeList: Badge label {} out of range (max {}), skipping.", local_index, std::size(_cur_gps.grffile->badge_list) - 1);
97 continue;
98 }
99
100 BadgeID index = _cur_gps.grffile->badge_list[local_index];
101
102 /* Is badge already present? */
103 if (std::ranges::find(badges, index) != std::end(badges)) continue;
104
105 badges.push_back(index);
106 MarkBadgeSeen(index, feature);
107 }
108
109 return badges;
110}
111
112bool HandleChangeInfoResult(std::string_view caller, ChangeInfoResult cir, GrfSpecFeature feature, uint8_t property)
113{
114 switch (cir) {
115 default: NOT_REACHED();
116
117 case CIR_DISABLED:
118 /* Error has already been printed; just stop parsing */
119 return true;
120
121 case CIR_SUCCESS:
122 return false;
123
124 case CIR_UNHANDLED:
125 GrfMsg(1, "{}: Ignoring property 0x{:02X} of feature 0x{:02X} (not implemented)", caller, property, feature);
126 return false;
127
128 case CIR_UNKNOWN:
129 GrfMsg(0, "{}: Unknown property 0x{:02X} of feature 0x{:02X}, disabling", caller, property, feature);
130 [[fallthrough]];
131
132 case CIR_INVALID_ID: {
133 /* No debug message for an invalid ID, as it has already been output */
134 GRFError *error = DisableGrf(cir == CIR_INVALID_ID ? STR_NEWGRF_ERROR_INVALID_ID : STR_NEWGRF_ERROR_UNKNOWN_PROPERTY);
135 if (cir != CIR_INVALID_ID) error->param_value[1] = property;
136 return true;
137 }
138 }
139}
140
142 struct InvokeGrfChangeInfoHandler {
143 template <GrfSpecFeature TFeature>
144 static ChangeInfoResult Invoke(uint first, uint last, int prop, ByteReader &buf, GrfLoadingStage stage)
145 {
146 switch (stage) {
147 case GLS_RESERVE: return GrfChangeInfoHandler<TFeature>::Reserve(first, last, prop, buf);
148 case GLS_ACTIVATION: return GrfChangeInfoHandler<TFeature>::Activation(first, last, prop, buf);
149 default: NOT_REACHED();
150 }
151 }
152
153 using Invoker = ChangeInfoResult(*)(uint first, uint last, int prop, ByteReader &buf, GrfLoadingStage stage);
154 static constexpr Invoker funcs[] { // Must be listed in feature order.
155 Invoke<GSF_TRAINS>, Invoke<GSF_ROADVEHICLES>, Invoke<GSF_SHIPS>, Invoke<GSF_AIRCRAFT>,
156 Invoke<GSF_STATIONS>, Invoke<GSF_CANALS>, Invoke<GSF_BRIDGES>, Invoke<GSF_HOUSES>,
157 Invoke<GSF_GLOBALVAR>, Invoke<GSF_INDUSTRYTILES>, Invoke<GSF_INDUSTRIES>, Invoke<GSF_CARGOES>,
158 Invoke<GSF_SOUNDFX>, Invoke<GSF_AIRPORTS>, nullptr /* GSF_SIGNALS */, Invoke<GSF_OBJECTS>,
159 Invoke<GSF_RAILTYPES>, Invoke<GSF_AIRPORTTILES>, Invoke<GSF_ROADTYPES>, Invoke<GSF_TRAMTYPES>,
160 Invoke<GSF_ROADSTOPS>, Invoke<GSF_BADGES>,
161 };
162
163 static ChangeInfoResult Invoke(GrfSpecFeature feature, uint first, uint last, int prop, ByteReader &buf, GrfLoadingStage stage)
164 {
165 Invoker func = feature < std::size(funcs) ? funcs[feature] : nullptr;
166 if (func == nullptr) return CIR_UNKNOWN;
167 return func(first, last, prop, buf, stage);
168 }
169};
170
171/* Action 0x00 */
172static void FeatureChangeInfo(ByteReader &buf)
173{
174 /* <00> <feature> <num-props> <num-info> <id> (<property <new-info>)...
175 *
176 * B feature
177 * B num-props how many properties to change per vehicle/station
178 * B num-info how many vehicles/stations to change
179 * E id ID of first vehicle/station to change, if num-info is
180 * greater than one, this one and the following
181 * vehicles/stations will be changed
182 * B property what property to change, depends on the feature
183 * V new-info new bytes of info (variable size; depends on properties) */
184
185 GrfSpecFeature feature{buf.ReadByte()};
186 uint8_t numprops = buf.ReadByte();
187 uint numinfo = buf.ReadByte();
188 uint engine = buf.ReadExtendedByte();
189
190 if (feature >= GSF_END) {
191 GrfMsg(1, "FeatureChangeInfo: Unsupported feature 0x{:02X}, skipping", feature);
192 return;
193 }
194
195 GrfMsg(6, "FeatureChangeInfo: Feature 0x{:02X}, {} properties, to apply to {}+{}",
196 feature, numprops, engine, numinfo);
197
198 /* Test if feature handles change. */
199 ChangeInfoResult cir_test = InvokeGrfChangeInfoHandler::Invoke(feature, 0, 0, 0, buf, GLS_ACTIVATION);
200 if (cir_test == CIR_UNHANDLED) return;
201 if (cir_test == CIR_UNKNOWN) {
202 GrfMsg(1, "FeatureChangeInfo: Unsupported feature 0x{:02X}, skipping", feature);
203 return;
204 }
205
206 /* Mark the feature as used by the grf */
207 _cur_gps.grffile->grf_features.Set(feature);
208
209 while (numprops-- && buf.HasData()) {
210 uint8_t prop = buf.ReadByte();
211
212 ChangeInfoResult cir = InvokeGrfChangeInfoHandler::Invoke(feature, engine, engine + numinfo, prop, buf, GLS_ACTIVATION);
213 if (HandleChangeInfoResult("FeatureChangeInfo", cir, feature, prop)) return;
214 }
215}
216
217/* Action 0x00 (GLS_SAFETYSCAN) */
218static void SafeChangeInfo(ByteReader &buf)
219{
220 GrfSpecFeature feature{buf.ReadByte()};
221 uint8_t numprops = buf.ReadByte();
222 uint numinfo = buf.ReadByte();
223 buf.ReadExtendedByte(); // id
224
225 if (feature == GSF_BRIDGES && numprops == 1) {
226 uint8_t prop = buf.ReadByte();
227 /* Bridge property 0x0D is redefinition of sprite layout tables, which
228 * is considered safe. */
229 if (prop == 0x0D) return;
230 } else if (feature == GSF_GLOBALVAR && numprops == 1) {
231 uint8_t prop = buf.ReadByte();
232 /* Engine ID Mappings are safe, if the source is static */
233 if (prop == 0x11) {
234 bool is_safe = true;
235 for (uint i = 0; i < numinfo; i++) {
236 uint32_t s = buf.ReadDWord();
237 buf.ReadDWord(); // dest
238 const GRFConfig *grfconfig = GetGRFConfig(s);
239 if (grfconfig != nullptr && !grfconfig->flags.Test(GRFConfigFlag::Static)) {
240 is_safe = false;
241 break;
242 }
243 }
244 if (is_safe) return;
245 }
246 }
247
248 GRFUnsafe(buf);
249}
250
251/* Action 0x00 (GLS_RESERVE) */
252static void ReserveChangeInfo(ByteReader &buf)
253{
254 GrfSpecFeature feature{buf.ReadByte()};
255
256 /* Test if feature handles reservation. */
257 ChangeInfoResult cir_test = InvokeGrfChangeInfoHandler::Invoke(feature, 0, 0, 0, buf, GLS_RESERVE);
258 if (cir_test == CIR_UNHANDLED) return;
259 if (cir_test == CIR_UNKNOWN) {
260 GrfMsg(1, "ReserveChangeInfo: Unsupported feature 0x{:02X}, skipping", feature);
261 return;
262 }
263
264 uint8_t numprops = buf.ReadByte();
265 uint8_t numinfo = buf.ReadByte();
266 uint16_t index = buf.ReadExtendedByte();
267
268 while (numprops-- && buf.HasData()) {
269 uint8_t prop = buf.ReadByte();
270
271 ChangeInfoResult cir = InvokeGrfChangeInfoHandler::Invoke(feature, index, index + numinfo, prop, buf, GLS_RESERVE);
272 if (HandleChangeInfoResult("ReserveChangeInfo", cir, feature, prop)) return;
273 }
274}
275
276template <> void GrfActionHandler<0x00>::FileScan(ByteReader &) { }
277template <> void GrfActionHandler<0x00>::SafetyScan(ByteReader &buf) { SafeChangeInfo(buf); }
278template <> void GrfActionHandler<0x00>::LabelScan(ByteReader &) { }
279template <> void GrfActionHandler<0x00>::Init(ByteReader &) { }
280template <> void GrfActionHandler<0x00>::Reserve(ByteReader &buf) { ReserveChangeInfo(buf); }
281template <> void GrfActionHandler<0x00>::Activation(ByteReader &buf) { FeatureChangeInfo(buf); }
BaseBitSet::Test
constexpr bool Test(Tvalue_type value) const
Test if the value-th bit is set.
Definition base_bitset_type.hpp:141
BaseBitSet::Set
constexpr Timpl & Set()
Set all bits.
Definition base_bitset_type.hpp:40
ByteReader
Class to read from a NewGRF file.
Definition newgrf_bytereader.h:18
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< LandscapeType, uint8_t >
TimerGameConst< struct Calendar >::DAYS_TILL_ORIGINAL_BASE_YEAR
static constexpr TimerGame< struct Calendar >::Date DAYS_TILL_ORIGINAL_BASE_YEAR
The date of the first day of the original base year.
Definition timer_game_common.h:184
GRFUnsafe
void GRFUnsafe(ByteReader &)
Set the current NewGRF as unsafe for static use.
Definition newgrf.cpp:373
DisableGrf
GRFError * DisableGrf(StringID message, GRFConfig *config)
Disable a GRF.
Definition newgrf.cpp:135
GrfSpecFeature
GrfSpecFeature
Definition newgrf.h:68
CommonVehicleChangeInfo
ChangeInfoResult CommonVehicleChangeInfo(EngineInfo *ei, int prop, ByteReader &buf)
Define properties common to all vehicles.
Definition newgrf_act0.cpp:33
ReadBadgeList
std::vector< BadgeID > ReadBadgeList(ByteReader &buf, GrfSpecFeature feature)
Read a list of badges.
Definition newgrf_act0.cpp:86
SkipBadgeList
void SkipBadgeList(ByteReader &buf)
Skip a list of badges.
Definition newgrf_act0.cpp:72
MarkBadgeSeen
void MarkBadgeSeen(BadgeID index, GrfSpecFeature feature)
Mark a badge a seen (used) by a feature.
Definition newgrf_badge.cpp:236
newgrf_bytereader.h
NewGRF buffer reader definition.
GetGRFConfig
GRFConfig * GetGRFConfig(uint32_t grfid, uint32_t mask)
Retrieve a NewGRF from the current config by its grfid.
Definition newgrf_config.cpp:622
Static
@ Static
GRF file is used statically (can be used in any MP game)
Definition newgrf_config.h:23
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
PROP_VEHICLE_LOAD_AMOUNT
@ PROP_VEHICLE_LOAD_AMOUNT
Loading speed.
Definition newgrf_properties.h:19
EngineInfo
Information about a vehicle.
Definition engine_type.h:198
EngineInfo::base_life
TimerGameCalendar::Year base_life
Basic duration of engine availability (without random parts). 0xFF means infinite life.
Definition engine_type.h:201
EngineInfo::climates
LandscapeTypes climates
Climates supported by the engine.
Definition engine_type.h:204
EngineInfo::base_intro
TimerGameCalendar::Date base_intro
Basic date of engine introduction (without random parts).
Definition engine_type.h:199
EngineInfo::lifelength
TimerGameCalendar::Year lifelength
Lifetime of a single vehicle.
Definition engine_type.h:200
GRFConfig
Information about GRF, used in the game and (part of it) in savegames.
Definition newgrf_config.h:158
GRFConfig::flags
GRFConfigFlags flags
NOSAVE: GCF_Flags, bitset.
Definition newgrf_config.h:178
GRFError
Information about why GRF had problems during initialisation.
Definition newgrf_config.h:109
GRFError::param_value
std::array< uint32_t, 2 > param_value
Values of GRF parameters to show for message and custom_message.
Definition newgrf_config.h:118
GRFFile::badge_list
std::vector< BadgeID > badge_list
Badge translation table (local index -> global index)
Definition newgrf.h:138
GRFFile::grf_features
GrfSpecFeatures grf_features
Bitset of GrfSpecFeature the grf uses.
Definition newgrf.h:158
GrfActionHandler
GRF action handler.
Definition newgrf_internal.h:37
GrfChangeInfoHandler
GRF feature handler.
Definition newgrf_internal.h:30
GrfProcessingState::grffile
GRFFile * grffile
Currently processed GRF file.
Definition newgrf_internal.h:67
InvokeGrfChangeInfoHandler
Helper class to invoke a GrfChangeInfoHandler.
Definition newgrf_act0.cpp:142
StrongType::Typedef
Templated helper to make a type-safe 'typedef' representing a single POD value.
Definition strong_typedef_type.hpp:130