2005-07-24 15:12:37 +01:00
/* $Id$ */
2008-05-06 16:11:33 +01:00
/** @file spritecache.cpp Caching of sprites. */
2007-04-04 02:35:16 +01:00
2004-08-09 18:04:08 +01:00
# include "stdafx.h"
2005-06-02 20:30:21 +01:00
# include "openttd.h"
2007-06-13 17:21:11 +01:00
# include "variables.h"
2005-02-05 15:58:59 +00:00
# include "debug.h"
2005-02-10 05:43:30 +00:00
# include "spritecache.h"
2004-08-09 18:04:08 +01:00
# include "fileio.h"
2007-06-11 12:50:49 +01:00
# include "spriteloader/grf.hpp"
2007-12-25 09:48:53 +00:00
# include "core/alloc_func.hpp"
2007-12-25 11:26:07 +00:00
# include "core/math_func.hpp"
2007-06-13 19:52:06 +01:00
# ifdef WITH_PNG
2007-06-13 17:21:11 +01:00
# include "spriteloader/png.hpp"
2007-06-13 19:52:06 +01:00
# endif /* WITH_PNG */
2007-06-17 21:30:28 +01:00
# include "blitter/factory.hpp"
2004-08-09 18:04:08 +01:00
2008-01-13 01:21:35 +00:00
# include "table/sprites.h"
2007-06-11 12:50:49 +01:00
/* Default of 4MB spritecache */
uint _sprite_cache_size = 4 ;
2004-08-09 18:04:08 +01:00
2007-03-07 12:11:48 +00:00
struct SpriteCache {
2008-03-26 19:18:30 +00:00
void * ptr ;
2008-05-27 22:41:00 +01:00
size_t file_pos ;
2008-07-04 15:45:51 +01:00
uint32 id ;
2008-01-21 23:55:57 +00:00
uint16 file_slot ;
2008-03-26 19:18:30 +00:00
int16 lru ;
2008-07-04 15:45:51 +01:00
bool real_sprite ; ///< In some cases a single sprite is misused by two NewGRFs. Once as real sprite and once as non-real sprite. If the non-real sprite gets into the cache it might be drawn as real sprite which causes enormous trouble.
2007-03-07 12:11:48 +00:00
} ;
2007-01-03 14:42:08 +00:00
static uint _spritecache_items = 0 ;
static SpriteCache * _spritecache = NULL ;
static inline SpriteCache * GetSpriteCache ( uint index )
{
return & _spritecache [ index ] ;
}
static SpriteCache * AllocateSpriteCache ( uint index )
{
if ( index > = _spritecache_items ) {
/* Add another 1024 items to the 'pool' */
2007-11-19 20:40:14 +00:00
uint items = Align ( index + 1 , 1024 ) ;
2007-01-03 14:42:08 +00:00
DEBUG ( sprite , 4 , " Increasing sprite cache to %d items (%d bytes) " , items , items * sizeof ( * _spritecache ) ) ;
2007-01-11 17:29:39 +00:00
_spritecache = ReallocT ( _spritecache , items ) ;
2007-01-03 14:42:08 +00:00
/* Reset the new items and update the count */
memset ( _spritecache + _spritecache_items , 0 , ( items - _spritecache_items ) * sizeof ( * _spritecache ) ) ;
_spritecache_items = items ;
}
return GetSpriteCache ( index ) ;
}
2004-08-09 18:04:08 +01:00
2007-03-07 12:11:48 +00:00
struct MemBlock {
2008-05-08 14:20:54 +01:00
size_t size ;
2005-02-11 13:35:27 +00:00
byte data [ VARARRAY_SIZE ] ;
2007-03-07 12:11:48 +00:00
} ;
2005-02-11 13:35:27 +00:00
2004-08-09 18:04:08 +01:00
static uint _sprite_lru_counter ;
2005-02-11 13:35:27 +00:00
static MemBlock * _spritecache_ptr ;
2004-08-09 18:04:08 +01:00
static int _compact_cache_counter ;
2007-03-07 11:47:46 +00:00
static void CompactSpriteCache ( ) ;
2004-08-09 18:04:08 +01:00
2008-08-30 10:46:52 +01:00
/**
* Skip the given amount of sprite graphics data .
* @ param type the type of sprite ( compressed etc )
* @ param num the amount of sprites to skip
*/
void SkipSpriteData ( byte type , uint16 num )
2004-08-09 18:04:08 +01:00
{
if ( type & 2 ) {
FioSkipBytes ( num ) ;
2005-02-11 14:33:43 +00:00
} else {
while ( num > 0 ) {
int8 i = FioReadByte ( ) ;
if ( i > = 0 ) {
2008-08-30 13:14:56 +01:00
int size = ( i = = 0 ) ? 0x80 : i ;
num - = size ;
FioSkipBytes ( size ) ;
2005-02-11 14:33:43 +00:00
} else {
i = - ( i > > 3 ) ;
num - = i ;
FioReadByte ( ) ;
}
2004-08-09 18:04:08 +01:00
}
}
2008-08-30 10:46:52 +01:00
}
/**
* Read the sprite header data and then skip the real payload .
* @ return true if the sprite is a pseudo sprite .
*/
static bool ReadSpriteHeaderSkipData ( )
{
uint16 num = FioReadWord ( ) ;
byte type ;
if ( num = = 0 ) return false ;
type = FioReadByte ( ) ;
if ( type = = 0xFF ) {
FioSkipBytes ( num ) ;
/* Some NewGRF files have "empty" pseudo-sprites which are 1
* byte long . Catch these so the sprites won ' t be displayed . */
return num ! = 1 ;
}
FioSkipBytes ( 7 ) ;
SkipSpriteData ( type , num - 8 ) ;
2005-08-11 14:09:12 +01:00
return true ;
2004-08-09 18:04:08 +01:00
}
2006-04-16 12:26:23 +01:00
/* Check if the given Sprite ID exists */
bool SpriteExists ( SpriteID id )
{
/* Special case for Sprite ID zero -- its position is also 0... */
2007-01-03 14:42:08 +00:00
if ( id = = 0 ) return true ;
2007-01-16 22:10:35 +00:00
if ( id > = _spritecache_items ) return false ;
2007-09-13 23:48:11 +01:00
return ! ( GetSpriteCache ( id ) - > file_pos = = 0 & & GetSpriteCache ( id ) - > file_slot = = 0 ) ;
2006-04-16 12:26:23 +01:00
}
2007-06-11 12:50:49 +01:00
void * AllocSprite ( size_t ) ;
2005-07-05 20:54:35 +01:00
2007-06-12 10:40:50 +01:00
static void * ReadSprite ( SpriteCache * sc , SpriteID id , bool real_sprite )
2004-08-09 18:04:08 +01:00
{
2007-09-13 19:22:34 +01:00
uint8 file_slot = sc - > file_slot ;
2008-05-27 22:41:00 +01:00
size_t file_pos = sc - > file_pos ;
2005-07-05 20:54:35 +01:00
2006-12-26 17:36:18 +00:00
DEBUG ( sprite , 9 , " Load sprite %d " , id ) ;
2004-09-08 19:05:49 +01:00
2006-04-16 12:26:23 +01:00
if ( ! SpriteExists ( id ) ) {
2007-02-08 14:02:12 +00:00
DEBUG ( sprite , 1 , " Tried to load non-existing sprite #%d. Probable cause: Wrong/missing NewGRFs " , id ) ;
/* SPR_IMG_QUERY is a BIG FAT RED ? */
id = SPR_IMG_QUERY ;
2007-09-13 19:22:34 +01:00
file_slot = GetSpriteCache ( SPR_IMG_QUERY ) - > file_slot ;
file_pos = GetSpriteCache ( SPR_IMG_QUERY ) - > file_pos ;
2005-02-17 15:53:47 +00:00
}
2008-07-29 22:45:30 +01:00
if ( real_sprite & & BlitterFactoryBase : : GetCurrentBlitter ( ) - > GetScreenDepth ( ) = = 32 ) {
2007-06-13 20:34:33 +01:00
# ifdef WITH_PNG
2007-06-13 17:21:11 +01:00
/* Try loading 32bpp graphics in case we are 32bpp output */
SpriteLoaderPNG sprite_loader ;
SpriteLoader : : Sprite sprite ;
2008-01-22 07:27:06 +00:00
if ( sprite_loader . LoadSprite ( & sprite , file_slot , sc - > id ) ) {
2007-06-13 17:21:11 +01:00
sc - > ptr = BlitterFactoryBase : : GetCurrentBlitter ( ) - > Encode ( & sprite , & AllocSprite ) ;
free ( sprite . data ) ;
2008-07-29 22:45:30 +01:00
sc - > real_sprite = real_sprite ;
2008-07-12 15:03:36 +01:00
2007-06-13 17:21:11 +01:00
return sc - > ptr ;
}
/* If the PNG couldn't be loaded, fall back to 8bpp grfs */
2007-06-13 20:34:33 +01:00
# else
static bool show_once = true ;
if ( show_once ) {
DEBUG ( misc , 0 , " You are running a 32bpp blitter, but this build is without libpng support; falling back to 8bpp graphics " ) ;
show_once = false ;
}
2007-06-13 19:52:06 +01:00
# endif /* WITH_PNG */
2007-06-13 20:34:33 +01:00
}
2007-06-13 17:21:11 +01:00
2007-09-13 19:22:34 +01:00
FioSeekToFile ( file_slot , file_pos ) ;
2005-02-11 13:46:25 +00:00
2007-06-11 12:50:49 +01:00
/* Read the size and type */
int num = FioReadWord ( ) ;
byte type = FioReadByte ( ) ;
/* Type 0xFF indicates either a colormap or some other non-sprite info */
2005-07-05 20:54:35 +01:00
if ( type = = 0xFF ) {
2007-06-12 10:40:50 +01:00
if ( real_sprite ) {
static byte warning_level = 0 ;
DEBUG ( sprite , warning_level , " Tried to load non sprite #%d as a real sprite. Probable cause: NewGRF interference " , id ) ;
warning_level = 6 ;
2008-06-05 21:54:52 +01:00
if ( id = = SPR_IMG_QUERY ) usererror ( " Uhm, would you be so kind not to load a NewGRF that makes the 'query' sprite a non- sprite? " ) ;
2008-07-29 22:45:30 +01:00
return ( void * ) GetRawSprite ( SPR_IMG_QUERY , true ) ;
2007-06-12 10:40:50 +01:00
}
2007-06-11 12:50:49 +01:00
byte * dest = ( byte * ) AllocSprite ( num ) ;
2005-07-05 20:54:35 +01:00
2007-01-03 14:42:08 +00:00
sc - > ptr = dest ;
2008-07-29 22:45:30 +01:00
sc - > real_sprite = real_sprite ;
2005-07-05 20:54:35 +01:00
FioReadBlock ( dest , num ) ;
2007-06-11 19:49:55 +01:00
return sc - > ptr ;
}
/* Ugly hack to work around the problem that the old landscape
* generator assumes that those sprites are stored uncompressed in
* the memory , and they are only read directly by the code , never
* send to the blitter . So do not send it to the blitter ( which will
* result in a data array in the format the blitter likes most ) , but
* read the data directly from disk and store that as sprite .
* Ugly : yes . Other solution : no . Blame the original author or
* something ; ) The image should really have been a data - stream
* ( so type = 0xFF basicly ) . */
if ( id > = 4845 & & id < = 4881 ) {
2008-07-29 22:45:30 +01:00
assert ( real_sprite ) ;
2007-06-11 19:49:55 +01:00
uint height = FioReadByte ( ) ;
uint width = FioReadWord ( ) ;
Sprite * sprite ;
byte * dest ;
num = width * height ;
sprite = ( Sprite * ) AllocSprite ( sizeof ( * sprite ) + num ) ;
sc - > ptr = sprite ;
sprite - > height = height ;
sprite - > width = width ;
sprite - > x_offs = FioReadWord ( ) ;
sprite - > y_offs = FioReadWord ( ) ;
dest = sprite - > data ;
while ( num > 0 ) {
int8 i = FioReadByte ( ) ;
if ( i > = 0 ) {
num - = i ;
for ( ; i > 0 ; - - i ) * dest + + = FioReadByte ( ) ;
} else {
const byte * rel = dest - ( ( ( i & 7 ) < < 8 ) | FioReadByte ( ) ) ;
i = - ( i > > 3 ) ;
num - = i ;
for ( ; i > 0 ; - - i ) * dest + + = * rel + + ;
}
}
2008-07-29 22:45:30 +01:00
sc - > real_sprite = real_sprite ;
2008-07-04 15:45:51 +01:00
2007-06-11 19:49:55 +01:00
return sc - > ptr ;
2007-06-11 12:50:49 +01:00
}
2005-02-11 14:33:43 +00:00
2007-06-12 10:40:50 +01:00
if ( ! real_sprite ) {
static byte warning_level = 0 ;
DEBUG ( sprite , warning_level , " Tried to load real sprite #%d as a non sprite. Probable cause: NewGRF interference " , id ) ;
warning_level = 6 ;
2008-07-29 22:45:30 +01:00
return ( void * ) GetRawSprite ( id , true ) ;
2007-06-12 10:40:50 +01:00
}
2007-06-11 12:50:49 +01:00
SpriteLoaderGrf sprite_loader ;
SpriteLoader : : Sprite sprite ;
2004-09-08 19:05:49 +01:00
2008-07-29 22:45:30 +01:00
sc - > real_sprite = real_sprite ;
2008-01-22 07:27:06 +00:00
if ( ! sprite_loader . LoadSprite ( & sprite , file_slot , file_pos ) ) return NULL ;
2007-06-11 12:50:49 +01:00
if ( id = = 142 ) sprite . height = 10 ; // Compensate for a TTD bug
2007-06-11 14:38:11 +01:00
sc - > ptr = BlitterFactoryBase : : GetCurrentBlitter ( ) - > Encode ( & sprite , & AllocSprite ) ;
2007-06-11 12:50:49 +01:00
free ( sprite . data ) ;
2005-07-05 20:54:35 +01:00
2007-06-11 12:50:49 +01:00
return sc - > ptr ;
2004-08-09 18:04:08 +01:00
}
2007-09-13 19:22:34 +01:00
bool LoadNextSprite ( int load_index , byte file_slot , uint file_sprite_id )
2004-08-09 18:04:08 +01:00
{
2007-01-03 14:42:08 +00:00
SpriteCache * sc ;
2008-05-27 22:41:00 +01:00
size_t file_pos = FioGetPos ( ) ;
2004-08-09 18:04:08 +01:00
2005-08-15 12:39:13 +01:00
if ( ! ReadSpriteHeaderSkipData ( ) ) return false ;
2004-11-12 18:47:19 +00:00
2006-04-20 06:57:47 +01:00
if ( load_index > = MAX_SPRITES ) {
2008-06-05 21:54:52 +01:00
usererror ( " Tried to load too many sprites (#%d; max %d) " , load_index , MAX_SPRITES ) ;
2006-04-20 06:57:47 +01:00
}
2007-01-03 14:42:08 +00:00
sc = AllocateSpriteCache ( load_index ) ;
2007-09-13 19:22:34 +01:00
sc - > file_slot = file_slot ;
2007-01-03 14:42:08 +00:00
sc - > file_pos = file_pos ;
sc - > ptr = NULL ;
sc - > lru = 0 ;
2007-06-14 15:31:48 +01:00
sc - > id = file_sprite_id ;
2008-07-04 15:45:51 +01:00
sc - > real_sprite = false ;
2007-06-13 17:21:11 +01:00
2004-08-09 18:04:08 +01:00
return true ;
}
2005-09-10 09:17:30 +01:00
2007-01-10 18:56:51 +00:00
void DupSprite ( SpriteID old_spr , SpriteID new_spr )
2005-09-10 09:17:30 +01:00
{
2008-01-29 00:29:28 +00:00
SpriteCache * scnew = AllocateSpriteCache ( new_spr ) ; // may reallocate: so put it first
2007-01-10 18:56:51 +00:00
SpriteCache * scold = GetSpriteCache ( old_spr ) ;
2007-01-03 14:42:08 +00:00
2007-09-13 19:22:34 +01:00
scnew - > file_slot = scold - > file_slot ;
2007-01-03 14:42:08 +00:00
scnew - > file_pos = scold - > file_pos ;
scnew - > ptr = NULL ;
2007-06-13 17:21:11 +01:00
scnew - > id = scold - > id ;
2008-07-04 15:45:51 +01:00
scnew - > real_sprite = scold - > real_sprite ;
2005-09-10 09:17:30 +01:00
}
2004-08-09 18:04:08 +01:00
# define S_FREE_MASK 1
2005-02-11 13:35:27 +00:00
static inline MemBlock * NextBlock ( MemBlock * block )
{
return ( MemBlock * ) ( ( byte * ) block + ( block - > size & ~ S_FREE_MASK ) ) ;
}
2004-08-09 18:04:08 +01:00
2008-05-27 22:41:00 +01:00
static size_t GetSpriteCacheUsage ( )
2004-08-09 18:04:08 +01:00
{
2008-05-27 22:41:00 +01:00
size_t tot_size = 0 ;
2005-02-11 13:35:27 +00:00
MemBlock * s ;
2008-02-14 15:13:36 +00:00
for ( s = _spritecache_ptr ; s - > size ! = 0 ; s = NextBlock ( s ) ) {
2005-02-11 13:35:27 +00:00
if ( ! ( s - > size & S_FREE_MASK ) ) tot_size + = s - > size ;
2008-02-14 15:13:36 +00:00
}
2004-08-09 18:04:08 +01:00
return tot_size ;
}
2007-03-07 11:47:46 +00:00
void IncreaseSpriteLRU ( )
2004-08-09 18:04:08 +01:00
{
2007-04-04 02:35:16 +01:00
/* Increase all LRU values */
2004-08-09 18:04:08 +01:00
if ( _sprite_lru_counter > 16384 ) {
2007-01-03 14:42:08 +00:00
SpriteID i ;
2006-12-26 17:36:18 +00:00
DEBUG ( sprite , 3 , " Fixing lru %d, inuse=%d " , _sprite_lru_counter , GetSpriteCacheUsage ( ) ) ;
2004-08-09 18:04:08 +01:00
2007-01-03 14:42:08 +00:00
for ( i = 0 ; i ! = _spritecache_items ; i + + ) {
SpriteCache * sc = GetSpriteCache ( i ) ;
if ( sc - > ptr ! = NULL ) {
if ( sc - > lru > = 0 ) {
sc - > lru = - 1 ;
} else if ( sc - > lru ! = - 32768 ) {
sc - > lru - - ;
2004-08-09 18:04:08 +01:00
}
}
2007-01-03 14:42:08 +00:00
}
2004-08-09 18:04:08 +01:00
_sprite_lru_counter = 0 ;
}
2007-04-04 02:35:16 +01:00
/* Compact sprite cache every now and then. */
2004-08-09 18:04:08 +01:00
if ( + + _compact_cache_counter > = 740 ) {
CompactSpriteCache ( ) ;
_compact_cache_counter = 0 ;
}
}
2007-04-04 02:35:16 +01:00
/** Called when holes in the sprite cache should be removed.
* That is accomplished by moving the cached data . */
2007-03-07 11:47:46 +00:00
static void CompactSpriteCache ( )
2004-08-09 18:04:08 +01:00
{
2005-02-11 13:35:27 +00:00
MemBlock * s ;
2004-09-08 19:05:49 +01:00
2006-12-26 17:36:18 +00:00
DEBUG ( sprite , 3 , " Compacting sprite cache, inuse=%d " , GetSpriteCacheUsage ( ) ) ;
2004-08-09 18:04:08 +01:00
2005-02-11 13:35:27 +00:00
for ( s = _spritecache_ptr ; s - > size ! = 0 ; ) {
if ( s - > size & S_FREE_MASK ) {
MemBlock * next = NextBlock ( s ) ;
MemBlock temp ;
2007-01-03 14:42:08 +00:00
SpriteID i ;
2004-09-08 19:05:49 +01:00
2007-04-04 02:35:16 +01:00
/* Since free blocks are automatically coalesced, this should hold true. */
2005-02-11 13:35:27 +00:00
assert ( ! ( next - > size & S_FREE_MASK ) ) ;
2004-09-08 19:05:49 +01:00
2007-04-04 02:35:16 +01:00
/* If the next block is the sentinel block, we can safely return */
2008-03-26 19:18:30 +00:00
if ( next - > size = = 0 ) break ;
2004-08-09 18:04:08 +01:00
2007-04-04 02:35:16 +01:00
/* Locate the sprite belonging to the next pointer. */
2007-01-03 14:42:08 +00:00
for ( i = 0 ; GetSpriteCache ( i ) - > ptr ! = next - > data ; i + + ) {
assert ( i ! = _spritecache_items ) ;
2005-02-11 13:35:27 +00:00
}
2004-09-08 19:05:49 +01:00
2007-01-03 14:42:08 +00:00
GetSpriteCache ( i ) - > ptr = s - > data ; // Adjust sprite array entry
2007-04-04 02:35:16 +01:00
/* Swap this and the next block */
2005-02-11 13:35:27 +00:00
temp = * s ;
memmove ( s , next , next - > size ) ;
s = NextBlock ( s ) ;
* s = temp ;
2007-04-04 02:35:16 +01:00
/* Coalesce free blocks */
2005-02-11 13:35:27 +00:00
while ( NextBlock ( s ) - > size & S_FREE_MASK ) {
s - > size + = NextBlock ( s ) - > size & ~ S_FREE_MASK ;
}
} else {
s = NextBlock ( s ) ;
2004-08-09 18:04:08 +01:00
}
}
}
2007-03-07 11:47:46 +00:00
static void DeleteEntryFromSpriteCache ( )
2004-08-09 18:04:08 +01:00
{
2007-01-03 14:42:08 +00:00
SpriteID i ;
2007-01-10 18:56:51 +00:00
uint best = UINT_MAX ;
2005-02-11 13:35:27 +00:00
MemBlock * s ;
2004-08-09 18:04:08 +01:00
int cur_lru ;
2006-12-26 17:36:18 +00:00
DEBUG ( sprite , 3 , " DeleteEntryFromSpriteCache, inuse=%d " , GetSpriteCacheUsage ( ) ) ;
2004-08-09 18:04:08 +01:00
cur_lru = 0xffff ;
2007-01-03 14:42:08 +00:00
for ( i = 0 ; i ! = _spritecache_items ; i + + ) {
SpriteCache * sc = GetSpriteCache ( i ) ;
if ( sc - > ptr ! = NULL & & sc - > lru < cur_lru ) {
cur_lru = sc - > lru ;
2004-08-09 18:04:08 +01:00
best = i ;
}
}
2007-04-04 02:35:16 +01:00
/* Display an error message and die, in case we found no sprite at all.
* This shouldn ' t really happen , unless all sprites are locked . */
2007-12-08 15:47:23 +00:00
if ( best = = ( uint ) - 1 ) error ( " Out of sprite memory " ) ;
2004-08-09 18:04:08 +01:00
2007-04-04 02:35:16 +01:00
/* Mark the block as free (the block must be in use) */
2007-01-03 14:42:08 +00:00
s = ( MemBlock * ) GetSpriteCache ( best ) - > ptr - 1 ;
2005-02-11 13:35:27 +00:00
assert ( ! ( s - > size & S_FREE_MASK ) ) ;
s - > size | = S_FREE_MASK ;
2007-01-03 14:42:08 +00:00
GetSpriteCache ( best ) - > ptr = NULL ;
2004-08-09 18:04:08 +01:00
2007-04-04 02:35:16 +01:00
/* And coalesce adjacent free blocks */
2005-02-11 13:35:27 +00:00
for ( s = _spritecache_ptr ; s - > size ! = 0 ; s = NextBlock ( s ) ) {
if ( s - > size & S_FREE_MASK ) {
while ( NextBlock ( s ) - > size & S_FREE_MASK ) {
s - > size + = NextBlock ( s ) - > size & ~ S_FREE_MASK ;
2004-08-09 18:04:08 +01:00
}
}
}
}
2007-06-11 12:50:49 +01:00
void * AllocSprite ( size_t mem_req )
2004-08-09 18:04:08 +01:00
{
2005-07-05 20:54:35 +01:00
mem_req + = sizeof ( MemBlock ) ;
2004-08-09 18:04:08 +01:00
2005-02-11 13:35:27 +00:00
/* Align this to an uint32 boundary. This also makes sure that the 2 least
* bits are not used , so we could use those for other things . */
2007-11-19 20:40:14 +00:00
mem_req = Align ( mem_req , sizeof ( uint32 ) ) ;
2004-08-09 18:04:08 +01:00
2005-02-11 13:35:27 +00:00
for ( ; ; ) {
MemBlock * s ;
2004-08-09 18:04:08 +01:00
2005-02-11 13:35:27 +00:00
for ( s = _spritecache_ptr ; s - > size ! = 0 ; s = NextBlock ( s ) ) {
if ( s - > size & S_FREE_MASK ) {
size_t cur_size = s - > size & ~ S_FREE_MASK ;
2004-08-09 18:04:08 +01:00
2005-02-11 13:35:27 +00:00
/* Is the block exactly the size we need or
* big enough for an additional free block ? */
if ( cur_size = = mem_req | |
cur_size > = mem_req + sizeof ( MemBlock ) ) {
2007-04-04 02:35:16 +01:00
/* Set size and in use */
2005-02-11 13:35:27 +00:00
s - > size = mem_req ;
2004-09-08 19:05:49 +01:00
2007-04-04 02:35:16 +01:00
/* Do we need to inject a free block too? */
2005-02-11 13:35:27 +00:00
if ( cur_size ! = mem_req ) {
NextBlock ( s ) - > size = ( cur_size - mem_req ) | S_FREE_MASK ;
}
2004-08-09 18:04:08 +01:00
2005-02-11 13:35:27 +00:00
return s - > data ;
}
}
2004-08-09 18:04:08 +01:00
}
2004-09-08 19:05:49 +01:00
2007-04-04 02:35:16 +01:00
/* Reached sentinel, but no block found yet. Delete some old entry. */
2005-02-11 13:35:27 +00:00
DeleteEntryFromSpriteCache ( ) ;
2004-08-09 18:04:08 +01:00
}
}
2007-06-12 10:40:50 +01:00
const void * GetRawSprite ( SpriteID sprite , bool real_sprite )
2004-08-09 18:04:08 +01:00
{
2007-01-03 14:42:08 +00:00
SpriteCache * sc ;
2005-07-05 20:54:35 +01:00
void * p ;
2004-08-09 18:04:08 +01:00
2007-01-16 22:10:35 +00:00
assert ( sprite < _spritecache_items ) ;
2004-08-09 18:04:08 +01:00
2007-01-03 14:42:08 +00:00
sc = GetSpriteCache ( sprite ) ;
2007-04-04 02:35:16 +01:00
/* Update LRU */
2007-01-03 14:42:08 +00:00
sc - > lru = + + _sprite_lru_counter ;
p = sc - > ptr ;
2004-08-09 18:04:08 +01:00
2007-04-04 02:35:16 +01:00
/* Load the sprite, if it is not loaded, yet */
2008-07-04 15:45:51 +01:00
if ( p = = NULL | | sc - > real_sprite ! = real_sprite ) p = ReadSprite ( sc , sprite , real_sprite ) ;
2008-02-14 15:13:36 +00:00
2004-09-08 19:05:49 +01:00
return p ;
2004-08-09 18:04:08 +01:00
}
2007-03-07 11:47:46 +00:00
void GfxInitSpriteMem ( )
2004-08-09 18:04:08 +01:00
{
2007-04-04 02:35:16 +01:00
/* initialize sprite cache heap */
2007-12-08 14:50:41 +00:00
if ( _spritecache_ptr = = NULL ) _spritecache_ptr = ( MemBlock * ) MallocT < byte > ( _sprite_cache_size * 1024 * 1024 ) ;
2004-08-09 18:04:08 +01:00
2007-04-04 02:35:16 +01:00
/* A big free block */
2007-06-05 11:40:29 +01:00
_spritecache_ptr - > size = ( ( _sprite_cache_size * 1024 * 1024 ) - sizeof ( MemBlock ) ) | S_FREE_MASK ;
2007-04-04 02:35:16 +01:00
/* Sentinel block (identified by size == 0) */
2005-02-11 13:35:27 +00:00
NextBlock ( _spritecache_ptr ) - > size = 0 ;
2004-08-09 18:04:08 +01:00
2007-01-03 14:42:08 +00:00
/* Reset the spritecache 'pool' */
free ( _spritecache ) ;
_spritecache_items = 0 ;
_spritecache = NULL ;
2004-08-09 18:04:08 +01:00
2005-08-14 19:10:18 +01:00
_compact_cache_counter = 0 ;
2004-08-09 18:04:08 +01:00
}