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

(cherry picked from commit fdc11a9f94)
pull/266/head
Rubidium 3 years ago committed by Jonathan G Rennison
parent 04b38dbfca
commit 6bd12e24d7

@ -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(uint 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,14 +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, std::string *output_filename)
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;
if (output_filename != nullptr) *output_filename = raf->filename;
_fio_files[slot] = file;
_fio_current_file = file;
return *file;
}
static const char * const _subdirs[] = {

@ -23,10 +23,10 @@ byte FioReadByte();
uint16 FioReadWord();
uint32 FioReadDword();
void FioCloseAll();
void FioOpenFile(int slot, const std::string &filename, Subdirectory subdir, std::string *output_filename = nullptr);
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, std::string *output_filename = nullptr);

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

@ -33,9 +33,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. */
@ -55,27 +52,28 @@ int _progsig_grf_file_index;
* @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) {
@ -92,23 +90,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");
}
@ -116,7 +115,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++;
@ -174,16 +173,14 @@ 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));
/* Progsignal sprites. */
_progsig_grf_file_index = i;
LoadGrfFile("progsignals.grf", SPR_PROGSIGNAL_BASE, i++);
LoadGrfFile("progsignals.grf", SPR_PROGSIGNAL_BASE, i++, false);
/* Fill duplicate programmable pre-signal graphics sprite block */
for (uint i = 0; i < PROGSIGNAL_SPRITE_COUNT; i++) {
@ -191,7 +188,7 @@ static void LoadSpriteTables()
}
/* Tracerestrict sprites. */
LoadGrfFile("tracerestrict.grf", SPR_TRACERESTRICT_BASE, i++);
LoadGrfFile("tracerestrict.grf", SPR_TRACERESTRICT_BASE, i++, false);
/* Fill duplicate original signal graphics sprite block */
for (uint i = 0; i < DUP_ORIGINAL_SIGNALS_SPRITE_COUNT; i++) {
@ -204,8 +201,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.
@ -213,18 +209,18 @@ 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)
);
}
LoadGrfFile("innerhighlight.grf", SPR_ZONING_INNER_HIGHLIGHT_BASE, i++);
LoadGrfFile("innerhighlight.grf", SPR_ZONING_INNER_HIGHLIGHT_BASE, i++, false);
/* Load route step graphics */
LoadGrfFile("route_step.grf", SPR_ROUTE_STEP_BASE, i++);
LoadGrfFile("route_step.grf", SPR_ROUTE_STEP_BASE, i++, false);
/* Initialize the unicode to sprite mapping table */
InitializeUnicodeGlyphMap();
@ -264,7 +260,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.

@ -103,11 +103,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)
@ -5029,7 +5028,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);
}
}
@ -6270,16 +6269,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;
}
@ -6328,7 +6327,7 @@ static void GraphicsNew(ByteReader *buf)
for (uint16 n = num; n > 0; n--) {
_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);
}
if (type == 0x04 && ((_cur.grfconfig->ident.grfid & 0x00FFFFFF) == OPENTTD_GRAPHICS_BASE_GRF_ID || _cur.grfconfig->ident.grfid == BSWAP32(0xFF4F4701))) {
@ -6567,7 +6566,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;
@ -6928,7 +6927,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. */
@ -7723,9 +7722,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();
}
}
@ -7757,10 +7756,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?)");
@ -7778,7 +7778,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;
}
@ -7792,7 +7792,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);
@ -7858,7 +7858,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);
}
}
}
@ -9892,32 +9892,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.
@ -9952,17 +9926,14 @@ void LoadNewGRFFile(GRFConfig *config, uint file_index, GrfLoadingStage stage, S
return;
}
config->full_filename.clear();
FioOpenFile(file_index, filename, subdir, &(config->full_filename));
_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);
config->full_filename = _cur.file->GetFilename();
_cur.grfconfig = config;
DEBUG(grf, 2, "LoadNewGRFFile: Reading NewGRF-file '%s'", config->GetDisplayPath());
_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;
}
@ -9970,13 +9941,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) {
@ -9988,7 +9959,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 {
@ -10000,7 +9971,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++;
@ -10022,12 +9993,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);
}
}

@ -312,8 +312,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

@ -12,7 +12,7 @@
#include <functional>
#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"
@ -954,7 +954,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:

@ -2631,13 +2631,13 @@ void DrawSingleSignal(TileIndex tile, const RailtypeInfo *rti, Track track, Sign
}
extern int _progsig_grf_file_index;
is_custom_sprite = (int) GetOriginFileSlot(sprite) != _progsig_grf_file_index;
//is_custom_sprite = (int) GetOriginFileSlot(sprite) != _progsig_grf_file_index;
} else {
/* Normal electric signals are stored in a different sprite block than all other signals. */
sprite = (type == SIGTYPE_NORMAL && variant == SIG_ELECTRIC) ? SPR_ORIGINAL_SIGNALS_BASE : SPR_SIGNALS_BASE - 16;
sprite += type * 16 + variant * 64 + image * 2 + condition + (IsSignalSpritePBS(type) ? 64 : 0);
int origin_slot = GetOriginFileSlot(sprite);
int origin_slot = 0; //GetOriginFileSlot(sprite);
extern int _first_user_grf_file_index;
extern int _opengfx_grf_file_index;
is_custom_sprite = origin_slot != _opengfx_grf_file_index && (origin_slot >= _first_user_grf_file_index);

@ -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"
@ -36,6 +36,7 @@
uint _sprite_cache_size = 4;
static size_t _spritecache_bytes_used = 0;
static uint32 _sprite_lru_counter;
PACK_N(class SpriteDataBuffer {
void *ptr = nullptr;
@ -88,11 +89,14 @@ public:
}, 4);
PACK_N(struct SpriteCache {
SpriteFile *file; ///< The file the sprite in this entry can be found in.
size_t file_pos;
SpriteDataBuffer buffer;
uint32 id;
uint32 lru;
uint16 file_slot;
uint count;
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.
/**
* Bits 6 - 0: 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.
@ -100,8 +104,6 @@ PACK_N(struct SpriteCache {
*/
byte type_field;
byte container_ver; ///< Container version of the GRF the sprite is from.
void *GetPtr() { return this->buffer.GetPtr(); }
SpriteType GetType() const { return (SpriteType) GB(this->type_field, 0, 7); }
@ -109,10 +111,10 @@ PACK_N(struct SpriteCache {
bool GetWarned() const { return GB(this->type_field, 7, 1); }
void SetWarned(bool warned) { SB(this->type_field, 7, 1, warned ? 1 : 0); }
}, 4);
static_assert(sizeof(SpriteCache) <= 32);
static std::vector<SpriteCache> _spritecache;
static SpriteDataBuffer _last_sprite_allocation;
static std::vector<std::unique_ptr<SpriteFile>> _sprite_files;
static inline SpriteCache *GetSpriteCache(uint index)
{
@ -133,7 +135,37 @@ static SpriteCache *AllocateSpriteCache(uint index)
return GetSpriteCache(index);
}
static uint32 _sprite_lru_counter;
/**
* 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;
}
static void *AllocSprite(size_t mem_req);
@ -143,22 +175,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();
}
}
}
@ -172,7 +204,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);
}
/**
@ -187,14 +219,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;
}
/**
@ -209,19 +241,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;
@ -398,7 +433,7 @@ static bool PadSprites(SpriteLoader::Sprite *sprite, unsigned int sprite_avail,
return true;
}
static bool ResizeSprites(SpriteLoader::Sprite *sprite, unsigned int sprite_avail, uint32 file_slot, uint32 file_pos, SpriteEncoder *encoder)
static bool ResizeSprites(SpriteLoader::Sprite *sprite, unsigned int 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));
@ -429,11 +464,11 @@ static bool ResizeSprites(SpriteLoader::Sprite *sprite, unsigned int sprite_avai
/**
* 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
@ -442,19 +477,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;
@ -474,10 +509,10 @@ 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();
uint file_slot = sc->file_slot;
SpriteFile &file = *sc->file;
size_t file_pos = sc->file_pos;
SCOPE_INFO_FMT([&], "ReadSprite: pos: " PRINTF_SIZE ", id: %u, slot: %u (%s), type: %u", file_pos, id, file_slot, FioGetFilename(file_slot), sprite_type);
SCOPE_INFO_FMT([&], "ReadSprite: pos: " PRINTF_SIZE ", id: %u, file: (%s), type: %u", file_pos, id, file.GetSimplifiedFilename().c_str(), sprite_type);
assert(sprite_type != ST_RECOLOUR);
assert(IsMapgenSpriteID(id) == (sprite_type == ST_MAPGEN));
@ -489,13 +524,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) {
@ -532,7 +567,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);
}
@ -569,27 +604,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);
}
}
@ -597,21 +632,21 @@ 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, uint 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();
SCOPE_INFO_FMT([&], "LoadNextSprite: pos: " PRINTF_SIZE ", slot: %u (%s), load_index: %d, file_sprite_id: %u, container_ver: %u", file_pos, file_slot, FioGetFilename(file_slot), load_index, file_sprite_id, container_version);
SCOPE_INFO_FMT([&], "LoadNextSprite: pos: " PRINTF_SIZE ", file: %s, load_index: %d, file_sprite_id: %u, container_ver: %u", file_pos, file.GetSimplifiedFilename().c_str(), load_index, file_sprite_id, file.GetContainerVersion());
/* 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;
@ -619,25 +654,25 @@ bool LoadNextSprite(int load_index, uint 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;
@ -654,7 +689,7 @@ bool LoadNextSprite(int load_index, uint 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;
if (data != nullptr) {
assert(data == _last_sprite_allocation.GetPtr());
@ -664,7 +699,6 @@ bool LoadNextSprite(int load_index, uint file_slot, uint file_sprite_id, byte co
sc->id = file_sprite_id;
sc->SetType(type);
sc->SetWarned(false);
sc->container_ver = container_version;
return true;
}
@ -675,12 +709,11 @@ 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->id = scold->id;
scnew->SetType(scold->GetType());
scnew->SetWarned(false);
scnew->container_ver = scold->container_ver;
}
static size_t GetSpriteCacheUsage()
@ -900,18 +933,18 @@ uint32 GetSpriteMainColour(SpriteID sprite_id, PaletteID palette_id)
const byte * const remap = (palette_id == PAL_NONE ? nullptr : GetNonSprite(GB(palette_id, 0, PALETTE_WIDTH), ST_RECOLOUR) + 1);
uint file_slot = sc->file_slot;
SpriteFile &file = *sc->file;
size_t file_pos = sc->file_pos;
SpriteLoader::Sprite sprites[ZOOM_LVL_COUNT];
sprites[ZOOM_LVL_NORMAL].type = ST_NORMAL;
SpriteLoaderGrf sprite_loader(sc->container_ver);
SpriteLoaderGrf sprite_loader(file.GetContainerVersion());
uint8 sprite_avail;
const uint8 screen_depth = BlitterFactory::GetCurrentBlitter()->GetScreenDepth();
/* Try to read the 32bpp sprite first. */
if (screen_depth == 32) {
sprite_avail = sprite_loader.LoadSprite(sprites, file_slot, file_pos, ST_NORMAL, true);
sprite_avail = sprite_loader.LoadSprite(sprites, file, file_pos, ST_NORMAL, true);
if (sprite_avail != 0) {
SpriteLoader::Sprite *sprite = &sprites[FindFirstBit(sprite_avail)];
/* Return the average colour. */
@ -941,7 +974,7 @@ uint32 GetSpriteMainColour(SpriteID sprite_id, PaletteID palette_id)
}
/* No 32bpp, try 8bpp. */
sprite_avail = sprite_loader.LoadSprite(sprites, file_slot, file_pos, ST_NORMAL, false);
sprite_avail = sprite_loader.LoadSprite(sprites, file, file_pos, ST_NORMAL, false);
if (sprite_avail != 0) {
SpriteLoader::Sprite *sprite = &sprites[FindFirstBit(sprite_avail)];
SpriteLoader::CommonPixel *pixel = sprite->data;
@ -987,6 +1020,7 @@ void GfxInitSpriteMem()
{
/* Reset the spritecache 'pool' */
_spritecache.clear();
_sprite_files.clear();
assert(_spritecache_bytes_used == 0);
}

@ -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, uint 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);
uint32 GetSpriteMainColour(SpriteID sprite_id, PaletteID palette_id);

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

@ -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(uint 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(uint 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(uint 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, uint 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, uint file_slot, size_t fil
/* 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, uint file_slot, size_t fil
}
}
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, uint file_slot, size_t fil
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, uint file_slot, size_t fil
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, uint file_slot, size_t fil
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, uint file_slot, size_t fil
}
} 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, uint file_slot, size_t fil
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, uint file_slot, size_t fil
return true;
}
uint8 LoadSpriteV1(SpriteLoader::Sprite *sprite, uint 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,16 +236,16 @@ uint8 LoadSpriteV1(SpriteLoader::Sprite *sprite, uint file_slot, size_t file_pos
* 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 (num < 0) {
WarnCorruptSprite(file_slot, file_pos, __LINE__);
WarnCorruptSprite(file, file_pos, __LINE__);
return 0;
}
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, uint 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};
@ -254,21 +253,21 @@ uint8 LoadSpriteV2(SpriteLoader::Sprite *sprite, uint file_slot, size_t file_pos
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;
@ -284,18 +283,18 @@ uint8 LoadSpriteV2(SpriteLoader::Sprite *sprite, uint file_slot, size_t file_pos
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;
}
@ -312,30 +311,30 @@ uint8 LoadSpriteV2(SpriteLoader::Sprite *sprite, uint file_slot, size_t file_pos
/* 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, uint 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);
}
}

@ -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, uint 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 */

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

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

@ -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, uint 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() { }
};

Loading…
Cancel
Save