OpenTTD Source 20260721-master-g25ec12c62d
newgrf_act11.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
9
10#include "../stdafx.h"
11#include "../debug.h"
12#include "../newgrf_house.h"
13#include "../newgrf_sound.h"
14#include "../spritecache.h"
15#include "../string_func.h"
16#include "newgrf_bytereader.h"
17#include "newgrf_internal.h"
18
19#include "../safeguards.h"
20
25static void ImportGRFSound(SoundEntry *sound)
26{
27 const GRFFile *file;
28 GrfID grfid = UnflattenNewGRFLabel<GrfID>(_cur_gps.file->ReadDword());
29 SoundID sound_id = _cur_gps.file->ReadWord();
30
31 file = GetFileByGRFID(grfid);
32 if (file == nullptr || file->sound_offset == 0) {
33 GrfMsg(1, "ImportGRFSound: Source file not available");
34 return;
35 }
36
37 if (sound_id >= file->num_sounds) {
38 GrfMsg(1, "ImportGRFSound: Sound effect {} is invalid", sound_id);
39 return;
40 }
41
42 GrfMsg(2, "ImportGRFSound: Copying sound {} ({}) from file {}", sound_id, file->sound_offset + sound_id, FormatArrayAsHex(grfid));
43
44 *sound = *GetSound(file->sound_offset + sound_id);
45
46 /* Reset volume and priority, which TTDPatch doesn't copy */
47 sound->volume = SOUND_EFFECT_MAX_VOLUME;
48 sound->priority = 0;
49}
50
56static void LoadGRFSound(size_t offs, SoundEntry *sound)
57{
58 /* Set default volume and priority */
59 sound->volume = SOUND_EFFECT_MAX_VOLUME;
60 sound->priority = 0;
61
62 if (offs != SIZE_MAX) {
63 /* Sound is present in the NewGRF. */
64 sound->file = _cur_gps.file;
65 sound->file_offset = offs;
66 sound->source = SoundSource::NewGRF;
67 sound->grf_container_ver = _cur_gps.file->GetContainerVersion();
68 }
69}
70
71/* Action 0x11 */
72static void GRFSound(ByteReader &buf)
73{
74 /* <11> <num>
75 *
76 * W num Number of sound files that follow */
77
78 uint16_t num = buf.ReadWord();
79 if (num == 0) return;
80
81 SoundEntry *sound;
82 if (_cur_gps.grffile->sound_offset == 0) {
83 _cur_gps.grffile->sound_offset = GetNumSounds();
84 _cur_gps.grffile->num_sounds = num;
85 sound = AllocateSound(num);
86 } else {
87 sound = GetSound(_cur_gps.grffile->sound_offset);
88 }
89
90 SpriteFile &file = *_cur_gps.file;
91 uint8_t grf_container_version = file.GetContainerVersion();
92 for (int i = 0; i < num; i++) {
93 _cur_gps.nfo_line++;
94
95 /* Check whether the index is in range. This might happen if multiple action 11 are present.
96 * While this is invalid, we do not check for this. But we should prevent it from causing bigger trouble */
97 bool invalid = i >= _cur_gps.grffile->num_sounds;
98
99 size_t offs = file.GetPos();
100
101 uint32_t len = grf_container_version >= 2 ? file.ReadDword() : file.ReadWord();
102 uint8_t type = file.ReadByte();
103
104 if (grf_container_version >= 2 && type == 0xFD) {
105 /* Reference to sprite section. */
106 if (invalid) {
107 GrfMsg(1, "GRFSound: Sound index out of range (multiple Action 11?)");
108 file.SkipBytes(len);
109 } else if (len != 4) {
110 GrfMsg(1, "GRFSound: Invalid sprite section import");
111 file.SkipBytes(len);
112 } else {
113 uint32_t id = file.ReadDword();
114 if (_cur_gps.stage == GrfLoadingStage::Init) LoadGRFSound(GetGRFSpriteOffset(id), sound + i);
115 }
116 continue;
117 }
118
119 if (type != 0xFF) {
120 GrfMsg(1, "GRFSound: Unexpected RealSprite found, skipping");
121 file.SkipBytes(7);
122 SkipSpriteData(*_cur_gps.file, type, len - 8);
123 continue;
124 }
125
126 if (invalid) {
127 GrfMsg(1, "GRFSound: Sound index out of range (multiple Action 11?)");
128 file.SkipBytes(len);
129 }
130
131 uint8_t action = file.ReadByte();
132 switch (action) {
133 case 0xFF:
134 /* Allocate sound only in init stage. */
135 if (_cur_gps.stage == GrfLoadingStage::Init) {
136 if (grf_container_version >= 2) {
137 GrfMsg(1, "GRFSound: Inline sounds are not supported for container version >= 2");
138 } else {
139 LoadGRFSound(offs, sound + i);
140 }
141 }
142 file.SkipBytes(len - 1); // already read <action>
143 break;
144
145 case 0xFE:
146 if (_cur_gps.stage == GrfLoadingStage::Activation) {
147 /* XXX 'Action 0xFE' isn't really specified. It is only mentioned for
148 * importing sounds, so this is probably all wrong... */
149 if (file.ReadByte() != 0) GrfMsg(1, "GRFSound: Import type mismatch");
150 ImportGRFSound(sound + i);
151 } else {
152 file.SkipBytes(len - 1); // already read <action>
153 }
154 break;
155
156 default:
157 GrfMsg(1, "GRFSound: Unexpected Action {:x} found, skipping", action);
158 file.SkipBytes(len - 1); // already read <action>
159 break;
160 }
161 }
162}
163
164/* Action 0x11 (SKIP) */
165static void SkipAct11(ByteReader &buf)
166{
167 /* <11> <num>
168 *
169 * W num Number of sound files that follow */
170
171 _cur_gps.skip_sprites = buf.ReadWord();
172
173 GrfMsg(3, "SkipAct11: Skipping {} sprites", _cur_gps.skip_sprites);
174}
175
177template <> void GrfActionHandler<0x11>::FileScan(ByteReader &buf) { SkipAct11(buf); }
181template <> void GrfActionHandler<0x11>::LabelScan(ByteReader &buf) { SkipAct11(buf); }
183template <> void GrfActionHandler<0x11>::Init(ByteReader &buf) { GRFSound(buf); }
185template <> void GrfActionHandler<0x11>::Reserve(ByteReader &buf) { SkipAct11(buf); }
187template <> void GrfActionHandler<0x11>::Activation(ByteReader &buf) { GRFSound(buf); }
Class to read from a NewGRF file.
uint16_t ReadWord()
Read a single Word (16 bits).
size_t GetPos() const
Get position in the file.
uint8_t ReadByte()
Read a byte from the file.
uint32_t ReadDword()
Read a double word (32 bits) from the file (in low endian format).
void SkipBytes(size_t n)
Skip n bytes ahead in the file.
uint16_t ReadWord()
Read a word (16 bits) from the file (in low endian format).
RandomAccessFile with some extra information specific for sprite files.
uint8_t GetContainerVersion() const
Get the version number of container type used by the file.
Functions related to debugging.
void GRFUnsafe(ByteReader &)
Set the current NewGRF as unsafe for static use.
Definition newgrf.cpp:379
GRFFile * GetFileByGRFID(GrfID grfid)
Obtain a NewGRF file by its grfID.
Definition newgrf.cpp:106
@ Init
Second step of NewGRF loading; load all actions into memory.
Definition newgrf.h:58
@ Activation
Forth step of NewGRF loading; activate the features.
Definition newgrf.h:60
constexpr T UnflattenNewGRFLabel(uint32_t value)
Unflatten a NewGRF related label from a 32 bits integer.
Definition newgrf.h:267
static void LoadGRFSound(size_t offs, SoundEntry *sound)
Load a sound from a file.
static void ImportGRFSound(SoundEntry *sound)
Process a sound import from another GRF file.
NewGRF buffer reader definition.
Functions related to NewGRF houses.
NewGRF internal processing state.
SoundEntry * AllocateSound(uint num)
Allocate sound slots.
Functions related to NewGRF provided sounds.
Label< struct GrfIDTag > GrfID
The unique identifier of a NewGRF.
Definition newgrf_type.h:17
A number of safeguards to prevent using unsafe methods.
@ NewGRF
Contained within a NewGRF.
Definition sound_type.h:17
size_t GetGRFSpriteOffset(uint32_t id)
Get the file offset for a specific sprite in the sprite section of a GRF.
bool SkipSpriteData(SpriteFile &file, uint8_t type, uint16_t num)
Skip the given amount of sprite graphics data.
Functions to cache sprites in memory.
Definition of base types and functions in a cross-platform compatible way.
std::string FormatArrayAsHex(std::span< const uint8_t > data)
Format a byte array into a continuous hex string.
Definition string.cpp:77
Functions related to low-level strings.
Dynamic data of a loaded NewGRF.
Definition newgrf.h:124
static void FileScan(ByteReader &buf)
Implementation of the GrfLoadingStage::FileScan stage of this action.
static void SafetyScan(ByteReader &buf)
Implementation of the GrfLoadingStage::SafetyScan stage of this action.
static void Reserve(ByteReader &buf)
Implementation of the GrfLoadingStage::Reserve stage of this action.
static void Activation(ByteReader &buf)
Implementation of the GrfLoadingStage::Activation stage of this action.
static void Init(ByteReader &buf)
Implementation of the GrfLoadingStage::Init stage of this action.
static void LabelScan(ByteReader &buf)
Implementation of the GrfLoadingStage::LabelScan stage of this action.
uint8_t grf_container_ver
NewGRF container version if the sound is from a NewGRF.
Definition sound_type.h:31