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
 
@@ -220,12 +220,14 @@ enum BlitterType {
 
/** 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:
 
};
src/gfxinit.cpp
Show inline comments
 
@@ -49,13 +49,22 @@ static uint LoadGrfFile(const char *file
 
	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.");
 
		}
 
	}
 
@@ -77,17 +86,26 @@ static void LoadGrfFileIndexed(const cha
 
	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);
 
	}
 
}
 

	
 
@@ -259,12 +277,34 @@ bool GraphicsSet::FillSetDetails(IniFile
 
		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
src/newgrf.cpp
Show inline comments
 
@@ -94,12 +94,13 @@ public:
 

	
 
	/* 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];
 
@@ -4329,13 +4330,13 @@ static void NewSpriteSet(ByteReader *buf
 
	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)
 
{
 
@@ -5422,22 +5423,22 @@ static void GraphicsNew(ByteReader *buf)
 
	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)) {
 
@@ -5470,13 +5471,13 @@ static void GraphicsNew(ByteReader *buf)
 

	
 
	/* 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;
 
}
 
@@ -5720,13 +5721,13 @@ static void CfgApply(ByteReader *buf)
 
	 *                 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);
 
@@ -6064,13 +6065,13 @@ static void SpriteReplace(ByteReader *bu
 
			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;
 
			}
 
@@ -6843,14 +6844,18 @@ 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>
 
@@ -6866,27 +6871,45 @@ static void GRFSound(ByteReader *buf)
 

	
 
	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
 
@@ -6941,13 +6964,13 @@ static void LoadFontGlyph(ByteReader *bu
 

	
 
		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)
 
@@ -8557,23 +8580,48 @@ static void DecodeSpecialSprite(byte *bu
 
		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.
 
@@ -8601,27 +8649,52 @@ void LoadNewGRFFile(GRFConfig *config, u
 
	_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);
 
@@ -8637,14 +8710,19 @@ void LoadNewGRFFile(GRFConfig *config, u
 
			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--;
 
	}
 
}
 

	
src/newgrf.h
Show inline comments
 
@@ -174,12 +174,14 @@ static inline bool HasGrfMiscBit(GrfMisc
 
	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();
 

	
src/newgrf_config.cpp
Show inline comments
 
@@ -291,12 +291,34 @@ bool UpdateNewGRFConfigPalette(int32 p1)
 
	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)
 
@@ -307,12 +329,16 @@ static bool CalcGRFMD5Sum(GRFConfig *con
 
	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);
src/newgrf_config.h
Show inline comments
 
@@ -215,12 +215,14 @@ 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);
src/newgrf_sound.cpp
Show inline comments
 
@@ -61,15 +61,19 @@ uint GetNumSounds()
 
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);
 
@@ -85,13 +89,15 @@ bool LoadNewGRFSound(SoundEntry *sound)
 
	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));
src/sound_type.h
Show inline comments
 
@@ -18,12 +18,13 @@ struct SoundEntry {
 
	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,
src/spritecache.cpp
Show inline comments
 
@@ -32,12 +32,13 @@ struct SpriteCache {
 
	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;
 

	
 
@@ -224,13 +225,13 @@ static void *ReadSprite(const SpriteCach
 
			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?");
 
@@ -266,18 +267,68 @@ static void *ReadSprite(const SpriteCach
 
	}
 

	
 
	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) {
 
@@ -286,15 +337,26 @@ bool LoadNextSprite(int load_index, byte
 
		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);
 
@@ -312,12 +374,13 @@ bool LoadNextSprite(int load_index, byte
 
	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)
 
@@ -328,12 +391,13 @@ void DupSprite(SpriteID old_spr, SpriteI
 
	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 -
src/spritecache.h
Show inline comments
 
@@ -48,11 +48,13 @@ static inline const byte *GetNonSprite(S
 
}
 

	
 
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
 
@@ -19,12 +19,21 @@
 
#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.
 
@@ -173,13 +182,13 @@ bool DecodeSingleSprite(SpriteLoader::Sp
 
		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();
 
@@ -196,6 +205,62 @@ bool SpriteLoaderGrf::LoadSprite(SpriteL
 
	/* 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
 
@@ -13,11 +13,13 @@
 
#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)