diff --git a/src/newgrf.cpp b/src/newgrf.cpp index 3dc2257354..d3287900da 100644 --- a/src/newgrf.cpp +++ b/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> - * - * 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> - * - * 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> + * + * 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); // 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> + * + * 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) -{ - /* '\0' */ - - if (_cur.data_blocks == 0) { - grfmsg(2, "GRFDataBlock: unexpected data block, skipping"); - return; - } - - uint8 name_len = buf->ReadByte(); - const char *name = reinterpret_cast(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) { diff --git a/src/newgrf_sound.cpp b/src/newgrf_sound.cpp index 47ddf2406e..401c773860 100644 --- a/src/newgrf_sound.cpp +++ b/src/newgrf_sound.cpp @@ -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 _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: '\0' */ + + 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 . + 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. diff --git a/src/newgrf_sound.h b/src/newgrf_sound.h index 2f2c034db4..a6375cee4a 100644 --- a/src/newgrf_sound.h +++ b/src/newgrf_sound.h @@ -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); diff --git a/src/sound.cpp b/src/sound.cpp index d572050bfe..f792dfdff1 100644 --- a/src/sound.cpp +++ b/src/sound.cpp @@ -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;