00001
00002
00005 #include "stdafx.h"
00006 #include "openttd.h"
00007 #include "variables.h"
00008 #include "debug.h"
00009 #include "spritecache.h"
00010 #include "fileio.h"
00011 #include "spriteloader/grf.hpp"
00012 #include "core/alloc_func.hpp"
00013 #include "core/math_func.hpp"
00014 #ifdef WITH_PNG
00015 #include "spriteloader/png.hpp"
00016 #endif
00017 #include "blitter/factory.hpp"
00018
00019 #include "table/sprites.h"
00020
00021
00022 uint _sprite_cache_size = 4;
00023
00024
00025 struct SpriteCache {
00026 void *ptr;
00027 uint32 id;
00028 uint32 file_pos;
00029 uint16 file_slot;
00030 int16 lru;
00031 bool real_sprite;
00032 };
00033
00034
00035 static uint _spritecache_items = 0;
00036 static SpriteCache *_spritecache = NULL;
00037
00038
00039 static inline SpriteCache *GetSpriteCache(uint index)
00040 {
00041 return &_spritecache[index];
00042 }
00043
00044
00045 static SpriteCache *AllocateSpriteCache(uint index)
00046 {
00047 if (index >= _spritecache_items) {
00048
00049 uint items = Align(index + 1, 1024);
00050
00051 DEBUG(sprite, 4, "Increasing sprite cache to %d items (%d bytes)", items, items * sizeof(*_spritecache));
00052
00053 _spritecache = ReallocT(_spritecache, items);
00054
00055
00056 memset(_spritecache + _spritecache_items, 0, (items - _spritecache_items) * sizeof(*_spritecache));
00057 _spritecache_items = items;
00058 }
00059
00060 return GetSpriteCache(index);
00061 }
00062
00063
00064 struct MemBlock {
00065 uint32 size;
00066 byte data[VARARRAY_SIZE];
00067 };
00068
00069 static uint _sprite_lru_counter;
00070 static MemBlock *_spritecache_ptr;
00071 static int _compact_cache_counter;
00072
00073 static void CompactSpriteCache();
00074
00080 void SkipSpriteData(byte type, uint16 num)
00081 {
00082 if (type & 2) {
00083 FioSkipBytes(num);
00084 } else {
00085 while (num > 0) {
00086 int8 i = FioReadByte();
00087 if (i >= 0) {
00088 int size = (i == 0) ? 0x80 : i;
00089 num -= size;
00090 FioSkipBytes(size);
00091 } else {
00092 i = -(i >> 3);
00093 num -= i;
00094 FioReadByte();
00095 }
00096 }
00097 }
00098 }
00099
00104 static bool ReadSpriteHeaderSkipData()
00105 {
00106 uint16 num = FioReadWord();
00107 byte type;
00108
00109 if (num == 0) return false;
00110
00111 type = FioReadByte();
00112 if (type == 0xFF) {
00113 FioSkipBytes(num);
00114
00115
00116 return num != 1;
00117 }
00118
00119 FioSkipBytes(7);
00120 SkipSpriteData(type, num - 8);
00121
00122 return true;
00123 }
00124
00125
00126 bool SpriteExists(SpriteID id)
00127 {
00128
00129 if (id == 0) return true;
00130 if (id >= _spritecache_items) return false;
00131 return !(GetSpriteCache(id)->file_pos == 0 && GetSpriteCache(id)->file_slot == 0);
00132 }
00133
00134 void* AllocSprite(size_t);
00135
00136 static void* ReadSprite(SpriteCache *sc, SpriteID id, bool real_sprite)
00137 {
00138 uint8 file_slot = sc->file_slot;
00139 uint32 file_pos = sc->file_pos;
00140
00141 DEBUG(sprite, 9, "Load sprite %d", id);
00142
00143 if (!SpriteExists(id)) {
00144 DEBUG(sprite, 1, "Tried to load non-existing sprite #%d. Probable cause: Wrong/missing NewGRFs", id);
00145
00146
00147 id = SPR_IMG_QUERY;
00148 file_slot = GetSpriteCache(SPR_IMG_QUERY)->file_slot;
00149 file_pos = GetSpriteCache(SPR_IMG_QUERY)->file_pos;
00150 }
00151
00152 if (real_sprite && BlitterFactoryBase::GetCurrentBlitter()->GetScreenDepth() == 32) {
00153 #ifdef WITH_PNG
00154
00155 SpriteLoaderPNG sprite_loader;
00156 SpriteLoader::Sprite sprite;
00157
00158 if (sprite_loader.LoadSprite(&sprite, file_slot, sc->id)) {
00159 sc->ptr = BlitterFactoryBase::GetCurrentBlitter()->Encode(&sprite, &AllocSprite);
00160 free(sprite.data);
00161
00162 sc->real_sprite = real_sprite;
00163
00164 return sc->ptr;
00165 }
00166
00167 #else
00168 static bool show_once = true;
00169 if (show_once) {
00170 DEBUG(misc, 0, "You are running a 32bpp blitter, but this build is without libpng support; falling back to 8bpp graphics");
00171 show_once = false;
00172 }
00173 #endif
00174 }
00175
00176 FioSeekToFile(file_slot, file_pos);
00177
00178
00179 int num = FioReadWord();
00180 byte type = FioReadByte();
00181
00182 if (type == 0xFF) {
00183 if (real_sprite) {
00184 static byte warning_level = 0;
00185 DEBUG(sprite, warning_level, "Tried to load non sprite #%d as a real sprite. Probable cause: NewGRF interference", id);
00186 warning_level = 6;
00187 if (id == SPR_IMG_QUERY) error("Uhm, would you be so kind not to load a NewGRF that makes the 'query' sprite a non- sprite?");
00188 return (void*)GetRawSprite(SPR_IMG_QUERY, true);
00189 }
00190
00191 byte *dest = (byte *)AllocSprite(num);
00192
00193 sc->ptr = dest;
00194 sc->real_sprite = real_sprite;
00195 FioReadBlock(dest, num);
00196
00197 return sc->ptr;
00198 }
00199
00200
00201
00202
00203
00204
00205
00206
00207
00208 if (id >= 4845 && id <= 4881) {
00209 assert(real_sprite);
00210 uint height = FioReadByte();
00211 uint width = FioReadWord();
00212 Sprite *sprite;
00213 byte *dest;
00214
00215 num = width * height;
00216 sprite = (Sprite *)AllocSprite(sizeof(*sprite) + num);
00217 sc->ptr = sprite;
00218 sprite->height = height;
00219 sprite->width = width;
00220 sprite->x_offs = FioReadWord();
00221 sprite->y_offs = FioReadWord();
00222
00223 dest = sprite->data;
00224 while (num > 0) {
00225 int8 i = FioReadByte();
00226 if (i >= 0) {
00227 num -= i;
00228 for (; i > 0; --i) *dest++ = FioReadByte();
00229 } else {
00230 const byte* rel = dest - (((i & 7) << 8) | FioReadByte());
00231 i = -(i >> 3);
00232 num -= i;
00233 for (; i > 0; --i) *dest++ = *rel++;
00234 }
00235 }
00236
00237 sc->real_sprite = real_sprite;
00238
00239 return sc->ptr;
00240 }
00241
00242 if (!real_sprite) {
00243 static byte warning_level = 0;
00244 DEBUG(sprite, warning_level, "Tried to load real sprite #%d as a non sprite. Probable cause: NewGRF interference", id);
00245 warning_level = 6;
00246 return (void*)GetRawSprite(id, true);
00247 }
00248
00249 SpriteLoaderGrf sprite_loader;
00250 SpriteLoader::Sprite sprite;
00251
00252 sc->real_sprite = real_sprite;
00253
00254 if (!sprite_loader.LoadSprite(&sprite, file_slot, file_pos)) return NULL;
00255 if (id == 142) sprite.height = 10;
00256 sc->ptr = BlitterFactoryBase::GetCurrentBlitter()->Encode(&sprite, &AllocSprite);
00257 free(sprite.data);
00258
00259 return sc->ptr;
00260 }
00261
00262
00263 bool LoadNextSprite(int load_index, byte file_slot, uint file_sprite_id)
00264 {
00265 SpriteCache *sc;
00266 uint32 file_pos = FioGetPos();
00267
00268 if (!ReadSpriteHeaderSkipData()) return false;
00269
00270 if (load_index >= MAX_SPRITES) {
00271 error("Tried to load too many sprites (#%d; max %d)", load_index, MAX_SPRITES);
00272 }
00273
00274 sc = AllocateSpriteCache(load_index);
00275 sc->file_slot = file_slot;
00276 sc->file_pos = file_pos;
00277 sc->ptr = NULL;
00278 sc->lru = 0;
00279 sc->id = file_sprite_id;
00280 sc->real_sprite = false;
00281
00282 return true;
00283 }
00284
00285
00286 void DupSprite(SpriteID old_spr, SpriteID new_spr)
00287 {
00288 SpriteCache *scnew = AllocateSpriteCache(new_spr);
00289 SpriteCache *scold = GetSpriteCache(old_spr);
00290
00291 scnew->file_slot = scold->file_slot;
00292 scnew->file_pos = scold->file_pos;
00293 scnew->ptr = NULL;
00294 scnew->id = scold->id;
00295 scnew->real_sprite = scold->real_sprite;
00296 }
00297
00298
00299 #define S_FREE_MASK 1
00300
00301 static inline MemBlock* NextBlock(MemBlock* block)
00302 {
00303 return (MemBlock*)((byte*)block + (block->size & ~S_FREE_MASK));
00304 }
00305
00306 static uint32 GetSpriteCacheUsage()
00307 {
00308 uint32 tot_size = 0;
00309 MemBlock* s;
00310
00311 for (s = _spritecache_ptr; s->size != 0; s = NextBlock(s)) {
00312 if (!(s->size & S_FREE_MASK)) tot_size += s->size;
00313 }
00314
00315 return tot_size;
00316 }
00317
00318
00319 void IncreaseSpriteLRU()
00320 {
00321
00322 if (_sprite_lru_counter > 16384) {
00323 SpriteID i;
00324
00325 DEBUG(sprite, 3, "Fixing lru %d, inuse=%d", _sprite_lru_counter, GetSpriteCacheUsage());
00326
00327 for (i = 0; i != _spritecache_items; i++) {
00328 SpriteCache *sc = GetSpriteCache(i);
00329 if (sc->ptr != NULL) {
00330 if (sc->lru >= 0) {
00331 sc->lru = -1;
00332 } else if (sc->lru != -32768) {
00333 sc->lru--;
00334 }
00335 }
00336 }
00337 _sprite_lru_counter = 0;
00338 }
00339
00340
00341 if (++_compact_cache_counter >= 740) {
00342 CompactSpriteCache();
00343 _compact_cache_counter = 0;
00344 }
00345 }
00346
00349 static void CompactSpriteCache()
00350 {
00351 MemBlock *s;
00352
00353 DEBUG(sprite, 3, "Compacting sprite cache, inuse=%d", GetSpriteCacheUsage());
00354
00355 for (s = _spritecache_ptr; s->size != 0;) {
00356 if (s->size & S_FREE_MASK) {
00357 MemBlock* next = NextBlock(s);
00358 MemBlock temp;
00359 SpriteID i;
00360
00361
00362 assert(!(next->size & S_FREE_MASK));
00363
00364
00365 if (next->size == 0)
00366 break;
00367
00368
00369 for (i = 0; GetSpriteCache(i)->ptr != next->data; i++) {
00370 assert(i != _spritecache_items);
00371 }
00372
00373 GetSpriteCache(i)->ptr = s->data;
00374
00375 temp = *s;
00376 memmove(s, next, next->size);
00377 s = NextBlock(s);
00378 *s = temp;
00379
00380
00381 while (NextBlock(s)->size & S_FREE_MASK) {
00382 s->size += NextBlock(s)->size & ~S_FREE_MASK;
00383 }
00384 } else {
00385 s = NextBlock(s);
00386 }
00387 }
00388 }
00389
00390 static void DeleteEntryFromSpriteCache()
00391 {
00392 SpriteID i;
00393 uint best = UINT_MAX;
00394 MemBlock* s;
00395 int cur_lru;
00396
00397 DEBUG(sprite, 3, "DeleteEntryFromSpriteCache, inuse=%d", GetSpriteCacheUsage());
00398
00399 cur_lru = 0xffff;
00400 for (i = 0; i != _spritecache_items; i++) {
00401 SpriteCache *sc = GetSpriteCache(i);
00402 if (sc->ptr != NULL && sc->lru < cur_lru) {
00403 cur_lru = sc->lru;
00404 best = i;
00405 }
00406 }
00407
00408
00409
00410 if (best == (uint)-1) error("Out of sprite memory");
00411
00412
00413 s = (MemBlock*)GetSpriteCache(best)->ptr - 1;
00414 assert(!(s->size & S_FREE_MASK));
00415 s->size |= S_FREE_MASK;
00416 GetSpriteCache(best)->ptr = NULL;
00417
00418
00419 for (s = _spritecache_ptr; s->size != 0; s = NextBlock(s)) {
00420 if (s->size & S_FREE_MASK) {
00421 while (NextBlock(s)->size & S_FREE_MASK) {
00422 s->size += NextBlock(s)->size & ~S_FREE_MASK;
00423 }
00424 }
00425 }
00426 }
00427
00428 void* AllocSprite(size_t mem_req)
00429 {
00430 mem_req += sizeof(MemBlock);
00431
00432
00433
00434 mem_req = Align(mem_req, sizeof(uint32));
00435
00436 for (;;) {
00437 MemBlock* s;
00438
00439 for (s = _spritecache_ptr; s->size != 0; s = NextBlock(s)) {
00440 if (s->size & S_FREE_MASK) {
00441 size_t cur_size = s->size & ~S_FREE_MASK;
00442
00443
00444
00445 if (cur_size == mem_req ||
00446 cur_size >= mem_req + sizeof(MemBlock)) {
00447
00448 s->size = mem_req;
00449
00450
00451 if (cur_size != mem_req) {
00452 NextBlock(s)->size = (cur_size - mem_req) | S_FREE_MASK;
00453 }
00454
00455 return s->data;
00456 }
00457 }
00458 }
00459
00460
00461 DeleteEntryFromSpriteCache();
00462 }
00463 }
00464
00465
00466 const void *GetRawSprite(SpriteID sprite, bool real_sprite)
00467 {
00468 SpriteCache *sc;
00469 void* p;
00470
00471 assert(sprite < _spritecache_items);
00472
00473 sc = GetSpriteCache(sprite);
00474
00475
00476 sc->lru = ++_sprite_lru_counter;
00477
00478 p = sc->ptr;
00479
00480
00481 if (p == NULL || sc->real_sprite != real_sprite) p = ReadSprite(sc, sprite, real_sprite);
00482
00483 return p;
00484 }
00485
00486
00487 void GfxInitSpriteMem()
00488 {
00489
00490 if (_spritecache_ptr == NULL) _spritecache_ptr = (MemBlock*)MallocT<byte>(_sprite_cache_size * 1024 * 1024);
00491
00492
00493 _spritecache_ptr->size = ((_sprite_cache_size * 1024 * 1024) - sizeof(MemBlock)) | S_FREE_MASK;
00494
00495 NextBlock(_spritecache_ptr)->size = 0;
00496
00497
00498 free(_spritecache);
00499 _spritecache_items = 0;
00500 _spritecache = NULL;
00501
00502 _compact_cache_counter = 0;
00503 }