Files @ r3764:4a2ca18ddafd
Branch filter:

Location: cpp/openttd-patchpack/source/spritecache.c - annotation

peter1138
(svn r4755) - Newstations: add a gui station tile drawing routine and use it in place of the existing one for waypoints.
  1
  2
  3
  4
  5
  6
  7
  8
  9
 10
 11
 12
 13
 14
 15
 16
 17
 18
 19
 20
 21
 22
 23
 24
 25
 26
 27
 28
 29
 30
 31
 32
 33
 34
 35
 36
 37
 38
 39
 40
 41
 42
 43
 44
 45
 46
 47
 48
 49
 50
 51
 52
 53
 54
 55
 56
 57
 58
 59
 60
 61
 62
 63
 64
 65
 66
 67
 68
 69
 70
 71
 72
 73
 74
 75
 76
 77
 78
 79
 80
 81
 82
 83
 84
 85
 86
 87
 88
 89
 90
 91
 92
 93
 94
 95
 96
 97
 98
 99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
r2186:5ee653b1b5e1
r2186:5ee653b1b5e1
r0:d63b455452f6
r1891:c5c5466afa35
r1299:07d5483b3f76
r2163:ae001e2aa5b0
r2548:9570bbbdacab
r1349:8fc4bdf6c588
r1363:a54abce83b4b
r0:d63b455452f6
r0:d63b455452f6
r0:d63b455452f6
r0:d63b455452f6
r0:d63b455452f6
r0:d63b455452f6
r0:d63b455452f6
r2187:60cea7c48bf4
r2187:60cea7c48bf4
r0:d63b455452f6
r0:d63b455452f6
r2187:60cea7c48bf4
r0:d63b455452f6
r2187:60cea7c48bf4
r2187:60cea7c48bf4
r0:d63b455452f6
r0:d63b455452f6
r1353:49e055e3fedb
r1353:49e055e3fedb
r1353:49e055e3fedb
r1353:49e055e3fedb
r1353:49e055e3fedb
r0:d63b455452f6
r1353:49e055e3fedb
r0:d63b455452f6
r0:d63b455452f6
r1093:18f56ef2d029
r0:d63b455452f6
r2342:c403fb19563d
r0:d63b455452f6
r2329:ea0e9adbf1e6
r0:d63b455452f6
r0:d63b455452f6
r2329:ea0e9adbf1e6
r2329:ea0e9adbf1e6
r0:d63b455452f6
r0:d63b455452f6
r2342:c403fb19563d
r2329:ea0e9adbf1e6
r0:d63b455452f6
r184:3abc663954cd
r0:d63b455452f6
r0:d63b455452f6
r2329:ea0e9adbf1e6
r0:d63b455452f6
r0:d63b455452f6
r0:d63b455452f6
r1355:bc74498c8a6c
r1355:bc74498c8a6c
r1355:bc74498c8a6c
r1355:bc74498c8a6c
r1355:bc74498c8a6c
r1355:bc74498c8a6c
r1355:bc74498c8a6c
r1355:bc74498c8a6c
r1355:bc74498c8a6c
r1355:bc74498c8a6c
r1355:bc74498c8a6c
r0:d63b455452f6
r0:d63b455452f6
r2329:ea0e9adbf1e6
r2329:ea0e9adbf1e6
r0:d63b455452f6
r0:d63b455452f6
r3565:99183437dfaa
r3565:99183437dfaa
r3565:99183437dfaa
r3565:99183437dfaa
r3565:99183437dfaa
r3565:99183437dfaa
r3565:99183437dfaa
r2014:c6a34c339dda
r2014:c6a34c339dda
r2014:c6a34c339dda
r0:d63b455452f6
r2321:0e6570b5e572
r0:d63b455452f6
r2014:c6a34c339dda
r2014:c6a34c339dda
r184:3abc663954cd
r3565:99183437dfaa
r1378:ec274610d6e6
r1378:ec274610d6e6
r1378:ec274610d6e6
r1378:ec274610d6e6
r1378:ec274610d6e6
r1378:ec274610d6e6
r1378:ec274610d6e6
r1354:17e108111550
r1354:17e108111550
r2321:0e6570b5e572
r0:d63b455452f6
r2014:c6a34c339dda
r2014:c6a34c339dda
r2014:c6a34c339dda
r2014:c6a34c339dda
r2014:c6a34c339dda
r2014:c6a34c339dda
r2014:c6a34c339dda
r2014:c6a34c339dda
r2014:c6a34c339dda
r2014:c6a34c339dda
r2014:c6a34c339dda
r2014:c6a34c339dda
r2014:c6a34c339dda
r2014:c6a34c339dda
r2014:c6a34c339dda
r2014:c6a34c339dda
r2014:c6a34c339dda
r2015:73fac0e1e64b
r2014:c6a34c339dda
r1351:f88b782c67ae
r1351:f88b782c67ae
r2014:c6a34c339dda
r1351:f88b782c67ae
r1355:bc74498c8a6c
r1355:bc74498c8a6c
r0:d63b455452f6
r1355:bc74498c8a6c
r1355:bc74498c8a6c
r2014:c6a34c339dda
r1355:bc74498c8a6c
r1355:bc74498c8a6c
r184:3abc663954cd
r1355:bc74498c8a6c
r1355:bc74498c8a6c
r1355:bc74498c8a6c
r2014:c6a34c339dda
r1355:bc74498c8a6c
r0:d63b455452f6
r2014:c6a34c339dda
r2014:c6a34c339dda
r0:d63b455452f6
r0:d63b455452f6
r0:d63b455452f6
r0:d63b455452f6
r2340:fbdaeafc90de
r0:d63b455452f6
r2321:0e6570b5e572
r0:d63b455452f6
r2342:c403fb19563d
r361:05fe8c541583
r3591:247760f3c528
r3591:247760f3c528
r3591:247760f3c528
r3591:247760f3c528
r361:05fe8c541583
r361:05fe8c541583
r0:d63b455452f6
r0:d63b455452f6
r0:d63b455452f6
r0:d63b455452f6
r184:3abc663954cd
r0:d63b455452f6
r0:d63b455452f6
r0:d63b455452f6
r0:d63b455452f6
r0:d63b455452f6
r0:d63b455452f6
r0:d63b455452f6
r2407:bc0da484827c
r2407:bc0da484827c
r2407:bc0da484827c
r2407:bc0da484827c
r2407:bc0da484827c
r2407:bc0da484827c
r2407:bc0da484827c
r2407:bc0da484827c
r2340:fbdaeafc90de
r37:ead8cba1b6f2
r2329:ea0e9adbf1e6
r2342:c403fb19563d
r37:ead8cba1b6f2
r37:ead8cba1b6f2
r37:ead8cba1b6f2
r0:d63b455452f6
r0:d63b455452f6
r1353:49e055e3fedb
r1353:49e055e3fedb
r1353:49e055e3fedb
r1353:49e055e3fedb
r1353:49e055e3fedb
r0:d63b455452f6
r1093:18f56ef2d029
r0:d63b455452f6
r1353:49e055e3fedb
r1353:49e055e3fedb
r1353:49e055e3fedb
r1353:49e055e3fedb
r1353:49e055e3fedb
r0:d63b455452f6
r0:d63b455452f6
r0:d63b455452f6
r0:d63b455452f6
r0:d63b455452f6
r1093:18f56ef2d029
r0:d63b455452f6
r0:d63b455452f6
r0:d63b455452f6
r0:d63b455452f6
r0:d63b455452f6
r0:d63b455452f6
r0:d63b455452f6
r0:d63b455452f6
r2187:60cea7c48bf4
r0:d63b455452f6
r0:d63b455452f6
r0:d63b455452f6
r0:d63b455452f6
r0:d63b455452f6
r0:d63b455452f6
r0:d63b455452f6
r0:d63b455452f6
r0:d63b455452f6
r0:d63b455452f6
r2187:60cea7c48bf4
r0:d63b455452f6
r0:d63b455452f6
r0:d63b455452f6
r0:d63b455452f6
r0:d63b455452f6
r0:d63b455452f6
r0:d63b455452f6
r0:d63b455452f6
r0:d63b455452f6
r0:d63b455452f6
r0:d63b455452f6
r0:d63b455452f6
r0:d63b455452f6
r0:d63b455452f6
r0:d63b455452f6
r1093:18f56ef2d029
r0:d63b455452f6
r1353:49e055e3fedb
r184:3abc663954cd
r1353:49e055e3fedb
r1353:49e055e3fedb
r1353:49e055e3fedb
r184:3abc663954cd
r1353:49e055e3fedb
r1353:49e055e3fedb
r1353:49e055e3fedb
r1353:49e055e3fedb
r2014:c6a34c339dda
r184:3abc663954cd
r0:d63b455452f6
r1353:49e055e3fedb
r184:3abc663954cd
r0:d63b455452f6
r1353:49e055e3fedb
r0:d63b455452f6
r0:d63b455452f6
r1353:49e055e3fedb
r1353:49e055e3fedb
r1353:49e055e3fedb
r1353:49e055e3fedb
r0:d63b455452f6
r1353:49e055e3fedb
r1353:49e055e3fedb
r1353:49e055e3fedb
r1353:49e055e3fedb
r1353:49e055e3fedb
r1353:49e055e3fedb
r184:3abc663954cd
r1353:49e055e3fedb
r1353:49e055e3fedb
r1353:49e055e3fedb
r0:d63b455452f6
r1353:49e055e3fedb
r1353:49e055e3fedb
r0:d63b455452f6
r0:d63b455452f6
r0:d63b455452f6
r0:d63b455452f6
r1093:18f56ef2d029
r0:d63b455452f6
r0:d63b455452f6
r0:d63b455452f6
r1353:49e055e3fedb
r0:d63b455452f6
r0:d63b455452f6
r0:d63b455452f6
r0:d63b455452f6
r0:d63b455452f6
r0:d63b455452f6
r2187:60cea7c48bf4
r2305:9c43b4b2a900
r0:d63b455452f6
r0:d63b455452f6
r0:d63b455452f6
r0:d63b455452f6
r0:d63b455452f6
r0:d63b455452f6
r0:d63b455452f6
r2187:60cea7c48bf4
r2305:9c43b4b2a900
r184:3abc663954cd
r0:d63b455452f6
r0:d63b455452f6
r0:d63b455452f6
r0:d63b455452f6
r0:d63b455452f6
r0:d63b455452f6
r0:d63b455452f6
r0:d63b455452f6
r0:d63b455452f6
r0:d63b455452f6
r0:d63b455452f6
r0:d63b455452f6
r0:d63b455452f6
r0:d63b455452f6
r0:d63b455452f6
r0:d63b455452f6
r0:d63b455452f6
r0:d63b455452f6
r0:d63b455452f6
r0:d63b455452f6
r0:d63b455452f6
r1353:49e055e3fedb
r1353:49e055e3fedb
r1353:49e055e3fedb
r0:d63b455452f6
r0:d63b455452f6
r0:d63b455452f6
r1353:49e055e3fedb
r1353:49e055e3fedb
r1353:49e055e3fedb
r1353:49e055e3fedb
r0:d63b455452f6
r0:d63b455452f6
r0:d63b455452f6
r0:d63b455452f6
r0:d63b455452f6
r2014:c6a34c339dda
r0:d63b455452f6
r2014:c6a34c339dda
r0:d63b455452f6
r1353:49e055e3fedb
r1353:49e055e3fedb
r2548:9570bbbdacab
r0:d63b455452f6
r1353:49e055e3fedb
r1353:49e055e3fedb
r0:d63b455452f6
r1353:49e055e3fedb
r1353:49e055e3fedb
r1353:49e055e3fedb
r0:d63b455452f6
r1353:49e055e3fedb
r1353:49e055e3fedb
r1353:49e055e3fedb
r1353:49e055e3fedb
r1353:49e055e3fedb
r1353:49e055e3fedb
r0:d63b455452f6
r1353:49e055e3fedb
r1353:49e055e3fedb
r1353:49e055e3fedb
r1353:49e055e3fedb
r0:d63b455452f6
r1353:49e055e3fedb
r1353:49e055e3fedb
r1353:49e055e3fedb
r0:d63b455452f6
r0:d63b455452f6
r1353:49e055e3fedb
r1353:49e055e3fedb
r0:d63b455452f6
r0:d63b455452f6
r0:d63b455452f6
r0:d63b455452f6
r0:d63b455452f6
r0:d63b455452f6
r0:d63b455452f6
r0:d63b455452f6
r0:d63b455452f6
r410:6458c1253cb1
r0:d63b455452f6
r0:d63b455452f6
r0:d63b455452f6
r0:d63b455452f6
r0:d63b455452f6
r0:d63b455452f6
r0:d63b455452f6
r0:d63b455452f6
r0:d63b455452f6
r0:d63b455452f6
r0:d63b455452f6
r0:d63b455452f6
r0:d63b455452f6
r0:d63b455452f6
r0:d63b455452f6
r0:d63b455452f6
r0:d63b455452f6
r0:d63b455452f6
r0:d63b455452f6
r0:d63b455452f6
r0:d63b455452f6
r0:d63b455452f6
r0:d63b455452f6
r0:d63b455452f6
r0:d63b455452f6
r0:d63b455452f6
r0:d63b455452f6
r0:d63b455452f6
r0:d63b455452f6
r0:d63b455452f6
r0:d63b455452f6
r0:d63b455452f6
r0:d63b455452f6
r0:d63b455452f6
r0:d63b455452f6
r0:d63b455452f6
r1361:d1246fcfda96
r0:d63b455452f6
r2014:c6a34c339dda
r0:d63b455452f6
r2187:60cea7c48bf4
r0:d63b455452f6
r0:d63b455452f6
r0:d63b455452f6
r0:d63b455452f6
r0:d63b455452f6
r0:d63b455452f6
r0:d63b455452f6
r0:d63b455452f6
r0:d63b455452f6
r2026:25935f6b221a
r0:d63b455452f6
r0:d63b455452f6
r0:d63b455452f6
r0:d63b455452f6
r2014:c6a34c339dda
r2014:c6a34c339dda
r184:3abc663954cd
r0:d63b455452f6
r0:d63b455452f6
r0:d63b455452f6
r2340:fbdaeafc90de
r0:d63b455452f6
r0:d63b455452f6
r2339:067e64e97ace
r0:d63b455452f6
r0:d63b455452f6
r2339:067e64e97ace
r1353:49e055e3fedb
r1353:49e055e3fedb
r0:d63b455452f6
r0:d63b455452f6
r2340:fbdaeafc90de
r2340:fbdaeafc90de
r0:d63b455452f6
/* $Id$ */

#include "stdafx.h"
#include "openttd.h"
#include "debug.h"
#include "functions.h"
#include "macros.h"
#include "spritecache.h"
#include "table/sprites.h"
#include "fileio.h"

#define SPRITE_CACHE_SIZE 1024*1024

#define WANT_NEW_LRU


static void* _sprite_ptr[MAX_SPRITES];
static uint32 _sprite_file_pos[MAX_SPRITES];

#if defined(WANT_NEW_LRU)
static int16 _sprite_lru_new[MAX_SPRITES];
#else
static uint16 _sprite_lru[MAX_SPRITES];
static uint16 _sprite_lru_cur[MAX_SPRITES];
#endif

typedef struct MemBlock {
	uint32 size;
	byte data[VARARRAY_SIZE];
} MemBlock;

static uint _sprite_lru_counter;
static MemBlock *_spritecache_ptr;
static int _compact_cache_counter;

static void CompactSpriteCache(void);

static bool ReadSpriteHeaderSkipData(void)
{
	uint16 num = FioReadWord();
	byte type;

	if (num == 0) return false;

	type = FioReadByte();
	if (type == 0xFF) {
		FioSkipBytes(num);
		return true;
	}

	FioSkipBytes(7);
	num -= 8;
	if (num == 0) return true;

	if (type & 2) {
		FioSkipBytes(num);
	} else {
		while (num > 0) {
			int8 i = FioReadByte();
			if (i >= 0) {
				num -= i;
				FioSkipBytes(i);
			} else {
				i = -(i >> 3);
				num -= i;
				FioReadByte();
			}
		}
	}

	return true;
}

/* Check if the given Sprite ID exists */
bool SpriteExists(SpriteID id)
{
	/* Special case for Sprite ID zero -- its position is also 0... */
	return _sprite_file_pos[id] != 0 || id == 0;
}

static void* AllocSprite(size_t);

static void* ReadSprite(SpriteID id)
{
	uint num;
	byte type;

	DEBUG(spritecache, 9) ("load sprite %d", id);

	if (!SpriteExists(id)) {
		error(
			"Tried to load non-existing sprite #%d.\n"
			"Probable cause: Wrong/missing NewGRFs",
			id
		);
	}

	FioSeekToFile(_sprite_file_pos[id]);

	num  = FioReadWord();
	type = FioReadByte();
	if (type == 0xFF) {
		byte* dest = AllocSprite(num);

		_sprite_ptr[id] = dest;
		FioReadBlock(dest, num);

		return dest;
	} else {
		uint height = FioReadByte();
		uint width  = FioReadWord();
		Sprite* sprite;
		byte* dest;

		num = (type & 0x02) ? width * height : num - 8;
		sprite = AllocSprite(sizeof(*sprite) + num);
		_sprite_ptr[id] = sprite;
		sprite->info   = type;
		sprite->height = (id != 142) ? height : 10; // Compensate for a TTD bug
		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++;
			}
		}

		return sprite;
	}
}


bool LoadNextSprite(int load_index, byte file_index)
{
	uint32 file_pos = FioGetPos() | (file_index << 24);

	if (!ReadSpriteHeaderSkipData()) return false;

	if (load_index >= MAX_SPRITES) {
		error("Tried to load too many sprites (#%d; max %d)", load_index, MAX_SPRITES);
	}

	_sprite_file_pos[load_index] = file_pos;

	_sprite_ptr[load_index] = NULL;

#if defined(WANT_NEW_LRU)
	_sprite_lru_new[load_index] = 0;
#else
	_sprite_lru[load_index] = 0xFFFF;
	_sprite_lru_cur[load_index] = 0;
#endif

	return true;
}


void DupSprite(SpriteID old, SpriteID new)
{
	_sprite_file_pos[new] = _sprite_file_pos[old];
	_sprite_ptr[new] = NULL;
}


void SkipSprites(uint count)
{
	for (; count > 0; --count) {
		if (!ReadSpriteHeaderSkipData()) return;
	}
}


#define S_FREE_MASK 1

static inline MemBlock* NextBlock(MemBlock* block)
{
	return (MemBlock*)((byte*)block + (block->size & ~S_FREE_MASK));
}

static uint32 GetSpriteCacheUsage(void)
{
	size_t tot_size = 0;
	MemBlock* s;

	for (s = _spritecache_ptr; s->size != 0; s = NextBlock(s))
		if (!(s->size & S_FREE_MASK)) tot_size += s->size;

	return tot_size;
}


void IncreaseSpriteLRU(void)
{
	int i;

	// Increase all LRU values
#if defined(WANT_NEW_LRU)
	if (_sprite_lru_counter > 16384) {
		DEBUG(spritecache, 2) ("fixing lru %d, inuse=%d", _sprite_lru_counter, GetSpriteCacheUsage());

		for (i = 0; i != MAX_SPRITES; i++)
			if (_sprite_ptr[i] != NULL) {
				if (_sprite_lru_new[i] >= 0) {
					_sprite_lru_new[i] = -1;
				} else if (_sprite_lru_new[i] != -32768) {
					_sprite_lru_new[i]--;
				}
			}
		_sprite_lru_counter = 0;
	}
#else
	for (i = 0; i != MAX_SPRITES; i++)
		if (_sprite_ptr[i] != NULL && _sprite_lru[i] != 65535)
			_sprite_lru[i]++;
	// Reset the lru counter.
	_sprite_lru_counter = 0;
#endif

	// Compact sprite cache every now and then.
	if (++_compact_cache_counter >= 740) {
		CompactSpriteCache();
		_compact_cache_counter = 0;
	}
}

// Called when holes in the sprite cache should be removed.
// That is accomplished by moving the cached data.
static void CompactSpriteCache(void)
{
	MemBlock *s;

	DEBUG(spritecache, 2) (
		"compacting sprite cache, inuse=%d", GetSpriteCacheUsage()
	);

	for (s = _spritecache_ptr; s->size != 0;) {
		if (s->size & S_FREE_MASK) {
			MemBlock* next = NextBlock(s);
			MemBlock temp;
			void** i;

			// Since free blocks are automatically coalesced, this should hold true.
			assert(!(next->size & S_FREE_MASK));

			// If the next block is the sentinel block, we can safely return
			if (next->size == 0)
				break;

			// Locate the sprite belonging to the next pointer.
			for (i = _sprite_ptr; *i != next->data; ++i) {
				assert(i != endof(_sprite_ptr));
			}

			*i = s->data; // Adjust sprite array entry
			// Swap this and the next block
			temp = *s;
			memmove(s, next, next->size);
			s = NextBlock(s);
			*s = temp;

			// Coalesce free blocks
			while (NextBlock(s)->size & S_FREE_MASK) {
				s->size += NextBlock(s)->size & ~S_FREE_MASK;
			}
		} else {
			s = NextBlock(s);
		}
	}
}

static void DeleteEntryFromSpriteCache(void)
{
	int i;
	int best = -1;
	MemBlock* s;
	int cur_lru;

	DEBUG(spritecache, 2) ("DeleteEntryFromSpriteCache, inuse=%d", GetSpriteCacheUsage());

#if defined(WANT_NEW_LRU)
	cur_lru = 0xffff;
	for (i = 0; i != MAX_SPRITES; i++) {
		if (_sprite_ptr[i] != NULL && _sprite_lru_new[i] < cur_lru) {
			cur_lru = _sprite_lru_new[i];
			best = i;
		}
	}
#else
	{
	uint16 cur_lru = 0, cur_lru_cur = 0xffff;
	for (i = 0; i != MAX_SPRITES; i++) {
		if (_sprite_ptr[i] == NULL || _sprite_lru[i] < cur_lru) continue;

		// Found a sprite with a higher LRU value, then remember it.
		if (_sprite_lru[i] != cur_lru) {
			cur_lru = _sprite_lru[i];
			best = i;

		// Else if both sprites were very recently referenced, compare by the cur value instead.
		} else if (cur_lru == 0 && _sprite_lru_cur[i] <= cur_lru_cur) {
			cur_lru_cur = _sprite_lru_cur[i];
			cur_lru = _sprite_lru[i];
			best = i;
		}
	}
	}
#endif

	// Display an error message and die, in case we found no sprite at all.
	// This shouldn't really happen, unless all sprites are locked.
	if (best == -1)
		error("Out of sprite memory");

	// Mark the block as free (the block must be in use)
	s = (MemBlock*)_sprite_ptr[best] - 1;
	assert(!(s->size & S_FREE_MASK));
	s->size |= S_FREE_MASK;
	_sprite_ptr[best] = NULL;

	// And coalesce adjacent free blocks
	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;
			}
		}
	}
}

static void* AllocSprite(size_t mem_req)
{
	mem_req += sizeof(MemBlock);

	/* 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. */
	mem_req = ALIGN(mem_req, sizeof(uint32));

	for (;;) {
		MemBlock* s;

		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;

				/* 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)) {
					// Set size and in use
					s->size = mem_req;

					// Do we need to inject a free block too?
					if (cur_size != mem_req) {
						NextBlock(s)->size = (cur_size - mem_req) | S_FREE_MASK;
					}

					return s->data;
				}
			}
		}

		// Reached sentinel, but no block found yet. Delete some old entry.
		DeleteEntryFromSpriteCache();
	}
}

#if defined(NEW_ROTATION)
#define X15(x) else if (s >= x && s < (x+15)) { s = _rotate_tile_sprite[s - x] + x; }
#define X19(x) else if (s >= x && s < (x+19)) { s = _rotate_tile_sprite[s - x] + x; }
#define MAP(from,to,map) else if (s >= from && s <= to) { s = map[s - from] + from; }


static uint RotateSprite(uint s)
{
	static const byte _rotate_tile_sprite[19] = { 0,2,4,6,8,10,12,14,1,3,5,7,9,11,13,17,18,16,15 };
	static const byte _coast_map[9] = {0, 4, 3, 1, 2, 6, 8, 5, 7};
	static const byte _fence_map[6] = {1, 0, 5, 4, 3, 2};

	if (0);
	X19(752)
	X15(990-1)
	X19(3924)
	X19(3943)
	X19(3962)
	X19(3981)
	X19(4000)
	X19(4023)
	X19(4042)
	MAP(4061,4069,_coast_map)
	X19(4126)
	X19(4145)
	X19(4164)
	X19(4183)
	X19(4202)
	X19(4221)
	X19(4240)
	X19(4259)
	X19(4259)
	X19(4278)
	MAP(4090, 4095, _fence_map)
	MAP(4096, 4101, _fence_map)
	MAP(4102, 4107, _fence_map)
	MAP(4108, 4113, _fence_map)
	MAP(4114, 4119, _fence_map)
	MAP(4120, 4125, _fence_map)
	return s;
}
#endif

const void *GetRawSprite(SpriteID sprite)
{
	void* p;

	assert(sprite < MAX_SPRITES);

#if defined(NEW_ROTATION)
	sprite = RotateSprite(sprite);
#endif

	// Update LRU
#if defined(WANT_NEW_LRU)
	_sprite_lru_new[sprite] = ++_sprite_lru_counter;
#else
	_sprite_lru_cur[sprite] = ++_sprite_lru_counter;
	_sprite_lru[sprite] = 0;
#endif

	p = _sprite_ptr[sprite];
	// Load the sprite, if it is not loaded, yet
	if (p == NULL) p = ReadSprite(sprite);
	return p;
}


void GfxInitSpriteMem(void)
{
	// initialize sprite cache heap
	if (_spritecache_ptr == NULL) _spritecache_ptr = malloc(SPRITE_CACHE_SIZE);

	// A big free block
	_spritecache_ptr->size = (SPRITE_CACHE_SIZE - sizeof(MemBlock)) | S_FREE_MASK;
	// Sentinel block (identified by size == 0)
	NextBlock(_spritecache_ptr)->size = 0;

	memset(_sprite_ptr, 0, sizeof(_sprite_ptr));

	_compact_cache_counter = 0;
}