Changeset - r19020:24d71054fe13
[Not reviewed]
master
0 12 0
michi_cc - 12 years ago 2012-02-04 13:29:04
michi_cc@openttd.org
(svn r23887) -Feature: [NewGRF] Support for container version 2.
12 files changed with 323 insertions and 33 deletions:
0 comments (0 inline, 0 general)
src/base_media_base.h
Show inline comments
 
@@ -214,24 +214,26 @@ enum GraphicsFileType {
 
/** Blitter type for base graphics sets. */
 
enum BlitterType {
 
	BLT_8BPP,       ///< Base set has 8 bpp sprites only.
 
	BLT_32BPP,      ///< Base set has both 8 bpp and 32 bpp sprites.
 
};
 

	
 
/** All data of a graphics set. */
 
struct GraphicsSet : BaseSet<GraphicsSet, MAX_GFT, true> {
 
	PaletteType palette;       ///< Palette of this graphics set
 
	BlitterType blitter;       ///< Blitter of this graphics set
 

	
 
	bool FillSetDetails(struct IniFile *ini, const char *path, const char *full_filename);
 

	
 
	static MD5File::ChecksumResult CheckMD5(const MD5File *file, Subdirectory subdir);
 
};
 

	
 
/** All data/functions related with replacing the base graphics. */
 
class BaseGraphics : public BaseMedia<GraphicsSet> {
 
public:
 
};
 

	
 
/** All data of a sounds set. */
 
struct SoundsSet : BaseSet<SoundsSet, 1, true> {
 
};
 

	
 
/** All data/functions related with replacing the base sounds */
src/gfxinit.cpp
Show inline comments
 
@@ -43,25 +43,34 @@ static const SpriteID * const _landscape
 
 * @param file_index The Fio offset to load the file in.
 
 * @return The number of loaded sprites.
 
 */
 
static uint LoadGrfFile(const char *filename, uint load_index, int file_index)
 
{
 
	uint load_index_org = load_index;
 
	uint sprite_id = 0;
 

	
 
	FioOpenFile(file_index, filename, BASESET_DIR);
 

	
 
	DEBUG(sprite, 2, "Reading grf-file '%s'", filename);
 

	
 
	while (LoadNextSprite(load_index, file_index, sprite_id)) {
 
	byte container_ver = GetGRFContainerVersion();
 
	if (container_ver == 0) usererror("Base grf '%s' is corrupt", filename);
 
	ReadGRFSpriteOffsets(container_ver);
 
	if (container_ver >= 2) {
 
		/* Read compression. */
 
		byte compression = FioReadByte();
 
		if (compression != 0) usererror("Unsupported compression format");
 
	}
 

	
 
	while (LoadNextSprite(load_index, file_index, sprite_id, container_ver)) {
 
		load_index++;
 
		sprite_id++;
 
		if (load_index >= MAX_SPRITES) {
 
			usererror("Too many sprites. Recompile with higher MAX_SPRITES value or remove some custom GRF files.");
 
		}
 
	}
 
	DEBUG(sprite, 2, "Currently %i sprites are loaded", load_index);
 

	
 
	return load_index - load_index_org;
 
}
 

	
 
/**
 
@@ -71,29 +80,38 @@ static uint LoadGrfFile(const char *file
 
 * @param file_index The Fio offset to load the file in.
 
 * @return The number of loaded sprites.
 
 */
 
static void LoadGrfFileIndexed(const char *filename, const SpriteID *index_tbl, int file_index)
 
{
 
	uint start;
 
	uint sprite_id = 0;
 

	
 
	FioOpenFile(file_index, filename, BASESET_DIR);
 

	
 
	DEBUG(sprite, 2, "Reading indexed grf-file '%s'", filename);
 

	
 
	byte container_ver = GetGRFContainerVersion();
 
	if (container_ver == 0) usererror("Base grf '%s' is corrupt", filename);
 
	ReadGRFSpriteOffsets(container_ver);
 
	if (container_ver >= 2) {
 
		/* Read compression. */
 
		byte compression = FioReadByte();
 
		if (compression != 0) usererror("Unsupported compression format");
 
	}
 

	
 
	while ((start = *index_tbl++) != END) {
 
		uint end = *index_tbl++;
 

	
 
		do {
 
			bool b = LoadNextSprite(start, file_index, sprite_id);
 
			bool b = LoadNextSprite(start, file_index, sprite_id, container_ver);
 
			assert(b);
 
			sprite_id++;
 
		} while (++start <= end);
 
	}
 
}
 

	
 
/**
 
 * Checks whether the MD5 checksums of the files are correct.
 
 *
 
 * @note Also checks sample.cat and other required non-NewGRF GRFs for corruption.
 
 */
 
void CheckExternalFiles()
 
@@ -253,24 +271,46 @@ bool GraphicsSet::FillSetDetails(IniFile
 
		IniItem *item;
 

	
 
		fetch_metadata("palette");
 
		this->palette = (*item->value == 'D' || *item->value == 'd') ? PAL_DOS : PAL_WINDOWS;
 

	
 
		/* Get optional blitter information. */
 
		item = metadata->GetItem("blitter", false);
 
		this->blitter = (item != NULL && *item->value == '3') ? BLT_32BPP : BLT_8BPP;
 
	}
 
	return ret;
 
}
 

	
 
/**
 
 * Calculate and check the MD5 hash of the supplied GRF.
 
 * @param file The file get the hash of.
 
 * @param subdir The sub directory to get the files from.
 
 * @return
 
 * - #CR_MATCH if the MD5 hash matches
 
 * - #CR_MISMATCH if the MD5 does not match
 
 * - #CR_NO_FILE if the file misses
 
 */
 
/* static */ MD5File::ChecksumResult GraphicsSet::CheckMD5(const MD5File *file, Subdirectory subdir)
 
{
 
	size_t size = 0;
 
	FILE *f = FioFOpenFile(file->filename, "rb", subdir, &size);
 
	if (f == NULL) return MD5File::CR_NO_FILE;
 

	
 
	size_t max = GRFGetSizeOfDataSection(f);
 

	
 
	FioFCloseFile(f);
 

	
 
	return file->CheckMD5(subdir, max);
 
}
 

	
 

	
 
/**
 
 * Calculate and check the MD5 hash of the supplied filename.
 
 * @param subdir The sub directory to get the files from
 
 * @param max_size Only calculate the hash for this many bytes from the file start.
 
 * @return
 
 * - #CR_MATCH if the MD5 hash matches
 
 * - #CR_MISMATCH if the MD5 does not match
 
 * - #CR_NO_FILE if the file misses
 
 */
 
MD5File::ChecksumResult MD5File::CheckMD5(Subdirectory subdir, size_t max_size) const
 
{
src/newgrf.cpp
Show inline comments
 
@@ -88,24 +88,25 @@ private:
 
	std::map<uint, SpriteSet> spritesets[GSF_END];
 

	
 
public:
 
	/* Global state */
 
	GrfLoadingStage stage;    ///< Current loading stage
 
	SpriteID spriteid;        ///< First available SpriteID for loading realsprites.
 

	
 
	/* Local state in the file */
 
	uint file_index;          ///< File index of currently processed GRF file.
 
	GRFFile *grffile;         ///< Currently processed GRF file.
 
	GRFConfig *grfconfig;     ///< Config of the currently processed GRF file.
 
	uint32 nfo_line;          ///< Currently processed pseudo sprite number in the GRF.
 
	byte grf_container_ver;   ///< Container format of the current GRF file.
 

	
 
	/* 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)
 

	
 
	/* Currently referenceable spritegroups */
 
	SpriteGroup *spritegroups[MAX_SPRITEGROUP + 1];
 

	
 
	/** Clear temporary data before processing the next file in the current loading stage */
 
	void ClearDataForNextFile()
 
	{
 
		this->nfo_line = 0;
 
		this->skip_sprites = 0;
 
@@ -4323,25 +4324,25 @@ static void NewSpriteSet(ByteReader *buf
 
		num_sets = buf->ReadExtendedByte();
 
	}
 
	uint16 num_ents = buf->ReadExtendedByte();
 

	
 
	_cur.AddSpriteSets(feature, _cur.spriteid, first_set, num_sets, num_ents);
 

	
 
	grfmsg(7, "New sprite set at %d of type %d, consisting of %d sets with %d views each (total %d)",
 
		_cur.spriteid, feature, num_sets, num_ents, num_sets * num_ents
 
	);
 

	
 
	for (int i = 0; i < num_sets * num_ents; i++) {
 
		_cur.nfo_line++;
 
		LoadNextSprite(_cur.spriteid++, _cur.file_index, _cur.nfo_line);
 
		LoadNextSprite(_cur.spriteid++, _cur.file_index, _cur.nfo_line, _cur.grf_container_ver);
 
	}
 
}
 

	
 
/* Action 0x01 (SKIP) */
 
static void SkipAct1(ByteReader *buf)
 
{
 
	buf->ReadByte();
 
	uint8 num_sets  = buf->ReadByte();
 
	uint16 num_ents = buf->ReadExtendedByte();
 

	
 
	_cur.skip_sprites = num_sets * num_ents;
 

	
 
@@ -5416,34 +5417,34 @@ static void GraphicsNew(ByteReader *buf)
 
	 * V other data    Graphics type specific data.  Currently unused. */
 
	/* TODO */
 

	
 
	uint8 type = buf->ReadByte();
 
	uint16 num = buf->ReadExtendedByte();
 
	uint16 offset = HasBit(type, 7) ? buf->ReadExtendedByte() : 0;
 
	ClrBit(type, 7); // Clear the high bit as that only indicates whether there is an offset.
 

	
 
	if ((type == 0x0D) && (num == 10) && _cur.grffile->is_ottdfile) {
 
		/* Special not-TTDP-compatible case used in openttd.grf
 
		 * Missing shore sprites and initialisation of SPR_SHORE_BASE */
 
		grfmsg(2, "GraphicsNew: Loading 10 missing shore sprites from extra grf.");
 
		LoadNextSprite(SPR_SHORE_BASE +  0, _cur.file_index, _cur.nfo_line++); // SLOPE_STEEP_S
 
		LoadNextSprite(SPR_SHORE_BASE +  5, _cur.file_index, _cur.nfo_line++); // SLOPE_STEEP_W
 
		LoadNextSprite(SPR_SHORE_BASE +  7, _cur.file_index, _cur.nfo_line++); // SLOPE_WSE
 
		LoadNextSprite(SPR_SHORE_BASE + 10, _cur.file_index, _cur.nfo_line++); // SLOPE_STEEP_N
 
		LoadNextSprite(SPR_SHORE_BASE + 11, _cur.file_index, _cur.nfo_line++); // SLOPE_NWS
 
		LoadNextSprite(SPR_SHORE_BASE + 13, _cur.file_index, _cur.nfo_line++); // SLOPE_ENW
 
		LoadNextSprite(SPR_SHORE_BASE + 14, _cur.file_index, _cur.nfo_line++); // SLOPE_SEN
 
		LoadNextSprite(SPR_SHORE_BASE + 15, _cur.file_index, _cur.nfo_line++); // SLOPE_STEEP_E
 
		LoadNextSprite(SPR_SHORE_BASE + 16, _cur.file_index, _cur.nfo_line++); // SLOPE_EW
 
		LoadNextSprite(SPR_SHORE_BASE + 17, _cur.file_index, _cur.nfo_line++); // SLOPE_NS
 
		LoadNextSprite(SPR_SHORE_BASE +  0, _cur.file_index, _cur.nfo_line++, _cur.grf_container_ver); // SLOPE_STEEP_S
 
		LoadNextSprite(SPR_SHORE_BASE +  5, _cur.file_index, _cur.nfo_line++, _cur.grf_container_ver); // SLOPE_STEEP_W
 
		LoadNextSprite(SPR_SHORE_BASE +  7, _cur.file_index, _cur.nfo_line++, _cur.grf_container_ver); // SLOPE_WSE
 
		LoadNextSprite(SPR_SHORE_BASE + 10, _cur.file_index, _cur.nfo_line++, _cur.grf_container_ver); // SLOPE_STEEP_N
 
		LoadNextSprite(SPR_SHORE_BASE + 11, _cur.file_index, _cur.nfo_line++, _cur.grf_container_ver); // SLOPE_NWS
 
		LoadNextSprite(SPR_SHORE_BASE + 13, _cur.file_index, _cur.nfo_line++, _cur.grf_container_ver); // SLOPE_ENW
 
		LoadNextSprite(SPR_SHORE_BASE + 14, _cur.file_index, _cur.nfo_line++, _cur.grf_container_ver); // SLOPE_SEN
 
		LoadNextSprite(SPR_SHORE_BASE + 15, _cur.file_index, _cur.nfo_line++, _cur.grf_container_ver); // SLOPE_STEEP_E
 
		LoadNextSprite(SPR_SHORE_BASE + 16, _cur.file_index, _cur.nfo_line++, _cur.grf_container_ver); // SLOPE_EW
 
		LoadNextSprite(SPR_SHORE_BASE + 17, _cur.file_index, _cur.nfo_line++, _cur.grf_container_ver); // SLOPE_NS
 
		if (_loaded_newgrf_features.shore == SHORE_REPLACE_NONE) _loaded_newgrf_features.shore = SHORE_REPLACE_ONLY_NEW;
 
		return;
 
	}
 

	
 
	/* Supported type? */
 
	if ((type >= lengthof(_action5_types)) || (_action5_types[type].block_type == A5BLOCK_INVALID)) {
 
		grfmsg(2, "GraphicsNew: Custom graphics (type 0x%02X) sprite block of length %u (unimplemented, ignoring)", type, num);
 
		_cur.skip_sprites = num;
 
		return;
 
	}
 

	
 
	const Action5Type *action5_type = &_action5_types[type];
 
@@ -5464,25 +5465,25 @@ static void GraphicsNew(ByteReader *buf)
 
		return;
 
	}
 

	
 
	/* Load at most max_sprites sprites. Skip remaining sprites. (for compatibility with TTDP and future extentions) */
 
	uint16 skip_num = SanitizeSpriteOffset(num, offset, action5_type->max_sprites, action5_type->name);
 
	SpriteID replace = action5_type->sprite_base + offset;
 

	
 
	/* Load <num> sprites starting from <replace>, then skip <skip_num> sprites. */
 
	grfmsg(2, "GraphicsNew: Replacing sprites %d to %d of %s (type 0x%02X) at SpriteID 0x%04X", offset, offset + num - 1, action5_type->name, type, replace);
 

	
 
	for (; num > 0; num--) {
 
		_cur.nfo_line++;
 
		LoadNextSprite(replace == 0 ? _cur.spriteid++ : replace++, _cur.file_index, _cur.nfo_line);
 
		LoadNextSprite(replace == 0 ? _cur.spriteid++ : replace++, _cur.file_index, _cur.nfo_line, _cur.grf_container_ver);
 
	}
 

	
 
	if (type == 0x0D) _loaded_newgrf_features.shore = SHORE_REPLACE_ACTION_5;
 

	
 
	_cur.skip_sprites = skip_num;
 
}
 

	
 
/* Action 0x05 (SKIP) */
 
static void SkipAct5(ByteReader *buf)
 
{
 
	/* Ignore type byte */
 
	buf->ReadByte();
 
@@ -5714,25 +5715,25 @@ static void CfgApply(ByteReader *buf)
 
	 *
 
	 * B param-num     Number of parameter to substitute (First = "zero")
 
	 *                 Ignored if that parameter was not specified in newgrf.cfg
 
	 * B param-size    How many bytes to replace.  If larger than 4, the
 
	 *                 bytes of the following parameter are used.  In that
 
	 *                 case, nothing is applied unless *all* parameters
 
	 *                 were specified.
 
	 * B offset        Offset into data from beginning of next sprite
 
	 *                 to place where parameter is to be stored. */
 

	
 
	/* Preload the next sprite */
 
	size_t pos = FioGetPos();
 
	uint16 num = FioReadWord();
 
	uint32 num = _cur.grf_container_ver >= 2 ? FioReadDword() : FioReadWord();
 
	uint8 type = FioReadByte();
 
	byte *preload_sprite = NULL;
 

	
 
	/* Check if the sprite is a pseudo sprite. We can't operate on real sprites. */
 
	if (type == 0xFF) {
 
		preload_sprite = MallocT<byte>(num);
 
		FioReadBlock(preload_sprite, num);
 
	}
 

	
 
	/* Reset the file position to the start of the next sprite */
 
	FioSeekTo(pos, SEEK_SET);
 

	
 
@@ -6058,25 +6059,25 @@ static void SpriteReplace(ByteReader *bu
 

	
 
	for (uint i = 0; i < num_sets; i++) {
 
		uint8 num_sprites = buf->ReadByte();
 
		uint16 first_sprite = buf->ReadWord();
 

	
 
		grfmsg(2, "SpriteReplace: [Set %d] Changing %d sprites, beginning with %d",
 
			i, num_sprites, first_sprite
 
		);
 

	
 
		for (uint j = 0; j < num_sprites; j++) {
 
			int load_index = first_sprite + j;
 
			_cur.nfo_line++;
 
			LoadNextSprite(load_index, _cur.file_index, _cur.nfo_line); // XXX
 
			LoadNextSprite(load_index, _cur.file_index, _cur.nfo_line, _cur.grf_container_ver); // XXX
 

	
 
			/* Shore sprites now located at different addresses.
 
			 * So detect when the old ones get replaced. */
 
			if (IsInsideMM(load_index, SPR_ORIGINALSHORE_START, SPR_ORIGINALSHORE_END + 1)) {
 
				if (_loaded_newgrf_features.shore != SHORE_REPLACE_ACTION_5) _loaded_newgrf_features.shore = SHORE_REPLACE_ACTION_A;
 
			}
 
		}
 
	}
 
}
 

	
 
/* Action 0x0A (SKIP) */
 
static void SkipActA(ByteReader *buf)
 
@@ -6837,62 +6838,84 @@ static void ImportGRFSound()
 
	sound->volume   = 128;
 
	sound->priority = 0;
 
}
 

	
 
static void LoadGRFSound(size_t offs)
 
{
 
	SoundEntry *sound = AllocateSound();
 

	
 
	/* Set default volume and priority */
 
	sound->volume = 0x80;
 
	sound->priority = 0;
 

	
 
	sound->file_slot = _cur.file_index;
 
	sound->file_offset = offs;
 
	if (offs != SIZE_MAX) {
 
		/* Sound is present in the NewGRF. */
 
		sound->file_slot = _cur.file_index;
 
		sound->file_offset = offs;
 
		sound->grf_container_ver = _cur.grf_container_ver;
 
	}
 
}
 

	
 
/* 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();
 
		uint32 len = _cur.grf_container_ver >= 2 ? FioReadDword() : FioReadWord();
 
		byte type = FioReadByte();
 

	
 
		if (_cur.grf_container_ver >= 2 && type == 0xFD) {
 
			/* Reference to sprite section. */
 
			if (len != 4) {
 
				grfmsg(1, "GRFSound: Invalid sprite section import");
 
				FioSkipBytes(len);
 
			} else {
 
				uint32 id = FioReadDword();
 
				if (_cur.stage == GLS_INIT) LoadGRFSound(GetGRFSpriteOffset(id));
 
			}
 
			continue;
 
		}
 

	
 
		if (type != 0xFF) {
 
			grfmsg(1, "GRFSound: Unexpected RealSprite found, skipping");
 
			FioSkipBytes(7);
 
			SkipSpriteData(type, num - 8);
 
			continue;
 
		}
 

	
 
		byte action = FioReadByte();
 
		switch (action) {
 
			case 0xFF:
 
				/* Allocate sound only in init stage. */
 
				if (_cur.stage == GLS_INIT) LoadGRFSound(offs);
 
				if (_cur.stage == GLS_INIT) {
 
					if (_cur.grf_container_ver >= 2) {
 
						grfmsg(1, "GRFSound: Inline sounds are not supported for container version >= 2");
 
					} else {
 
						LoadGRFSound(offs);
 
					}
 
				}
 
				FioSkipBytes(len - 1); // <type> is not included in the length for pseudo-sprites.
 
				break;
 

	
 
			case 0xFE:
 
				if (_cur.stage == GLS_ACTIVATION) {
 
					/* 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();
 
				} else {
 
					FioSkipBytes(len - 1);
 
				}
 
@@ -6935,25 +6958,25 @@ static void LoadFontGlyph(ByteReader *bu
 
		uint8  num_char  = buf->ReadByte();
 
		uint16 base_char = buf->ReadWord();
 

	
 
		if (size >= FS_END) {
 
			grfmsg(1, "LoadFontGlyph: Size %u is not supported, ignoring", size);
 
		}
 

	
 
		grfmsg(7, "LoadFontGlyph: Loading %u glyph(s) at 0x%04X for size %u", num_char, base_char, size);
 

	
 
		for (uint c = 0; c < num_char; c++) {
 
			if (size < FS_END) SetUnicodeGlyph(size, base_char + c, _cur.spriteid);
 
			_cur.nfo_line++;
 
			LoadNextSprite(_cur.spriteid++, _cur.file_index, _cur.nfo_line);
 
			LoadNextSprite(_cur.spriteid++, _cur.file_index, _cur.nfo_line, _cur.grf_container_ver);
 
		}
 
	}
 
}
 

	
 
/** Action 0x12 (SKIP) */
 
static void SkipAct12(ByteReader *buf)
 
{
 
	/* <12> <num_def> <font_size> <num_char> <base_char>
 
	 *
 
	 * B num_def      Number of definitions
 
	 * B font_size    Size of font (0 = normal, 1 = small, 2 = large)
 
	 * B num_char     Number of consecutive glyphs
 
@@ -8551,35 +8574,60 @@ static void DecodeSpecialSprite(byte *bu
 
			grfmsg(7, "DecodeSpecialSprite: Skipping action 0x%02X in stage %d", action, stage);
 
		} else {
 
			grfmsg(7, "DecodeSpecialSprite: Handling action 0x%02X in stage %d", action, stage);
 
			handlers[action][stage](bufp);
 
		}
 
	} catch (...) {
 
		grfmsg(1, "DecodeSpecialSprite: Tried to read past end of pseudo-sprite data");
 
		DisableGrf(STR_NEWGRF_ERROR_READ_BOUNDS);
 
	}
 
}
 

	
 

	
 
/** Signature of a container version 2 GRF. */
 
extern const byte _grf_cont_v2_sig[8] = {'G', 'R', 'F', 0x82, 0x0D, 0x0A, 0x1A, 0x0A};
 

	
 
/**
 
 * Get the container version of the currently opened GRF file.
 
 * @return Container version of the GRF file or 0 if the file is corrupt/no GRF file.
 
 */
 
byte GetGRFContainerVersion()
 
{
 
	size_t pos = FioGetPos();
 

	
 
	if (FioReadWord() == 0) {
 
		/* Check for GRF container version 2, which is identified by the bytes
 
		 * '47 52 46 82 0D 0A 1A 0A' at the start of the file. */
 
		for (uint i = 0; i < lengthof(_grf_cont_v2_sig); i++) {
 
			if (FioReadByte() != _grf_cont_v2_sig[i]) return 0; // Invalid format
 
		}
 

	
 
		return 2;
 
	}
 

	
 
	/* Container version 1 has no header, rewind to start. */
 
	FioSeekTo(pos, SEEK_SET);
 
	return 1;
 
}
 

	
 
/**
 
 * Load a particular NewGRF.
 
 * @param config     The configuration of the to be loaded NewGRF.
 
 * @param file_index The Fio index of the first NewGRF to load.
 
 * @param stage      The loading stage of the NewGRF.
 
 * @param subdir     The sub directory to find the NewGRF in.
 
 */
 
void LoadNewGRFFile(GRFConfig *config, uint file_index, GrfLoadingStage stage, Subdirectory subdir)
 
{
 
	const char *filename = config->filename;
 
	uint16 num;
 

	
 
	/* A .grf file is activated only if it was active when the game was
 
	 * started.  If a game is loaded, only its active .grfs will be
 
	 * reactivated, unless "loadallgraphics on" is used.  A .grf file is
 
	 * considered active if its action 8 has been processed, i.e. its
 
	 * action 8 hasn't been skipped using an action 7.
 
	 *
 
	 * During activation, only actions 0, 1, 2, 3, 4, 5, 7, 8, 9, 0A and 0B are
 
	 * carried out.  All others are ignored, because they only need to be
 
	 * processed once at initialization.  */
 
	if (stage != GLS_FILESCAN && stage != GLS_SAFETYSCAN && stage != GLS_LABELSCAN) {
 
		_cur.grffile = GetFileByFilename(filename);
 
@@ -8595,62 +8643,92 @@ void LoadNewGRFFile(GRFConfig *config, u
 
		config->error  = new GRFError(STR_NEWGRF_ERROR_MSG_FATAL, STR_NEWGRF_ERROR_TOO_MANY_NEWGRFS_LOADED);
 
		return;
 
	}
 

	
 
	FioOpenFile(file_index, filename, subdir);
 
	_cur.file_index = file_index; // XXX
 
	_palette_remap_grf[_cur.file_index] = (config->palette & GRFP_USE_MASK);
 

	
 
	_cur.grfconfig = config;
 

	
 
	DEBUG(grf, 2, "LoadNewGRFFile: Reading NewGRF-file '%s'", filename);
 

	
 
	_cur.grf_container_ver = GetGRFContainerVersion();
 
	if (_cur.grf_container_ver == 0) {
 
		DEBUG(grf, 7, "LoadNewGRFFile: Custom .grf has invalid format");
 
		return;
 
	}
 

	
 
	if (stage == GLS_INIT || stage == GLS_ACTIVATION) {
 
		/* We need the sprite offsets in the init stage for NewGRF sounds
 
		 * and in the activation stage for real sprites. */
 
		ReadGRFSpriteOffsets(_cur.grf_container_ver);
 
	} else {
 
		/* Skip sprite section offset if present. */
 
		if (_cur.grf_container_ver >= 2) FioReadDword();
 
	}
 

	
 
	if (_cur.grf_container_ver >= 2) {
 
		/* Read compression value. */
 
		byte compression = FioReadByte();
 
		if (compression != 0) {
 
			DEBUG(grf, 7, "LoadNewGRFFile: Unsupported compression format");
 
			return;
 
		}
 
	}
 

	
 
	/* Skip the first sprite; we don't care about how many sprites this
 
	 * does contain; newest TTDPatches and George's longvehicles don't
 
	 * neither, apparently. */
 
	if (FioReadWord() == 4 && FioReadByte() == 0xFF) {
 
	uint32 num = _cur.grf_container_ver >= 2 ? FioReadDword() : FioReadWord();
 
	if (num == 4 && FioReadByte() == 0xFF) {
 
		FioReadDword();
 
	} else {
 
		DEBUG(grf, 7, "LoadNewGRFFile: Custom .grf has invalid format");
 
		return;
 
	}
 

	
 
	_cur.ClearDataForNextFile();
 

	
 
	ReusableBuffer<byte> buf;
 

	
 
	while ((num = FioReadWord()) != 0) {
 
	while ((num = (_cur.grf_container_ver >= 2 ? FioReadDword() : FioReadWord())) != 0) {
 
		byte type = FioReadByte();
 
		_cur.nfo_line++;
 

	
 
		if (type == 0xFF) {
 
			if (_cur.skip_sprites == 0) {
 
				DecodeSpecialSprite(buf.Allocate(num), num, stage);
 

	
 
				/* Stop all processing if we are to skip the remaining sprites */
 
				if (_cur.skip_sprites == -1) break;
 

	
 
				continue;
 
			} else {
 
				FioSkipBytes(num);
 
			}
 
		} else {
 
			if (_cur.skip_sprites == 0) {
 
				grfmsg(0, "LoadNewGRFFile: Unexpected sprite, disabling");
 
				DisableGrf(STR_NEWGRF_ERROR_UNEXPECTED_SPRITE);
 
				break;
 
			}
 

	
 
			FioSkipBytes(7);
 
			SkipSpriteData(type, num - 8);
 
			if (_cur.grf_container_ver >= 2 && type == 0xFD) {
 
				/* Reference to data section. Container version >= 2 only. */
 
				FioSkipBytes(num);
 
			} else {
 
				FioSkipBytes(7);
 
				SkipSpriteData(type, num - 8);
 
			}
 
		}
 

	
 
		if (_cur.skip_sprites > 0) _cur.skip_sprites--;
 
	}
 
}
 

	
 
/**
 
 * Relocates the old shore sprites at new positions.
 
 *
 
 * 1. If shore sprites are neither loaded by Action5 nor ActionA, the extra sprites from openttd(w/d).grf are used. (SHORE_REPLACE_ONLY_NEW)
 
 * 2. If a newgrf replaces some shore sprites by ActionA. The (maybe also replaced) grass tiles are used for corner shores. (SHORE_REPLACE_ACTION_A)
 
 * 3. If a newgrf replaces shore sprites by Action5 any shore replacement by ActionA has no effect. (SHORE_REPLACE_ACTION_5)
src/newgrf.h
Show inline comments
 
@@ -168,24 +168,26 @@ struct GRFLoadedFeatures {
 
 * @param bit The bit to check.
 
 * @return Whether the bit is set.
 
 */
 
static inline bool HasGrfMiscBit(GrfMiscBit bit)
 
{
 
	extern byte _misc_grf_features;
 
	return HasBit(_misc_grf_features, bit);
 
}
 

	
 
/* Indicates which are the newgrf features currently loaded ingame */
 
extern GRFLoadedFeatures _loaded_newgrf_features;
 

	
 
byte GetGRFContainerVersion();
 

	
 
void LoadNewGRFFile(struct GRFConfig *config, uint file_index, GrfLoadingStage stage, Subdirectory subdir);
 
void LoadNewGRF(uint load_index, uint file_index);
 
void ReloadNewGRFData(); // in saveload/afterload.cpp
 
void ResetNewGRFData();
 
void ResetPersistentNewGRFData();
 

	
 
void CDECL grfmsg(int severity, const char *str, ...) WARN_FORMAT(2, 3);
 

	
 
bool GetGlobalVariable(byte param, uint32 *value, const GRFFile *grffile);
 

	
 
StringID MapGRFStringID(uint32 grfid, StringID str);
 
void ShowNewGRFError();
src/newgrf_config.cpp
Show inline comments
 
@@ -285,40 +285,66 @@ void GRFParameterInfo::SetValue(struct G
 
 * @param p1 Unused.
 
 * @return Always true.
 
 */
 
bool UpdateNewGRFConfigPalette(int32 p1)
 
{
 
	for (GRFConfig *c = _grfconfig_newgame; c != NULL; c = c->next) c->SetSuitablePalette();
 
	for (GRFConfig *c = _grfconfig_static;  c != NULL; c = c->next) c->SetSuitablePalette();
 
	for (GRFConfig *c = _all_grfs;          c != NULL; c = c->next) c->SetSuitablePalette();
 
	return true;
 
}
 

	
 
/**
 
 * Get the data section size of a GRF.
 
 * @param f GRF.
 
 * @return Size of the data section or SIZE_MAX if the file has no separate data section.
 
 */
 
size_t GRFGetSizeOfDataSection(FILE *f)
 
{
 
	extern const byte _grf_cont_v2_sig[];
 
	static const uint header_len = 14;
 

	
 
	byte data[header_len];
 
	if (fread(data, 1, header_len, f) == header_len) {
 
		if (data[0] == 0 && data[1] == 0 && MemCmpT(data + 2, _grf_cont_v2_sig, 8) == 0) {
 
			/* Valid container version 2, get data section size. */
 
			size_t offset = (data[13] << 24) | (data[12] << 16) | (data[11] << 8) | data[10];
 
			return header_len + offset;
 
		}
 
	}
 

	
 
	return SIZE_MAX;
 
}
 

	
 
/**
 
 * Calculate the MD5 sum for a GRF, and store it in the config.
 
 * @param config GRF to compute.
 
 * @param subdir The subdirectory to look in.
 
 * @return MD5 sum was successfully computed
 
 */
 
static bool CalcGRFMD5Sum(GRFConfig *config, Subdirectory subdir)
 
{
 
	FILE *f;
 
	Md5 checksum;
 
	uint8 buffer[1024];
 
	size_t len, size;
 

	
 
	/* open the file */
 
	f = FioFOpenFile(config->filename, "rb", subdir, &size);
 
	if (f == NULL) return false;
 

	
 
	size_t start = ftell(f);
 
	size = min(size, GRFGetSizeOfDataSection(f));
 
	fseek(f, start, SEEK_SET);
 

	
 
	/* calculate md5sum */
 
	while ((len = fread(buffer, 1, (size > sizeof(buffer)) ? sizeof(buffer) : size, f)) != 0 && size != 0) {
 
		size -= len;
 
		checksum.Append(buffer, len);
 
	}
 
	checksum.Finish(config->ident.md5sum);
 

	
 
	FioFCloseFile(f);
 

	
 
	return true;
 
}
 

	
src/newgrf_config.h
Show inline comments
 
@@ -209,24 +209,26 @@ extern GRFConfig *_all_grfs;          //
 
extern GRFConfig *_grfconfig;         ///< First item in list of current GRF set up
 
extern GRFConfig *_grfconfig_newgame; ///< First item in list of default GRF set up
 
extern GRFConfig *_grfconfig_static;  ///< First item in list of static GRF set up
 

	
 
/** Callback for NewGRF scanning. */
 
struct NewGRFScanCallback {
 
	/** Make sure the right destructor gets called. */
 
	virtual ~NewGRFScanCallback() {}
 
	/** Called whenever the NewGRF scan completed. */
 
	virtual void OnNewGRFsScanned() = 0;
 
};
 

	
 
size_t GRFGetSizeOfDataSection(FILE *f);
 

	
 
void ScanNewGRFFiles(NewGRFScanCallback *callback);
 
void CheckForMissingSprites();
 
const GRFConfig *FindGRFConfig(uint32 grfid, FindGRFConfigMode mode, const uint8 *md5sum = NULL, uint32 desired_version = 0);
 
GRFConfig *GetGRFConfig(uint32 grfid, uint32 mask = 0xFFFFFFFF);
 
GRFConfig **CopyGRFConfigList(GRFConfig **dst, const GRFConfig *src, bool init_only);
 
void AppendStaticGRFConfigs(GRFConfig **dst);
 
void AppendToGRFConfigList(GRFConfig **dst, GRFConfig *el);
 
void ClearGRFConfigList(GRFConfig **config);
 
void ResetGRFConfig(bool defaults);
 
GRFListCompatibility IsGoodGRFConfigList(GRFConfig *grfconfig);
 
bool FillGRFDetails(GRFConfig *config, bool is_static, Subdirectory subdir = NEWGRF_DIR);
 
char *GRFBuildParamList(char *dst, const GRFConfig *c, const char *last);
src/newgrf_sound.cpp
Show inline comments
 
@@ -55,49 +55,55 @@ 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);
 

	
 
	/* Skip ID for container version >= 2 as we only look at the first
 
	 * entry and ignore any further entries with the same ID. */
 
	if (sound->grf_container_ver >= 2) FioReadDword();
 

	
 
	/* Format: <num> <FF> <FF> <name_len> <name> '\0' <data> */
 

	
 
	uint16 num = FioReadWord();
 
	uint32 num = sound->grf_container_ver >= 2 ? FioReadDword() : 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>.
 
	uint header_size = 11;
 
	if (sound->grf_container_ver >= 2) header_size++; // The first FF in the sprite is only counted for container version >= 2.
 
	if (total_size + name_len + header_size > 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();
src/sound_type.h
Show inline comments
 
@@ -12,24 +12,25 @@
 
#ifndef SOUND_TYPE_H
 
#define SOUND_TYPE_H
 

	
 
struct SoundEntry {
 
	uint8 file_slot;
 
	size_t file_offset;
 
	size_t file_size;
 
	uint16 rate;
 
	uint8 bits_per_sample;
 
	uint8 channels;
 
	uint8 volume;
 
	uint8 priority;
 
	byte grf_container_ver; ///< NewGRF container version if the sound is from a NewGRF.
 
};
 

	
 
enum SoundFx {
 
	SND_BEGIN = 0,
 
	SND_02_SPLAT = 0,                          //  0 == 0x00 !
 
	SND_03_FACTORY_WHISTLE,
 
	SND_04_TRAIN,
 
	SND_05_TRAIN_THROUGH_TUNNEL,
 
	SND_06_SHIP_HORN,
 
	SND_07_FERRY_HORN,
 
	SND_08_PLANE_TAKE_OFF,
 
	SND_09_JET,
src/spritecache.cpp
Show inline comments
 
@@ -26,24 +26,25 @@
 
uint _sprite_cache_size = 4;
 

	
 
typedef SimpleTinyEnumT<SpriteType, byte> SpriteTypeByte;
 

	
 
struct SpriteCache {
 
	void *ptr;
 
	size_t file_pos;
 
	uint32 id;
 
	uint16 file_slot;
 
	int16 lru;
 
	SpriteTypeByte type; ///< In some cases a single sprite is misused by two NewGRFs. Once as real sprite and once as recolour sprite. If the recolour sprite gets into the cache it might be drawn as real sprite which causes enormous trouble.
 
	bool warned;         ///< True iff the user has been warned about incorrect use of this sprite
 
	byte container_ver;  ///< Container version of the GRF the sprite is from.
 
};
 

	
 

	
 
static uint _spritecache_items = 0;
 
static SpriteCache *_spritecache = NULL;
 

	
 

	
 
static inline SpriteCache *GetSpriteCache(uint index)
 
{
 
	return &_spritecache[index];
 
}
 

	
 
@@ -218,25 +219,25 @@ static void *ReadSprite(const SpriteCach
 
			return BlitterFactoryBase::GetCurrentBlitter()->Encode(&sprite, allocator);
 
		}
 
		/* If the PNG couldn't be loaded, fall back to 8bpp grfs */
 
#else
 
		static bool show_once = true;
 
		if (show_once) {
 
			DEBUG(misc, 0, "You are running a 32bpp blitter, but this build is without libpng support; falling back to 8bpp graphics");
 
			show_once = false;
 
		}
 
#endif /* WITH_PNG */
 
	}
 

	
 
	SpriteLoaderGrf sprite_loader;
 
	SpriteLoaderGrf sprite_loader(sc->container_ver);
 
	SpriteLoader::Sprite sprite;
 
	sprite.type = sprite_type;
 

	
 
	if (!sprite_loader.LoadSprite(&sprite, file_slot, file_pos, sprite_type)) {
 
		if (sprite_type == ST_MAPGEN) return NULL;
 
		if (id == SPR_IMG_QUERY) usererror("Okay... something went horribly wrong. I couldn't load the fallback sprite. What should I do?");
 
		return (void*)GetRawSprite(SPR_IMG_QUERY, ST_NORMAL, allocator);
 
	}
 

	
 
	if (sprite_type == ST_MAPGEN) {
 
		/* Ugly hack to work around the problem that the old landscape
 
		 *  generator assumes that those sprites are stored uncompressed in
 
@@ -260,86 +261,149 @@ static void *ReadSprite(const SpriteCach
 
		while (num-- > 0) {
 
			*dest++ = src->m;
 
			src++;
 
		}
 

	
 
		return s;
 
	}
 

	
 
	return BlitterFactoryBase::GetCurrentBlitter()->Encode(&sprite, allocator);
 
}
 

	
 

	
 
bool LoadNextSprite(int load_index, byte file_slot, uint file_sprite_id)
 
/** */
 
static std::map<uint32, size_t> _grf_sprite_offsets;
 

	
 
/**
 
 * Get the file offset for a specific sprite in the sprite section of a GRF.
 
 * @param id ID of the sprite to look up.
 
 * @return Position of the sprite in the sprite section or SIZE_MAX if no such sprite is present.
 
 */
 
size_t GetGRFSpriteOffset(uint32 id)
 
{
 
	return _grf_sprite_offsets.find(id) != _grf_sprite_offsets.end() ? _grf_sprite_offsets[id] : SIZE_MAX;
 
}
 

	
 
/**
 
 * Parse the sprite section of GRFs.
 
 * @param container_version Container version of the GRF we're currently processing.
 
 */
 
void ReadGRFSpriteOffsets(byte container_version)
 
{
 
	_grf_sprite_offsets.clear();
 

	
 
	if (container_version >= 2) {
 
		/* Seek to sprite section of the GRF. */
 
		size_t data_offset = FioReadDword();
 
		size_t old_pos = FioGetPos();
 
		FioSeekTo(data_offset, SEEK_CUR);
 

	
 
		/* Loop over all sprite section entries and store the file
 
		 * offset for each newly encountered ID. */
 
		uint32 id, prev_id = 0;
 
		while ((id = FioReadDword()) != 0) {
 
			if (id != prev_id) _grf_sprite_offsets[id] = FioGetPos() - 4;
 
			prev_id = id;
 
			FioSkipBytes(FioReadDword());
 
		}
 

	
 
		/* Continue processing the data section. */
 
		FioSeekTo(old_pos, SEEK_SET);
 
	}
 
}
 

	
 

	
 
/**
 
 * Load a real or recolour sprite.
 
 * @param load_index Global sprite index.
 
 * @param file_slot GRF to load from.
 
 * @param file_sprite_id Sprite number in the GRF.
 
 * @param container_version Container version of the GRF.
 
 * @return True if a valid sprite was loaded, false on any error.
 
 */
 
bool LoadNextSprite(int load_index, byte file_slot, uint file_sprite_id, byte container_version)
 
{
 
	size_t file_pos = FioGetPos();
 

	
 
	/* Read sprite header. */
 
	uint16 num = FioReadWord();
 
	uint32 num = container_version >= 2 ? FioReadDword() : FioReadWord();
 
	if (num == 0) return false;
 
	byte grf_type = FioReadByte();
 

	
 
	SpriteType type;
 
	void *data = NULL;
 
	if (grf_type == 0xFF) {
 
		/* Some NewGRF files have "empty" pseudo-sprites which are 1
 
		 * byte long. Catch these so the sprites won't be displayed. */
 
		if (num == 1) {
 
			FioReadByte();
 
			return false;
 
		}
 
		type = ST_RECOLOUR;
 
		data = ReadRecolourSprite(file_slot, num);
 
	} else if (container_version >= 2 && grf_type == 0xFD) {
 
		if (num != 4) {
 
			/* Invalid sprite section include, ignore. */
 
			FioSkipBytes(num);
 
			return false;
 
		}
 
		/* It is not an error if no sprite with the provided ID is found in the sprite section. */
 
		file_pos = GetGRFSpriteOffset(FioReadDword());
 
		type = ST_NORMAL;
 
	} else {
 
		FioSkipBytes(7);
 
		type = SkipSpriteData(grf_type, num - 8) ? ST_NORMAL : ST_INVALID;
 
		/* Inline sprites are not supported for container version >= 2. */
 
		if (container_version >= 2) return false;
 
	}
 

	
 
	if (type == ST_INVALID) return false;
 

	
 
	if (load_index >= MAX_SPRITES) {
 
		usererror("Tried to load too many sprites (#%d; max %d)", load_index, MAX_SPRITES);
 
	}
 

	
 
	bool is_mapgen = IsMapgenSpriteID(load_index);
 

	
 
	if (is_mapgen) {
 
		if (type != ST_NORMAL) usererror("Uhm, would you be so kind not to load a NewGRF that changes the type of the map generator sprites?");
 
		type = ST_MAPGEN;
 
	}
 

	
 
	SpriteCache *sc = AllocateSpriteCache(load_index);
 
	sc->file_slot = file_slot;
 
	sc->file_pos = file_pos;
 
	sc->ptr = data;
 
	sc->lru = 0;
 
	sc->id = file_sprite_id;
 
	sc->type = type;
 
	sc->warned = false;
 
	sc->container_ver = container_version;
 

	
 
	return true;
 
}
 

	
 

	
 
void DupSprite(SpriteID old_spr, SpriteID new_spr)
 
{
 
	SpriteCache *scnew = AllocateSpriteCache(new_spr); // may reallocate: so put it first
 
	SpriteCache *scold = GetSpriteCache(old_spr);
 

	
 
	scnew->file_slot = scold->file_slot;
 
	scnew->file_pos = scold->file_pos;
 
	scnew->ptr = NULL;
 
	scnew->id = scold->id;
 
	scnew->type = scold->type;
 
	scnew->warned = false;
 
	scnew->container_ver = scold->container_ver;
 
}
 

	
 
/**
 
 * S_FREE_MASK is used to mask-out lower bits of MemBlock::size
 
 * If they are non-zero, the block is free.
 
 * S_FREE_MASK has to ensure MemBlock is correctly aligned -
 
 * it means 8B (S_FREE_MASK == 7) on 64bit systems!
 
 */
 
static const size_t S_FREE_MASK = sizeof(size_t) - 1;
 

	
 
/* to make sure nobody adds things to MemBlock without checking S_FREE_MASK first */
 
assert_compile(sizeof(MemBlock) == sizeof(size_t));
src/spritecache.h
Show inline comments
 
@@ -42,17 +42,19 @@ static inline const Sprite *GetSprite(Sp
 
}
 

	
 
static inline const byte *GetNonSprite(SpriteID sprite, SpriteType type)
 
{
 
	assert(type == ST_RECOLOUR);
 
	return (byte*)GetRawSprite(sprite, type);
 
}
 

	
 
void GfxInitSpriteMem();
 
void GfxClearSpriteCache();
 
void IncreaseSpriteLRU();
 

	
 
bool LoadNextSprite(int load_index, byte file_index, uint file_sprite_id);
 
void ReadGRFSpriteOffsets(byte container_version);
 
size_t GetGRFSpriteOffset(uint32 id);
 
bool LoadNextSprite(int load_index, byte file_index, uint file_sprite_id, byte container_version);
 
bool SkipSpriteData(byte type, uint16 num);
 
void DupSprite(SpriteID old_spr, SpriteID new_spr);
 

	
 
#endif /* SPRITECACHE_H */
src/spriteloader/grf.cpp
Show inline comments
 
@@ -13,24 +13,33 @@
 
#include "../gfx_func.h"
 
#include "../fileio_func.h"
 
#include "../debug.h"
 
#include "../strings_func.h"
 
#include "table/strings.h"
 
#include "../error.h"
 
#include "../core/math_func.hpp"
 
#include "../core/alloc_type.hpp"
 
#include "grf.hpp"
 

	
 
extern const byte _palmap_w2d[];
 

	
 
/** The different colour components a sprite can have. */
 
enum SpriteColourComponent {
 
	SCC_RGB   = 1 << 0, ///< Sprite has RGB.
 
	SCC_ALPHA = 1 << 1, ///< Sprite has alpha.
 
	SCC_PAL   = 1 << 2, ///< Sprite has palette data.
 
	SCC_MASK  = SCC_RGB | SCC_ALPHA | SCC_PAL, ///< Mask of valid colour bits.
 
};
 
DECLARE_ENUM_AS_BIT_SET(SpriteColourComponent)
 

	
 
/**
 
 * We found a corrupted sprite. This means that the sprite itself
 
 * contains invalid data or is too small for the given dimensions.
 
 * @param file_slot the file the errored sprite is in
 
 * @param file_pos the location in the file of the errored sprite
 
 * @param line the line where the error occurs.
 
 * @return always false (to tell loading the sprite failed)
 
 */
 
static bool WarnCorruptSprite(uint8 file_slot, size_t file_pos, int line)
 
{
 
	static byte warning_level = 0;
 
	if (warning_level == 0) {
 
@@ -167,35 +176,91 @@ bool DecodeSingleSprite(SpriteLoader::Sp
 
		sprite->x_offs *= ZOOM_LVL_BASE;
 
		sprite->y_offs *= ZOOM_LVL_BASE;
 
	}
 

	
 
	/* Make sure to mark all transparent pixels transparent on the alpha channel too */
 
	for (int i = 0; i < sprite->width * sprite->height; i++) {
 
		if (sprite->data[i].m != 0) sprite->data[i].a = 0xFF;
 
	}
 

	
 
	return true;
 
}
 

	
 
bool SpriteLoaderGrf::LoadSprite(SpriteLoader::Sprite *sprite, uint8 file_slot, size_t file_pos, SpriteType sprite_type)
 
bool LoadSpriteV1(SpriteLoader::Sprite *sprite, uint8 file_slot, size_t file_pos, SpriteType sprite_type)
 
{
 
	/* Open the right file and go to the correct position */
 
	FioSeekToFile(file_slot, file_pos);
 

	
 
	/* Read the size and type */
 
	int num = FioReadWord();
 
	byte type = FioReadByte();
 

	
 
	/* Type 0xFF indicates either a colourmap or some other non-sprite info; we do not handle them here */
 
	if (type == 0xFF) return false;
 

	
 
	sprite->height = FioReadByte();
 
	sprite->width  = FioReadWord();
 
	sprite->x_offs = FioReadWord();
 
	sprite->y_offs = FioReadWord();
 

	
 
	/* 0x02 indicates it is a compressed sprite, so we can't rely on 'num' to be valid.
 
	 * In case it is uncompressed, the size is 'num' - 8 (header-size). */
 
	num = (type & 0x02) ? sprite->width * sprite->height : num - 8;
 

	
 
	return DecodeSingleSprite(sprite, file_slot, file_pos, sprite_type, num, type);
 
}
 

	
 
bool LoadSpriteV2(SpriteLoader::Sprite *sprite, uint8 file_slot, size_t file_pos, SpriteType sprite_type)
 
{
 
	/* Is the sprite not present/stripped in the GRF? */
 
	if (file_pos == SIZE_MAX) return false;
 

	
 
	/* Open the right file and go to the correct position */
 
	FioSeekToFile(file_slot, file_pos);
 

	
 
	uint32 id = FioReadDword();
 

	
 
	do {
 
		int64 num = FioReadDword();
 
		size_t start_pos = FioGetPos();
 
		byte type = FioReadByte();
 

	
 
		/* Type 0xFF indicates either a colourmap or some other non-sprite info; we do not handle them here. */
 
		if (type == 0xFF) return false;
 

	
 
		byte colour = type & SCC_MASK;
 
		byte zoom = FioReadByte();
 

	
 
		if (colour == SCC_PAL && zoom == 0) {
 
			sprite->height = FioReadWord();
 
			sprite->width  = FioReadWord();
 
			sprite->x_offs = FioReadWord();
 
			sprite->y_offs = FioReadWord();
 

	
 
			/* Mask out colour information. */
 
			type = type & ~SCC_MASK;
 

	
 
			/* For chunked encoding we store the decompressed size in the file,
 
			 * otherwise we can calculate it from the image dimensions. */
 
			uint decomp_size = (type & 0x08) ? FioReadDword() : sprite->width * sprite->height;
 

	
 
			bool valid = DecodeSingleSprite(sprite, file_slot, file_pos, sprite_type, decomp_size, type);
 
			if (FioGetPos() != start_pos + num) return WarnCorruptSprite(file_slot, file_pos, __LINE__);
 
			return valid;
 
		} else {
 
			/* Not the wanted zoom level or colour depth, continue searching. */
 
			FioSkipBytes(num - 2);
 
		}
 

	
 
	} while (FioReadDword() == id);
 

	
 
	return false;
 
}
 

	
 
bool SpriteLoaderGrf::LoadSprite(SpriteLoader::Sprite *sprite, uint8 file_slot, size_t file_pos, SpriteType sprite_type)
 
{
 
	if (this->container_ver >= 2) {
 
		return LoadSpriteV2(sprite, file_slot, file_pos, sprite_type);
 
	} else {
 
		return LoadSpriteV1(sprite, file_slot, file_pos, sprite_type);
 
	}
 
}
src/spriteloader/grf.hpp
Show inline comments
 
@@ -7,17 +7,19 @@
 
 * 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/>.
 
 */
 

	
 
/** @file grf.hpp Base for reading sprites from (New)GRFs. */
 

	
 
#ifndef SPRITELOADER_GRF_HPP
 
#define SPRITELOADER_GRF_HPP
 

	
 
#include "spriteloader.hpp"
 

	
 
/** Sprite loader for graphics coming from a (New)GRF. */
 
class SpriteLoaderGrf : public SpriteLoader {
 
	byte container_ver;
 
public:
 
	SpriteLoaderGrf(byte container_ver) : container_ver(container_ver) {}
 
	bool LoadSprite(SpriteLoader::Sprite *sprite, uint8 file_slot, size_t file_pos, SpriteType sprite_type);
 
};
 

	
 
#endif /* SPRITELOADER_GRF_HPP */
0 comments (0 inline, 0 general)