OpenTTD Source 20250328-master-gc3457cd4c0
newgrf_act5.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 "../gfx_type.h"
13#include "../newgrf_act5.h"
14#include "../spritecache.h"
15#include "newgrf_bytereader.h"
16#include "newgrf_internal.h"
17
18#include "../table/sprites.h"
19
20#include "../safeguards.h"
21
30static uint16_t SanitizeSpriteOffset(uint16_t &num, uint16_t offset, int max_sprites, const std::string_view name)
31{
32
33 if (offset >= max_sprites) {
34 GrfMsg(1, "GraphicsNew: {} sprite offset must be less than {}, skipping", name, max_sprites);
35 uint orig_num = num;
36 num = 0;
37 return orig_num;
38 }
39
40 if (offset + num > max_sprites) {
41 GrfMsg(4, "GraphicsNew: {} sprite overflow, truncating...", name);
42 uint orig_num = num;
43 num = std::max(max_sprites - offset, 0);
44 return orig_num - num;
45 }
46
47 return 0;
48}
49
50
52static constexpr auto _action5_types = std::to_array<Action5Type>({
53 /* Note: min_sprites should not be changed. Therefore these constants are directly here and not in sprites.h */
54 /* 0x00 */ { A5BLOCK_INVALID, 0, 0, 0, "Type 0x00" },
55 /* 0x01 */ { A5BLOCK_INVALID, 0, 0, 0, "Type 0x01" },
56 /* 0x02 */ { A5BLOCK_INVALID, 0, 0, 0, "Type 0x02" },
57 /* 0x03 */ { A5BLOCK_INVALID, 0, 0, 0, "Type 0x03" },
58 /* 0x04 */ { A5BLOCK_ALLOW_OFFSET, SPR_SIGNALS_BASE, 1, PRESIGNAL_SEMAPHORE_AND_PBS_SPRITE_COUNT, "Signal graphics" },
59 /* 0x05 */ { A5BLOCK_ALLOW_OFFSET, SPR_ELRAIL_BASE, 1, ELRAIL_SPRITE_COUNT, "Rail catenary graphics" },
60 /* 0x06 */ { A5BLOCK_ALLOW_OFFSET, SPR_SLOPES_BASE, 1, NORMAL_AND_HALFTILE_FOUNDATION_SPRITE_COUNT, "Foundation graphics" },
61 /* 0x07 */ { A5BLOCK_INVALID, 0, 75, 0, "TTDP GUI graphics" }, // Not used by OTTD.
62 /* 0x08 */ { A5BLOCK_ALLOW_OFFSET, SPR_CANALS_BASE, 1, CANALS_SPRITE_COUNT, "Canal graphics" },
63 /* 0x09 */ { A5BLOCK_ALLOW_OFFSET, SPR_ONEWAY_BASE, 1, ONEWAY_SPRITE_COUNT, "One way road graphics" },
64 /* 0x0A */ { A5BLOCK_ALLOW_OFFSET, SPR_2CCMAP_BASE, 1, TWOCCMAP_SPRITE_COUNT, "2CC colour maps" },
65 /* 0x0B */ { A5BLOCK_ALLOW_OFFSET, SPR_TRAMWAY_BASE, 1, TRAMWAY_SPRITE_COUNT, "Tramway graphics" },
66 /* 0x0C */ { A5BLOCK_INVALID, 0, 133, 0, "Snowy temperate tree" }, // Not yet used by OTTD.
67 /* 0x0D */ { A5BLOCK_FIXED, SPR_SHORE_BASE, 16, SHORE_SPRITE_COUNT, "Shore graphics" },
68 /* 0x0E */ { A5BLOCK_INVALID, 0, 0, 0, "New Signals graphics" }, // Not yet used by OTTD.
69 /* 0x0F */ { A5BLOCK_ALLOW_OFFSET, SPR_TRACKS_FOR_SLOPES_BASE, 1, TRACKS_FOR_SLOPES_SPRITE_COUNT, "Sloped rail track" },
70 /* 0x10 */ { A5BLOCK_ALLOW_OFFSET, SPR_AIRPORTX_BASE, 1, AIRPORTX_SPRITE_COUNT, "Airport graphics" },
71 /* 0x11 */ { A5BLOCK_ALLOW_OFFSET, SPR_ROADSTOP_BASE, 1, ROADSTOP_SPRITE_COUNT, "Road stop graphics" },
72 /* 0x12 */ { A5BLOCK_ALLOW_OFFSET, SPR_AQUEDUCT_BASE, 1, AQUEDUCT_SPRITE_COUNT, "Aqueduct graphics" },
73 /* 0x13 */ { A5BLOCK_ALLOW_OFFSET, SPR_AUTORAIL_BASE, 1, AUTORAIL_SPRITE_COUNT, "Autorail graphics" },
74 /* 0x14 */ { A5BLOCK_INVALID, 0, 1, 0, "Flag graphics" }, // deprecated, no longer used.
75 /* 0x15 */ { A5BLOCK_ALLOW_OFFSET, SPR_OPENTTD_BASE, 1, OPENTTD_SPRITE_COUNT, "OpenTTD GUI graphics" },
76 /* 0x16 */ { A5BLOCK_ALLOW_OFFSET, SPR_AIRPORT_PREVIEW_BASE, 1, AIRPORT_PREVIEW_SPRITE_COUNT, "Airport preview graphics" },
77 /* 0x17 */ { A5BLOCK_ALLOW_OFFSET, SPR_RAILTYPE_TUNNEL_BASE, 1, RAILTYPE_TUNNEL_BASE_COUNT, "Railtype tunnel base" },
78 /* 0x18 */ { A5BLOCK_ALLOW_OFFSET, SPR_PALETTE_BASE, 1, PALETTE_SPRITE_COUNT, "Palette" },
79 /* 0x19 */ { A5BLOCK_ALLOW_OFFSET, SPR_ROAD_WAYPOINTS_BASE, 1, ROAD_WAYPOINTS_SPRITE_COUNT, "Road waypoints" },
80 /* 0x1A */ { A5BLOCK_ALLOW_OFFSET, SPR_OVERLAY_ROCKS_BASE, 1, OVERLAY_ROCKS_SPRITE_COUNT, "Overlay rocks" },
81});
82
87std::span<const Action5Type> GetAction5Types()
88{
89 return _action5_types;
90}
91
92/* Action 0x05 */
93static void GraphicsNew(ByteReader &buf)
94{
95 /* <05> <graphics-type> <num-sprites> <other data...>
96 *
97 * B graphics-type What set of graphics the sprites define.
98 * E num-sprites How many sprites are in this set?
99 * V other data Graphics type specific data. Currently unused. */
100
101 uint8_t type = buf.ReadByte();
102 uint16_t num = buf.ReadExtendedByte();
103 uint16_t offset = HasBit(type, 7) ? buf.ReadExtendedByte() : 0;
104 ClrBit(type, 7); // Clear the high bit as that only indicates whether there is an offset.
105
106 if ((type == 0x0D) && (num == 10) && _cur.grfconfig->flags.Test(GRFConfigFlag::System)) {
107 /* Special not-TTDP-compatible case used in openttd.grf
108 * Missing shore sprites and initialisation of SPR_SHORE_BASE */
109 GrfMsg(2, "GraphicsNew: Loading 10 missing shore sprites from extra grf.");
110 LoadNextSprite(SPR_SHORE_BASE + 0, *_cur.file, _cur.nfo_line++); // SLOPE_STEEP_S
111 LoadNextSprite(SPR_SHORE_BASE + 5, *_cur.file, _cur.nfo_line++); // SLOPE_STEEP_W
112 LoadNextSprite(SPR_SHORE_BASE + 7, *_cur.file, _cur.nfo_line++); // SLOPE_WSE
113 LoadNextSprite(SPR_SHORE_BASE + 10, *_cur.file, _cur.nfo_line++); // SLOPE_STEEP_N
114 LoadNextSprite(SPR_SHORE_BASE + 11, *_cur.file, _cur.nfo_line++); // SLOPE_NWS
115 LoadNextSprite(SPR_SHORE_BASE + 13, *_cur.file, _cur.nfo_line++); // SLOPE_ENW
116 LoadNextSprite(SPR_SHORE_BASE + 14, *_cur.file, _cur.nfo_line++); // SLOPE_SEN
117 LoadNextSprite(SPR_SHORE_BASE + 15, *_cur.file, _cur.nfo_line++); // SLOPE_STEEP_E
118 LoadNextSprite(SPR_SHORE_BASE + 16, *_cur.file, _cur.nfo_line++); // SLOPE_EW
119 LoadNextSprite(SPR_SHORE_BASE + 17, *_cur.file, _cur.nfo_line++); // SLOPE_NS
121 return;
122 }
123
124 /* Supported type? */
125 if ((type >= std::size(_action5_types)) || (_action5_types[type].block_type == A5BLOCK_INVALID)) {
126 GrfMsg(2, "GraphicsNew: Custom graphics (type 0x{:02X}) sprite block of length {} (unimplemented, ignoring)", type, num);
127 _cur.skip_sprites = num;
128 return;
129 }
130
131 const Action5Type *action5_type = &_action5_types[type];
132
133 /* Contrary to TTDP we allow always to specify too few sprites as we allow always an offset,
134 * except for the long version of the shore type:
135 * Ignore offset if not allowed */
136 if ((action5_type->block_type != A5BLOCK_ALLOW_OFFSET) && (offset != 0)) {
137 GrfMsg(1, "GraphicsNew: {} (type 0x{:02X}) do not allow an <offset> field. Ignoring offset.", action5_type->name, type);
138 offset = 0;
139 }
140
141 /* Ignore action5 if too few sprites are specified. (for TTDP compatibility)
142 * This does not make sense, if <offset> is allowed */
143 if ((action5_type->block_type == A5BLOCK_FIXED) && (num < action5_type->min_sprites)) {
144 GrfMsg(1, "GraphicsNew: {} (type 0x{:02X}) count must be at least {}. Only {} were specified. Skipping.", action5_type->name, type, action5_type->min_sprites, num);
145 _cur.skip_sprites = num;
146 return;
147 }
148
149 /* Load at most max_sprites sprites. Skip remaining sprites. (for compatibility with TTDP and future extensions) */
150 uint16_t skip_num = SanitizeSpriteOffset(num, offset, action5_type->max_sprites, action5_type->name);
151 SpriteID replace = action5_type->sprite_base + offset;
152
153 /* Load <num> sprites starting from <replace>, then skip <skip_num> sprites. */
154 GrfMsg(2, "GraphicsNew: Replacing sprites {} to {} of {} (type 0x{:02X}) at SpriteID 0x{:04X}", offset, offset + num - 1, action5_type->name, type, replace);
155
157
158 if (type == 0x0B) {
159 static const SpriteID depot_with_track_offset = SPR_TRAMWAY_DEPOT_WITH_TRACK - SPR_TRAMWAY_BASE;
160 static const SpriteID depot_no_track_offset = SPR_TRAMWAY_DEPOT_NO_TRACK - SPR_TRAMWAY_BASE;
161 if (offset <= depot_with_track_offset && offset + num > depot_with_track_offset) _loaded_newgrf_features.tram = TRAMWAY_REPLACE_DEPOT_WITH_TRACK;
162 if (offset <= depot_no_track_offset && offset + num > depot_no_track_offset) _loaded_newgrf_features.tram = TRAMWAY_REPLACE_DEPOT_NO_TRACK;
163 }
164
165 /* If the baseset or grf only provides sprites for flat tiles (pre #10282), duplicate those for use on slopes. */
166 bool dup_oneway_sprites = ((type == 0x09) && (offset + num <= ONEWAY_SLOPE_N_OFFSET));
167
168 for (; num > 0; num--) {
169 _cur.nfo_line++;
170 SpriteID load_index = (replace == 0 ? _cur.spriteid++ : replace++);
171 LoadNextSprite(load_index, *_cur.file, _cur.nfo_line);
172 if (dup_oneway_sprites) {
173 DupSprite(load_index, load_index + ONEWAY_SLOPE_N_OFFSET);
174 DupSprite(load_index, load_index + ONEWAY_SLOPE_S_OFFSET);
175 }
176 }
177
178 _cur.skip_sprites = skip_num;
179}
180
181/* Action 0x05 (SKIP) */
182static void SkipAct5(ByteReader &buf)
183{
184 /* Ignore type byte */
185 buf.ReadByte();
186
187 /* Skip the sprites of this action */
188 _cur.skip_sprites = buf.ReadExtendedByte();
189
190 GrfMsg(3, "SkipAct5: Skipping {} sprites", _cur.skip_sprites);
191}
192
193template <> void GrfActionHandler<0x05>::FileScan(ByteReader &buf) { SkipAct5(buf); }
194template <> void GrfActionHandler<0x05>::SafetyScan(ByteReader &buf) { SkipAct5(buf); }
195template <> void GrfActionHandler<0x05>::LabelScan(ByteReader &buf) { SkipAct5(buf); }
196template <> void GrfActionHandler<0x05>::Init(ByteReader &buf) { SkipAct5(buf); }
197template <> void GrfActionHandler<0x05>::Reserve(ByteReader &buf) { SkipAct5(buf); }
198template <> void GrfActionHandler<0x05>::Activation(ByteReader &buf) { GraphicsNew(buf); }
debug_inline constexpr bool HasBit(const T x, const uint8_t y)
Checks if a bit in a value is set.
constexpr T ClrBit(T &x, const uint8_t y)
Clears a bit in a variable.
constexpr bool Test(Tvalue_type value) const
Test if the value-th bit is set.
Class to read from a NewGRF file.
uint16_t ReadExtendedByte()
Read a single Extended Byte (8 or 16 bits).
uint8_t ReadByte()
Read a single byte (8 bits).
uint32_t SpriteID
The number of a sprite, without mapping bits and colourtables.
Definition gfx_type.h:17
GRFLoadedFeatures _loaded_newgrf_features
Indicates which are the newgrf features currently loaded ingame.
Definition newgrf.cpp:72
@ SHORE_REPLACE_NONE
No shore sprites were replaced.
Definition newgrf.h:171
@ SHORE_REPLACE_ONLY_NEW
Only corner-shores were loaded by Action5 (openttd(w/d).grf only).
Definition newgrf.h:174
@ SHORE_REPLACE_ACTION_5
Shore sprites were replaced by Action5.
Definition newgrf.h:172
@ TRAMWAY_REPLACE_DEPOT_WITH_TRACK
Electrified depot graphics with tram track were loaded.
Definition newgrf.h:179
@ TRAMWAY_REPLACE_DEPOT_NO_TRACK
Electrified depot graphics without tram track were loaded.
Definition newgrf.h:180
std::span< const Action5Type > GetAction5Types()
Get list of all action 5 types.
static uint16_t SanitizeSpriteOffset(uint16_t &num, uint16_t offset, int max_sprites, const std::string_view name)
Sanitize incoming sprite offsets for Action 5 graphics replacements.
static constexpr auto _action5_types
The information about action 5 types.
@ A5BLOCK_ALLOW_OFFSET
Allow replacing any subset by specifiing an offset.
Definition newgrf_act5.h:16
@ A5BLOCK_INVALID
unknown/not-implemented type
Definition newgrf_act5.h:17
@ A5BLOCK_FIXED
Only allow replacing a whole block of sprites. (TTDP compatible)
Definition newgrf_act5.h:15
NewGRF buffer reader definition.
@ System
GRF file is an openttd-internal system grf.
NewGRF internal processing state.
bool LoadNextSprite(SpriteID load_index, SpriteFile &file, uint file_sprite_id)
Load a real or recolour sprite.
static constexpr SpriteID SPR_OVERLAY_ROCKS_BASE
Overlay rocks sprites.
Definition sprites.h:319
static const SpriteID SPR_AQUEDUCT_BASE
Sprites for the Aqueduct.
Definition sprites.h:186
static const SpriteID SPR_OPENTTD_BASE
Extra graphic spritenumbers.
Definition sprites.h:56
static const SpriteID SPR_TRACKS_FOR_SLOPES_BASE
Sprites for 'highlighting' tracks on sloped land.
Definition sprites.h:198
static const SpriteID SPR_ROAD_WAYPOINTS_BASE
Road waypoint sprites.
Definition sprites.h:311
static const SpriteID SPR_RAILTYPE_TUNNEL_BASE
Tunnel sprites with grass only for custom railtype tunnel.
Definition sprites.h:299
static const SpriteID SPR_TRAMWAY_BASE
Tramway sprites.
Definition sprites.h:272
static const SpriteID SPR_AIRPORT_PREVIEW_BASE
Airport preview sprites.
Definition sprites.h:248
static const SpriteID SPR_ONEWAY_BASE
One way road sprites.
Definition sprites.h:293
static const SpriteID SPR_SHORE_BASE
shore tiles - action 05-0D
Definition sprites.h:224
Information about a single action 5 type.
Definition newgrf_act5.h:21
Action5BlockType block_type
How is this Action5 type processed?
Definition newgrf_act5.h:22
uint16_t max_sprites
If the Action5 contains more sprites, only the first max_sprites sprites will be used.
Definition newgrf_act5.h:25
uint16_t min_sprites
If the Action5 contains less sprites, the whole block will be ignored.
Definition newgrf_act5.h:24
SpriteID sprite_base
Load the sprites starting from this sprite.
Definition newgrf_act5.h:23
const std::string_view name
Name for error messages.
Definition newgrf_act5.h:26
GRFConfigFlags flags
NOSAVE: GCF_Flags, bitset.
ShoreReplacement shore
In which way shore sprites were replaced.
Definition newgrf.h:186
TramReplacement tram
In which way tram depots were replaced.
Definition newgrf.h:187
GRF action handler.
SpriteFile * file
File of currently processed GRF file.
uint32_t nfo_line
Currently processed pseudo sprite number in the GRF.
SpriteID spriteid
First available SpriteID for loading realsprites.
GRFConfig * grfconfig
Config of the 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)