@ -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 ) {
Fio SkipBytes( num ) ;
file. SkipBytes( num ) ;
} else {
while ( num > 0 ) {
int8 i = Fio ReadByte( ) ;
int8 i = file. ReadByte( ) ;
if ( i > = 0 ) {
int size = ( i = = 0 ) ? 0x80 : i ;
if ( size > num ) return false ;
num - = size ;
Fio SkipBytes( size ) ;
file. SkipBytes( size ) ;
} else {
i = - ( i > > 3 ) ;
num - = i ;
Fio ReadByte( ) ;
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 GetSpriteCountFor Slot( uint file_slot , SpriteID begin , SpriteID end )
uint GetSpriteCountFor File( 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 ) ;
Fio ReadBlock( 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 {
Fio ReadBlock( 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 = Fio ReadDword( ) ;
size_t old_pos = Fio GetPos( ) ;
Fio SeekTo( 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 = Fio ReadDword( ) ) ! = 0 ) {
if ( id ! = prev_id ) _grf_sprite_offsets [ id ] = Fio GetPos( ) - 4 ;
while ( ( id = file. ReadDword( ) ) ! = 0 ) {
if ( id ! = prev_id ) _grf_sprite_offsets [ id ] = file. GetPos( ) - 4 ;
prev_id = id ;
FioSkipBytes( Fio ReadDword( ) ) ;
file. SkipBytes ( file . ReadDword( ) ) ;
}
/* Continue processing the data section. */
Fio SeekTo( 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 = Fio GetPos( ) ;
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 ( ) : Fio ReadWord( ) ;
uint32 num = file. GetContainerVersion ( ) > = 2 ? file . ReadDword ( ) : file . ReadWord( ) ;
if ( num = = 0 ) return false ;
byte grf_type = Fio ReadByte( ) ;
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 ) {
Fio ReadByte( ) ;
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. */
Fio SkipBytes( 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 ( Fio ReadDword( ) ) ;
file_pos = GetGRFSpriteOffset ( file. ReadDword( ) ) ;
type = ST_NORMAL ;
} else {
Fio SkipBytes( 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 ) ;
}