Codechange: introduce SpriteFile to be used by the sprite loader instead of the global FIO slot functionality

This commit is contained in:
Rubidium 2021-04-14 17:20:39 +02:00 committed by rubidium42
parent 0dd339ecd8
commit fdc11a9f94
15 changed files with 317 additions and 220 deletions

View File

@ -9,7 +9,7 @@
#include "stdafx.h"
#include "fileio_func.h"
#include "random_access_file_type.h"
#include "spriteloader/spriteloader.hpp"
#include "debug.h"
#include "fios.h"
#include "string_func.h"
@ -30,8 +30,8 @@
#include "safeguards.h"
static RandomAccessFile *_fio_current_file; ///< current file handle for the Fio functions
static std::array<RandomAccessFile *, MAX_FILE_SLOTS> _fio_files; ///< array of random access files we can have open
static SpriteFile *_fio_current_file; ///< current file handle for the Fio functions
static std::array<SpriteFile *, MAX_FILE_SLOTS> _fio_files; ///< array of random access files we can have open
/** Whether the working directory should be scanned. */
static bool _do_scan_working_directory = true;
@ -40,9 +40,9 @@ extern std::string _config_file;
extern std::string _highscore_file;
/**
* Transition helper to get the RandomAccessFile associated with a given slot.
* Transition helper to get the SpriteFile associated with a given slot.
*/
RandomAccessFile *FioGetRandomAccessFile(int slot)
SpriteFile *FioGetSpriteFile(int slot)
{
return _fio_files[slot];
}
@ -83,9 +83,9 @@ void FioSeekTo(size_t pos, int mode)
*/
void FioSeekToFile(uint8 slot, size_t pos)
{
RandomAccessFile *raf = _fio_files[slot];
assert(raf != nullptr);
_fio_current_file = raf;
SpriteFile *file = _fio_files[slot];
assert(file != nullptr);
_fio_current_file = file;
_fio_current_file->SeekTo(pos, SEEK_SET);
}
@ -149,13 +149,15 @@ void FioCloseAll()
* @param slot Index to assign.
* @param filename Name of the file at the disk.
* @param subdir The sub directory to search this file in.
* @param palette_remap Whether palette remapping needs to take place.
*/
void FioOpenFile(int slot, const std::string &filename, Subdirectory subdir)
SpriteFile &FioOpenFile(int slot, const std::string &filename, Subdirectory subdir, bool palette_remap)
{
RandomAccessFile *raf = new RandomAccessFile(filename, subdir);
SpriteFile *file = new SpriteFile(filename, subdir, palette_remap);
delete _fio_files[slot];
_fio_files[slot] = raf;
_fio_current_file = raf;
_fio_files[slot] = file;
_fio_current_file = file;
return *file;
}
static const char * const _subdirs[] = {

View File

@ -23,10 +23,10 @@ byte FioReadByte();
uint16 FioReadWord();
uint32 FioReadDword();
void FioCloseAll();
void FioOpenFile(int slot, const std::string &filename, Subdirectory subdir);
class SpriteFile &FioOpenFile(int slot, const std::string &filename, Subdirectory subdir, bool palette_remap = false);
void FioReadBlock(void *ptr, size_t size);
void FioSkipBytes(int n);
class RandomAccessFile *FioGetRandomAccessFile(int slot);
class SpriteFile *FioGetSpriteFile(int slot);
void FioFCloseFile(FILE *f);
FILE *FioFOpenFile(const std::string &filename, const char *mode, Subdirectory subdir, size_t *filesize = nullptr);

View File

@ -178,8 +178,6 @@ TextColour GetContrastColour(uint8 background, uint8 threshold = 128);
*/
extern byte _colour_gradient[COLOUR_END][8];
extern bool _palette_remap_grf[];
/**
* Return the colour for a particular greyscale level.
* @param level Intensity, 0 = black, 15 = white

View File

@ -26,9 +26,6 @@
#include "safeguards.h"
/** Whether the given NewGRFs must get a palette remap from windows to DOS or not. */
bool _palette_remap_grf[MAX_FILE_SLOTS];
#include "table/landscape_sprite.h"
/** Offsets for loading the different "replacement" sprites in the files. */
@ -43,27 +40,28 @@ static const SpriteID * const _landscape_spriteindexes[] = {
* @param filename The name of the file to open.
* @param load_index The offset of the first sprite.
* @param file_index The Fio offset to load the file in.
* @param needs_palette_remap Whether the colours in the GRF file need a palette remap.
* @return The number of loaded sprites.
*/
static uint LoadGrfFile(const char *filename, uint load_index, int file_index)
static uint LoadGrfFile(const char *filename, uint load_index, int file_index, bool needs_palette_remap)
{
uint load_index_org = load_index;
uint sprite_id = 0;
FioOpenFile(file_index, filename, BASESET_DIR);
SpriteFile &file = FioOpenFile(file_index, filename, BASESET_DIR, needs_palette_remap);
DEBUG(sprite, 2, "Reading grf-file '%s'", filename);
byte container_ver = GetGRFContainerVersion();
byte container_ver = file.GetContainerVersion();
if (container_ver == 0) usererror("Base grf '%s' is corrupt", filename);
ReadGRFSpriteOffsets(container_ver);
ReadGRFSpriteOffsets(file);
if (container_ver >= 2) {
/* Read compression. */
byte compression = FioReadByte();
byte compression = file.ReadByte();
if (compression != 0) usererror("Unsupported compression format");
}
while (LoadNextSprite(load_index, file_index, sprite_id, container_ver)) {
while (LoadNextSprite(load_index, file, sprite_id)) {
load_index++;
sprite_id++;
if (load_index >= MAX_SPRITES) {
@ -80,23 +78,24 @@ static uint LoadGrfFile(const char *filename, uint load_index, int file_index)
* @param filename The name of the file to open.
* @param index_tbl The offsets of each of the sprites.
* @param file_index The Fio offset to load the file in.
* @param needs_palette_remap Whether the colours in the GRF file need a palette remap.
* @return The number of loaded sprites.
*/
static void LoadGrfFileIndexed(const char *filename, const SpriteID *index_tbl, int file_index)
static void LoadGrfFileIndexed(const char *filename, const SpriteID *index_tbl, int file_index, bool needs_palette_remap)
{
uint start;
uint sprite_id = 0;
FioOpenFile(file_index, filename, BASESET_DIR);
SpriteFile &file = FioOpenFile(file_index, filename, BASESET_DIR, needs_palette_remap);
DEBUG(sprite, 2, "Reading indexed grf-file '%s'", filename);
byte container_ver = GetGRFContainerVersion();
byte container_ver = file.GetContainerVersion();
if (container_ver == 0) usererror("Base grf '%s' is corrupt", filename);
ReadGRFSpriteOffsets(container_ver);
ReadGRFSpriteOffsets(file);
if (container_ver >= 2) {
/* Read compression. */
byte compression = FioReadByte();
byte compression = file.ReadByte();
if (compression != 0) usererror("Unsupported compression format");
}
@ -104,7 +103,7 @@ static void LoadGrfFileIndexed(const char *filename, const SpriteID *index_tbl,
uint end = *index_tbl++;
do {
bool b = LoadNextSprite(start, file_index, sprite_id, container_ver);
bool b = LoadNextSprite(start, file, sprite_id);
(void)b; // Unused without asserts
assert(b);
sprite_id++;
@ -162,12 +161,10 @@ void CheckExternalFiles()
/** Actually load the sprite tables. */
static void LoadSpriteTables()
{
memset(_palette_remap_grf, 0, sizeof(_palette_remap_grf));
uint i = FIRST_GRF_SLOT;
const GraphicsSet *used_set = BaseGraphics::GetUsedSet();
_palette_remap_grf[i] = (PAL_DOS != used_set->palette);
LoadGrfFile(used_set->files[GFT_BASE].filename, 0, i++);
LoadGrfFile(used_set->files[GFT_BASE].filename, 0, i++, (PAL_DOS != used_set->palette));
/*
* The second basic file always starts at the given location and does
@ -175,8 +172,7 @@ static void LoadSpriteTables()
* has a few sprites less. However, we do not care about those missing
* sprites as they are not shown anyway (logos in intro game).
*/
_palette_remap_grf[i] = (PAL_DOS != used_set->palette);
LoadGrfFile(used_set->files[GFT_LOGOS].filename, 4793, i++);
LoadGrfFile(used_set->files[GFT_LOGOS].filename, 4793, i++, (PAL_DOS != used_set->palette));
/*
* Load additional sprites for climates other than temperate.
@ -184,11 +180,11 @@ static void LoadSpriteTables()
* and the ground sprites.
*/
if (_settings_game.game_creation.landscape != LT_TEMPERATE) {
_palette_remap_grf[i] = (PAL_DOS != used_set->palette);
LoadGrfFileIndexed(
used_set->files[GFT_ARCTIC + _settings_game.game_creation.landscape - 1].filename,
_landscape_spriteindexes[_settings_game.game_creation.landscape - 1],
i++
i++,
(PAL_DOS != used_set->palette)
);
}
@ -230,7 +226,7 @@ static void LoadSpriteTables()
LoadNewGRF(SPR_NEWGRFS_BASE, i, 2);
uint total_extra_graphics = SPR_NEWGRFS_BASE - SPR_OPENTTD_BASE;
_missing_extra_graphics = GetSpriteCountForSlot(i, SPR_OPENTTD_BASE, SPR_NEWGRFS_BASE);
_missing_extra_graphics = GetSpriteCountForFile(used_set->files[GFT_EXTRA].filename, SPR_OPENTTD_BASE, SPR_NEWGRFS_BASE);
DEBUG(sprite, 1, "%u extra sprites, %u from baseset, %u from fallback", total_extra_graphics, total_extra_graphics - _missing_extra_graphics, _missing_extra_graphics);
/* The original baseset extra graphics intentionally make use of the fallback graphics.

View File

@ -99,11 +99,10 @@ public:
SpriteID spriteid; ///< First available SpriteID for loading realsprites.
/* Local state in the file */
uint file_index; ///< File index of currently processed GRF file.
SpriteFile *file; ///< File 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 pseudo sprites to skip before processing the next one. (-1 to skip to end of file)
@ -4884,7 +4883,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, _cur.grf_container_ver);
LoadNextSprite(_cur.spriteid++, *_cur.file, _cur.nfo_line);
}
}
@ -6120,16 +6119,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++, _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
LoadNextSprite(SPR_SHORE_BASE + 0, *_cur.file, _cur.nfo_line++); // SLOPE_STEEP_S
LoadNextSprite(SPR_SHORE_BASE + 5, *_cur.file, _cur.nfo_line++); // SLOPE_STEEP_W
LoadNextSprite(SPR_SHORE_BASE + 7, *_cur.file, _cur.nfo_line++); // SLOPE_WSE
LoadNextSprite(SPR_SHORE_BASE + 10, *_cur.file, _cur.nfo_line++); // SLOPE_STEEP_N
LoadNextSprite(SPR_SHORE_BASE + 11, *_cur.file, _cur.nfo_line++); // SLOPE_NWS
LoadNextSprite(SPR_SHORE_BASE + 13, *_cur.file, _cur.nfo_line++); // SLOPE_ENW
LoadNextSprite(SPR_SHORE_BASE + 14, *_cur.file, _cur.nfo_line++); // SLOPE_SEN
LoadNextSprite(SPR_SHORE_BASE + 15, *_cur.file, _cur.nfo_line++); // SLOPE_STEEP_E
LoadNextSprite(SPR_SHORE_BASE + 16, *_cur.file, _cur.nfo_line++); // SLOPE_EW
LoadNextSprite(SPR_SHORE_BASE + 17, *_cur.file, _cur.nfo_line++); // SLOPE_NS
if (_loaded_newgrf_features.shore == SHORE_REPLACE_NONE) _loaded_newgrf_features.shore = SHORE_REPLACE_ONLY_NEW;
return;
}
@ -6177,7 +6176,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, _cur.grf_container_ver);
LoadNextSprite(replace == 0 ? _cur.spriteid++ : replace++, *_cur.file, _cur.nfo_line);
}
_cur.skip_sprites = skip_num;
@ -6397,7 +6396,7 @@ static void CfgApply(ByteReader *buf)
/* Preload the next sprite */
size_t pos = FioGetPos();
uint32 num = _cur.grf_container_ver >= 2 ? FioReadDword() : FioReadWord();
uint32 num = _cur.file->GetContainerVersion() >= 2 ? FioReadDword() : FioReadWord();
uint8 type = FioReadByte();
byte *preload_sprite = nullptr;
@ -6758,7 +6757,7 @@ static void SpriteReplace(ByteReader *buf)
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, _cur.grf_container_ver); // XXX
LoadNextSprite(load_index, *_cur.file, _cur.nfo_line); // XXX
/* Shore sprites now located at different addresses.
* So detect when the old ones get replaced. */
@ -7553,9 +7552,9 @@ static void LoadGRFSound(size_t offs, SoundEntry *sound)
if (offs != SIZE_MAX) {
/* Sound is present in the NewGRF. */
sound->file = FioGetRandomAccessFile(_cur.file_index);
sound->file = _cur.file;
sound->file_offset = offs;
sound->grf_container_ver = _cur.grf_container_ver;
sound->grf_container_ver = _cur.file->GetContainerVersion();
}
}
@ -7587,10 +7586,11 @@ static void GRFSound(ByteReader *buf)
size_t offs = FioGetPos();
uint32 len = _cur.grf_container_ver >= 2 ? FioReadDword() : FioReadWord();
byte grf_container_version = _cur.file->GetContainerVersion();
uint32 len = grf_container_version >= 2 ? FioReadDword() : FioReadWord();
byte type = FioReadByte();
if (_cur.grf_container_ver >= 2 && type == 0xFD) {
if (grf_container_version >= 2 && type == 0xFD) {
/* Reference to sprite section. */
if (invalid) {
grfmsg(1, "GRFSound: Sound index out of range (multiple Action 11?)");
@ -7608,7 +7608,7 @@ static void GRFSound(ByteReader *buf)
if (type != 0xFF) {
grfmsg(1, "GRFSound: Unexpected RealSprite found, skipping");
FioSkipBytes(7);
SkipSpriteData(type, len - 8);
SkipSpriteData(*_cur.file, type, len - 8);
continue;
}
@ -7622,7 +7622,7 @@ static void GRFSound(ByteReader *buf)
case 0xFF:
/* Allocate sound only in init stage. */
if (_cur.stage == GLS_INIT) {
if (_cur.grf_container_ver >= 2) {
if (grf_container_version >= 2) {
grfmsg(1, "GRFSound: Inline sounds are not supported for container version >= 2");
} else {
LoadGRFSound(offs, sound + i);
@ -7688,7 +7688,7 @@ static void LoadFontGlyph(ByteReader *buf)
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, _cur.grf_container_ver);
LoadNextSprite(_cur.spriteid++, *_cur.file, _cur.nfo_line);
}
}
}
@ -9269,32 +9269,6 @@ static void DecodeSpecialSprite(byte *buf, uint num, GrfLoadingStage stage)
}
/** 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.
@ -9329,16 +9303,13 @@ void LoadNewGRFFile(GRFConfig *config, uint file_index, GrfLoadingStage stage, S
return;
}
FioOpenFile(file_index, filename, subdir);
_cur.file_index = file_index; // XXX
_palette_remap_grf[_cur.file_index] = (config->palette & GRFP_USE_MASK);
_cur.file = &FioOpenFile(file_index, filename, subdir, 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) {
byte grf_container_version = _cur.file->GetContainerVersion();
if (grf_container_version == 0) {
DEBUG(grf, 7, "LoadNewGRFFile: Custom .grf has invalid format");
return;
}
@ -9346,13 +9317,13 @@ void LoadNewGRFFile(GRFConfig *config, uint file_index, GrfLoadingStage stage, S
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);
ReadGRFSpriteOffsets(*_cur.file);
} else {
/* Skip sprite section offset if present. */
if (_cur.grf_container_ver >= 2) FioReadDword();
if (grf_container_version >= 2) FioReadDword();
}
if (_cur.grf_container_ver >= 2) {
if (grf_container_version >= 2) {
/* Read compression value. */
byte compression = FioReadByte();
if (compression != 0) {
@ -9364,7 +9335,7 @@ void LoadNewGRFFile(GRFConfig *config, uint file_index, GrfLoadingStage stage, S
/* 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. */
uint32 num = _cur.grf_container_ver >= 2 ? FioReadDword() : FioReadWord();
uint32 num = grf_container_version >= 2 ? FioReadDword() : FioReadWord();
if (num == 4 && FioReadByte() == 0xFF) {
FioReadDword();
} else {
@ -9376,7 +9347,7 @@ void LoadNewGRFFile(GRFConfig *config, uint file_index, GrfLoadingStage stage, S
ReusableBuffer<byte> buf;
while ((num = (_cur.grf_container_ver >= 2 ? FioReadDword() : FioReadWord())) != 0) {
while ((num = (grf_container_version >= 2 ? FioReadDword() : FioReadWord())) != 0) {
byte type = FioReadByte();
_cur.nfo_line++;
@ -9398,12 +9369,12 @@ void LoadNewGRFFile(GRFConfig *config, uint file_index, GrfLoadingStage stage, S
break;
}
if (_cur.grf_container_ver >= 2 && type == 0xFD) {
if (grf_container_version >= 2 && type == 0xFD) {
/* Reference to data section. Container version >= 2 only. */
FioSkipBytes(num);
} else {
FioSkipBytes(7);
SkipSpriteData(type, num - 8);
SkipSpriteData(*_cur.file, type, num - 8);
}
}

View File

@ -192,8 +192,6 @@ static inline bool HasGrfMiscBit(GrfMiscBit 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, uint num_baseset);
void ReloadNewGRFData(); // in saveload/afterload.cpp

View File

@ -11,7 +11,7 @@
#include <stdarg.h>
#include "window_gui.h"
#include "window_func.h"
#include "fileio_func.h"
#include "random_access_file_type.h"
#include "spritecache.h"
#include "string_func.h"
#include "strings_func.h"
@ -828,7 +828,7 @@ struct SpriteAlignerWindow : Window {
switch (widget) {
case WID_SA_CAPTION:
SetDParam(0, this->current_sprite);
SetDParamStr(1, FioGetFilename(GetOriginFileSlot(this->current_sprite)));
SetDParamStr(1, GetOriginFile(this->current_sprite)->GetSimplifiedFilename().c_str());
break;
case WID_SA_OFFSETS_ABS:

View File

@ -8,7 +8,7 @@
/** @file spritecache.cpp Caching of sprites. */
#include "stdafx.h"
#include "fileio_func.h"
#include "random_access_file_type.h"
#include "spriteloader/grf.hpp"
#include "gfx_func.h"
#include "error.h"
@ -31,18 +31,17 @@ uint _sprite_cache_size = 4;
struct SpriteCache {
void *ptr;
size_t file_pos;
SpriteFile *file; ///< The file the sprite in this entry can be found in.
uint32 id;
uint16 file_slot;
int16 lru;
SpriteType 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 = nullptr;
static std::vector<std::unique_ptr<SpriteFile>> _sprite_files;
static inline SpriteCache *GetSpriteCache(uint index)
{
@ -72,6 +71,37 @@ static SpriteCache *AllocateSpriteCache(uint index)
return GetSpriteCache(index);
}
/**
* Get the cached SpriteFile given the name of the file.
* @param filename The name of the file at the disk.
* @return The SpriteFile or \c null.
*/
static SpriteFile *GetCachedSpriteFileByName(const std::string &filename) {
for (auto &f : _sprite_files) {
if (f->GetFilename() == filename) {
return f.get();
}
}
return nullptr;
}
/**
* Open/get the SpriteFile that is cached for use in the sprite cache.
* @param filename Name of the file at the disk.
* @param subdir The sub directory to search this file in.
* @param palette_remap Whether a palette remap needs to be performed for this file.
* @return The reference to the SpriteCache.
*/
SpriteFile &OpenCachedSpriteFile(const std::string &filename, Subdirectory subdir, bool palette_remap)
{
SpriteFile *file = GetCachedSpriteFileByName(filename);
if (file == nullptr) {
file = _sprite_files.emplace_back(new SpriteFile(filename, subdir, palette_remap)).get();
} else {
file->SeekToBegin();
}
return *file;
}
struct MemBlock {
size_t size;
@ -92,22 +122,22 @@ static void *AllocSprite(size_t mem_req);
* @param num the amount of sprites to skip
* @return true if the data could be correctly skipped.
*/
bool SkipSpriteData(byte type, uint16 num)
bool SkipSpriteData(SpriteFile &file, byte type, uint16 num)
{
if (type & 2) {
FioSkipBytes(num);
file.SkipBytes(num);
} else {
while (num > 0) {
int8 i = FioReadByte();
int8 i = file.ReadByte();
if (i >= 0) {
int size = (i == 0) ? 0x80 : i;
if (size > num) return false;
num -= size;
FioSkipBytes(size);
file.SkipBytes(size);
} else {
i = -(i >> 3);
num -= i;
FioReadByte();
file.ReadByte();
}
}
}
@ -121,7 +151,7 @@ bool SpriteExists(SpriteID id)
/* Special case for Sprite ID zero -- its position is also 0... */
if (id == 0) return true;
return !(GetSpriteCache(id)->file_pos == 0 && GetSpriteCache(id)->file_slot == 0);
return !(GetSpriteCache(id)->file_pos == 0 && GetSpriteCache(id)->file == nullptr);
}
/**
@ -136,14 +166,14 @@ SpriteType GetSpriteType(SpriteID sprite)
}
/**
* Get the (FIOS) file slot of a given sprite.
* Get the SpriteFile of a given sprite.
* @param sprite The sprite to look at.
* @return the FIOS file slot
* @return The SpriteFile.
*/
uint GetOriginFileSlot(SpriteID sprite)
SpriteFile *GetOriginFile(SpriteID sprite)
{
if (!SpriteExists(sprite)) return 0;
return GetSpriteCache(sprite)->file_slot;
if (!SpriteExists(sprite)) return nullptr;
return GetSpriteCache(sprite)->file;
}
/**
@ -158,19 +188,22 @@ uint32 GetSpriteLocalID(SpriteID sprite)
}
/**
* Count the sprites which originate from a specific file slot in a range of SpriteIDs.
* @param file_slot FIOS file slot.
* Count the sprites which originate from a specific file in a range of SpriteIDs.
* @param file The loaded SpriteFile.
* @param begin First sprite in range.
* @param end First sprite not in range.
* @return Number of sprites.
*/
uint GetSpriteCountForSlot(uint file_slot, SpriteID begin, SpriteID end)
uint GetSpriteCountForFile(const std::string &filename, SpriteID begin, SpriteID end)
{
SpriteFile *file = GetCachedSpriteFileByName(filename);
if (file == nullptr) return 0;
uint count = 0;
for (SpriteID i = begin; i != end; i++) {
if (SpriteExists(i)) {
SpriteCache *sc = GetSpriteCache(i);
if (sc->file_slot == file_slot) count++;
if (sc->file == file) count++;
}
}
return count;
@ -348,7 +381,7 @@ static bool PadSprites(SpriteLoader::Sprite *sprite, uint8 sprite_avail, SpriteE
return true;
}
static bool ResizeSprites(SpriteLoader::Sprite *sprite, uint8 sprite_avail, uint32 file_slot, uint32 file_pos, SpriteEncoder *encoder)
static bool ResizeSprites(SpriteLoader::Sprite *sprite, uint8 sprite_avail, SpriteEncoder *encoder)
{
/* Create a fully zoomed image if it does not exist */
ZoomLevel first_avail = static_cast<ZoomLevel>(FIND_FIRST_BIT(sprite_avail));
@ -379,11 +412,11 @@ static bool ResizeSprites(SpriteLoader::Sprite *sprite, uint8 sprite_avail, uint
/**
* Load a recolour sprite into memory.
* @param file_slot GRF we're reading from.
* @param file GRF we're reading from.
* @param num Size of the sprite in the GRF.
* @return Sprite data.
*/
static void *ReadRecolourSprite(uint16 file_slot, uint num)
static void *ReadRecolourSprite(SpriteFile &file, uint num)
{
/* "Normal" recolour sprites are ALWAYS 257 bytes. Then there is a small
* number of recolour sprites that are 17 bytes that only exist in DOS
@ -392,19 +425,19 @@ static void *ReadRecolourSprite(uint16 file_slot, uint num)
static const uint RECOLOUR_SPRITE_SIZE = 257;
byte *dest = (byte *)AllocSprite(std::max(RECOLOUR_SPRITE_SIZE, num));
if (_palette_remap_grf[file_slot]) {
if (file.NeedsPaletteRemap()) {
byte *dest_tmp = AllocaM(byte, std::max(RECOLOUR_SPRITE_SIZE, num));
/* Only a few recolour sprites are less than 257 bytes */
if (num < RECOLOUR_SPRITE_SIZE) memset(dest_tmp, 0, RECOLOUR_SPRITE_SIZE);
FioReadBlock(dest_tmp, num);
file.ReadBlock(dest_tmp, num);
/* The data of index 0 is never used; "literal 00" according to the (New)GRF specs. */
for (uint i = 1; i < RECOLOUR_SPRITE_SIZE; i++) {
dest[i] = _palmap_w2d[dest_tmp[_palmap_d2w[i - 1] + 1]];
}
} else {
FioReadBlock(dest, num);
file.ReadBlock(dest, num);
}
return dest;
@ -424,7 +457,7 @@ static void *ReadSprite(const SpriteCache *sc, SpriteID id, SpriteType sprite_ty
/* Use current blitter if no other sprite encoder is given. */
if (encoder == nullptr) encoder = BlitterFactory::GetCurrentBlitter();
uint8 file_slot = sc->file_slot;
SpriteFile &file = *sc->file;
size_t file_pos = sc->file_pos;
assert(sprite_type != ST_RECOLOUR);
@ -437,13 +470,13 @@ static void *ReadSprite(const SpriteCache *sc, SpriteID id, SpriteType sprite_ty
uint8 sprite_avail = 0;
sprite[ZOOM_LVL_NORMAL].type = sprite_type;
SpriteLoaderGrf sprite_loader(sc->container_ver);
SpriteLoaderGrf sprite_loader(file.GetContainerVersion());
if (sprite_type != ST_MAPGEN && encoder->Is32BppSupported()) {
/* Try for 32bpp sprites first. */
sprite_avail = sprite_loader.LoadSprite(sprite, file_slot, file_pos, sprite_type, true);
sprite_avail = sprite_loader.LoadSprite(sprite, file, file_pos, sprite_type, true);
}
if (sprite_avail == 0) {
sprite_avail = sprite_loader.LoadSprite(sprite, file_slot, file_pos, sprite_type, false);
sprite_avail = sprite_loader.LoadSprite(sprite, file, file_pos, sprite_type, false);
}
if (sprite_avail == 0) {
@ -480,7 +513,7 @@ static void *ReadSprite(const SpriteCache *sc, SpriteID id, SpriteType sprite_ty
return s;
}
if (!ResizeSprites(sprite, sprite_avail, file_slot, sc->id, encoder)) {
if (!ResizeSprites(sprite, sprite_avail, encoder)) {
if (id == SPR_IMG_QUERY) usererror("Okay... something went horribly wrong. I couldn't resize the fallback sprite. What should I do?");
return (void*)GetRawSprite(SPR_IMG_QUERY, ST_NORMAL, allocator, encoder);
}
@ -516,27 +549,27 @@ size_t GetGRFSpriteOffset(uint32 id)
* Parse the sprite section of GRFs.
* @param container_version Container version of the GRF we're currently processing.
*/
void ReadGRFSpriteOffsets(byte container_version)
void ReadGRFSpriteOffsets(SpriteFile &file)
{
_grf_sprite_offsets.clear();
if (container_version >= 2) {
if (file.GetContainerVersion() >= 2) {
/* Seek to sprite section of the GRF. */
size_t data_offset = FioReadDword();
size_t old_pos = FioGetPos();
FioSeekTo(data_offset, SEEK_CUR);
size_t data_offset = file.ReadDword();
size_t old_pos = file.GetPos();
file.SeekTo(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;
while ((id = file.ReadDword()) != 0) {
if (id != prev_id) _grf_sprite_offsets[id] = file.GetPos() - 4;
prev_id = id;
FioSkipBytes(FioReadDword());
file.SkipBytes(file.ReadDword());
}
/* Continue processing the data section. */
FioSeekTo(old_pos, SEEK_SET);
file.SeekTo(old_pos, SEEK_SET);
}
}
@ -544,19 +577,19 @@ void ReadGRFSpriteOffsets(byte container_version)
/**
* Load a real or recolour sprite.
* @param load_index Global sprite index.
* @param file_slot GRF to load from.
* @param file 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)
bool LoadNextSprite(int load_index, SpriteFile &file, uint file_sprite_id)
{
size_t file_pos = FioGetPos();
size_t file_pos = file.GetPos();
/* Read sprite header. */
uint32 num = container_version >= 2 ? FioReadDword() : FioReadWord();
uint32 num = file.GetContainerVersion() >= 2 ? file.ReadDword() : file.ReadWord();
if (num == 0) return false;
byte grf_type = FioReadByte();
byte grf_type = file.ReadByte();
SpriteType type;
void *data = nullptr;
@ -564,25 +597,25 @@ bool LoadNextSprite(int load_index, byte file_slot, uint file_sprite_id, byte co
/* 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();
file.ReadByte();
return false;
}
type = ST_RECOLOUR;
data = ReadRecolourSprite(file_slot, num);
} else if (container_version >= 2 && grf_type == 0xFD) {
data = ReadRecolourSprite(file, num);
} else if (file.GetContainerVersion() >= 2 && grf_type == 0xFD) {
if (num != 4) {
/* Invalid sprite section include, ignore. */
FioSkipBytes(num);
file.SkipBytes(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());
file_pos = GetGRFSpriteOffset(file.ReadDword());
type = ST_NORMAL;
} else {
FioSkipBytes(7);
type = SkipSpriteData(grf_type, num - 8) ? ST_NORMAL : ST_INVALID;
file.SkipBytes(7);
type = SkipSpriteData(file, grf_type, num - 8) ? ST_NORMAL : ST_INVALID;
/* Inline sprites are not supported for container version >= 2. */
if (container_version >= 2) return false;
if (file.GetContainerVersion() >= 2) return false;
}
if (type == ST_INVALID) return false;
@ -599,14 +632,13 @@ bool LoadNextSprite(int load_index, byte file_slot, uint file_sprite_id, byte co
}
SpriteCache *sc = AllocateSpriteCache(load_index);
sc->file_slot = file_slot;
sc->file = &file;
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;
}
@ -617,13 +649,12 @@ 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 = scold->file;
scnew->file_pos = scold->file_pos;
scnew->ptr = nullptr;
scnew->id = scold->id;
scnew->type = scold->type;
scnew->warned = false;
scnew->container_ver = scold->container_ver;
}
/**
@ -967,6 +998,7 @@ void GfxInitSpriteMem()
_spritecache = nullptr;
_compact_cache_counter = 0;
_sprite_files.clear();
}
/**

View File

@ -31,9 +31,9 @@ void *GetRawSprite(SpriteID sprite, SpriteType type, AllocatorProc *allocator =
bool SpriteExists(SpriteID sprite);
SpriteType GetSpriteType(SpriteID sprite);
uint GetOriginFileSlot(SpriteID sprite);
SpriteFile *GetOriginFile(SpriteID sprite);
uint32 GetSpriteLocalID(SpriteID sprite);
uint GetSpriteCountForSlot(uint file_slot, SpriteID begin, SpriteID end);
uint GetSpriteCountForFile(const std::string &filename, SpriteID begin, SpriteID end);
uint GetMaxSpriteID();
@ -53,10 +53,12 @@ void GfxInitSpriteMem();
void GfxClearSpriteCache();
void IncreaseSpriteLRU();
void ReadGRFSpriteOffsets(byte container_version);
SpriteFile &OpenCachedSpriteFile(const std::string &filename, Subdirectory subdir, bool palette_remap);
void ReadGRFSpriteOffsets(SpriteFile &file);
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);
bool LoadNextSprite(int load_index, SpriteFile &file, uint file_sprite_id);
bool SkipSpriteData(SpriteFile &file, byte type, uint16 num);
void DupSprite(SpriteID old_spr, SpriteID new_spr);
#endif /* SPRITECACHE_H */

View File

@ -1,5 +1,7 @@
add_files(
grf.cpp
grf.hpp
sprite_file.cpp
sprite_file_type.hpp
spriteloader.hpp
)

View File

@ -9,7 +9,6 @@
#include "../stdafx.h"
#include "../gfx_func.h"
#include "../fileio_func.h"
#include "../debug.h"
#include "../settings_type.h"
#include "../strings_func.h"
@ -32,14 +31,14 @@ extern const byte _palmap_w2d[];
* @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 bool WarnCorruptSprite(const SpriteFile &file, size_t file_pos, int line)
{
static byte warning_level = 0;
if (warning_level == 0) {
SetDParamStr(0, FioGetFilename(file_slot));
SetDParamStr(0, file.GetSimplifiedFilename().c_str());
ShowErrorMessage(STR_NEWGRF_ERROR_CORRUPT_SPRITE, INVALID_STRING_ID, WL_ERROR);
}
DEBUG(sprite, warning_level, "[%i] Loading corrupted sprite from %s at position %i", line, FioGetFilename(file_slot), (int)file_pos);
DEBUG(sprite, warning_level, "[%i] Loading corrupted sprite from %s at position %i", line, file.GetSimplifiedFilename().c_str(), (int)file_pos);
warning_level = 6;
return false;
}
@ -47,7 +46,7 @@ static bool WarnCorruptSprite(uint8 file_slot, size_t file_pos, int line)
/**
* Decode the image data of a single sprite.
* @param[in,out] sprite Filled with the sprite image data.
* @param file_slot File slot.
* @param file The file with the sprite data.
* @param file_pos File position.
* @param sprite_type Type of the sprite we're decoding.
* @param num Size of the decompressed sprite.
@ -57,7 +56,7 @@ static bool WarnCorruptSprite(uint8 file_slot, size_t file_pos, int line)
* @param container_format Container format of the GRF this sprite is in.
* @return True if the sprite was successfully loaded.
*/
bool DecodeSingleSprite(SpriteLoader::Sprite *sprite, uint8 file_slot, size_t file_pos, SpriteType sprite_type, int64 num, byte type, ZoomLevel zoom_lvl, byte colour_fmt, byte container_format)
bool DecodeSingleSprite(SpriteLoader::Sprite *sprite, SpriteFile &file, size_t file_pos, SpriteType sprite_type, int64 num, byte type, ZoomLevel zoom_lvl, byte colour_fmt, byte container_format)
{
std::unique_ptr<byte[]> dest_orig(new byte[num]);
byte *dest = dest_orig.get();
@ -65,24 +64,24 @@ bool DecodeSingleSprite(SpriteLoader::Sprite *sprite, uint8 file_slot, size_t fi
/* Read the file, which has some kind of compression */
while (num > 0) {
int8 code = FioReadByte();
int8 code = file.ReadByte();
if (code >= 0) {
/* Plain bytes to read */
int size = (code == 0) ? 0x80 : code;
num -= size;
if (num < 0) return WarnCorruptSprite(file_slot, file_pos, __LINE__);
if (num < 0) return WarnCorruptSprite(file, file_pos, __LINE__);
for (; size > 0; size--) {
*dest = FioReadByte();
*dest = file.ReadByte();
dest++;
}
} else {
/* Copy bytes from earlier in the sprite */
const uint data_offset = ((code & 7) << 8) | FioReadByte();
if (dest - data_offset < dest_orig.get()) return WarnCorruptSprite(file_slot, file_pos, __LINE__);
const uint data_offset = ((code & 7) << 8) | file.ReadByte();
if (dest - data_offset < dest_orig.get()) return WarnCorruptSprite(file, file_pos, __LINE__);
int size = -(code >> 3);
num -= size;
if (num < 0) return WarnCorruptSprite(file_slot, file_pos, __LINE__);
if (num < 0) return WarnCorruptSprite(file, file_pos, __LINE__);
for (; size > 0; size--) {
*dest = *(dest - data_offset);
dest++;
@ -90,7 +89,7 @@ bool DecodeSingleSprite(SpriteLoader::Sprite *sprite, uint8 file_slot, size_t fi
}
}
if (num != 0) return WarnCorruptSprite(file_slot, file_pos, __LINE__);
if (num != 0) return WarnCorruptSprite(file, file_pos, __LINE__);
sprite->AllocateData(zoom_lvl, sprite->width * sprite->height);
@ -117,7 +116,7 @@ bool DecodeSingleSprite(SpriteLoader::Sprite *sprite, uint8 file_slot, size_t fi
do {
if (dest + (container_format >= 2 && sprite->width > 256 ? 4 : 2) > dest_orig.get() + dest_size) {
return WarnCorruptSprite(file_slot, file_pos, __LINE__);
return WarnCorruptSprite(file, file_pos, __LINE__);
}
SpriteLoader::CommonPixel *data;
@ -143,7 +142,7 @@ bool DecodeSingleSprite(SpriteLoader::Sprite *sprite, uint8 file_slot, size_t fi
data = &sprite->data[y * sprite->width + skip];
if (skip + length > sprite->width || dest + length * bpp > dest_orig.get() + dest_size) {
return WarnCorruptSprite(file_slot, file_pos, __LINE__);
return WarnCorruptSprite(file, file_pos, __LINE__);
}
for (int x = 0; x < length; x++) {
@ -155,7 +154,7 @@ bool DecodeSingleSprite(SpriteLoader::Sprite *sprite, uint8 file_slot, size_t fi
data->a = (colour_fmt & SCC_ALPHA) ? *dest++ : 0xFF;
if (colour_fmt & SCC_PAL) {
switch (sprite_type) {
case ST_NORMAL: data->m = _palette_remap_grf[file_slot] ? _palmap_w2d[*dest] : *dest; break;
case ST_NORMAL: data->m = file.NeedsPaletteRemap() ? _palmap_w2d[*dest] : *dest; break;
case ST_FONT: data->m = std::min<uint>(*dest, 2u); break;
default: data->m = *dest; break;
}
@ -169,12 +168,12 @@ bool DecodeSingleSprite(SpriteLoader::Sprite *sprite, uint8 file_slot, size_t fi
}
} else {
if (dest_size < sprite->width * sprite->height * bpp) {
return WarnCorruptSprite(file_slot, file_pos, __LINE__);
return WarnCorruptSprite(file, file_pos, __LINE__);
}
if (dest_size > sprite->width * sprite->height * bpp) {
static byte warning_level = 0;
DEBUG(sprite, warning_level, "Ignoring " OTTD_PRINTF64 " unused extra bytes from the sprite from %s at position %i", dest_size - sprite->width * sprite->height * bpp, FioGetFilename(file_slot), (int)file_pos);
DEBUG(sprite, warning_level, "Ignoring " OTTD_PRINTF64 " unused extra bytes from the sprite from %s at position %i", dest_size - sprite->width * sprite->height * bpp, file.GetSimplifiedFilename().c_str(), (int)file_pos);
warning_level = 6;
}
@ -191,7 +190,7 @@ bool DecodeSingleSprite(SpriteLoader::Sprite *sprite, uint8 file_slot, size_t fi
sprite->data[i].a = (colour_fmt & SCC_ALPHA) ? *pixel++ : 0xFF;
if (colour_fmt & SCC_PAL) {
switch (sprite_type) {
case ST_NORMAL: sprite->data[i].m = _palette_remap_grf[file_slot] ? _palmap_w2d[*pixel] : *pixel; break;
case ST_NORMAL: sprite->data[i].m = file.NeedsPaletteRemap() ? _palmap_w2d[*pixel] : *pixel; break;
case ST_FONT: sprite->data[i].m = std::min<uint>(*pixel, 2u); break;
default: sprite->data[i].m = *pixel; break;
}
@ -205,31 +204,31 @@ bool DecodeSingleSprite(SpriteLoader::Sprite *sprite, uint8 file_slot, size_t fi
return true;
}
uint8 LoadSpriteV1(SpriteLoader::Sprite *sprite, uint8 file_slot, size_t file_pos, SpriteType sprite_type, bool load_32bpp)
uint8 LoadSpriteV1(SpriteLoader::Sprite *sprite, SpriteFile &file, size_t file_pos, SpriteType sprite_type, bool load_32bpp)
{
/* Check the requested colour depth. */
if (load_32bpp) return 0;
/* Open the right file and go to the correct position */
FioSeekToFile(file_slot, file_pos);
file.SeekTo(file_pos, SEEK_SET);
/* Read the size and type */
int num = FioReadWord();
byte type = FioReadByte();
int num = file.ReadWord();
byte type = file.ReadByte();
/* Type 0xFF indicates either a colourmap or some other non-sprite info; we do not handle them here */
if (type == 0xFF) return 0;
ZoomLevel zoom_lvl = (sprite_type != ST_MAPGEN) ? ZOOM_LVL_OUT_4X : ZOOM_LVL_NORMAL;
sprite[zoom_lvl].height = FioReadByte();
sprite[zoom_lvl].width = FioReadWord();
sprite[zoom_lvl].x_offs = FioReadWord();
sprite[zoom_lvl].y_offs = FioReadWord();
sprite[zoom_lvl].height = file.ReadByte();
sprite[zoom_lvl].width = file.ReadWord();
sprite[zoom_lvl].x_offs = file.ReadWord();
sprite[zoom_lvl].y_offs = file.ReadWord();
sprite[zoom_lvl].colours = SCC_PAL;
if (sprite[zoom_lvl].width > INT16_MAX) {
WarnCorruptSprite(file_slot, file_pos, __LINE__);
WarnCorruptSprite(file, file_pos, __LINE__);
return 0;
}
@ -237,12 +236,12 @@ uint8 LoadSpriteV1(SpriteLoader::Sprite *sprite, uint8 file_slot, size_t file_po
* In case it is uncompressed, the size is 'num' - 8 (header-size). */
num = (type & 0x02) ? sprite[zoom_lvl].width * sprite[zoom_lvl].height : num - 8;
if (DecodeSingleSprite(&sprite[zoom_lvl], file_slot, file_pos, sprite_type, num, type, zoom_lvl, SCC_PAL, 1)) return 1 << zoom_lvl;
if (DecodeSingleSprite(&sprite[zoom_lvl], file, file_pos, sprite_type, num, type, zoom_lvl, SCC_PAL, 1)) return 1 << zoom_lvl;
return 0;
}
uint8 LoadSpriteV2(SpriteLoader::Sprite *sprite, uint8 file_slot, size_t file_pos, SpriteType sprite_type, bool load_32bpp)
uint8 LoadSpriteV2(SpriteLoader::Sprite *sprite, SpriteFile &file, size_t file_pos, SpriteType sprite_type, bool load_32bpp)
{
static const ZoomLevel zoom_lvl_map[6] = {ZOOM_LVL_OUT_4X, ZOOM_LVL_NORMAL, ZOOM_LVL_OUT_2X, ZOOM_LVL_OUT_8X, ZOOM_LVL_OUT_16X, ZOOM_LVL_OUT_32X};
@ -250,21 +249,21 @@ uint8 LoadSpriteV2(SpriteLoader::Sprite *sprite, uint8 file_slot, size_t file_po
if (file_pos == SIZE_MAX) return 0;
/* Open the right file and go to the correct position */
FioSeekToFile(file_slot, file_pos);
file.SeekTo(file_pos, SEEK_SET);
uint32 id = FioReadDword();
uint32 id = file.ReadDword();
uint8 loaded_sprites = 0;
do {
int64 num = FioReadDword();
size_t start_pos = FioGetPos();
byte type = FioReadByte();
int64 num = file.ReadDword();
size_t start_pos = file.GetPos();
byte type = file.ReadByte();
/* Type 0xFF indicates either a colourmap or some other non-sprite info; we do not handle them here. */
if (type == 0xFF) return 0;
byte colour = type & SCC_MASK;
byte zoom = FioReadByte();
byte zoom = file.ReadByte();
bool is_wanted_colour_depth = (colour != 0 && (load_32bpp ? colour != SCC_PAL : colour == SCC_PAL));
bool is_wanted_zoom_lvl;
@ -280,18 +279,18 @@ uint8 LoadSpriteV2(SpriteLoader::Sprite *sprite, uint8 file_slot, size_t file_po
if (HasBit(loaded_sprites, zoom_lvl)) {
/* We already have this zoom level, skip sprite. */
DEBUG(sprite, 1, "Ignoring duplicate zoom level sprite %u from %s", id, FioGetFilename(file_slot));
FioSkipBytes(num - 2);
DEBUG(sprite, 1, "Ignoring duplicate zoom level sprite %u from %s", id, file.GetSimplifiedFilename().c_str());
file.SkipBytes(num - 2);
continue;
}
sprite[zoom_lvl].height = FioReadWord();
sprite[zoom_lvl].width = FioReadWord();
sprite[zoom_lvl].x_offs = FioReadWord();
sprite[zoom_lvl].y_offs = FioReadWord();
sprite[zoom_lvl].height = file.ReadWord();
sprite[zoom_lvl].width = file.ReadWord();
sprite[zoom_lvl].x_offs = file.ReadWord();
sprite[zoom_lvl].y_offs = file.ReadWord();
if (sprite[zoom_lvl].width > INT16_MAX || sprite[zoom_lvl].height > INT16_MAX) {
WarnCorruptSprite(file_slot, file_pos, __LINE__);
WarnCorruptSprite(file, file_pos, __LINE__);
return 0;
}
@ -308,30 +307,30 @@ uint8 LoadSpriteV2(SpriteLoader::Sprite *sprite, uint8 file_slot, size_t file_po
/* 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[zoom_lvl].width * sprite[zoom_lvl].height * bpp;
uint decomp_size = (type & 0x08) ? file.ReadDword() : sprite[zoom_lvl].width * sprite[zoom_lvl].height * bpp;
bool valid = DecodeSingleSprite(&sprite[zoom_lvl], file_slot, file_pos, sprite_type, decomp_size, type, zoom_lvl, colour, 2);
if (FioGetPos() != start_pos + num) {
WarnCorruptSprite(file_slot, file_pos, __LINE__);
bool valid = DecodeSingleSprite(&sprite[zoom_lvl], file, file_pos, sprite_type, decomp_size, type, zoom_lvl, colour, 2);
if (file.GetPos() != start_pos + num) {
WarnCorruptSprite(file, file_pos, __LINE__);
return 0;
}
if (valid) SetBit(loaded_sprites, zoom_lvl);
} else {
/* Not the wanted zoom level or colour depth, continue searching. */
FioSkipBytes(num - 2);
file.SkipBytes(num - 2);
}
} while (FioReadDword() == id);
} while (file.ReadDword() == id);
return loaded_sprites;
}
uint8 SpriteLoaderGrf::LoadSprite(SpriteLoader::Sprite *sprite, uint8 file_slot, size_t file_pos, SpriteType sprite_type, bool load_32bpp)
uint8 SpriteLoaderGrf::LoadSprite(SpriteLoader::Sprite *sprite, SpriteFile &file, size_t file_pos, SpriteType sprite_type, bool load_32bpp)
{
if (this->container_ver >= 2) {
return LoadSpriteV2(sprite, file_slot, file_pos, sprite_type, load_32bpp);
return LoadSpriteV2(sprite, file, file_pos, sprite_type, load_32bpp);
} else {
return LoadSpriteV1(sprite, file_slot, file_pos, sprite_type, load_32bpp);
return LoadSpriteV1(sprite, file, file_pos, sprite_type, load_32bpp);
}
}

View File

@ -17,7 +17,7 @@ class SpriteLoaderGrf : public SpriteLoader {
byte container_ver;
public:
SpriteLoaderGrf(byte container_ver) : container_ver(container_ver) {}
uint8 LoadSprite(SpriteLoader::Sprite *sprite, uint8 file_slot, size_t file_pos, SpriteType sprite_type, bool load_32bpp);
uint8 LoadSprite(SpriteLoader::Sprite *sprite, SpriteFile &file, size_t file_pos, SpriteType sprite_type, bool load_32bpp);
};
#endif /* SPRITELOADER_GRF_HPP */

View File

@ -0,0 +1,50 @@
/*
* This file is part of OpenTTD.
* OpenTTD is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, version 2.
* OpenTTD is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
* 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 sprite_file.cpp Implementation of logic specific to the SpriteFile class. */
#include "../stdafx.h"
#include "sprite_file_type.hpp"
/** 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.
*/
static byte GetGRFContainerVersion(SpriteFile &file)
{
size_t pos = file.GetPos();
if (file.ReadWord() == 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 (file.ReadByte() != _grf_cont_v2_sig[i]) return 0; // Invalid format
}
return 2;
}
/* Container version 1 has no header, rewind to start. */
file.SeekTo(pos, SEEK_SET);
return 1;
}
/**
* Create the SpriteFile.
* @param filename Name of the file at the disk.
* @param subdir The sub directory to search this file in.
* @param palette_remap Whether a palette remap needs to be performed for this file.
*/
SpriteFile::SpriteFile(const std::string &filename, Subdirectory subdir, bool palette_remap)
: RandomAccessFile(filename, subdir), palette_remap(palette_remap)
{
this->container_version = GetGRFContainerVersion(*this);
this->content_begin = this->GetPos();
}

View File

@ -0,0 +1,46 @@
/*
* This file is part of OpenTTD.
* OpenTTD is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, version 2.
* OpenTTD is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
* 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 sprite_file_type.hpp Random Access File specialised for accessing sprites. */
#ifndef SPRITE_FILE_TYPE_HPP
#define SPRITE_FILE_TYPE_HPP
#include "../random_access_file_type.h"
/**
* RandomAccessFile with some extra information specific for sprite files.
* It automatically detects and stores the container version upload opening the file.
*/
class SpriteFile : public RandomAccessFile {
bool palette_remap; ///< Whether or not a remap of the palette is required for this file.
byte container_version; ///< Container format of the sprite file.
size_t content_begin; ///< The begin of the content of the sprite file, i.e. after the container metadata.
public:
SpriteFile(const std::string &filename, Subdirectory subdir, bool palette_remap);
SpriteFile(const SpriteFile&) = delete;
void operator=(const SpriteFile&) = delete;
/**
* Whether a palette remap is needed when loading sprites from this file.
* @return True when needed, otherwise false.
*/
bool NeedsPaletteRemap() const { return this->palette_remap; }
/**
* Get the version number of container type used by the file.
* @return The version.
*/
byte GetContainerVersion() const { return this->container_version; }
/**
* Seek to the begin of the content, i.e. the position just after the container version has been determined.
*/
void SeekToBegin() { this->SeekTo(this->content_begin, SEEK_SET); }
};
#endif /* SPRITE_FILE_TYPE_HPP */

View File

@ -13,6 +13,7 @@
#include "../core/alloc_type.hpp"
#include "../core/enum_type.hpp"
#include "../gfx_type.h"
#include "sprite_file_type.hpp"
struct Sprite;
typedef void *AllocatorProc(size_t size);
@ -73,7 +74,7 @@ public:
* @param load_32bpp True if 32bpp sprites should be loaded, false for a 8bpp sprite.
* @return Bit mask of the zoom levels successfully loaded or 0 if no sprite could be loaded.
*/
virtual uint8 LoadSprite(SpriteLoader::Sprite *sprite, uint8 file_slot, size_t file_pos, SpriteType sprite_type, bool load_32bpp) = 0;
virtual uint8 LoadSprite(SpriteLoader::Sprite *sprite, SpriteFile &file, size_t file_pos, SpriteType sprite_type, bool load_32bpp) = 0;
virtual ~SpriteLoader() { }
};