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
 
@@ -223,6 +223,8 @@ struct GraphicsSet : BaseSet<GraphicsSet
 
	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. */
src/gfxinit.cpp
Show inline comments
 
@@ -52,7 +52,16 @@ static uint LoadGrfFile(const char *file
 

	
 
	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) {
 
@@ -80,11 +89,20 @@ static void LoadGrfFileIndexed(const cha
 

	
 
	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);
 
@@ -262,6 +280,28 @@ bool GraphicsSet::FillSetDetails(IniFile
 
	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.
src/newgrf.cpp
Show inline comments
 
@@ -97,6 +97,7 @@ public:
 
	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)
 
@@ -4332,7 +4333,7 @@ static void NewSpriteSet(ByteReader *buf
 

	
 
	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);
 
	}
 
}
 

	
 
@@ -5425,16 +5426,16 @@ static void GraphicsNew(ByteReader *buf)
 
		/* 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;
 
	}
 
@@ -5473,7 +5474,7 @@ static void GraphicsNew(ByteReader *buf)
 

	
 
	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;
 
@@ -5723,7 +5724,7 @@ static void CfgApply(ByteReader *buf)
 

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

	
 
@@ -6067,7 +6068,7 @@ static void SpriteReplace(ByteReader *bu
 
		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. */
 
@@ -6846,8 +6847,12 @@ static void LoadGRFSound(size_t offs)
 
	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 */
 
@@ -6869,9 +6874,21 @@ static void GRFSound(ByteReader *buf)
 

	
 
		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);
 
@@ -6883,7 +6900,13 @@ static void GRFSound(ByteReader *buf)
 
		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;
 

	
 
@@ -6944,7 +6967,7 @@ static void LoadFontGlyph(ByteReader *bu
 
		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);
 
		}
 
	}
 
}
 
@@ -8560,6 +8583,32 @@ static void DecodeSpecialSprite(byte *bu
 
}
 

	
 

	
 
/** 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.
 
@@ -8570,7 +8619,6 @@ static void DecodeSpecialSprite(byte *bu
 
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
 
@@ -8604,10 +8652,35 @@ void LoadNewGRFFile(GRFConfig *config, u
 

	
 
	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");
 
@@ -8618,7 +8691,7 @@ void LoadNewGRFFile(GRFConfig *config, u
 

	
 
	ReusableBuffer<byte> buf;
 

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

	
 
@@ -8640,8 +8713,13 @@ void LoadNewGRFFile(GRFConfig *config, u
 
				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--;
src/newgrf.h
Show inline comments
 
@@ -177,6 +177,8 @@ static inline bool HasGrfMiscBit(GrfMisc
 
/* 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
src/newgrf_config.cpp
Show inline comments
 
@@ -294,6 +294,28 @@ bool UpdateNewGRFConfigPalette(int32 p1)
 
}
 

	
 
/**
 
 * 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.
 
@@ -310,6 +332,10 @@ static bool CalcGRFMD5Sum(GRFConfig *con
 
	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;
src/newgrf_config.h
Show inline comments
 
@@ -218,6 +218,8 @@ struct NewGRFScanCallback {
 
	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);
src/newgrf_sound.cpp
Show inline comments
 
@@ -64,9 +64,13 @@ bool LoadNewGRFSound(SoundEntry *sound)
 

	
 
	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;
 

	
 
@@ -88,7 +92,9 @@ bool LoadNewGRFSound(SoundEntry *sound)
 
	}
 

	
 
	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;
 
	}
src/sound_type.h
Show inline comments
 
@@ -21,6 +21,7 @@ struct SoundEntry {
 
	uint8 channels;
 
	uint8 volume;
 
	uint8 priority;
 
	byte grf_container_ver; ///< NewGRF container version if the sound is from a NewGRF.
 
};
 

	
 
enum SoundFx {
src/spritecache.cpp
Show inline comments
 
@@ -35,6 +35,7 @@ struct SpriteCache {
 
	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.
 
};
 

	
 

	
 
@@ -227,7 +228,7 @@ static void *ReadSprite(const SpriteCach
 
#endif /* WITH_PNG */
 
	}
 

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

	
 
@@ -269,12 +270,62 @@ static void *ReadSprite(const SpriteCach
 
}
 

	
 

	
 
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();
 

	
 
@@ -289,9 +340,20 @@ bool LoadNextSprite(int load_index, byte
 
		}
 
		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;
 
@@ -315,6 +377,7 @@ bool LoadNextSprite(int load_index, byte
 
	sc->id = file_sprite_id;
 
	sc->type = type;
 
	sc->warned = false;
 
	sc->container_ver = container_version;
 

	
 
	return true;
 
}
 
@@ -331,6 +394,7 @@ void DupSprite(SpriteID old_spr, SpriteI
 
	scnew->id = scold->id;
 
	scnew->type = scold->type;
 
	scnew->warned = false;
 
	scnew->container_ver = scold->container_ver;
 
}
 

	
 
/**
src/spritecache.h
Show inline comments
 
@@ -51,7 +51,9 @@ 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);
 

	
src/spriteloader/grf.cpp
Show inline comments
 
@@ -22,6 +22,15 @@
 

	
 
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.
 
@@ -176,7 +185,7 @@ bool DecodeSingleSprite(SpriteLoader::Sp
 
	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);
 
@@ -199,3 +208,59 @@ bool SpriteLoaderGrf::LoadSprite(SpriteL
 

	
 
	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
 
@@ -16,7 +16,9 @@
 

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

	
0 comments (0 inline, 0 general)