mirror of https://github.com/OpenTTD/OpenTTD.git
(svn r23882) -Codechange: Delay parsing of NewGRF sound effects until first usage.
This commit is contained in:
parent
5af68295b0
commit
12f0e80dad
226
src/newgrf.cpp
226
src/newgrf.cpp
|
@ -73,10 +73,6 @@ static uint32 _ttdpatch_flags[8];
|
|||
/** Indicates which are the newgrf features currently loaded ingame */
|
||||
GRFLoadedFeatures _loaded_newgrf_features;
|
||||
|
||||
enum GrfDataType {
|
||||
GDT_SOUND,
|
||||
};
|
||||
|
||||
static const uint MAX_SPRITEGROUP = UINT8_MAX; ///< Maximum GRF-local ID for a spritegroup.
|
||||
|
||||
/** Temporary data during loading of GRFs */
|
||||
|
@ -104,8 +100,6 @@ public:
|
|||
|
||||
/* Kind of return values when processing certain actions */
|
||||
int skip_sprites; ///< Number of psuedo sprites to skip before processing the next one. (-1 to skip to end of file)
|
||||
byte data_blocks; ///< Number of binary include sprites to read before processing the next pseudo sprite.
|
||||
GrfDataType data_type; ///< Type of the binary include sprites to read.
|
||||
|
||||
/* Currently referenceable spritegroups */
|
||||
SpriteGroup *spritegroups[MAX_SPRITEGROUP + 1];
|
||||
|
@ -115,7 +109,6 @@ public:
|
|||
{
|
||||
this->nfo_line = 0;
|
||||
this->skip_sprites = 0;
|
||||
this->data_blocks = 0;
|
||||
|
||||
for (uint i = 0; i < GSF_END; i++) {
|
||||
this->spritesets[i].clear();
|
||||
|
@ -6818,42 +6811,12 @@ static void DefineGotoLabel(ByteReader *buf)
|
|||
grfmsg(2, "DefineGotoLabel: GOTO target with label 0x%02X", label->label);
|
||||
}
|
||||
|
||||
/* Action 0x11 */
|
||||
static void GRFSound(ByteReader *buf)
|
||||
{
|
||||
/* <11> <num>
|
||||
*
|
||||
* W num Number of sound files that follow */
|
||||
|
||||
uint16 num = buf->ReadWord();
|
||||
|
||||
_cur.data_blocks = num;
|
||||
_cur.data_type = GDT_SOUND;
|
||||
|
||||
if (_cur.grffile->sound_offset == 0) {
|
||||
_cur.grffile->sound_offset = GetNumSounds();
|
||||
_cur.grffile->num_sounds = num;
|
||||
}
|
||||
}
|
||||
|
||||
/* Action 0x11 (SKIP) */
|
||||
static void SkipAct11(ByteReader *buf)
|
||||
{
|
||||
/* <11> <num>
|
||||
*
|
||||
* W num Number of sound files that follow */
|
||||
|
||||
_cur.skip_sprites = buf->ReadWord();
|
||||
|
||||
grfmsg(3, "SkipAct11: Skipping %d sprites", _cur.skip_sprites);
|
||||
}
|
||||
|
||||
static void ImportGRFSound(ByteReader *buf)
|
||||
static void ImportGRFSound()
|
||||
{
|
||||
const GRFFile *file;
|
||||
SoundEntry *sound = AllocateSound();
|
||||
uint32 grfid = buf->ReadDWord();
|
||||
SoundID sound_id = buf->ReadWord();
|
||||
uint32 grfid = FioReadDword();
|
||||
SoundID sound_id = FioReadWord();
|
||||
|
||||
file = GetFileByGRFID(grfid);
|
||||
if (file == NULL || file->sound_offset == 0) {
|
||||
|
@ -6875,102 +6838,79 @@ static void ImportGRFSound(ByteReader *buf)
|
|||
sound->priority = 0;
|
||||
}
|
||||
|
||||
/* 'Action 0xFE' */
|
||||
static void GRFImportBlock(ByteReader *buf)
|
||||
static void LoadGRFSound(size_t offs)
|
||||
{
|
||||
if (_cur.data_blocks == 0) {
|
||||
grfmsg(2, "GRFImportBlock: Unexpected import block, skipping");
|
||||
return;
|
||||
SoundEntry *sound = AllocateSound();
|
||||
|
||||
/* Set default volume and priority */
|
||||
sound->volume = 0x80;
|
||||
sound->priority = 0;
|
||||
|
||||
sound->file_slot = _cur.file_index;
|
||||
sound->file_offset = offs;
|
||||
}
|
||||
|
||||
/* Action 0x11 */
|
||||
static void GRFSound(ByteReader *buf)
|
||||
{
|
||||
/* <11> <num>
|
||||
*
|
||||
* W num Number of sound files that follow */
|
||||
|
||||
uint16 num = buf->ReadWord();
|
||||
|
||||
if (_cur.grffile->sound_offset == 0) {
|
||||
_cur.grffile->sound_offset = GetNumSounds();
|
||||
_cur.grffile->num_sounds = num;
|
||||
}
|
||||
|
||||
_cur.data_blocks--;
|
||||
for (int i = 0; i < num; i++) {
|
||||
_cur.nfo_line++;
|
||||
|
||||
/* XXX 'Action 0xFE' isn't really specified. It is only mentioned for
|
||||
* importing sounds, so this is probably all wrong... */
|
||||
if (buf->ReadByte() != _cur.data_type) {
|
||||
grfmsg(1, "GRFImportBlock: Import type mismatch");
|
||||
}
|
||||
size_t offs = FioGetPos();
|
||||
|
||||
switch (_cur.data_type) {
|
||||
case GDT_SOUND: ImportGRFSound(buf); break;
|
||||
default: NOT_REACHED();
|
||||
uint16 len = FioReadWord();
|
||||
byte type = FioReadByte();
|
||||
|
||||
if (type != 0xFF) {
|
||||
grfmsg(1, "GRFSound: Unexpected RealSprite found, skipping");
|
||||
FioSkipBytes(7);
|
||||
SkipSpriteData(type, num - 8);
|
||||
continue;
|
||||
}
|
||||
|
||||
byte action = FioReadByte();
|
||||
switch (action) {
|
||||
case 0xFF:
|
||||
LoadGRFSound(offs);
|
||||
FioSkipBytes(len - 1); // <type> is not included in the length for pseudo-sprites.
|
||||
break;
|
||||
|
||||
case 0xFE:
|
||||
/* XXX 'Action 0xFE' isn't really specified. It is only mentioned for
|
||||
* importing sounds, so this is probably all wrong... */
|
||||
if (FioReadByte() != 0) grfmsg(1, "GRFSound: Import type mismatch");
|
||||
ImportGRFSound();
|
||||
break;
|
||||
|
||||
default:
|
||||
grfmsg(1, "GRFSound: Unexpected Action %x found, skipping", action);
|
||||
FioSkipBytes(len - 1);
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
static void LoadGRFSound(ByteReader *buf)
|
||||
/* Action 0x11 (SKIP) */
|
||||
static void SkipAct11(ByteReader *buf)
|
||||
{
|
||||
/* Allocate a sound entry. This is done even if the data is not loaded
|
||||
* so that the indices used elsewhere are still correct. */
|
||||
SoundEntry *sound = AllocateSound();
|
||||
/* <11> <num>
|
||||
*
|
||||
* W num Number of sound files that follow */
|
||||
|
||||
if (buf->ReadDWord() != BSWAP32('RIFF')) {
|
||||
grfmsg(1, "LoadGRFSound: Missing RIFF header");
|
||||
return;
|
||||
}
|
||||
_cur.skip_sprites = buf->ReadWord();
|
||||
|
||||
uint32 total_size = buf->ReadDWord();
|
||||
if (total_size > buf->Remaining()) {
|
||||
grfmsg(1, "LoadGRFSound: RIFF was truncated");
|
||||
return;
|
||||
}
|
||||
|
||||
if (buf->ReadDWord() != BSWAP32('WAVE')) {
|
||||
grfmsg(1, "LoadGRFSound: Invalid RIFF type");
|
||||
return;
|
||||
}
|
||||
|
||||
while (total_size >= 8) {
|
||||
uint32 tag = buf->ReadDWord();
|
||||
uint32 size = buf->ReadDWord();
|
||||
total_size -= 8;
|
||||
if (total_size < size) {
|
||||
grfmsg(1, "LoadGRFSound: Invalid RIFF");
|
||||
return;
|
||||
}
|
||||
total_size -= size;
|
||||
|
||||
switch (tag) {
|
||||
case ' tmf': // 'fmt '
|
||||
/* Audio format, must be 1 (PCM) */
|
||||
if (size < 16 || buf->ReadWord() != 1) {
|
||||
grfmsg(1, "LoadGRFSound: Invalid audio format");
|
||||
return;
|
||||
}
|
||||
sound->channels = buf->ReadWord();
|
||||
sound->rate = buf->ReadDWord();
|
||||
buf->ReadDWord();
|
||||
buf->ReadWord();
|
||||
sound->bits_per_sample = buf->ReadWord();
|
||||
|
||||
/* The rest will be skipped */
|
||||
size -= 16;
|
||||
break;
|
||||
|
||||
case 'atad': // 'data'
|
||||
sound->file_size = size;
|
||||
sound->file_offset = FioGetPos() - buf->Remaining();
|
||||
sound->file_slot = _cur.file_index;
|
||||
|
||||
/* Set default volume and priority */
|
||||
sound->volume = 0x80;
|
||||
sound->priority = 0;
|
||||
|
||||
grfmsg(2, "LoadGRFSound: channels %u, sample rate %u, bits per sample %u, length %u", sound->channels, sound->rate, sound->bits_per_sample, size);
|
||||
return; // the fmt chunk has to appear before data, so we are finished
|
||||
|
||||
default:
|
||||
/* Skip unknown chunks */
|
||||
break;
|
||||
}
|
||||
|
||||
/* Skip rest of chunk */
|
||||
for (; size > 0; size--) buf->ReadByte();
|
||||
}
|
||||
|
||||
grfmsg(1, "LoadGRFSound: RIFF does not contain any sound data");
|
||||
|
||||
/* Clear everything that was read */
|
||||
MemSetT(sound, 0);
|
||||
grfmsg(3, "SkipAct11: Skipping %d sprites", _cur.skip_sprites);
|
||||
}
|
||||
|
||||
/** Action 0x12 */
|
||||
|
@ -7575,36 +7515,6 @@ static void StaticGRFInfo(ByteReader *buf)
|
|||
HandleNodes(buf, _tags_root);
|
||||
}
|
||||
|
||||
/** 'Action 0xFF' */
|
||||
static void GRFDataBlock(ByteReader *buf)
|
||||
{
|
||||
/* <FF> <name_len> <name> '\0' <data> */
|
||||
|
||||
if (_cur.data_blocks == 0) {
|
||||
grfmsg(2, "GRFDataBlock: unexpected data block, skipping");
|
||||
return;
|
||||
}
|
||||
|
||||
uint8 name_len = buf->ReadByte();
|
||||
const char *name = reinterpret_cast<const char *>(buf->Data());
|
||||
buf->Skip(name_len);
|
||||
|
||||
/* Test string termination */
|
||||
if (buf->ReadByte() != 0) {
|
||||
grfmsg(2, "GRFDataBlock: Name not properly terminated");
|
||||
return;
|
||||
}
|
||||
|
||||
grfmsg(2, "GRFDataBlock: block name '%s'...", name);
|
||||
|
||||
_cur.data_blocks--;
|
||||
|
||||
switch (_cur.data_type) {
|
||||
case GDT_SOUND: LoadGRFSound(buf); break;
|
||||
default: NOT_REACHED();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Set the current NewGRF as unsafe for static use
|
||||
* @param buf Unused.
|
||||
|
@ -8627,11 +8537,9 @@ static void DecodeSpecialSprite(byte *buf, uint num, GrfLoadingStage stage)
|
|||
byte action = bufp->ReadByte();
|
||||
|
||||
if (action == 0xFF) {
|
||||
grfmsg(7, "DecodeSpecialSprite: Handling data block in stage %d", stage);
|
||||
GRFDataBlock(bufp);
|
||||
grfmsg(2, "DecodeSpecialSprite: Unexpected data block, skipping");
|
||||
} else if (action == 0xFE) {
|
||||
grfmsg(7, "DecodeSpecialSprite: Handling import block in stage %d", stage);
|
||||
GRFImportBlock(bufp);
|
||||
grfmsg(2, "DecodeSpecialSprite: Unexpected import block, skipping");
|
||||
} else if (action >= lengthof(handlers)) {
|
||||
grfmsg(7, "DecodeSpecialSprite: Skipping unknown action 0x%02X", action);
|
||||
} else if (handlers[action][stage] == NULL) {
|
||||
|
|
|
@ -16,6 +16,8 @@
|
|||
#include "newgrf_sound.h"
|
||||
#include "vehicle_base.h"
|
||||
#include "sound_func.h"
|
||||
#include "fileio_func.h"
|
||||
#include "debug.h"
|
||||
|
||||
static SmallVector<SoundEntry, 8> _sounds;
|
||||
|
||||
|
@ -51,6 +53,102 @@ uint GetNumSounds()
|
|||
}
|
||||
|
||||
|
||||
/**
|
||||
* Extract meta data from a NewGRF sound.
|
||||
* @param sound Sound to load.
|
||||
* @return True if a valid sound was loaded.
|
||||
*/
|
||||
bool LoadNewGRFSound(SoundEntry *sound)
|
||||
{
|
||||
if (sound->file_offset == SIZE_MAX || sound->file_slot == 0) return false;
|
||||
|
||||
FioSeekToFile(sound->file_slot, sound->file_offset);
|
||||
|
||||
/* Format: <num> <FF> <FF> <name_len> <name> '\0' <data> */
|
||||
|
||||
uint16 num = FioReadWord();
|
||||
if (FioReadByte() != 0xFF) return false;
|
||||
if (FioReadByte() != 0xFF) return false;
|
||||
|
||||
uint8 name_len = FioReadByte();
|
||||
char *name = AllocaM(char, name_len + 1);
|
||||
FioReadBlock(name, name_len + 1);
|
||||
|
||||
/* Test string termination */
|
||||
if (name[name_len] != 0) {
|
||||
DEBUG(grf, 2, "LoadNewGRFSound [%s]: Name not properly terminated", FioGetFilename(sound->file_slot));
|
||||
return false;
|
||||
}
|
||||
|
||||
DEBUG(grf, 2, "LoadNewGRFSound [%s]: Sound name '%s'...", FioGetFilename(sound->file_slot), name);
|
||||
|
||||
if (FioReadDword() != BSWAP32('RIFF')) {
|
||||
DEBUG(grf, 1, "LoadNewGRFSound [%s]: Missing RIFF header", FioGetFilename(sound->file_slot));
|
||||
return false;
|
||||
}
|
||||
|
||||
uint32 total_size = FioReadDword();
|
||||
if (total_size + name_len + 11 > num) { // The first FF in the sprite is not counted for <num>.
|
||||
DEBUG(grf, 1, "LoadNewGRFSound [%s]: RIFF was truncated", FioGetFilename(sound->file_slot));
|
||||
return false;
|
||||
}
|
||||
|
||||
if (FioReadDword() != BSWAP32('WAVE')) {
|
||||
DEBUG(grf, 1, "LoadNewGRFSound [%s]: Invalid RIFF type", FioGetFilename(sound->file_slot));
|
||||
return false;
|
||||
}
|
||||
|
||||
while (total_size >= 8) {
|
||||
uint32 tag = FioReadDword();
|
||||
uint32 size = FioReadDword();
|
||||
total_size -= 8;
|
||||
if (total_size < size) {
|
||||
DEBUG(grf, 1, "LoadNewGRFSound [%s]: Invalid RIFF", FioGetFilename(sound->file_slot));
|
||||
return false;
|
||||
}
|
||||
total_size -= size;
|
||||
|
||||
switch (tag) {
|
||||
case ' tmf': // 'fmt '
|
||||
/* Audio format, must be 1 (PCM) */
|
||||
if (size < 16 || FioReadWord() != 1) {
|
||||
DEBUG(grf, 1, "LoadGRFSound [%s]: Invalid audio format", FioGetFilename(sound->file_slot));
|
||||
return false;
|
||||
}
|
||||
sound->channels = FioReadWord();
|
||||
sound->rate = FioReadDword();
|
||||
FioReadDword();
|
||||
FioReadWord();
|
||||
sound->bits_per_sample = FioReadWord();
|
||||
|
||||
/* The rest will be skipped */
|
||||
size -= 16;
|
||||
break;
|
||||
|
||||
case 'atad': // 'data'
|
||||
sound->file_size = size;
|
||||
sound->file_offset = FioGetPos();
|
||||
|
||||
DEBUG(grf, 2, "LoadNewGRFSound [%s]: channels %u, sample rate %u, bits per sample %u, length %u", FioGetFilename(sound->file_slot), sound->channels, sound->rate, sound->bits_per_sample, size);
|
||||
return true; // the fmt chunk has to appear before data, so we are finished
|
||||
|
||||
default:
|
||||
/* Skip unknown chunks */
|
||||
break;
|
||||
}
|
||||
|
||||
/* Skip rest of chunk */
|
||||
if (size > 0) FioSkipBytes(size);
|
||||
}
|
||||
|
||||
DEBUG(grf, 1, "LoadNewGRFSound [%s]: RIFF does not contain any sound data", FioGetFilename(sound->file_slot));
|
||||
|
||||
/* Clear everything that was read */
|
||||
MemSetT(sound, 0);
|
||||
return false;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Checks whether a NewGRF wants to play a different vehicle sound effect.
|
||||
* @param v Vehicle to play sound effect for.
|
||||
|
|
|
@ -32,6 +32,7 @@ enum VehicleSoundEvent {
|
|||
|
||||
SoundEntry *AllocateSound();
|
||||
void InitializeSoundPool();
|
||||
bool LoadNewGRFSound(SoundEntry *sound);
|
||||
SoundEntry *GetSound(SoundID sound_id);
|
||||
uint GetNumSounds();
|
||||
bool PlayVehicleSound(const Vehicle *v, VehicleSoundEvent event);
|
||||
|
|
|
@ -159,9 +159,18 @@ static void StartSound(SoundID sound_id, float pan, uint volume)
|
|||
{
|
||||
if (volume == 0) return;
|
||||
|
||||
const SoundEntry *sound = GetSound(sound_id);
|
||||
SoundEntry *sound = GetSound(sound_id);
|
||||
if (sound == NULL) return;
|
||||
|
||||
/* NewGRF sound that wasn't loaded yet? */
|
||||
if (sound->rate == 0 && sound->file_slot != 0) {
|
||||
if (!LoadNewGRFSound(sound)) {
|
||||
/* Mark as invalid. */
|
||||
sound->file_slot = 0;
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
/* Empty sound? */
|
||||
if (sound->rate == 0) return;
|
||||
|
||||
|
|
Loading…
Reference in New Issue