changeset 19025:d94c45de416a draft

(svn r23882) -Codechange: Delay parsing of NewGRF sound effects until first usage.
author michi_cc <michi_cc@openttd.org>
date Sat, 04 Feb 2012 13:28:44 +0000
parents ca0151ea9685
children fec98a4a1acb
files src/newgrf.cpp src/newgrf_sound.cpp src/newgrf_sound.h src/sound.cpp
diffstat 4 files changed, 183 insertions(+), 167 deletions(-) [+]
line wrap: on
line diff
--- a/src/newgrf.cpp
+++ b/src/newgrf.cpp
@@ -73,10 +73,6 @@
 /** 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 @@
 
 	/* 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 @@
 	{
 		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 @@
 	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 @@
 	sound->priority = 0;
 }
 
-/* 'Action 0xFE' */
-static void GRFImportBlock(ByteReader *buf)
-{
-	if (_cur.data_blocks == 0) {
-		grfmsg(2, "GRFImportBlock: Unexpected import block, skipping");
-		return;
-	}
-
-	_cur.data_blocks--;
-
-	/* 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");
-	}
-
-	switch (_cur.data_type) {
-		case GDT_SOUND: ImportGRFSound(buf); break;
-		default: NOT_REACHED();
-	}
-}
-
-static void LoadGRFSound(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. */
+static void LoadGRFSound(size_t offs)
+{
 	SoundEntry *sound = AllocateSound();
 
-	if (buf->ReadDWord() != BSWAP32('RIFF')) {
-		grfmsg(1, "LoadGRFSound: Missing RIFF header");
-		return;
-	}
-
-	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
+	/* 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;
+	}
+
+	for (int i = 0; i < num; i++) {
+		_cur.nfo_line++;
+
+		size_t offs = FioGetPos();
+
+		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:
-				/* 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(1, "GRFSound: Unexpected Action %x found, skipping", action);
+				FioSkipBytes(len - 1);
+				break;
+		}
+	}
+}
+
+/* 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);
 }
 
 /** Action 0x12 */
@@ -7575,36 +7515,6 @@
 	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 @@
 		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) {
--- 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<SoundEntry, 8> _sounds;
 
@@ -52,6 +54,102 @@
 
 
 /**
+ * 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.
  * @param event Trigger for the sound effect.
--- a/src/newgrf_sound.h
+++ b/src/newgrf_sound.h
@@ -32,6 +32,7 @@
 
 SoundEntry *AllocateSound();
 void InitializeSoundPool();
+bool LoadNewGRFSound(SoundEntry *sound);
 SoundEntry *GetSound(SoundID sound_id);
 uint GetNumSounds();
 bool PlayVehicleSound(const Vehicle *v, VehicleSoundEvent event);
--- a/src/sound.cpp
+++ b/src/sound.cpp
@@ -159,9 +159,18 @@
 {
 	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;