1 /*
2  * Copyright (C) Volition, Inc. 1999.  All rights reserved.
3  *
4  * All source code herein is the property of Volition, Inc. You may not sell
5  * or otherwise commercially exploit the source or things you created based on the
6  * source.
7  *
8 */
9 #ifndef NDEBUG
10 #define BMPMAN_NDEBUG
11 #endif
12 
13 #define WIN32_LEAN_AND_MEAN
14 #define BMPMAN_INTERNAL
15 
16 #include "anim/animplay.h"
17 #include "anim/packunpack.h"
18 #include "bmpman/bm_internal.h"
19 #include "ddsutils/ddsutils.h"
20 #include "debugconsole/console.h"
21 #include "globalincs/systemvars.h"
22 #include "graphics/2d.h"
23 #include "graphics/matrix.h"
24 #include "io/key.h"
25 #include "io/timer.h"
26 #include "jpgutils/jpgutils.h"
27 #include "network/multiutil.h"
28 #include "parse/parselo.h"
29 #include "pcxutils/pcxutils.h"
30 #include "pngutils/pngutils.h"
31 #include "ship/ship.h"
32 #include "tgautils/tgautils.h"
33 #include "tracing/Monitor.h"
34 #include "tracing/tracing.h"
35 
36 #include <cctype>
37 #include <climits>
38 #include <iomanip>
39 #include <memory>
40 
41 // --------------------------------------------------------------------------------------------------------------------
42 // Private macros.
43 
44 /**
45  * @todo upgrade this to an inline funciton, taking bitmap_entry and const char* as arguments
46  */
47 #define EFF_FILENAME_CHECK { if ( be->type == BM_TYPE_EFF ) strcpy_s( filename, be->info.ani.eff.filename ); else strcpy_s( filename, be->filename ); }
48 // --------------------------------------------------------------------------------------------------------------------
49 // Monitor variables
50 MONITOR(NumBitmapPage)
51 MONITOR(SizeBitmapPage)
52 
53 // --------------------------------------------------------------------------------------------------------------------
54 // Definition of public variables (declared as extern in bmpman.h).
55 int ENVMAP = -1;
56 
57 size_t bm_texture_ram = 0;
58 int Bm_paging = 0;
59 
60 // Extension type lists
61 const BM_TYPE bm_type_list[] = { BM_TYPE_DDS, BM_TYPE_TGA, BM_TYPE_PNG, BM_TYPE_JPG, BM_TYPE_PCX };
62 const char *bm_ext_list[] = { ".dds", ".tga", ".png", ".jpg", ".pcx" };
63 const int BM_NUM_TYPES = sizeof(bm_type_list) / sizeof(bm_type_list[0]);
64 
65 const BM_TYPE bm_ani_type_list[] = { BM_TYPE_EFF, BM_TYPE_ANI, BM_TYPE_PNG };
66 // NOTE: it would be better to have apng files use the .apng extension
67 // However there's lots of assumptions throughout the codebase that file extensions are only
68 // three chars long. If this is ever changed the the .apng extension could be used
69 const char *bm_ani_ext_list[] = { ".eff", ".ani", ".png" };
70 const int BM_ANI_NUM_TYPES = sizeof(bm_ani_type_list) / sizeof(bm_ani_type_list[0]);
71 
72 void(*bm_set_components)(ubyte *pixel, ubyte *r, ubyte *g, ubyte *b, ubyte *a) = NULL;
73 
74 // --------------------------------------------------------------------------------------------------------------------
75 // Definition of public variables (declared as extern in bm_internal.h).
76 SCP_vector<std::array<bitmap_slot, BM_BLOCK_SIZE>> bm_blocks;
77 
78 // --------------------------------------------------------------------------------------------------------------------
79 // Definition of private variables at file scope (static).
80 static bool bm_inited = false;
81 static uint Bm_next_signature = 0x1234;
82 static int Bm_low_mem = 0;
83 
84 SCP_map<int,ubyte*> bm_lookup_cache;
85 
86 /**
87  * How much RAM bmpman can use for textures.
88  *
89  * @details Set to <1 to make it use all it wants.
90  *
91  * @note was initialized to 16*1024*1024 at some point to "use only 16MB for textures"
92  */
93 static int Bm_max_ram = 0;
94 
95 static int Bm_ignore_duplicates = 0;
96 static int Bm_ignore_load_count = 0;
97 
98 // This needs to be declared somewhere and bm_internal.h has no own source file
99 gr_bitmap_info::~gr_bitmap_info() = default;
100 
101 /**
102  * Finds if a bitmap entry contains an animation
103  */
bm_is_anim(bitmap_entry * entry)104 static bool bm_is_anim(bitmap_entry* entry)
105 {
106 	return ((entry->type == BM_TYPE_ANI) ||
107 		(entry->type == BM_TYPE_EFF) ||
108 		(entry->type == BM_TYPE_PNG && entry->info.ani.apng.is_apng));
109 }
110 
bm_get_slot(int handle,bool separate_ani_frames)111 bitmap_slot* bm_get_slot(int handle, bool separate_ani_frames) {
112 	Assertion(handle >= 0, "Invalid handle %d passed to bm_get_slot!", handle);
113 
114 	// The lower 16-bit contain the index in the bitmap block
115 	auto index = handle & 0xFFFF;
116 	// The upper 16-bit contain the number of the used block
117 	auto block_index = handle >> 16;
118 
119 	Assertion(block_index >= 0 && block_index < (int) bm_blocks.size(),
120 			  "Bitmap handle %d has an invalid block number %d!",
121 			  handle,
122 			  block_index);
123 	Assertion(index >= 0 && index < (int) bm_blocks[block_index].size(),
124 			  "Bitmap handle %d has an invalid block index %d!",
125 			  handle,
126 			  index);
127 
128 	auto slot = &bm_blocks[block_index][index];
129 	auto entry = &slot->entry;
130 
131 	if (separate_ani_frames || !bm_is_anim(entry)) {
132 		// Not an animation or we don't want the first frame so we are done now
133 		return slot;
134 	}
135 
136 	// Get the entry of the first frame by calling ourself again but this time we enforce separate ani frames (since we
137 	// already know that this handle is the correct one)
138 	return bm_get_slot(entry->info.ani.first_frame, true);
139 }
140 
141 // --------------------------------------------------------------------------------------------------------------------
142 // Declaration of private functions and templates(declared as static type func(type param);)
143 
bitmap_lookup(int bitmap_num)144 bitmap_lookup::bitmap_lookup(int bitmap_num):
145 	Bitmap_data(NULL)
146 {
147 	if ( !bm_is_valid(bitmap_num) ) return;
148 
149 	Num_channels = 3;
150 
151 	if ( bm_has_alpha_channel(bitmap_num) ) {
152 		Num_channels = 4;
153 	}
154 
155 	bitmap_entry *be = bm_get_entry(bitmap_num);
156 
157 	Width = be->bm.w;
158 	Height = be->bm.h;
159 
160 
161 	auto cache_search = bm_lookup_cache.find(bitmap_num);
162 	if (cache_search == bm_lookup_cache.end()) {
163 		Bitmap_data = (ubyte*)vm_malloc(Width * Height * Num_channels * sizeof(ubyte));
164 
165 		gr_get_bitmap_from_texture((void*)Bitmap_data, bitmap_num);
166 		bm_lookup_cache.insert({bitmap_num, Bitmap_data});
167 	} else {
168 		Bitmap_data = cache_search->second;
169 	}
170 }
171 
~bitmap_lookup()172 bitmap_lookup::~bitmap_lookup()
173 {
174 }
175 
valid()176 bool bitmap_lookup::valid()
177 {
178 	return Bitmap_data != NULL;
179 }
180 
map_texture_address(float address)181 float bitmap_lookup::map_texture_address(float address)
182 {
183 	// assume we're just wrapping
184 	return address - floorf(address);
185 }
186 
get_channel_alpha(float u,float v)187 float bitmap_lookup::get_channel_alpha(float u, float v)
188 {
189 	Assert( Bitmap_data != NULL );
190 
191 	int x = fl2i(map_texture_address(u) * (Width-1));
192 	int y = fl2i(map_texture_address(v) * (Height-1));
193 
194 	return i2fl(Bitmap_data[(y*Width + x)*Num_channels + 3]) / 255.0f;
195 }
196 
clear_bm_lookup_cache()197 void clear_bm_lookup_cache() {
198 	for(auto &iter: bm_lookup_cache) {
199 		free(iter.second);
200 	}
201 	bm_lookup_cache.clear();
202 }
203 
204 /**
205  * Converts the bitmap referenced by bmp to the type specified by flags
206  */
207 static void bm_convert_format(bitmap *bmp, ushort flags);
208 
209 /**
210  * Frees a bitmap's data if it can
211  */
212 static void bm_free_data(bitmap_slot* n, bool release = false);
213 
214 /**
215  * A special version of bm_free_data() that can be safely used in gr_*_texture
216  * to save system memory once textures have been transfered to API memory
217  * it doesn't restore the slot to a pristine state, it only releases the data
218  *
219  * @attention: THIS SHOULD ONLY BE USED FROM bm_unload_fast()!!!
220  */
221 static void bm_free_data_fast(int handle);
222 
223 /**
224  * Given a raw filename and an extension set, try and find the bitmap
225  * that isn't already loaded and may exist somewhere on the disk
226  *
227  * @returns -1 if it could not be found,
228  * @returns index into ext_list[] if it was found as a file, fills img_cfg if available
229  */
230 static int bm_load_sub_slow(const char *real_filename, const int num_ext, const char **ext_list, CFILE **img_cfp = NULL, int dir_type = CF_TYPE_ANY);
231 
232 /**
233  * Given a raw filename, try and find a bitmap that's already loaded
234  *
235  * @returns 0 if it could not be found,
236  * @returns 1 if it already exists, fills in handle
237  */
238 static int bm_load_sub_fast(const char *real_filename, int *handle, int dir_type = CF_TYPE_ANY, bool animated_type = false);
239 
240 /**
241  * @brief Finds a start handle to a block of contiguous bitmap slots
242  *
243  * This will return a bitmap handle which has space for n slots.
244  *
245  * @param n How many slots the bitmap needs
246  * @param start_block At which block the search should start. Do not use. This is only for internal purposes.
247  *
248  * @returns -1 if the block could not be found
249  * @returns the handle of the block ?
250  */
251 static int find_block_of(int n, int start_block = 0);
252 
253 
get_handle(int block,int index)254 static int get_handle(int block, int index) {
255 	Assertion(block >= 0, "Negative block values are not allowed!");
256 	Assertion(index >= 0, "Negative index values are not allowed!");
257 
258 	return (uint32_t) block << 16 | (uint16_t) index;
259 }
260 
allocate_new_block()261 static void allocate_new_block() {
262 	bm_blocks.emplace_back();
263 	auto& new_block = bm_blocks.back();
264 
265 	for (auto& slot : new_block) {
266 		auto& entry = slot.entry;
267 
268 		entry.filename[0] = '\0';
269 		entry.type = BM_TYPE_NONE;
270 		entry.comp_type = BM_TYPE_NONE;
271 		entry.dir_type = CF_TYPE_ANY;
272 		entry.info.user.data = nullptr;
273 		entry.mem_taken = 0;
274 		entry.bm.data = 0;
275 		entry.bm.palette = nullptr;
276 		entry.info.ani.eff.type = BM_TYPE_NONE;
277 		entry.info.ani.eff.filename[0] = '\0';
278 #ifdef BMPMAN_NDEBUG
279 		entry.data_size = 0;
280 		entry.used_count = 0;
281 		entry.used_last_frame = 0;
282 		entry.used_this_frame = 0;
283 #endif
284 		entry.load_count = 0;
285 
286 		gr_bm_init(&slot);
287 
288 		// clears flags, bbp, data, etc
289 		bm_free_data(&slot);
290 	}
291 }
292 
293 // --------------------------------------------------------------------------------------------------------------------
294 // Macro-defined functions
295 
296 DCF(bm_frag, "Shows BmpMan fragmentation") {
297 	if (dc_optional_string_either("help", "--help")) {
298 		dc_printf("Displays a graphic showing the BmpMan fragmentation. Color key:\n");
299 		dc_printf("\tGray  : NONE\n");
300 		dc_printf("\tRed   : PCXn");
301 		dc_printf("\tGreen : USER, TGA, PNG, DDS, other\n");
302 		dc_printf("\tBlue  : ANI, EFF\n\n");
303 
304 		dc_printf("Once done reviewing the graphic, press any key to return to the console\n");
305 		return;
306 	}
307 
308 	gr_clear();
309 
310 	int x = 0, y = 0;
311 	int xs = 2, ys = 2;
312 	int w = 4, h = 4;
313 
314 	for (auto& block : bm_blocks) {
315 		for (size_t i = 0; i < BM_BLOCK_SIZE; ++i) {
316 			switch (block[i].entry.type) {
317 			case BM_TYPE_NONE:
318 				gr_set_color(128, 128, 128);
319 				break;
320 			case BM_TYPE_PCX:
321 				gr_set_color(255, 0, 0);
322 				break;
323 			case BM_TYPE_USER:
324 			case BM_TYPE_TGA:
325 			case BM_TYPE_PNG:
326 			case BM_TYPE_DDS:
327 				gr_set_color(0, 255, 0);
328 				break;
329 			case BM_TYPE_ANI:
330 			case BM_TYPE_EFF:
331 				gr_set_color(0, 0, 255);
332 				break;
333 			default:
334 				gr_set_color(0, 255, 0);
335 				break;
336 			}
337 
338 			gr_rect(x + xs, y + ys, w, h);
339 			x += w + xs + xs;
340 			if (x > 639) {
341 				x = 0;
342 				y += h + ys + ys;
343 			}
344 		}
345 	}
346 
347 	gr_flip();
348 	key_getch();
349 }
350 
351 DCF(bm_used, "Shows BmpMan Slot Usage") {
352 	if (dc_optional_string_either("help", "--help")) {
353 		dc_printf("Displays used bmpman slots usage with a breakdown per filetype\n\n");
354 		return;
355 	}
356 
357 	int none = 0, pcx = 0, user = 0, tga = 0, png = 0; int jpg = 0, dds = 0, ani = 0;
358 	int eff = 0, eff_dds = 0, eff_tga = 0, eff_png = 0, eff_jpg = 0, eff_pcx = 0;
359 	int render_target_dynamic = 0, render_target_static = 0;
360 
361 	for (auto& block : bm_blocks) {
362 		for (size_t i = 0; i < BM_BLOCK_SIZE; ++i) {
363 			switch (block[i].entry.type) {
364 			case BM_TYPE_NONE:
365 				none++;
366 				break;
367 			case BM_TYPE_PCX:
368 				pcx++;
369 				break;
370 			case BM_TYPE_USER:
371 				user++;
372 				break;
373 			case BM_TYPE_TGA:
374 				tga++;
375 				break;
376 			case BM_TYPE_PNG:
377 				// TODO distinguish png(static) from apng
378 				png++;
379 				break;
380 			case BM_TYPE_JPG:
381 				jpg++;
382 				break;
383 			case BM_TYPE_DDS:
384 				dds++;
385 				break;
386 			case BM_TYPE_ANI:
387 				ani++;
388 				break;
389 			case BM_TYPE_EFF:
390 				eff++;
391 				switch (block[i].entry.info.ani.eff.type) {
392 				case BM_TYPE_DDS:
393 					eff_dds++;
394 					break;
395 				case BM_TYPE_TGA:
396 					eff_tga++;
397 					break;
398 				case BM_TYPE_PNG:
399 					eff_png++;
400 					break;
401 				case BM_TYPE_JPG:
402 					eff_jpg++;
403 					break;
404 				case BM_TYPE_PCX:
405 					eff_pcx++;
406 					break;
407 				default:
408 					Warning(LOCATION, "Unhandled EFF image type (%i), get a coder!", block[i].entry.info.ani.eff.type);
409 					break;
410 				}
411 				break;
412 			case BM_TYPE_RENDER_TARGET_STATIC:
413 				render_target_static++;
414 				break;
415 			case BM_TYPE_RENDER_TARGET_DYNAMIC:
416 				render_target_dynamic++;
417 				break;
418 			default:
419 				Warning(LOCATION, "Unhandled image type (%i), get a coder!", block[i].entry.type);
420 				break;
421 			}
422 		}
423 	}
424 
425 	SCP_stringstream text;
426 	text << "BmpMan Used Slots\n";
427 	text << "  " << std::dec << std::setw(4) << std::setfill('0') << pcx  << ", PCX\n";
428 	text << "  " << std::dec << std::setw(4) << std::setfill('0') << user << ", User\n";
429 	text << "  " << std::dec << std::setw(4) << std::setfill('0') << tga  << ", TGA\n";
430 	text << "  " << std::dec << std::setw(4) << std::setfill('0') << png  << ", PNG\n";
431 	text << "  " << std::dec << std::setw(4) << std::setfill('0') << jpg  << ", JPG\n";
432 	text << "  " << std::dec << std::setw(4) << std::setfill('0') << dds  << ", DDS\n";
433 	text << "  " << std::dec << std::setw(4) << std::setfill('0') << ani  << ", ANI\n";
434 	text << "  " << std::dec << std::setw(4) << std::setfill('0') << eff  << ", EFF\n";
435 	text << "  " << std::dec << std::setw(4) << std::setfill('0') << eff_dds  << ", EFF/DDS\n";
436 	text << "  " << std::dec << std::setw(4) << std::setfill('0') << eff_tga  << ", EFF/TGA\n";
437 	text << "  " << std::dec << std::setw(4) << std::setfill('0') << eff_png  << ", EFF/PNG\n";
438 	text << "  " << std::dec << std::setw(4) << std::setfill('0') << eff_jpg  << ", EFF/JPG\n";
439 	text << "  " << std::dec << std::setw(4) << std::setfill('0') << eff_pcx  << ", EFF/PCX\n";
440 	text << "  " << std::dec << std::setw(4) << std::setfill('0') << render_target_static  << ", Render/Static\n";
441 	text << "  " << std::dec << std::setw(4) << std::setfill('0') << render_target_dynamic  << ", Render/Dynamic\n";
442 	text << "  " << std::dec << std::setw(4) << std::setfill('0') << bmpman_count_bitmaps() << "/" << bmpman_count_available_slots()  << ", Total\n";
443 	text << "\n";
444 
445 	// TODO consider converting 1's to monospace to make debug console output prettier
446 	mprintf(("%s", text.str().c_str())); // log for ease for copying data
447 	dc_printf("%s", text.str().c_str()); // instant gratification
448 }
449 
450 
451 DCF(bmpman, "Shows/changes bitmap caching parameters and usage") {
452 	if (dc_optional_string_either("help", "--help")) {
453 		dc_printf("Usage: BmpMan [arg]\nWhere arg can be any of the following:\n");
454 		dc_printf("\tflush    Unloads all bitmaps.\n");
455 		dc_printf("\tram [x]  Sets max mem usage to x MB. (Set to 0 to have no limit.)\n");
456 		dc_printf("\t?        Displays status of Bitmap manager.\n");
457 		return;
458 	}
459 
460 	if (dc_optional_string_either("status", "--status") || dc_optional_string_either("?", "--?")) {
461 		dc_printf("Total RAM usage: " SIZE_T_ARG " bytes\n", bm_texture_ram);
462 
463 		if (Bm_max_ram > 1024 * 1024) {
464 			dc_printf("\tMax RAM allowed: %.1f MB\n", i2fl(Bm_max_ram) / (1024.0f*1024.0f));
465 		} else if (Bm_max_ram > 1024) {
466 			dc_printf("\tMax RAM allowed: %.1f KB\n", i2fl(Bm_max_ram) / (1024.0f));
467 		} else if (Bm_max_ram > 0) {
468 			dc_printf("\tMax RAM allowed: %d bytes\n", Bm_max_ram);
469 		} else {
470 			dc_printf("\tNo RAM limit\n");
471 		}
472 		return;
473 	}
474 
475 
476 	if (dc_optional_string("flush")) {
477 		dc_printf("Total RAM usage before flush: " SIZE_T_ARG " bytes\n", bm_texture_ram);
478 		for (auto& block : bm_blocks) {
479 			for (size_t i = 0; i < BM_BLOCK_SIZE; ++i) {
480 				if (block[i].entry.type != BM_TYPE_NONE) {
481 					bm_free_data(&block[i]);
482 				}
483 			}
484 		}
485 		dc_printf("Total RAM after flush: " SIZE_T_ARG " bytes\n", bm_texture_ram);
486 	} else if (dc_optional_string("ram")) {
487 		dc_stuff_int(&Bm_max_ram);
488 
489 		if (Bm_max_ram > 0) {
490 			dc_printf("BmpMan limited to %i, MB's\n", Bm_max_ram);
491 			Bm_max_ram *= 1024 * 1024;
492 		} else if (Bm_max_ram == 0) {
493 			dc_printf("!!BmpMan memory is unlimited!!\n");
494 		} else {
495 			dc_printf("Illegal value. Must be non-negative.");
496 		}
497 	} else {
498 		dc_printf("<BmpMan> No argument given\n");
499 	}
500 }
501 
502 DCF(bmpslots, "Writes bitmap slot info to fs2_open.log") {
503 	if (dc_optional_string_either("help", "--help")) {
504 		dc_printf("Usage: bmpslots\n");
505 		dc_printf("\tWrites bitmap slot info to fs2_open.log\n");
506 		return;
507 	}
508 	bm_print_bitmaps();
509 }
510 
511 // --------------------------------------------------------------------------------------------------------------------
512 // Definition of all functions, in alphabetical order
bm_close()513 void bm_close() {
514 	if (bm_inited) {
515 		for (auto& block : bm_blocks) {
516 			for (auto& slot : block) {
517 				bm_free_data(&slot);            // clears flags, bbp, data, etc
518 
519 				if (slot.gr_info != nullptr) {
520 					// free graphics data
521 					delete slot.gr_info;
522 					slot.gr_info = nullptr;
523 				}
524 			}
525 		}
526 		bm_blocks.clear();
527 		bm_inited = false;
528 	}
529 }
530 
bm_create(int bpp,int w,int h,void * data,int flags)531 int bm_create(int bpp, int w, int h, void *data, int flags) {
532 	if (bpp == 8) {
533 		Assert(flags & BMP_AABITMAP);
534 	} else {
535 		Assert((bpp == 16) || (bpp == 24) || (bpp == 32));
536 	}
537 
538 	Assertion(bm_inited, "bmpman must be initialized before this function can be called!");
539 
540 	int n = find_block_of(1);
541 
542 	// Out of bitmap slots
543 	if (n == -1)
544 		return -1;
545 
546 	// make sure that we have valid data
547 	if (data == NULL) {
548 		Int3();
549 		return -1;
550 	}
551 
552 	auto entry = bm_get_entry(n);
553 
554 	memset(entry, 0, sizeof(bitmap_entry));
555 
556 	sprintf_safe(entry->filename, "TMP%dx%d+%d", w, h, bpp);
557 	entry->type = BM_TYPE_USER;
558 	entry->comp_type = BM_TYPE_NONE;
559 
560 	entry->bm.w = (short)w;
561 	entry->bm.h = (short)h;
562 	entry->bm.rowsize = (short)w;
563 	entry->bm.bpp = (ubyte)bpp;
564 	entry->bm.true_bpp = (ubyte)bpp;
565 	entry->bm.flags = (ubyte)flags;
566 	entry->bm.data = 0;
567 	entry->bm.palette = nullptr;
568 
569 	entry->info.user.bpp = (ubyte)bpp;
570 	entry->info.user.data = data;
571 	entry->info.user.flags = (ubyte)flags;
572 
573 	entry->signature = Bm_next_signature++;
574 
575 	entry->handle = n;
576 	entry->mem_taken = (w * h * (bpp >> 3));
577 
578 	entry->load_count++;
579 
580 	bm_update_memory_used(n, (int)entry->mem_taken);
581 
582 	gr_bm_create(bm_get_slot(n));
583 
584 	return n;
585 }
586 
bm_convert_format(bitmap * bmp,ushort flags)587 void bm_convert_format(bitmap *bmp, ushort flags) {
588 	int idx;
589 
590 	// no transparency for 24 bpp images
591 	if (!(flags & BMP_AABITMAP) && (bmp->bpp == 24))
592 		return;
593 
594 	if (Is_standalone) {
595 		Assert(bmp->bpp == 8);
596 		return;
597 	} else {
598 		if (flags & BMP_AABITMAP)
599 			Assert(bmp->bpp == 8);
600 		else
601 			Assert((bmp->bpp == 16) || (bmp->bpp == 32));
602 	}
603 
604 	// maybe swizzle to be an xparent texture
605 	if (!(bmp->flags & BMP_TEX_XPARENT) && (flags & BMP_TEX_XPARENT)) {
606 		for (idx = 0; idx<bmp->w*bmp->h; idx++) {
607 			// if the pixel is transparent
608 			if (((ushort*)bmp->data)[idx] == Gr_t_green.mask) {
609 				((ushort*)bmp->data)[idx] = 0;
610 			}
611 		}
612 
613 		bmp->flags |= BMP_TEX_XPARENT;
614 	}
615 }
616 
bm_free_data(bitmap_slot * bs,bool release)617 void bm_free_data(bitmap_slot* bs, bool release)
618 {
619 	bitmap *bmp;
620 
621 	auto be = &bs->entry;
622 	bmp = &be->bm;
623 
624 	gr_bm_free_data(bs, release);
625 
626 	// If there isn't a bitmap in this structure, don't
627 	// do anything but clear out the bitmap info
628 	if (be->type==BM_TYPE_NONE)
629 		goto SkipFree;
630 
631 	// Don't free up memory for user defined bitmaps, since
632 	// BmpMan isn't the one in charge of allocating/deallocing them.
633 	if (be->type==BM_TYPE_USER) {
634 #ifdef BMPMAN_NDEBUG
635 		if ( be->data_size != 0 )
636 			bm_texture_ram -= be->data_size;
637 #endif
638 		goto SkipFree;
639 	}
640 
641 	// If this bitmap doesn't have any data to free, skip
642 	// the freeing it part of this.
643 	if (bmp->data == 0) {
644 #ifdef BMPMAN_NDEBUG
645 		if ( be->data_size != 0 )
646 			bm_texture_ram -= be->data_size;
647 #endif
648 		goto SkipFree;
649 	}
650 
651 	// Free up the data now!
652 #ifdef BMPMAN_NDEBUG
653 	bm_texture_ram -= be->data_size;
654 #endif
655 	vm_free((void *)bmp->data);
656 
657 	// reset the load_count to at least 1, don't do this in SkipFree though
658 	// since the real count ends up wrong
659 	be->load_count = 1;
660 
661 SkipFree:
662 
663 	// Clear out & reset the bitmap data structure
664 	bmp->flags = 0;
665 	bmp->bpp = 0;
666 	bmp->data = 0;
667 	bmp->palette = NULL;
668 #ifdef BMPMAN_NDEBUG
669 	be->data_size = 0;
670 #endif
671 	be->signature = Bm_next_signature++;
672 }
673 
bm_free_data_fast(int handle)674 void bm_free_data_fast(int handle)
675 {
676 	bitmap_entry *be;
677 	bitmap *bmp;
678 
679 	be = bm_get_entry(handle);
680 	bmp = &be->bm;
681 
682 	// If there isn't a bitmap in this structure, don't
683 	// do anything but clear out the bitmap info
684 	if (be->type == BM_TYPE_NONE)
685 		return;
686 
687 	// Don't free up memory for user defined bitmaps, since
688 	// BmpMan isn't the one in charge of allocating/deallocing them.
689 	if (be->type == BM_TYPE_USER) {
690 #ifdef BMPMAN_NDEBUG
691 		if ( be->data_size != 0 )
692 			bm_texture_ram -= be->data_size;
693 #endif
694 		return;
695 	}
696 
697 	// If this bitmap doesn't have any data to free, skip
698 	// the freeing it part of this.
699 	if (bmp->data == 0) {
700 #ifdef BMPMAN_NDEBUG
701 		if ( be->data_size != 0 ) {
702 			bm_texture_ram -= be->data_size;
703 			be->data_size = 0;
704 		}
705 #endif
706 		return;
707 	}
708 
709 	// Free up the data now!
710 #ifdef BMPMAN_NDEBUG
711 	bm_texture_ram -= be->data_size;
712 	be->data_size = 0;
713 #endif
714 	vm_free((void *)bmp->data);
715 	bmp->data = 0;
716 }
717 
bm_get_anim_frame(const int frame1_handle,float elapsed_time,const float divisor,const bool loop)718 int bm_get_anim_frame(const int frame1_handle, float elapsed_time, const float divisor, const bool loop)
719 {
720 	bitmap_entry *be = bm_get_entry(frame1_handle);
721 
722 	if (be->info.ani.num_frames <= 1) {
723 		// this is a still image
724 		return 0;
725 	}
726 	int last_frame = be->info.ani.num_frames - 1;
727 	bitmap_entry *last_framep = bm_get_entry(frame1_handle + last_frame);
728 
729 	if (elapsed_time < 0.0f) {
730 		elapsed_time = 0.0f;
731 	}
732 
733 	int frame = 0;
734 	// variable frame delay animations
735 	if (be->info.ani.apng.is_apng == true) {
736 		if (divisor > 0.0f) {
737 			// scale to get the real elapsed time
738 			elapsed_time = elapsed_time / divisor * last_framep->info.ani.apng.frame_delay;
739 		}
740 
741 		if (loop == true) {
742 			elapsed_time = fmod(elapsed_time, last_framep->info.ani.apng.frame_delay);
743 		}
744 
745 		int i = frame1_handle;
746 		for ( ; i < (frame1_handle + be->info.ani.num_frames); ++i) {
747 			// see bm_lock_apng for precalculated incremental delay for each frame
748 			if (elapsed_time <= bm_get_entry(i)->info.ani.apng.frame_delay) {
749 				break;
750 			}
751 		}
752 		frame = i - frame1_handle;
753 	}
754 	// fixed frame delay animations; simpler
755 	else {
756 		if (divisor > 0.0f) {
757 			// scale to get the real elapsed time
758 			frame = fl2i(elapsed_time / divisor * be->info.ani.num_frames);
759 		}
760 		else {
761 			frame = fl2i(elapsed_time * i2fl(be->info.ani.fps));
762 		}
763 
764 		if (loop == true) {
765 			frame %= be->info.ani.num_frames;
766 		}
767 	}
768 	// note; this also makes non-looping anims hold on their last frame
769 	CLAMP(frame, 0, last_frame);
770 
771 	return frame;
772 }
773 
bm_get_components(ubyte * pixel,ubyte * r,ubyte * g,ubyte * b,ubyte * a)774 void bm_get_components(ubyte *pixel, ubyte *r, ubyte *g, ubyte *b, ubyte *a) {
775 	int bit_32 = 0;
776 
777 	if ((gr_screen.bits_per_pixel == 32) && (Gr_current_red == &Gr_red)) {
778 		bit_32 = 1;
779 	}
780 
781 	if (r != NULL) {
782 		if (bit_32) {
783 			*r = ubyte(((*((unsigned int*)pixel) & Gr_current_red->mask) >> Gr_current_red->shift)*Gr_current_red->scale);
784 		} else {
785 			*r = ubyte(((((unsigned short*)pixel)[0] & Gr_current_red->mask) >> Gr_current_red->shift)*Gr_current_red->scale);
786 		}
787 	}
788 
789 	if (g != NULL) {
790 		if (bit_32) {
791 			*g = ubyte(((*((unsigned int*)pixel) & Gr_current_green->mask) >> Gr_current_green->shift)*Gr_current_green->scale);
792 		} else {
793 			*g = ubyte(((((unsigned short*)pixel)[0] & Gr_current_green->mask) >> Gr_current_green->shift)*Gr_current_green->scale);
794 		}
795 	}
796 
797 	if (b != NULL) {
798 		if (bit_32) {
799 			*b = ubyte(((*((unsigned int*)pixel) & Gr_current_blue->mask) >> Gr_current_blue->shift)*Gr_current_blue->scale);
800 		} else {
801 			*b = ubyte(((((unsigned short*)pixel)[0] & Gr_current_blue->mask) >> Gr_current_blue->shift)*Gr_current_blue->scale);
802 		}
803 	}
804 
805 	// get the alpha value
806 	if (a != NULL) {
807 		*a = 1;
808 
809 		Assert(!bit_32);
810 		if (!(((unsigned short*)pixel)[0] & 0x8000)) {
811 			*a = 0;
812 		}
813 	}
814 }
815 
bm_get_filename(int handle)816 const char *bm_get_filename(int handle) {
817 	auto entry = bm_get_entry(handle);
818 	return entry->filename;
819 }
820 
bm_get_filename(int bitmapnum,char * filename)821 void bm_get_filename(int bitmapnum, char *filename) {
822 	if (!bm_is_valid(bitmapnum)) {
823 		strcpy(filename, "");
824 		return;
825 	}
826 
827 	// return filename
828 	strcpy(filename, bm_get_entry(bitmapnum)->filename);
829 }
830 
bm_get_frame_usage(int * ntotal,int * nnew)831 void bm_get_frame_usage(int *ntotal, int *nnew) {
832 #ifdef BMPMAN_NDEBUG
833 	*ntotal = 0;
834 	*nnew = 0;
835 
836 	for (auto& block : bm_blocks) {
837 		for (auto& slot : block) {
838 			auto& entry = slot.entry;
839 			if ((entry.type != BM_TYPE_NONE) && (entry.used_this_frame)) {
840 				if (!entry.used_last_frame) {
841 					*nnew += (int)entry.mem_taken;
842 				}
843 				*ntotal += (int)entry.mem_taken;
844 			}
845 			entry.used_last_frame = entry.used_this_frame;
846 			entry.used_this_frame = 0;
847 		}
848 	}
849 #endif
850 }
851 
bm_get_info(int handle,int * w,int * h,ushort * flags,int * nframes,int * fps)852 int bm_get_info(int handle, int *w, int * h, ushort* flags, int *nframes, int *fps) {
853 	bitmap * bmp;
854 
855 	if (!bm_inited) return -1;
856 
857 	auto entry = bm_get_entry(handle);
858 
859 	Assertion(entry->handle == handle, "Invalid bitmap handle %d passed to bm_get_info().\nThis might be due to an invalid animation somewhere else.\n", handle);		// INVALID BITMAP HANDLE!
860 
861 	if ((entry->type == BM_TYPE_NONE) || (entry->handle != handle)) {
862 		if (w) *w = 0;
863 		if (h) *h = 0;
864 		if (flags) *flags = 0;
865 		if (nframes) *nframes = 0;
866 		if (fps) *fps = 0;
867 		return -1;
868 	}
869 
870 	bmp = &(entry->bm);
871 
872 	if (w) *w = bmp->w;
873 	if (h) *h = bmp->h;
874 	if (flags) *flags = bmp->flags;
875 
876 	if (bm_is_anim(entry)) {
877 		if (nframes) {
878 			*nframes = entry->info.ani.num_frames;
879 		}
880 		if (fps) {
881 			*fps = entry->info.ani.fps;
882 		}
883 
884 		return bm_get_entry(entry->info.ani.first_frame)->handle;
885 	} else {
886 		if (nframes) {
887 			*nframes = 1;
888 		}
889 		if (fps) {
890 			*fps = 0;
891 		}
892 
893 		return handle;
894 	}
895 }
896 
bm_get_num_mipmaps(int num)897 int bm_get_num_mipmaps(int num) {
898 	auto entry = bm_get_entry(num);
899 
900 	if (entry->num_mipmaps == 0)
901 		return 1;
902 
903 	return entry->num_mipmaps;
904 }
905 
bm_get_palette(int handle,ubyte * pal,char * name)906 void bm_get_palette(int handle, ubyte *pal, char *name) {
907 	int w, h;
908 
909 	const char* filename = bm_get_entry(handle)->filename;
910 
911 	if (name) {
912 		strcpy(name, filename);
913 	}
914 
915 	int pcx_error = pcx_read_header(filename, NULL, &w, &h, NULL, pal);
916 	if (pcx_error != PCX_ERROR_NONE) {
917 		// Error(LOCATION, "Couldn't open '%s'\n", filename );
918 	}
919 }
920 
bm_get_tcache_type(int num)921 int bm_get_tcache_type(int num) {
922 	if (bm_is_compressed(num))
923 		return TCACHE_TYPE_COMPRESSED;
924 
925 	return TCACHE_TYPE_NORMAL;
926 }
927 
bm_get_type(int handle)928 BM_TYPE bm_get_type(int handle) {
929 	Assertion(bm_inited, "bmpman must be initialized before this function can be called!");
930 
931 	return bm_get_entry(handle)->type;
932 }
933 
bm_has_alpha_channel(int handle)934 bool bm_has_alpha_channel(int handle) {
935 	auto entry = bm_get_entry(handle);
936 
937 	// assume that PCX never has a real alpha channel (it may be 32-bit, but without any alpha)
938 	if (entry->type == BM_TYPE_PCX)
939 		return 0;
940 
941 	return (entry->bm.true_bpp == 32);
942 }
943 
bm_init()944 void bm_init() {
945 	Assertion(!bm_inited, "bmpman cannot be initialized more than once!");
946 
947 	// Allocate one block by default
948 	allocate_new_block();
949 
950 	bm_inited = true;
951 }
952 
bm_is_compressed(int num)953 int bm_is_compressed(int num) {
954 	auto entry = bm_get_entry(num);
955 	BM_TYPE type = BM_TYPE_NONE;
956 
957 	//duh
958 	if (!Use_compressed_textures)
959 		return 0;
960 
961 	type = entry->comp_type;
962 
963 	switch (type) {
964 	case BM_TYPE_NONE:
965 	case BM_TYPE_DDS:
966 		return 0;
967 
968 	case BM_TYPE_DXT1:
969 		return DDS_DXT1;
970 
971 	case BM_TYPE_DXT3:
972 		return DDS_DXT3;
973 
974 	case BM_TYPE_DXT5:
975 		return DDS_DXT5;
976 
977 	case BM_TYPE_BC7:
978 		return DDS_BC7;
979 
980 	case BM_TYPE_CUBEMAP_DXT1:
981 		return DDS_CUBEMAP_DXT1;
982 
983 	case BM_TYPE_CUBEMAP_DXT3:
984 		return DDS_CUBEMAP_DXT3;
985 
986 	case BM_TYPE_CUBEMAP_DXT5:
987 		return DDS_CUBEMAP_DXT5;
988 
989 	default:
990 		return 0;
991 	}
992 }
993 
bm_is_render_target(int bitmap_id)994 int bm_is_render_target(int bitmap_id) {
995 	auto entry = bm_get_entry(bitmap_id);
996 
997 	if (!((entry->type == BM_TYPE_RENDER_TARGET_STATIC) || (entry->type == BM_TYPE_RENDER_TARGET_DYNAMIC))) {
998 		return 0;
999 	}
1000 
1001 	return entry->type;
1002 }
1003 
bm_is_valid(int handle)1004 int bm_is_valid(int handle) {
1005 	// Ensure that certain known false or out of range handles are quickly returned as invalid,
1006 	// prior to utilising the handle in a way which leads to memory access outside bm_bitmaps[]
1007 	if (!bm_inited) return 0;
1008 	if (handle < 0) return 0;
1009 
1010 	auto entry = bm_get_entry(handle);
1011 
1012 	return entry->handle == handle;
1013 }
1014 
1015 
1016 // Load an image and validate it while retrieving information for later use
1017 // Input:	type		= current BM_TYPE_*
1018 //			n			= location in bm_bitmaps[]
1019 //			filename	= name of the current file
1020 //			img_cfp		= already open CFILE handle, if available
1021 //
1022 // Output:	w			= bmp width
1023 //			h			= bmp height
1024 //			bpp			= bmp bits per pixel
1025 //			c_type		= output for an updated BM_TYPE_*
1026 //			mm_lvl		= number of mipmap levels for the image
1027 //			size		= size of the data contained in the image
bm_load_info(BM_TYPE type,const char * filename,CFILE * img_cfp,int * w,int * h,int * bpp,BM_TYPE * c_type,int * mm_lvl,size_t * size)1028 static int bm_load_info(BM_TYPE type, const char *filename, CFILE *img_cfp, int *w, int *h, int *bpp, BM_TYPE *c_type, int *mm_lvl, size_t *size)
1029 {
1030 	int dds_ct;
1031 
1032 	if (type == BM_TYPE_DDS) {
1033 		int dds_error = dds_read_header(filename, img_cfp, w, h, bpp, &dds_ct, mm_lvl, size);
1034 
1035 		if (dds_error != DDS_ERROR_NONE) {
1036 			mprintf(("DDS ERROR: Couldn't open '%s' -- %s\n", filename, dds_error_string(dds_error)));
1037 			return -1;
1038 		}
1039 
1040 		switch (dds_ct) {
1041 		case DDS_DXT1:
1042 			*c_type = BM_TYPE_DXT1;
1043 			break;
1044 
1045 		case DDS_DXT3:
1046 			*c_type = BM_TYPE_DXT3;
1047 			break;
1048 
1049 		case DDS_DXT5:
1050 			*c_type = BM_TYPE_DXT5;
1051 			break;
1052 
1053 		case DDS_BC7:
1054 			*c_type = BM_TYPE_BC7;
1055 			break;
1056 
1057 		case DDS_UNCOMPRESSED:
1058 			*c_type = BM_TYPE_DDS;
1059 			break;
1060 
1061 		case DDS_CUBEMAP_DXT1:
1062 			*c_type = BM_TYPE_CUBEMAP_DXT1;
1063 			break;
1064 
1065 		case DDS_CUBEMAP_DXT3:
1066 			*c_type = BM_TYPE_CUBEMAP_DXT3;
1067 			break;
1068 
1069 		case DDS_CUBEMAP_DXT5:
1070 			*c_type = BM_TYPE_CUBEMAP_DXT5;
1071 			break;
1072 
1073 		case DDS_CUBEMAP_UNCOMPRESSED:
1074 			*c_type = BM_TYPE_CUBEMAP_DDS;
1075 			break;
1076 
1077 		default:
1078 			Error(LOCATION, "Bad DDS file compression! Not using DXT1,3,5: %s", filename);
1079 			return -1;
1080 		}
1081 	}
1082 	// if its a tga file
1083 	else if (type == BM_TYPE_TGA) {
1084 		int tga_error = targa_read_header(filename, img_cfp, w, h, bpp, NULL);
1085 		if (tga_error != TARGA_ERROR_NONE) {
1086 			mprintf(("tga: Couldn't open '%s'\n", filename));
1087 			return -1;
1088 		}
1089 	}
1090 	// if its a png file
1091 	else if (type == BM_TYPE_PNG) {
1092 		int png_error = png_read_header(filename, img_cfp, w, h, bpp, NULL);
1093 		if (png_error != PNG_ERROR_NONE) {
1094 			mprintf(("png: Couldn't open '%s'\n", filename));
1095 			return -1;
1096 		}
1097 	}
1098 	// if its a jpg file
1099 	else if (type == BM_TYPE_JPG) {
1100 		int jpg_error = jpeg_read_header(filename, img_cfp, w, h, bpp, NULL);
1101 		if (jpg_error != JPEG_ERROR_NONE) {
1102 			mprintf(("jpg: Couldn't open '%s'\n", filename));
1103 			return -1;
1104 		}
1105 	}
1106 	// if its a pcx file
1107 	else if (type == BM_TYPE_PCX) {
1108 		int pcx_error = pcx_read_header(filename, img_cfp, w, h, bpp, NULL);
1109 		if (pcx_error != PCX_ERROR_NONE) {
1110 			mprintf(("pcx: Couldn't open '%s'\n", filename));
1111 			return -1;
1112 		}
1113 	}
1114 	else {
1115 		UNREACHABLE("Unknown file type specified! This is probably a coding error.");
1116 
1117 		return -1;
1118 	}
1119 
1120 	return 0;
1121 }
1122 
bm_load(const char * real_filename)1123 int bm_load(const char *real_filename) {
1124 	int free_slot = -1;
1125 	int w, h, bpp = 8;
1126 	int rc = 0;
1127 	size_t bm_size = 0;
1128 	int mm_lvl = 0;
1129 	char filename[MAX_FILENAME_LEN];
1130 	BM_TYPE type = BM_TYPE_NONE;
1131 	BM_TYPE c_type = BM_TYPE_NONE;
1132 	CFILE *img_cfp = NULL;
1133 	int handle = -1;
1134 
1135 	Assertion(bm_inited, "bmpman must be initialized before this function can be called!");
1136 
1137 	// if no file was passed then get out now
1138 	if ((real_filename == NULL) || (strlen(real_filename) <= 0))
1139 		return -1;
1140 
1141 	// make sure no one passed an extension
1142 	memset(filename, 0, MAX_FILENAME_LEN);
1143 	strncpy(filename, real_filename, MAX_FILENAME_LEN - 1);
1144 	char *p = strrchr(filename, '.');
1145 	if (p) {
1146 		mprintf(("Someone passed an extension to bm_load for file '%s'\n", real_filename));
1147 		*p = 0;
1148 	}
1149 
1150 	// If we are standalone server keep replacing the 'right_bracket' (right side help bracket) as the filename
1151 	// should keep the game happy while loading only single pcx file which the needs to be present in any case
1152 	if (Is_standalone) {
1153 		char standalone_filename[MAX_FILENAME_LEN] = "right_bracket";
1154 		strcpy_s(filename, standalone_filename);
1155 	}
1156 
1157 	// safety catch for strcat...
1158 	// MAX_FILENAME_LEN-5 == '.' plus 3 letter ext plus NULL terminator
1159 	if (strlen(filename) > MAX_FILENAME_LEN - 5) {
1160 		Warning(LOCATION, "Passed filename, '%s', is too long to support an extension!!\n\nMaximum length, minus the extension, is %i characters.\n", filename, MAX_FILENAME_LEN - 5);
1161 		return -1;
1162 	}
1163 
1164 	// Lets find out what type it is
1165 	{
1166 		// see if it's already loaded (checks for any type with filename)
1167 		if (bm_load_sub_fast(filename, &handle))
1168 			return handle;
1169 
1170 		// if we are still here then we need to fall back to a file-based search
1171 		int rval = bm_load_sub_slow(filename, BM_NUM_TYPES, bm_ext_list, &img_cfp);
1172 
1173 		if (rval < 0)
1174 			return -1;
1175 
1176 		strcat_s(filename, bm_ext_list[rval]);
1177 		type = bm_type_list[rval];
1178 	}
1179 
1180 	Assert(type != BM_TYPE_NONE);
1181 
1182 	// Find an open slot
1183 	free_slot = find_block_of(1);
1184 
1185 	if (free_slot < 0) {
1186 		Assertion(free_slot < 0, "Could not find free BMPMAN slot for bitmap: %s", real_filename);
1187 		if (img_cfp != nullptr)
1188 			cfclose(img_cfp);
1189 		return -1;
1190 	}
1191 
1192 	rc = bm_load_info(type, filename, img_cfp, &w, &h, &bpp, &c_type, &mm_lvl, &bm_size);
1193 
1194 	if (rc != 0) {
1195 		if (img_cfp != nullptr)
1196 			cfclose(img_cfp);
1197 		return -1;
1198 	}
1199 
1200 	if ((bm_size <= 0) && (w) && (h) && (bpp))
1201 		bm_size = (w * h * (bpp >> 3));
1202 
1203 
1204 	handle = free_slot;
1205 
1206 	auto entry = bm_get_entry(handle);
1207 
1208 	// ensure fields are cleared out from previous bitmap
1209 	memset(entry, 0, sizeof(bitmap_entry));
1210 
1211 	// Mark the slot as filled, because cf_read might load a new bitmap
1212 	// into this slot.
1213 	strcpy_s(entry->filename, filename);
1214 	entry->type = type;
1215 	entry->comp_type = c_type;
1216 	entry->signature = Bm_next_signature++;
1217 	entry->bm.w = (short)w;
1218 	entry->bm.rowsize = (short)w;
1219 	entry->bm.h = (short)h;
1220 	entry->bm.bpp = 0;
1221 	entry->bm.true_bpp = (ubyte)bpp;
1222 	entry->bm.flags = 0;
1223 	entry->bm.data = 0;
1224 	entry->bm.palette = nullptr;
1225 	entry->num_mipmaps = mm_lvl;
1226 	entry->mem_taken = (size_t)bm_size;
1227 	entry->dir_type = CF_TYPE_ANY;
1228 	entry->handle = handle;
1229 
1230 	entry->load_count++;
1231 
1232 	if (img_cfp != nullptr)
1233 		cfclose(img_cfp);
1234 
1235 	return handle;
1236 }
1237 
bm_load(const SCP_string & filename)1238 int bm_load(const SCP_string& filename) {
1239 	return bm_load(filename.c_str());
1240 }
1241 
bm_load_and_parse_eff(const char * filename,int dir_type,int * nframes,int * nfps,int * key,BM_TYPE * type)1242 bool bm_load_and_parse_eff(const char *filename, int dir_type, int *nframes, int *nfps, int *key, BM_TYPE *type) {
1243 	int frames = 0, fps = 30, keyframe = 0;
1244 	char ext[8];
1245 	BM_TYPE c_type = BM_TYPE_NONE;
1246 	char file_text[1024];
1247 	char file_text_raw[1024];
1248 
1249 	memset(ext, 0, sizeof(ext));
1250 	memset(file_text, 0, sizeof(file_text));
1251 	memset(file_text_raw, 0, sizeof(file_text_raw));
1252 
1253 	// pause anything that may happen to be parsing right now
1254 	pause_parse();
1255 
1256 	try
1257 	{
1258 		// now start parsing the EFF
1259 		read_file_text(filename, dir_type, file_text, file_text_raw);
1260 		reset_parse(file_text);
1261 
1262 		required_string("$Type:");
1263 		stuff_string(ext, F_NAME, sizeof(ext));
1264 
1265 		required_string("$Frames:");
1266 		stuff_int(&frames);
1267 
1268 		if (optional_string("$FPS:"))
1269 			stuff_int(&fps);
1270 
1271 		if (optional_string("$Keyframe:"))
1272 			stuff_int(&keyframe);
1273 	}
1274 	catch (const parse::ParseException& e)
1275 	{
1276 		mprintf(("BMPMAN: Unable to parse '%s'!  Error message = %s.\n", filename, e.what()));
1277 		unpause_parse();
1278 		return false;
1279 	}
1280 
1281 	// done with EFF so unpause parsing so whatever can continue
1282 	unpause_parse();
1283 
1284 	if (!stricmp(NOX("dds"), ext)) {
1285 		c_type = BM_TYPE_DDS;
1286 	} else if (!stricmp(NOX("tga"), ext)) {
1287 		c_type = BM_TYPE_TGA;
1288 	} else if (!stricmp(NOX("png"), ext)) {
1289 		c_type = BM_TYPE_PNG;
1290 	} else if (!stricmp(NOX("jpg"), ext)) {
1291 		c_type = BM_TYPE_JPG;
1292 	} else if (!stricmp(NOX("pcx"), ext)) {
1293 		c_type = BM_TYPE_PCX;
1294 	} else {
1295 		mprintf(("BMPMAN: Unknown file type in EFF parse!\n"));
1296 		return false;
1297 	}
1298 
1299 	// did we do anything?
1300 	if (c_type == BM_TYPE_NONE || frames == 0) {
1301 		mprintf(("BMPMAN: EFF parse ERROR!\n"));
1302 		return false;
1303 	}
1304 
1305 	if (type)
1306 		*type = c_type;
1307 
1308 	if (nframes)
1309 		*nframes = frames;
1310 
1311 	if (nfps)
1312 		*nfps = fps;
1313 
1314 	if (key)
1315 		*key = keyframe;
1316 
1317 	return true;
1318 }
1319 
1320 /**
1321 * Lock an image files data into memory
1322 */
bm_load_image_data(int handle,int bpp,ushort flags,bool nodebug)1323 static int bm_load_image_data(int handle, int bpp, ushort flags, bool nodebug)
1324 {
1325 	BM_TYPE c_type = BM_TYPE_NONE;
1326 	int true_bpp;
1327 
1328 	auto bs = bm_get_slot(handle);
1329 	auto be = &bs->entry;
1330 	bitmap *bmp = &be->bm;
1331 
1332 	if (bmp->true_bpp > bpp)
1333 		true_bpp = bmp->true_bpp;
1334 	else
1335 		true_bpp = bpp;
1336 
1337 	// don't do a bpp check here since it could be different in OGL - taylor
1338 	if (bmp->data == 0) {
1339 		Assert(be->ref_count == 1);
1340 
1341 		if (be->type != BM_TYPE_USER && !nodebug) {
1342 			if (bmp->data == 0)
1343 				nprintf(("BmpMan", "Loading %s for the first time.\n", be->filename));
1344 		}
1345 
1346 		if (!Bm_paging) {
1347 			if (be->type != BM_TYPE_USER && !nodebug)
1348 				nprintf(("Paging", "Loading %s (%dx%dx%d)\n", be->filename, bmp->w, bmp->h, true_bpp));
1349 		}
1350 
1351 		// select proper format
1352 		if (flags & BMP_AABITMAP)
1353 			BM_SELECT_ALPHA_TEX_FORMAT();
1354 		else if (flags & BMP_TEX_ANY)
1355 			BM_SELECT_TEX_FORMAT();
1356 		else
1357 			BM_SELECT_SCREEN_FORMAT();
1358 
1359 		// make sure we use the real graphic type for EFFs
1360 		if (be->type == BM_TYPE_EFF) {
1361 			c_type = be->info.ani.eff.type;
1362 		}
1363 		else {
1364 			c_type = be->type;
1365 		}
1366 
1367 		switch (c_type)
1368 		{
1369 		case BM_TYPE_PCX:
1370 			bm_lock_pcx(handle, bs, bmp, true_bpp, flags);
1371 			break;
1372 
1373 		case BM_TYPE_ANI:
1374 			bm_lock_ani(handle, bs, bmp, true_bpp, flags);
1375 			break;
1376 
1377 		case BM_TYPE_TGA:
1378 			bm_lock_tga(handle, bs, bmp, true_bpp, flags);
1379 			break;
1380 
1381 		case BM_TYPE_PNG:
1382 			//libpng handles compression with zlib
1383 			if (be->info.ani.apng.is_apng == true) {
1384 				bm_lock_apng( handle, bs, bmp, true_bpp, flags );
1385 			}
1386 			else {
1387 				bm_lock_png( handle, bs, bmp, true_bpp, flags );
1388 			}
1389 			break;
1390 
1391 		case BM_TYPE_JPG:
1392 			bm_lock_jpg(handle, bs, bmp, true_bpp, flags);
1393 			break;
1394 
1395 		case BM_TYPE_DDS:
1396 		case BM_TYPE_DXT1:
1397 		case BM_TYPE_DXT3:
1398 		case BM_TYPE_DXT5:
1399 		case BM_TYPE_BC7:
1400 		case BM_TYPE_CUBEMAP_DDS:
1401 		case BM_TYPE_CUBEMAP_DXT1:
1402 		case BM_TYPE_CUBEMAP_DXT3:
1403 		case BM_TYPE_CUBEMAP_DXT5:
1404 			bm_lock_dds(handle, bs, bmp, true_bpp, flags);
1405 			break;
1406 
1407 		case BM_TYPE_USER:
1408 			bm_lock_user(handle, bs, bmp, true_bpp, flags);
1409 			break;
1410 
1411 		default:
1412 			Warning(LOCATION, "Unsupported type in bm_lock -- %d\n", c_type);
1413 			return -1;
1414 		}
1415 
1416 		// always go back to screen format
1417 		BM_SELECT_SCREEN_FORMAT();
1418 
1419 		// make sure we actually did something
1420 		if (!(bmp->data))
1421 			return -1;
1422 	}
1423 
1424 	return 0;
1425 }
1426 
bm_load_animation(const char * real_filename,int * nframes,int * fps,int * keyframe,float * total_time,bool can_drop_frames,int dir_type)1427 int bm_load_animation(const char *real_filename, int *nframes, int *fps, int *keyframe, float *total_time, bool can_drop_frames, int dir_type) {
1428 	int	i, n;
1429 	anim	the_anim;
1430 	CFILE	*img_cfp = nullptr;
1431 	char filename[MAX_FILENAME_LEN];
1432 	int reduced = 0;
1433 	int anim_fps = 0, anim_frames = 0, key = 0;
1434 	float anim_total_time = 0.0f;
1435 	int anim_width = 0, anim_height = 0;
1436 	BM_TYPE type = BM_TYPE_NONE, eff_type = BM_TYPE_NONE, c_type = BM_TYPE_NONE;
1437 	int bpp = 0, mm_lvl = 0;
1438 	size_t img_size = 0;
1439 	char clean_name[MAX_FILENAME_LEN];
1440 
1441 	Assertion(bm_inited, "bmpman must be initialized before this function can be called!");
1442 
1443 	// set output param defaults before going any further
1444 	if (nframes != nullptr)
1445 		*nframes = 0;
1446 
1447 	if (fps != nullptr)
1448 		*fps = 0;
1449 
1450 	if (keyframe != nullptr)
1451 		*keyframe = 0;
1452 
1453 	if (total_time != nullptr)
1454 		*total_time = 0.0f;
1455 
1456 	memset(filename, 0, MAX_FILENAME_LEN);
1457 	strncpy(filename, real_filename, MAX_FILENAME_LEN - 1);
1458 	char *p = strchr(filename, '.');
1459 	if (p) {
1460 		mprintf(("Someone passed an extension to bm_load_animation for file '%s'\n", real_filename));
1461 		*p = 0;
1462 	}
1463 
1464 	// If we are standalone server keep replacing the 'cursorweb' (mouse cursor) as the filename
1465 	// should keep the game happy while loading only single ani file which the needs to be present in any case
1466 	if (Is_standalone) {
1467 		char standalone_filename[MAX_FILENAME_LEN] = "cursorweb";
1468 		strcpy_s(filename, standalone_filename);
1469 	}
1470 
1471 	// safety catch for strcat...
1472 	// MAX_FILENAME_LEN-5 == '.' plus 3 letter ext plus NULL terminator
1473 	if (strlen(filename) > MAX_FILENAME_LEN - 5) {
1474 		Warning(LOCATION, "Passed filename, '%s', is too long to support an extension!!\n\nMaximum length, minus the extension, is %i characters.\n", filename, MAX_FILENAME_LEN - 5);
1475 		return -1;
1476 	}
1477 
1478 	// used later if EFF type
1479 	strcpy_s(clean_name, filename);
1480 
1481 	// Lets find out what type it is
1482 	{
1483 		int handle = -1;
1484 
1485 		// do a search for any previously loaded files (looks at filename only)
1486 		if (bm_load_sub_fast(filename, &handle, dir_type, true)) {
1487 			auto entry = bm_get_entry(handle);
1488 
1489 			if (nframes)
1490 				*nframes = entry->info.ani.num_frames;
1491 
1492 			if (fps)
1493 				*fps = entry->info.ani.fps;
1494 
1495 			if (keyframe)
1496 				*keyframe = entry->info.ani.keyframe;
1497 
1498 			if (total_time != nullptr)
1499 				*total_time = entry->info.ani.total_time;
1500 
1501 			return handle;
1502 		}
1503 
1504 		// if we are still here then we need to fall back to a file-based search
1505 		int rval = bm_load_sub_slow(filename, BM_ANI_NUM_TYPES, bm_ani_ext_list, &img_cfp, dir_type);
1506 
1507 		if (rval < 0)
1508 			return -1;
1509 
1510 		strcat_s(filename, bm_ani_ext_list[rval]);
1511 		type = bm_ani_type_list[rval];
1512 	}
1513 
1514 	// If we found an animation then there is an extra 5 char size limit to adhere to. We don't do this check earlier since it's only needed if we found an anim
1515 	// an ANI needs about 5 extra characters to have the "[###]" frame designator
1516 	// EFF/APNG need 5 extra characters for each frame filename too, which just happens to be the same length as the frame designator needed otherwise
1517 	// MAX_FILENAME_LEN-10 == 5 character frame designator plus '.' plus 3 letter ext plus NULL terminator
1518 	// we only check for -5 here since the filename should already have the extension on it, and it must have passed the previous check
1519 	if (strlen(filename) > MAX_FILENAME_LEN - 5) {
1520 		Warning(LOCATION, "Passed filename, '%s', is too long to support an extension and frames!!\n\nMaximum length for an ANI/EFF/APNG, minus the extension, is %i characters.\n", filename, MAX_FILENAME_LEN - 10);
1521 		if (img_cfp != nullptr)
1522 			cfclose(img_cfp);
1523 		return -1;
1524 	}
1525 
1526 	// it's an effect file, any readable image type with eff being txt
1527 	if (type == BM_TYPE_EFF) {
1528 		if (!bm_load_and_parse_eff(filename, dir_type, &anim_frames, &anim_fps, &key, &eff_type)) {
1529 			mprintf(("BMPMAN: Error reading EFF\n"));
1530 			if (img_cfp != nullptr)
1531 				cfclose(img_cfp);
1532 			return -1;
1533 		} else {
1534 			mprintf(("BMPMAN: Found EFF (%s) with %d frames at %d fps.\n", filename, anim_frames, anim_fps));
1535 		}
1536 		if (anim_fps == 0) {
1537 			Error(LOCATION, "animation (%s) has invalid fps of 0, fix this!", filename);
1538 		}
1539 		anim_total_time = anim_frames / i2fl(anim_fps);
1540 	}
1541 	// regular ani file
1542 	else if (type == BM_TYPE_ANI) {
1543 #ifndef NDEBUG
1544 		// for debug of ANI sizes
1545 		strcpy_s(the_anim.name, real_filename);
1546 #endif
1547 		anim_read_header(&the_anim, img_cfp);
1548 
1549 		if (the_anim.width < 0 || the_anim.height < 0) {
1550 			Error(LOCATION, "Ani file %s has a faulty header and cannot be loaded.", real_filename);
1551 		}
1552 
1553 		anim_frames = the_anim.total_frames;
1554 		anim_fps = the_anim.fps;
1555 		if (anim_fps == 0) {
1556 			Error(LOCATION, "animation (%s) has invalid fps of 0, fix this!", filename);
1557 		}
1558 		anim_total_time = anim_frames / i2fl(anim_fps);
1559 		anim_width = the_anim.width;
1560 		anim_height = the_anim.height;
1561 		bpp = 8;
1562 		img_size = (anim_width * anim_height * bpp);
1563 		//we only care if there are 2 keyframes - first frame, other frame to jump to for ship/weapons
1564 		//mainhall door anis hav every frame as keyframe, so we don't care
1565 		//other anis only have the first frame
1566 		if (the_anim.num_keys == 2) {
1567 			the_anim.keys = (key_frame*)vm_malloc(sizeof(key_frame) * the_anim.num_keys);
1568 			Assert(the_anim.keys != NULL);
1569 
1570 			for (i = 0; i<the_anim.num_keys; i++) {
1571 				the_anim.keys[i].frame_num = 0;
1572 				cfread(&the_anim.keys[i].frame_num, 2, 1, img_cfp);
1573 				cfread(&the_anim.keys[i].offset, 4, 1, img_cfp);
1574 				the_anim.keys[i].frame_num = INTEL_INT(the_anim.keys[i].frame_num); //-V570
1575 				the_anim.keys[i].offset = INTEL_INT(the_anim.keys[i].offset); //-V570
1576 			}
1577 			//some retail anis have their keyframes reversed
1578 			key = MAX(the_anim.keys[0].frame_num, the_anim.keys[1].frame_num);
1579 
1580 			vm_free(the_anim.keys);
1581 			the_anim.keys = nullptr;
1582 		}
1583 	}
1584 	else if (type == BM_TYPE_PNG) {
1585 		nprintf(("apng", "Loading apng: %s\n", filename));
1586 		try {
1587 			apng::apng_ani the_apng = apng::apng_ani(filename);
1588 			anim_frames = the_apng.nframes;
1589 			anim_total_time = the_apng.anim_time;
1590 			anim_fps = fl2i(i2fl(anim_frames) / the_apng.anim_time); // note; apng bails on loading if anim_time is <= 0.0f
1591 			anim_width = the_apng.w;
1592 			anim_height = the_apng.h;
1593 			bpp = the_apng.bpp;
1594 			img_size = the_apng.imgsize();
1595 		}
1596 		catch (const apng::ApngException& e) {
1597 			mprintf(("Failed to load apng: %s\n", e.what() ));
1598 			if (img_cfp != nullptr)
1599 				cfclose(img_cfp);
1600 			return -1;
1601 		}
1602 	}
1603 	else {
1604 		Warning(LOCATION, "Unsupported image type: %i", type);
1605 		if (img_cfp != nullptr)
1606 			cfclose(img_cfp);
1607 		return -1;
1608 	}
1609 
1610 	if ((can_drop_frames == true) && (type == BM_TYPE_ANI)) {
1611 		if (Bm_low_mem == 1) {
1612 			reduced = 1;
1613 			anim_frames = (anim_frames + 1) / 2;
1614 			anim_fps = (anim_fps / 2);
1615 		} else if (Bm_low_mem == 2) {
1616 			anim_frames = 1;
1617 		}
1618 	}
1619 
1620 
1621 	n = find_block_of(anim_frames);
1622 
1623 	if (n < 0) {
1624 		if (img_cfp != nullptr)
1625 			cfclose(img_cfp);
1626 
1627 		return -1;
1628 	}
1629 
1630 	auto first_entry = bm_get_entry(n);
1631 	// if all images of the animation have the same size then we can use a texture array
1632 	bool is_array = true;
1633 
1634 	for (i = 0; i < anim_frames; i++) {
1635 		auto entry = bm_get_entry(n + i);
1636 		memset(entry, 0, sizeof(bitmap_entry));
1637 
1638 		if (type == BM_TYPE_EFF) {
1639 			entry->info.ani.eff.type = eff_type;
1640 			sprintf_safe(entry->info.ani.eff.filename, "%s_%.4d", clean_name, i);
1641 
1642 			// bm_load_info() returns non-0 on failure
1643 			if (bm_load_info(eff_type, entry->info.ani.eff.filename,
1644 							 nullptr, &anim_width, &anim_height, &bpp, &c_type, &mm_lvl, &img_size)) {
1645 				// if we didn't get anything then bail out now
1646 				if (i == 0) {
1647 					Warning(LOCATION, "EFF: No frame images were found.  EFF, %s, is invalid.\n", filename);
1648 
1649 					if (img_cfp != nullptr)
1650 						cfclose(img_cfp);
1651 
1652 					return -1;
1653 				}
1654 
1655 				Warning(LOCATION, "EFF: Unable to load all frames for '%s', stopping at #%d\n", filename, i);
1656 
1657 				// reset total frames to current
1658 				anim_frames = i;
1659 
1660 				// update all previous frames with the new count
1661 				for (int j = 0; j<anim_frames; j++)
1662 					bm_get_entry(n + j)->info.ani.num_frames = anim_frames;
1663 
1664 				break;
1665 			}
1666 
1667 			if ((img_size <= 0) && (anim_width) && (anim_height) && (bpp)) {
1668 				img_size = (anim_width * anim_height * (bpp >> 3));
1669 			}
1670 		}
1671 
1672 		entry->info.ani.first_frame = n;
1673 		entry->info.ani.num_frames = anim_frames;
1674 		entry->info.ani.fps = (ubyte)anim_fps;
1675 		entry->info.ani.keyframe = key;
1676 		entry->info.ani.total_time = anim_total_time;
1677 		entry->bm.w = (short)anim_width;
1678 		entry->bm.rowsize = (short)anim_width;
1679 		entry->bm.h = (short)anim_height;
1680 		if (reduced) {
1681 			entry->bm.w /= 2;
1682 			entry->bm.rowsize /= 2;
1683 			entry->bm.h /= 2;
1684 		}
1685 		entry->bm.flags = 0;
1686 		entry->bm.bpp = 0;
1687 		entry->bm.true_bpp = (ubyte)bpp;
1688 		entry->bm.data = 0;
1689 		entry->bm.palette = nullptr;
1690 		entry->type = type;
1691 		entry->comp_type = c_type;
1692 		entry->signature = Bm_next_signature++;
1693 		entry->handle = n + i;
1694 		entry->num_mipmaps = mm_lvl;
1695 		entry->mem_taken = (size_t)img_size;
1696 		entry->dir_type = dir_type;
1697 
1698 		entry->load_count++;
1699 
1700 		if (i == 0) {
1701 			sprintf_safe(entry->filename, "%s", filename);
1702 		} else {
1703 			if (type == BM_TYPE_PNG) {
1704 				sprintf_safe(entry->filename, "%s_%04d", filename, i);
1705 			}
1706 			else {
1707 				sprintf_safe(entry->filename, "%s[%d]", filename, i);
1708 			}
1709 		}
1710 
1711 		entry->info.ani.apng.frame_delay = 0.0f;
1712 		if (type == BM_TYPE_PNG) {
1713 			entry->info.ani.apng.is_apng = true;
1714 		}
1715 		else {
1716 			entry->info.ani.apng.is_apng = false;
1717 		}
1718 
1719 		if (first_entry->bm.w != entry->bm.w || first_entry->bm.h != entry->bm.h) {
1720 			// We found a frame with a different size than the first frame -> this can't be used as a texture array
1721 			is_array = false;
1722 
1723 			Warning(LOCATION, "Animation '%s' has images that are of different sizes (currently at frame %d)."
1724 				"Performance could be improved by making all images the same size.", filename, i + 1);
1725 		}
1726 		if (first_entry->comp_type != entry->comp_type) {
1727 			// Different compression type
1728 			is_array = false;
1729 
1730 			Warning(LOCATION, "Animation '%s' has images that are of different compression formats (currently at frame %d)."
1731 				"Performance could be improved by making all images the same compression format.", filename, i + 1);
1732 		}
1733 		if (first_entry->bm.true_bpp != entry->bm.true_bpp) {
1734 			// We found a frame with an incompatible pixel format
1735 			is_array = false;
1736 
1737 			Warning(LOCATION, "Animation '%s' has images that are of different pixel formats (currently at frame %d)."
1738 				"Performance could be improved by making all images the same pixel format.", filename, i + 1);
1739 		}
1740 		if (first_entry->num_mipmaps != entry->num_mipmaps) {
1741 			// We found a frame with a different number of mipmaps
1742 			is_array = false;
1743 
1744 			Warning(LOCATION, "Animation '%s' has images that have a different number of mipmaps (currently at frame %d)."
1745 				"Performance could be improved by giving all frames the same number of mipmaps.", filename, i + 1);
1746 		}
1747 	}
1748 
1749 	// Set array flag of first frame
1750 	first_entry->info.ani.is_array = is_array;
1751 
1752 	if (nframes != nullptr)
1753 		*nframes = anim_frames;
1754 
1755 	if (fps != nullptr)
1756 		*fps = anim_fps;
1757 
1758 	if (img_cfp != nullptr)
1759 		cfclose(img_cfp);
1760 
1761 	if (keyframe != nullptr)
1762 		*keyframe = key;
1763 
1764 	if (total_time != nullptr)
1765 		*total_time = anim_total_time;
1766 
1767 	return first_entry->handle;
1768 }
1769 
bm_load_duplicate(const char * filename)1770 int bm_load_duplicate(const char *filename) {
1771 	int ret;
1772 
1773 	// ignore duplicates
1774 	Bm_ignore_duplicates = 1;
1775 
1776 	// load
1777 	ret = bm_load(filename);
1778 
1779 	// back to normal
1780 	Bm_ignore_duplicates = 0;
1781 
1782 	return ret;
1783 }
1784 
bm_load_either(const char * filename,int * nframes,int * fps,int * keyframe,bool can_drop_frames,int dir_type)1785 int bm_load_either(const char *filename, int *nframes, int *fps, int *keyframe, bool can_drop_frames, int dir_type) {
1786 	if (nframes != NULL)
1787 		*nframes = 0;
1788 	if (fps != NULL)
1789 		*fps = 0;
1790 	int tidx = bm_load_animation(filename, nframes, fps, keyframe, nullptr, can_drop_frames, dir_type);
1791 	if (tidx == -1) {
1792 		tidx = bm_load(filename);
1793 		if (tidx != -1 && nframes != NULL)
1794 			*nframes = 1;
1795 	}
1796 
1797 	return tidx;
1798 }
1799 
bm_load_sub_fast(const char * real_filename,int * handle,int dir_type,bool animated_type)1800 int bm_load_sub_fast(const char *real_filename, int *handle, int dir_type, bool animated_type) {
1801 	if (Bm_ignore_duplicates)
1802 		return 0;
1803 
1804 	for (auto& block : bm_blocks) {
1805 		for (auto& slot : block) {
1806 			auto& entry = slot.entry;
1807 			if (entry.type == BM_TYPE_NONE)
1808 				continue;
1809 
1810 			if (entry.dir_type != dir_type)
1811 				continue;
1812 
1813 			bool animated = bm_is_anim(&entry);
1814 
1815 			if (animated_type && !animated)
1816 				continue;
1817 			else if (!animated_type && animated)
1818 				continue;
1819 
1820 			if (!strextcmp(real_filename, entry.filename)) {
1821 				entry.load_count++;
1822 				*handle = entry.handle;
1823 				return 1;
1824 			}
1825 		}
1826 	}
1827 
1828 	// not found to be loaded already
1829 	return 0;
1830 }
1831 
bm_load_sub_slow(const char * real_filename,const int num_ext,const char ** ext_list,CFILE ** img_cfp,int dir_type)1832 int bm_load_sub_slow(const char *real_filename, const int num_ext, const char **ext_list, CFILE **img_cfp, int dir_type) {
1833 	auto res = cf_find_file_location_ext(real_filename, num_ext, ext_list, dir_type, false);
1834 
1835 	// could not be found, or is invalid for some reason
1836 	if (!res.found)
1837 		return -1;
1838 
1839 	CFILE *test = cfopen_special(res, "rb", dir_type);
1840 
1841 	if (test != NULL) {
1842 		if (img_cfp != NULL)
1843 			*img_cfp = test;
1844 
1845 		return res.extension_index;
1846 	}
1847 
1848 	// umm, that's not good...
1849 	Warning(LOCATION, "Could not open file %s!", real_filename);
1850 	return -1;
1851 }
1852 
bm_lock(int handle,int bpp,ushort flags,bool nodebug)1853 bitmap * bm_lock(int handle, int bpp, ushort flags, bool nodebug) {
1854 	bitmap			*bmp;
1855 
1856 	Assertion(bm_inited, "bmpman must be initialized before this function can be called!");
1857 
1858 	auto be = bm_get_entry(handle);
1859 
1860 	// to fix a couple of OGL bpp passes, force 8bit on AABITMAP - taylor
1861 	if (flags & BMP_AABITMAP)
1862 		bpp = 8;
1863 
1864 	// if we're on a standalone server, aways for it to lock to 8 bits
1865 	if (Is_standalone) {
1866 		bpp = 8;
1867 		flags = 0;
1868 	}
1869 	// otherwise do it as normal
1870 	else {
1871 		if (flags & BMP_AABITMAP) {
1872 			Assert(bpp == 8);
1873 		} else if ((flags & BMP_TEX_NONCOMP) && (!(flags & BMP_TEX_COMP))) {
1874 			Assert(bpp >= 16);  // cheating but bpp passed isn't what we normally end up with
1875 		} else if ((flags & BMP_TEX_DXT1) || (flags & BMP_TEX_DXT3) || (flags & BMP_TEX_DXT5) || (flags & BMP_TEX_BC7)) {
1876 			Assert(bpp >= 16); // cheating but bpp passed isn't what we normally end up with
1877 		} else if (flags & BMP_TEX_CUBEMAP) {
1878 			Assert((be->type == BM_TYPE_CUBEMAP_DDS) ||
1879 				(be->type == BM_TYPE_CUBEMAP_DXT1) ||
1880 				(be->type == BM_TYPE_CUBEMAP_DXT3) ||
1881 				(be->type == BM_TYPE_CUBEMAP_DXT5));
1882 			Assert(bpp >= 16);
1883 		} else {
1884 			Assert(0);		//?
1885 		}
1886 	}
1887 
1888 	bmp = &be->bm;
1889 
1890 	// If you hit this assert, chances are that someone freed the
1891 	// wrong bitmap and now someone is trying to use that bitmap.
1892 	// See John.
1893 	Assert(be->type != BM_TYPE_NONE);
1894 
1895 	// Increment ref count for bitmap since lock was made on it.
1896 	Assert(be->ref_count >= 0);
1897 	be->ref_count++;					// Lock it before we page in data; this prevents a callback from freeing this
1898 	// as it gets read in
1899 
1900 	// Mark this bitmap as used this frame
1901 #ifdef BMPMAN_NDEBUG
1902 	if (be->used_this_frame < 255) {
1903 		be->used_this_frame++;
1904 	}
1905 #endif
1906 
1907 	// read the file data
1908 	if (bm_load_image_data(handle, bpp, flags, nodebug) == -1) {
1909 		// oops, this isn't good - reset and return NULL
1910 		bm_unlock( handle );
1911 		bm_unload( handle );
1912 
1913 		return NULL;
1914 	}
1915 
1916 	if (!gr_bm_data(handle, bmp)) {
1917 		// graphics subsystem failed, reset and return NULL
1918 		bm_unlock( handle );
1919 		bm_unload( handle );
1920 
1921 		return NULL;
1922 	}
1923 
1924 	MONITOR_INC(NumBitmapPage, 1);
1925 	MONITOR_INC(SizeBitmapPage, bmp->w*bmp->h);
1926 
1927 	if (bm_is_anim(be) == true) {
1928 		int i;
1929 		auto first = be->info.ani.first_frame;
1930 
1931 		auto frames = bm_get_entry(first)->info.ani.num_frames;
1932 		for (i = 0; i< frames; i++) {
1933 			// Mark all the bitmaps in this bitmap or animation as recently used
1934 			auto frame_entry = bm_get_entry(first + i);
1935 
1936 #ifdef BMPMAN_NDEBUG
1937 			// Mark all the bitmaps in this bitmap or animation as used for the usage tracker.
1938 			frame_entry->used_count++;
1939 #endif
1940 
1941 			frame_entry->used_flags = flags;
1942 		}
1943 	} else {
1944 #ifdef BMPMAN_NDEBUG
1945 		// Mark all the bitmaps in this bitmap or animation as used for the usage tracker.
1946 		be->used_count++;
1947 #endif
1948 		be->used_flags = flags;
1949 	}
1950 
1951 	return bmp;
1952 }
1953 
bm_lock_ani(int,bitmap_slot * bs,bitmap *,int bpp,ushort flags)1954 void bm_lock_ani(int /*handle*/, bitmap_slot *bs, bitmap* /*bmp*/, int bpp, ushort flags) {
1955 	anim				*the_anim;
1956 	anim_instance	*the_anim_instance;
1957 	bitmap			*bm;
1958 	ubyte				*frame_data;
1959 	int				size, i;
1960 	int				first_frame, nframes;
1961 
1962 	auto be = &bs->entry;
1963 	first_frame = be->info.ani.first_frame;
1964 
1965 	auto first_entry = bm_get_entry(first_frame);
1966 
1967 	nframes = first_entry->info.ani.num_frames;
1968 
1969 	if ((the_anim = anim_load(first_entry->filename, first_entry->dir_type)) == nullptr) {
1970 		nprintf(("BMPMAN", "Error opening %s in bm_lock\n", be->filename));
1971 		return;
1972 	}
1973 
1974 	if ((the_anim_instance = init_anim_instance(the_anim, bpp)) == nullptr) {
1975 		nprintf(("BMPMAN", "Error opening %s in bm_lock\n", be->filename));
1976 		anim_free(the_anim);
1977 		return;
1978 	}
1979 
1980 	int can_drop_frames = 0;
1981 
1982 	if (the_anim->total_frames != first_entry->info.ani.num_frames) {
1983 		can_drop_frames = 1;
1984 	}
1985 
1986 	bm = &first_entry->bm;
1987 	size = bm->w * bm->h * (bpp >> 3);
1988 	be->mem_taken = (size_t)size;
1989 
1990 	Assert(size > 0);
1991 
1992 	for (i = 0; i<nframes; i++) {
1993 		auto slot = bm_get_slot(first_frame + i);
1994 		be = &slot->entry;
1995 		bm = &be->bm;
1996 
1997 		// Unload any existing data
1998 		bm_free_data(slot);
1999 
2000 		bm->flags = 0;
2001 
2002 		// briefing editor in Fred2 uses aabitmaps (ani's) - force to 8 bit
2003 		bm->bpp = Is_standalone ? (ubyte)8 : bpp;
2004 
2005 		bm->data = (ptr_u)bm_malloc(first_frame + i, size);
2006 
2007 		frame_data = anim_get_next_raw_buffer(the_anim_instance, 0, flags & BMP_AABITMAP ? 1 : 0, bm->bpp);
2008 
2009 		ubyte *dptr, *sptr;
2010 
2011 		sptr = frame_data;
2012 		dptr = (ubyte *)bm->data;
2013 
2014 		if ((bm->w != the_anim->width) || (bm->h != the_anim->height)) {
2015 			// Scale it down
2016 			// 8 bit
2017 			if (bpp == 8) {
2018 				int w, h;
2019 				fix u, utmp, v, du, dv;
2020 
2021 				u = v = 0;
2022 
2023 				du = (the_anim->width*F1_0) / bm->w;
2024 				dv = (the_anim->height*F1_0) / bm->h;
2025 
2026 				for (h = 0; h < bm->h; h++) {
2027 					ubyte *drow = &dptr[bm->w * h];
2028 					ubyte *srow = &sptr[f2i(v)*the_anim->width];
2029 
2030 					utmp = u;
2031 
2032 					for (w = 0; w < bm->w; w++) {
2033 						*drow++ = srow[f2i(utmp)];
2034 						utmp += du;
2035 					}
2036 					v += dv;
2037 				}
2038 			}
2039 			// 16 bpp
2040 			else {
2041 				int w, h;
2042 				fix u, utmp, v, du, dv;
2043 
2044 				u = v = 0;
2045 
2046 				du = (the_anim->width*F1_0) / bm->w;
2047 				dv = (the_anim->height*F1_0) / bm->h;
2048 
2049 				for (h = 0; h < bm->h; h++) {
2050 					unsigned short *drow = &((unsigned short*)dptr)[bm->w * h];
2051 					unsigned short *srow = &((unsigned short*)sptr)[f2i(v)*the_anim->width];
2052 
2053 					utmp = u;
2054 
2055 					for (w = 0; w < bm->w; w++) {
2056 						*drow++ = srow[f2i(utmp)];
2057 						utmp += du;
2058 					}
2059 					v += dv;
2060 				}
2061 			}
2062 		} else {
2063 			// 1-to-1 mapping
2064 			memcpy(dptr, sptr, size);
2065 		}
2066 
2067 		bm_convert_format(bm, flags);
2068 
2069 		// Skip a frame
2070 		if ((i < nframes - 1) && can_drop_frames) {
2071 			frame_data = anim_get_next_raw_buffer(the_anim_instance, 0, flags & BMP_AABITMAP ? 1 : 0, bm->bpp);
2072 		}
2073 	}
2074 
2075 	free_anim_instance(the_anim_instance);
2076 	anim_free(the_anim);
2077 }
2078 
2079 
bm_lock_apng(int,bitmap_slot * bs,bitmap * bmp,int bpp,ushort)2080 void bm_lock_apng(int /*handle*/, bitmap_slot *bs, bitmap *bmp, int bpp, ushort /*flags*/) {
2081 	auto be = &bs->entry;
2082 	int first_frame = be->info.ani.first_frame;
2083 	auto first_entry = bm_get_entry(first_frame);
2084 	int nframes = first_entry->info.ani.num_frames;
2085 
2086 	std::unique_ptr<apng::apng_ani> the_apng;
2087 	try {
2088 		the_apng.reset(new apng::apng_ani(first_entry->filename));
2089 	}
2090 	catch (const apng::ApngException& e) {
2091 		Warning(LOCATION, "Failed to load apng: %s", e.what());
2092 		return;
2093 	}
2094 
2095 	float cumulative_frame_delay = 0.0f;
2096 	for (int i = 0; i<nframes; i++) {
2097 		auto slot = bm_get_slot(first_frame + i);
2098 		be = &slot->entry;
2099 		bitmap* bm = &be->bm;
2100 
2101 		// Unload any existing data
2102 		bm_free_data(slot);
2103 
2104 		ubyte* data = static_cast<ubyte*>(bm_malloc(first_frame + i, be->mem_taken));
2105 		try {
2106 			// this is a reasonably expensive operation
2107 			// especially when run on all frames at once
2108 			// i.e. try to preload the apng before using it
2109 			the_apng->next_frame();
2110 		}
2111 		catch (const apng::ApngException& e) {
2112 			Warning(LOCATION, "Failed to get next apng frame: %s", e.what());
2113 			bm_release(first_frame);
2114 			return;
2115 		}
2116 		memcpy(data, the_apng->frame.data.data(), be->mem_taken);
2117 
2118 		bm->data = reinterpret_cast<ptr_u>(data);
2119 		bm->palette = nullptr;
2120 		bm->bpp = bpp;
2121 		bm->flags = 0;
2122 		cumulative_frame_delay += the_apng->frame.delay;
2123 		be->info.ani.apng.frame_delay = cumulative_frame_delay;
2124 
2125 		nprintf(("apng", "locking apng frame: %s (%i|%i|%i) (%f) " SIZE_T_ARG "\n", be->filename, bpp, bmp->bpp, bm->true_bpp, be->info.ani.apng.frame_delay, be->mem_taken));
2126 	}
2127 }
2128 
2129 
bm_lock_dds(int handle,bitmap_slot * bs,bitmap * bmp,int,ushort)2130 void bm_lock_dds(int handle, bitmap_slot *bs, bitmap *bmp, int /*bpp*/, ushort /*flags*/) {
2131 	ubyte *data = NULL;
2132 	int error;
2133 	ubyte dds_bpp = 0;
2134 	char filename[MAX_FILENAME_LEN];
2135 
2136 	auto be = &bs->entry;
2137 
2138 	// free any existing data
2139 	bm_free_data(bs);
2140 
2141 	Assert(be->mem_taken > 0);
2142 	Assert(&be->bm == bmp);
2143 
2144 	data = (ubyte*)bm_malloc(handle, be->mem_taken);
2145 
2146 	if (data == NULL)
2147 		return;
2148 
2149 	memset(data, 0, be->mem_taken);
2150 
2151 	// make sure we are using the correct filename in the case of an EFF.
2152 	// this will populate filename[] whether it's EFF or not
2153 	EFF_FILENAME_CHECK;
2154 
2155 	error = dds_read_bitmap(filename, data, &dds_bpp, be->dir_type);
2156 
2157 #if BYTE_ORDER == BIG_ENDIAN
2158 	// same as with TGA, we need to byte swap 16 & 32-bit, uncompressed, DDS images
2159 	if ((be->comp_type == BM_TYPE_DDS) || (be->comp_type == BM_TYPE_CUBEMAP_DDS)) {
2160 		size_t i = 0;
2161 
2162 		if (dds_bpp == 32) {
2163 			unsigned int *swap_tmp;
2164 
2165 			for (i = 0; i < be->mem_taken; i += 4) {
2166 				swap_tmp = (unsigned int *)(data + i);
2167 				*swap_tmp = INTEL_INT(*swap_tmp);
2168 			}
2169 		} else if (dds_bpp == 16) {
2170 			unsigned short *swap_tmp;
2171 
2172 			for (i = 0; i < be->mem_taken; i += 2) {
2173 				swap_tmp = (unsigned short *)(data + i);
2174 				*swap_tmp = INTEL_SHORT(*swap_tmp);
2175 			}
2176 		}
2177 	}
2178 #endif
2179 
2180 	bmp->bpp = dds_bpp;
2181 	bmp->data = (ptr_u)data;
2182 	bmp->flags = 0;
2183 
2184 	if (error != DDS_ERROR_NONE) {
2185 		bm_free_data(bs);
2186 		return;
2187 	}
2188 
2189 #ifdef BMPMAN_NDEBUG
2190 	Assert(be->data_size > 0);
2191 #endif
2192 }
2193 
bm_lock_jpg(int handle,bitmap_slot * bs,bitmap * bmp,int bpp,ushort)2194 void bm_lock_jpg(int handle, bitmap_slot *bs, bitmap *bmp, int bpp, ushort /*flags*/) {
2195 	ubyte *data = NULL;
2196 	int d_size = 0;
2197 	int jpg_error = JPEG_ERROR_INVALID;
2198 	char filename[MAX_FILENAME_LEN];
2199 
2200 	auto be = &bs->entry;
2201 
2202 	// Unload any existing data
2203 	bm_free_data(bs);
2204 
2205 	// JPEG actually only support 24 bits per pixel so we enforce that here
2206 	bpp = 24;
2207 
2208 	d_size = (bpp >> 3);
2209 
2210 	// allocate bitmap data
2211 	Assert(be->mem_taken > 0);
2212 	data = (ubyte*)bm_malloc(handle, be->mem_taken);
2213 
2214 	if (data == NULL)
2215 		return;
2216 
2217 	memset(data, 0, be->mem_taken);
2218 
2219 	bmp->bpp = bpp;
2220 	bmp->data = (ptr_u)data;
2221 	bmp->palette = NULL;
2222 
2223 	Assert(&be->bm == bmp);
2224 
2225 	// make sure we are using the correct filename in the case of an EFF.
2226 	// this will populate filename[] whether it's EFF or not
2227 	EFF_FILENAME_CHECK;
2228 
2229 	jpg_error = jpeg_read_bitmap(filename, data, NULL, d_size, be->dir_type);
2230 
2231 	if (jpg_error != JPEG_ERROR_NONE) {
2232 		bm_free_data(bs);
2233 		return;
2234 	}
2235 
2236 #ifdef BMPMAN_NDEBUG
2237 	Assert(be->data_size > 0);
2238 #endif
2239 }
2240 
bm_lock_pcx(int handle,bitmap_slot * bs,bitmap * bmp,int bpp,ushort flags)2241 void bm_lock_pcx(int handle, bitmap_slot *bs, bitmap *bmp, int bpp, ushort flags) {
2242 	ubyte *data;
2243 	int pcx_error;
2244 	char filename[MAX_FILENAME_LEN];
2245 
2246 	auto be = &bs->entry;
2247 
2248 	// Unload any existing data
2249 	bm_free_data(bs);
2250 
2251 	be->mem_taken = (bmp->w * bmp->h * (bpp >> 3));
2252 	data = (ubyte *)bm_malloc(handle, be->mem_taken);
2253 	bmp->bpp = bpp;
2254 	bmp->data = (ptr_u)data;
2255 	bmp->palette = NULL;
2256 	memset(data, 0, be->mem_taken);
2257 
2258 	Assert(&be->bm == bmp);
2259 #ifdef BMPMAN_NDEBUG
2260 	Assert(be->data_size > 0);
2261 #endif
2262 
2263 	// some sanity checks on flags
2264 	Assert(!((flags & BMP_AABITMAP) && (flags & BMP_TEX_ANY)));						// no aabitmap textures
2265 
2266 	// make sure we are using the correct filename in the case of an EFF.
2267 	// this will populate filename[] whether it's EFF or not
2268 	EFF_FILENAME_CHECK;
2269 
2270 	pcx_error = pcx_read_bitmap(filename, data, NULL, (bpp >> 3), (flags & BMP_AABITMAP), (flags & BMP_MASK_BITMAP) != 0, be->dir_type);
2271 
2272 	if (pcx_error != PCX_ERROR_NONE) {
2273 		mprintf(("Couldn't load PCX!!! (%s)\n", filename));
2274 		return;
2275 	}
2276 
2277 #ifdef BMPMAN_NDEBUG
2278 	Assert(be->data_size > 0);
2279 #endif
2280 
2281 	bmp->flags = 0;
2282 
2283 	bm_convert_format(bmp, flags);
2284 }
2285 
bm_lock_png(int handle,bitmap_slot * bs,bitmap * bmp,int,ushort)2286 void bm_lock_png(int handle, bitmap_slot *bs, bitmap *bmp, int /*bpp*/, ushort /*flags*/) {
2287 	ubyte *data = NULL;
2288 	//assume 32 bit - libpng should expand everything
2289 	int d_size;
2290 	int png_error = PNG_ERROR_INVALID;
2291 	char filename[MAX_FILENAME_LEN];
2292 
2293 	auto be = &bs->entry;
2294 
2295 	// Unload any existing data
2296 	bm_free_data(bs);
2297 
2298 	// allocate bitmap data
2299 	Assert(bmp->w * bmp->h > 0);
2300 
2301 	//if it's not 32-bit, we expand when we read it
2302 	bmp->bpp = 32;
2303 	d_size = bmp->bpp >> 3;
2304 	//we waste memory if it turns out to be 24-bit, but the way this whole thing works is dodgy anyway
2305 	data = (ubyte*)bm_malloc(handle, bmp->w * bmp->h * d_size);
2306 	if (data == NULL)
2307 		return;
2308 	memset(data, 0, bmp->w * bmp->h * d_size);
2309 	bmp->data = (ptr_u)data;
2310 	bmp->palette = NULL;
2311 
2312 	Assert(&be->bm == bmp);
2313 
2314 	// make sure we are using the correct filename in the case of an EFF.
2315 	// this will populate filename[] whether it's EFF or not
2316 	EFF_FILENAME_CHECK;
2317 
2318 	//bmp->bpp gets set correctly in here after reading into memory
2319 	png_error = png_read_bitmap(filename, data, &bmp->bpp, d_size, be->dir_type);
2320 
2321 	if (png_error != PNG_ERROR_NONE) {
2322 		bm_free_data(bs);
2323 		return;
2324 	}
2325 
2326 #ifdef BMPMAN_NDEBUG
2327 	Assert(be->data_size > 0);
2328 #endif
2329 }
2330 
bm_lock_tga(int handle,bitmap_slot * bs,bitmap * bmp,int bpp,ushort flags)2331 void bm_lock_tga(int handle, bitmap_slot *bs, bitmap *bmp, int bpp, ushort flags) {
2332 	ubyte *data = NULL;
2333 	int byte_size;
2334 	char filename[MAX_FILENAME_LEN];
2335 
2336 	auto be = &bs->entry;
2337 
2338 	// Unload any existing data
2339 	bm_free_data(bs);
2340 
2341 	bpp = be->bm.true_bpp;
2342 
2343 	if (Is_standalone) {
2344 		Assert(bpp == 8);
2345 	} else {
2346 		Assert((bpp == 16) || (bpp == 24) || (bpp == 32));
2347 	}
2348 
2349 	// allocate bitmap data
2350 	byte_size = (bpp >> 3);
2351 
2352 	Assert(byte_size);
2353 	Assert(be->mem_taken > 0);
2354 
2355 	data = (ubyte*)bm_malloc(handle, static_cast<size_t>(bmp->w * bmp->h * byte_size));
2356 
2357 	if (data) {
2358 		memset(data, 0, be->mem_taken);
2359 	} else {
2360 		return;
2361 	}
2362 
2363 	bmp->bpp = bpp;
2364 	bmp->data = (ptr_u)data;
2365 	bmp->palette = NULL;
2366 
2367 	Assert(&be->bm == bmp);
2368 #ifdef BMPMAN_NDEBUG
2369 	Assert(be->data_size > 0);
2370 #endif
2371 
2372 	int tga_error;
2373 
2374 	// make sure we are using the correct filename in the case of an EFF.
2375 	// this will populate filename[] whether it's EFF or not
2376 	EFF_FILENAME_CHECK;
2377 
2378 	tga_error = targa_read_bitmap(filename, data, nullptr, byte_size, be->dir_type);
2379 
2380 	if (tga_error != TARGA_ERROR_NONE) {
2381 		bm_free_data(bs);
2382 		return;
2383 	}
2384 
2385 	bmp->flags = 0;
2386 
2387 	bm_convert_format(bmp, flags);
2388 }
2389 
bm_lock_user(int,bitmap_slot * bs,bitmap * bmp,int bpp,ushort flags)2390 void bm_lock_user(int /*handle*/, bitmap_slot *bs, bitmap *bmp, int bpp, ushort flags) {
2391 	auto be = &bs->entry;
2392 
2393 	// Unload any existing data
2394 	bm_free_data(bs);
2395 
2396 	if ((bpp != be->info.user.bpp) && !(flags & BMP_AABITMAP))
2397 		bpp = be->info.user.bpp;
2398 
2399 	switch (bpp) {
2400 	case 32:	// user 32-bit bitmap
2401 		bmp->bpp = bpp;
2402 		bmp->flags = be->info.user.flags;
2403 		bmp->data = (ptr_u)be->info.user.data;
2404 		break;
2405 
2406 	case 24:	// user 24-bit bitmap
2407 		bmp->bpp = bpp;
2408 		bmp->flags = be->info.user.flags;
2409 		bmp->data = (ptr_u)be->info.user.data;
2410 		break;
2411 
2412 	case 16:			// user 16 bit bitmap
2413 		bmp->bpp = bpp;
2414 		bmp->flags = be->info.user.flags;
2415 		bmp->data = (ptr_u)be->info.user.data;
2416 		break;
2417 
2418 	case 8:			// Going from 8 bpp to something (probably only for aabitmaps)
2419 		Assert(flags & BMP_AABITMAP);
2420 		bmp->bpp = bpp;
2421 		bmp->flags = be->info.user.flags;
2422 		bmp->data = (ptr_u)be->info.user.data;
2423 		break;
2424 
2425 	default:
2426 		Error(LOCATION, "Unhandled user bitmap conversion from %d to %d bpp", be->info.user.bpp, bmp->bpp);
2427 		break;
2428 	}
2429 
2430 	bm_convert_format(bmp, flags);
2431 }
2432 
bm_make_render_target(int width,int height,int flags)2433 int bm_make_render_target(int width, int height, int flags) {
2434 	int mm_lvl = 0;
2435 	// final w and h may be different from passed width and height
2436 	int w = width, h = height;
2437 	int bpp = 32;
2438 	int size = 0;
2439 
2440 	Assertion(bm_inited, "bmpman must be initialized before this function can be called!");
2441 
2442 	// Find an open slot (starting from the end)
2443 	int n = find_block_of(1);
2444 
2445 	// Out of bitmap slots
2446 	if (n == -1)
2447 		return -1;
2448 
2449 	if (!gr_bm_make_render_target(n, &w, &h, &bpp, &mm_lvl, flags))
2450 		return -1;
2451 
2452 	Assert(mm_lvl > 0);
2453 
2454 	if (flags & BMP_FLAG_RENDER_TARGET_STATIC) {
2455 		// data size
2456 		size = (w * h * (bpp >> 3));
2457 
2458 		if (mm_lvl > 1) {
2459 			// include size of all mipmap levels (there should be a full chain)
2460 			size += (size / 3) - 1;
2461 		}
2462 
2463 		// make sure to count all faces if a cubemap
2464 		if (flags & BMP_FLAG_CUBEMAP) {
2465 			size *= 6;
2466 		}
2467 	}
2468 
2469 	auto entry = bm_get_entry(n);
2470 	// ensure fields are cleared out from previous bitmap
2471 	memset(entry, 0, sizeof(bitmap_entry));
2472 
2473 	entry->type = (flags & BMP_FLAG_RENDER_TARGET_STATIC) ? BM_TYPE_RENDER_TARGET_STATIC : BM_TYPE_RENDER_TARGET_DYNAMIC;
2474 	entry->signature = Bm_next_signature++;
2475 	sprintf_safe(entry->filename, "RT_%dx%d+%d", w, h, bpp);
2476 	entry->bm.w = (short)w;
2477 	entry->bm.h = (short)h;
2478 	entry->bm.rowsize = (short)w;
2479 	entry->bm.bpp = bpp;
2480 	entry->bm.true_bpp = bpp;
2481 	entry->bm.flags = (ubyte)flags;
2482 	entry->bm.data = 0;
2483 	entry->bm.palette = nullptr;
2484 	entry->num_mipmaps = mm_lvl;
2485 	entry->mem_taken = (size_t)size;
2486 	entry->dir_type = CF_TYPE_ANY;
2487 
2488 	entry->handle = n;
2489 
2490 	if (entry->mem_taken) {
2491 		entry->bm.data = (ptr_u)bm_malloc(n, entry->mem_taken);
2492 	}
2493 
2494 	return entry->handle;
2495 }
2496 
bm_malloc(int n,size_t size)2497 void *bm_malloc(int n, size_t size) {
2498 	if (size == 0)
2499 		return nullptr;
2500 
2501 #ifdef BMPMAN_NDEBUG
2502 	auto entry = bm_get_entry(n);
2503 	Assert(entry->data_size == 0);
2504 	entry->data_size += size;
2505 	bm_texture_ram += size;
2506 #endif
2507 
2508 	return vm_malloc(size);
2509 }
2510 
bm_page_in_aabitmap(int handle,int nframes)2511 void bm_page_in_aabitmap(int handle, int nframes) {
2512 	int i;
2513 
2514 	if (handle == -1)
2515 		return;
2516 
2517 	Assert(bm_get_entry(handle)->handle == handle);
2518 
2519 	for (i = 0; i<nframes; i++) {
2520 		auto frame_entry = bm_get_entry(handle + i);
2521 
2522 		frame_entry->preloaded = 2;
2523 		frame_entry->preload_count++;
2524 		frame_entry->used_flags = BMP_AABITMAP;
2525 	}
2526 }
2527 
bm_page_in_start()2528 void bm_page_in_start() {
2529 	Bm_paging = 1;
2530 
2531 	// Mark all as inited
2532 	for (auto& block : bm_blocks) {
2533 		for (auto& slot : block) {
2534 			auto& entry = slot.entry;
2535 
2536 			entry.preloaded = 0;
2537 			entry.preload_count = 0;
2538 #ifdef BMPMAN_NDEBUG
2539 			entry.used_count = 0;
2540 #endif
2541 			entry.used_flags = 0;
2542 		}
2543 	}
2544 
2545 	gr_bm_page_in_start();
2546 }
2547 
bm_page_in_stop()2548 void bm_page_in_stop() {
2549 	TRACE_SCOPE(tracing::PageInStop);
2550 
2551 #ifndef NDEBUG
2552 	char busy_text[60];
2553 #endif
2554 
2555 	nprintf(("BmpInfo", "BMPMAN: Loading all used bitmaps.\n"));
2556 
2557 	// Load all the ones that are supposed to be loaded for this level.
2558 	int n = 0;
2559 
2560 	int bm_preloading = 1;
2561 
2562 	for (auto& block : bm_blocks) {
2563 		for (auto& slot : block) {
2564 			auto& entry = slot.entry;
2565 
2566 			if ((entry.type != BM_TYPE_NONE) && (entry.type != BM_TYPE_RENDER_TARGET_DYNAMIC)
2567 				&& (entry.type != BM_TYPE_RENDER_TARGET_STATIC)) {
2568 				if (entry.preloaded) {
2569 					TRACE_SCOPE(tracing::PageInSingleBitmap);
2570 					if (bm_preloading) {
2571 						if (!gr_preload(entry.handle, (entry.preloaded == 2))) {
2572 							mprintf(("Out of VRAM.  Done preloading.\n"));
2573 							bm_preloading = 0;
2574 						}
2575 					} else {
2576 						bm_lock(entry.handle, (entry.used_flags == BMP_AABITMAP) ? 8 : 16, entry.used_flags);
2577 						if (entry.ref_count >= 1) {
2578 							bm_unlock(entry.handle);
2579 						}
2580 					}
2581 
2582 					n++;
2583 
2584 					multi_send_anti_timeout_ping();
2585 
2586 					if ((entry.info.ani.first_frame == 0) || (entry.info.ani.first_frame == entry.handle)) {
2587 #ifndef NDEBUG
2588 						memset(busy_text, 0, sizeof(busy_text));
2589 
2590 						strcat_s(busy_text, "** BmpMan: ");
2591 						strcat_s(busy_text, entry.filename);
2592 						strcat_s(busy_text, " **");
2593 
2594 						game_busy(busy_text);
2595 #else
2596 						game_busy();
2597 #endif
2598 					}
2599 				} else {
2600 					bm_unload_fast(entry.handle);
2601 				}
2602 			}
2603 		}
2604 	}
2605 
2606 	nprintf(("BmpInfo", "BMPMAN: Loaded %d bitmaps that are marked as used for this level.\n", n));
2607 
2608 #ifndef NDEBUG
2609 	int total_bitmaps = 0;
2610 	int total_slots = 0;
2611 	for (auto& block : bm_blocks) {
2612 		total_slots += BM_BLOCK_SIZE;
2613 		for (auto& slot : block) {
2614 			auto& entry = slot.entry;
2615 
2616 			if (entry.type != BM_TYPE_NONE) {
2617 				total_bitmaps++;
2618 			}
2619 			if (entry.type == BM_TYPE_USER) {
2620 				mprintf(("User bitmap '%s'\n", entry.filename));
2621 			}
2622 		}
2623 	}
2624 
2625 	mprintf(("Bmpman: %d/%d bitmap slots in use.\n", total_bitmaps, total_slots));
2626 #endif
2627 
2628 	Bm_paging = 0;
2629 }
2630 
bm_page_in_texture(int bitmapnum,int nframes)2631 void bm_page_in_texture(int bitmapnum, int nframes) {
2632 	int i;
2633 
2634 	if (bitmapnum < 0)
2635 		return;
2636 
2637 	auto entry = bm_get_entry(bitmapnum);
2638 
2639 	if (nframes <= 0) {
2640 		if (bm_is_anim(entry) == true)
2641 			nframes = entry->info.ani.num_frames;
2642 		else
2643 			nframes = 1;
2644 	}
2645 
2646 	for (i = 0; i < nframes; i++) {
2647 		auto frame_entry = bm_get_entry(bitmapnum + i);
2648 
2649 		frame_entry->preloaded = 1;
2650 
2651 		frame_entry->preload_count++;
2652 
2653 		frame_entry->used_flags = BMP_TEX_OTHER;
2654 
2655 		//check if its compressed
2656 		switch (frame_entry->comp_type) {
2657 		case BM_TYPE_NONE:
2658 			continue;
2659 
2660 		case BM_TYPE_DXT1:
2661 			frame_entry->used_flags = BMP_TEX_DXT1;
2662 			continue;
2663 
2664 		case BM_TYPE_DXT3:
2665 			frame_entry->used_flags = BMP_TEX_DXT3;
2666 			continue;
2667 
2668 		case BM_TYPE_DXT5:
2669 			frame_entry->used_flags = BMP_TEX_DXT5;
2670 			continue;
2671 
2672 		case BM_TYPE_BC7:
2673 			frame_entry->used_flags = BMP_TEX_BC7;
2674 			continue;
2675 
2676 		case BM_TYPE_CUBEMAP_DXT1:
2677 		case BM_TYPE_CUBEMAP_DXT3:
2678 		case BM_TYPE_CUBEMAP_DXT5:
2679 			frame_entry->used_flags = BMP_TEX_CUBEMAP;
2680 			continue;
2681 
2682 		default:
2683 			continue;
2684 		}
2685 	}
2686 }
2687 
bm_page_in_xparent_texture(int bitmapnum,int nframes)2688 void bm_page_in_xparent_texture(int bitmapnum, int nframes) {
2689 	int i;
2690 
2691 	for (i = 0; i < nframes; i++) {
2692 		auto entry = bm_get_entry(bitmapnum + i);
2693 
2694 		entry->preloaded = 3;
2695 
2696 		entry->preload_count++;
2697 
2698 		entry->used_flags = BMP_TEX_XPARENT;
2699 
2700 		//check if its compressed
2701 		switch (entry->comp_type) {
2702 		case BM_TYPE_NONE:
2703 			continue;
2704 
2705 		case BM_TYPE_DXT1:
2706 			entry->used_flags = BMP_TEX_DXT1;
2707 			continue;
2708 
2709 		case BM_TYPE_DXT3:
2710 			entry->used_flags = BMP_TEX_DXT3;
2711 			continue;
2712 
2713 		case BM_TYPE_DXT5:
2714 			entry->used_flags = BMP_TEX_DXT5;
2715 			continue;
2716 
2717 		case BM_TYPE_BC7:
2718 			entry->used_flags = BMP_TEX_BC7;
2719 			continue;
2720 
2721 		case BM_TYPE_CUBEMAP_DXT1:
2722 		case BM_TYPE_CUBEMAP_DXT3:
2723 		case BM_TYPE_CUBEMAP_DXT5:
2724 			entry->used_flags = BMP_TEX_CUBEMAP;
2725 			continue;
2726 
2727 		default:
2728 			continue;
2729 		}
2730 	}
2731 }
2732 
bm_print_bitmaps()2733 void bm_print_bitmaps() {
2734 #ifdef BMPMAN_NDEBUG
2735 	for (auto& block : bm_blocks) {
2736 		for (auto& slot : block) {
2737 			auto& entry = slot.entry;
2738 
2739 			if (entry.type != BM_TYPE_NONE) {
2740 				if (entry.data_size) {
2741 					nprintf(("BMP DEBUG", "BMPMAN = num: %d, name: %s, handle: %d - (%s) size: %.3fM\n", entry.handle, entry.filename, entry.handle, entry.data_size
2742 																																					 ? NOX(
2743 							"*LOCKED*") : NOX(""), ((float) entry.data_size / 1024.0f) / 1024.0f));
2744 				} else {
2745 					nprintf(("BMP DEBUG", "BMPMAN = num: %d, name: %s, handle: %d\n", entry.handle, entry.filename, entry.handle));
2746 				}
2747 			}
2748 		}
2749 	}
2750 
2751 	nprintf(("BMP DEBUG", "BMPMAN = LOCKED memory usage: %.3fM\n", ((float) bm_texture_ram / 1024.0f) / 1024.0f));
2752 #endif
2753 }
2754 
bm_release(int handle,int clear_render_targets)2755 int bm_release(int handle, int clear_render_targets) {
2756 	Assert(handle >= 0);
2757 
2758 	bitmap_entry *be;
2759 
2760 	be = bm_get_entry(handle);
2761 
2762 	if (be->type == BM_TYPE_NONE) {
2763 		return 0;	// Already been released?
2764 	}
2765 
2766 	Assertion(be->handle == handle, "Invalid bitmap handle number %d (expected %d) for %s passed to bm_release()\n", be->handle, handle, be->filename);
2767 
2768 	if (!clear_render_targets && ((be->type == BM_TYPE_RENDER_TARGET_STATIC) || (be->type == BM_TYPE_RENDER_TARGET_DYNAMIC))) {
2769 		nprintf(("BmpMan", "Tried to release a render target!\n"));
2770 		return 0;
2771 	}
2772 
2773 	// If it is locked, cannot free it.
2774 	if (be->ref_count != 0) {
2775 		nprintf(("BmpMan", "Tried to release %s that has a lock count of %d.. not releasing\n", be->filename, be->ref_count));
2776 		return 0;
2777 	}
2778 
2779 	// kind of like ref_count except it gets around the lock/unlock usage problem
2780 	// this gets set for each bm_load() call so we can make sure and not unload it
2781 	// from memory, even if we *can*, until it's really not needed anymore
2782 	if (be->load_count > 0)
2783 		be->load_count--;
2784 
2785 	if (be->load_count != 0) {
2786 		nprintf(("BmpMan", "Tried to release %s that has a load count of %d.. not releasing\n", be->filename, be->load_count + 1));
2787 		return 0;
2788 	}
2789 
2790 	if (be->type != BM_TYPE_USER) {
2791 		nprintf(("BmpMan", "Releasing bitmap %s with handle %i\n", be->filename, handle));
2792 	}
2793 
2794 	// be sure that all frames of an ani are unloaded - taylor
2795 	if (bm_is_anim(be)) {
2796 		int i, first = be->info.ani.first_frame;
2797 		int total = bm_get_entry(first)->info.ani.num_frames;
2798 
2799 		// The graphics system requires that the bitmap information for the whole animation is still valid when the data
2800 		// is freed so we first free all the data and then clear the bitmaps data
2801 		for (i = 0; i < total; i++) {
2802 			bm_free_data(bm_get_slot(first + i), true);
2803 		}
2804 
2805 		for (i = 0; i < total; i++) {
2806 			auto entry = bm_get_entry(first + i);
2807 
2808 			memset(entry, 0, sizeof(bitmap_entry));
2809 
2810 			entry->type = BM_TYPE_NONE;
2811 			entry->comp_type = BM_TYPE_NONE;
2812 			entry->dir_type = CF_TYPE_ANY;
2813 			// Fill in bogus structures!
2814 
2815 			// For debugging:
2816 			strcpy_s(entry->filename, "IVE_BEEN_RELEASED!");
2817 			entry->signature = 0xDEADBEEF;									// a unique signature identifying the data
2818 
2819 			// bookeeping
2820 			entry->ref_count = -1;									// Number of locks on bitmap.  Can't unload unless ref_count is 0.
2821 
2822 			// Stuff needed for animations
2823 			entry->info.ani.first_frame = -1;
2824 
2825 			entry->handle = -1;
2826 		}
2827 	} else {
2828 		auto slot = bm_get_slot(handle);
2829 		auto entry = &slot->entry;
2830 
2831 		bm_free_data(slot, true);		// clears flags, bbp, data, etc
2832 
2833 
2834 		memset(entry, 0, sizeof(bitmap_entry));
2835 
2836 		entry->type = BM_TYPE_NONE;
2837 		entry->comp_type = BM_TYPE_NONE;
2838 		entry->dir_type = CF_TYPE_ANY;
2839 		// Fill in bogus structures!
2840 
2841 		// For debugging:
2842 		strcpy_s(entry->filename, "IVE_BEEN_RELEASED!");
2843 		entry->signature = 0xDEADBEEF;									// a unique signature identifying the data
2844 
2845 		// bookeeping
2846 		entry->ref_count = -1;									// Number of locks on bitmap.  Can't unload unless ref_count is 0.
2847 
2848 		// Stuff needed for animations
2849 		entry->info.ani.first_frame = -1;
2850 
2851 		entry->handle = -1;
2852 	}
2853 
2854 	return 1;
2855 }
2856 
bm_release_rendertarget(int handle)2857 bool bm_release_rendertarget(int handle) {
2858 	Assert(handle >= 0);
2859 
2860 	bitmap_entry* be = bm_get_entry(handle);
2861 
2862 	if (be->type == BM_TYPE_NONE) {
2863 		return false;	// Already been released?
2864 	}
2865 
2866 	Assertion(be->handle == handle, "Invalid bitmap handle number %d (expected %d) for %s passed to bm_release_rendertarget()\n", be->handle, handle, be->filename);
2867 	Assertion(!bm_is_anim(be), "Cannot release a render target of an animation (bitmap handle number %d for %s)!\n", be->handle, be->filename);
2868 
2869 	if (!((be->type == BM_TYPE_RENDER_TARGET_STATIC) || (be->type == BM_TYPE_RENDER_TARGET_DYNAMIC))) {
2870 		nprintf(("BmpMan", "Tried to release a render target of a non-rendered bitmap!\n"));
2871 		return false;
2872 	}
2873 
2874 	gr_bm_free_data(bm_get_slot(handle), false);
2875 
2876 	return true;
2877 }
2878 
bm_reload(int bitmap_handle,const char * filename)2879 int bm_reload(int bitmap_handle, const char* filename) {
2880 	Assertion(bm_inited, "bmpman must be initialized before this function can be called!");
2881 
2882 	// if no file was passed then get out now
2883 	if ((filename == NULL) || (strlen(filename) <= 0))
2884 		return -1;
2885 
2886 	auto entry = bm_get_entry(bitmap_handle);
2887 
2888 	if (entry->type == BM_TYPE_NONE)
2889 		return -1;
2890 
2891 	if (entry->ref_count) {
2892 		nprintf(("BmpMan", "Trying to reload a bitmap that is still locked. Filename: %s, ref_count: %d", entry->filename, entry->ref_count));
2893 		return -1;
2894 	}
2895 
2896 	strcpy_s(entry->filename, filename);
2897 	return bitmap_handle;
2898 }
2899 
BM_SELECT_ALPHA_TEX_FORMAT()2900 void BM_SELECT_ALPHA_TEX_FORMAT() {
2901 	Gr_current_red = &Gr_ta_red;
2902 	Gr_current_green = &Gr_ta_green;
2903 	Gr_current_blue = &Gr_ta_blue;
2904 	Gr_current_alpha = &Gr_ta_alpha;
2905 
2906 	// setup pointers
2907 	// should always assume that 16-bit is the default request
2908 	bm_set_components = bm_set_components_argb_16_tex;
2909 }
2910 
BM_SELECT_SCREEN_FORMAT()2911 void BM_SELECT_SCREEN_FORMAT() {
2912 	Gr_current_red = &Gr_red;
2913 	Gr_current_green = &Gr_green;
2914 	Gr_current_blue = &Gr_blue;
2915 	Gr_current_alpha = &Gr_alpha;
2916 
2917 	// setup pointers
2918 	// should always assume that 16-bit is the default request
2919 	bm_set_components = bm_set_components_argb_16_screen;
2920 }
2921 
BM_SELECT_TEX_FORMAT()2922 void BM_SELECT_TEX_FORMAT() {
2923 	Gr_current_red = &Gr_t_red;
2924 	Gr_current_green = &Gr_t_green;
2925 	Gr_current_blue = &Gr_t_blue;
2926 	Gr_current_alpha = &Gr_t_alpha;
2927 
2928 	// setup pointers
2929 	// should always assume that 16-bit is the default request
2930 	bm_set_components = bm_set_components_argb_16_tex;
2931 }
2932 
bm_set_components_argb_16_screen(ubyte * pixel,ubyte * rv,ubyte * gv,ubyte * bv,ubyte * av)2933 void bm_set_components_argb_16_screen(ubyte *pixel, ubyte *rv, ubyte *gv, ubyte *bv, ubyte *av) {
2934 	if (*av == 0) {
2935 		*((unsigned short*)pixel) = (unsigned short)Gr_current_green->mask;
2936 		return;
2937 	}
2938 
2939 	*((unsigned short*)pixel) = (unsigned short)(((int)*rv / Gr_current_red->scale) << Gr_current_red->shift);
2940 	*((unsigned short*)pixel) |= (unsigned short)(((int)*gv / Gr_current_green->scale) << Gr_current_green->shift);
2941 	*((unsigned short*)pixel) |= (unsigned short)(((int)*bv / Gr_current_blue->scale) << Gr_current_blue->shift);
2942 }
2943 
bm_set_components_argb_16_tex(ubyte * pixel,ubyte * rv,ubyte * gv,ubyte * bv,ubyte * av)2944 void bm_set_components_argb_16_tex(ubyte *pixel, ubyte *rv, ubyte *gv, ubyte *bv, ubyte *av) {
2945 	if (*av == 0) {
2946 		*((unsigned short*)pixel) = 0;
2947 		return;
2948 	}
2949 
2950 	*((unsigned short*)pixel) = (unsigned short)(((int)*rv / Gr_current_red->scale) << Gr_current_red->shift);
2951 	*((unsigned short*)pixel) |= (unsigned short)(((int)*gv / Gr_current_green->scale) << Gr_current_green->shift);
2952 	*((unsigned short*)pixel) |= (unsigned short)(((int)*bv / Gr_current_blue->scale) << Gr_current_blue->shift);
2953 	*((unsigned short*)pixel) |= (unsigned short)(Gr_current_alpha->mask);
2954 }
2955 
bm_set_low_mem(int mode)2956 void bm_set_low_mem(int mode) {
2957 	Assert((mode >= 0) && (mode <= 2));
2958 
2959 	CLAMP(mode, 0, 2);
2960 	Bm_low_mem = mode;
2961 }
2962 
bm_set_render_target(int handle,int face)2963 bool bm_set_render_target(int handle, int face) {
2964 	GR_DEBUG_SCOPE("Set render target");
2965 
2966 	auto entry = handle >= 0 ? bm_get_entry(handle) : nullptr;
2967 
2968 	if (handle >= 0) {
2969 		if ((entry->type != BM_TYPE_RENDER_TARGET_STATIC) && (entry->type != BM_TYPE_RENDER_TARGET_DYNAMIC)) {
2970 			// odds are that someone passed a normal texture created with bm_load()
2971 			mprintf(("Trying to set invalid bitmap (handle: %i) as render target!\n", handle));
2972 			return false;
2973 		}
2974 	}
2975 
2976 	if (gr_bm_set_render_target(handle, face)) {
2977 		if (gr_screen.rendering_to_texture == -1) {
2978 			//if we are moving from the back buffer to a texture save whatever the current settings are
2979 			gr_screen.save_max_w = gr_screen.max_w;
2980 			gr_screen.save_max_h = gr_screen.max_h;
2981 
2982 			gr_screen.save_max_w_unscaled = gr_screen.max_w_unscaled;
2983 			gr_screen.save_max_h_unscaled = gr_screen.max_h_unscaled;
2984 
2985 			gr_screen.save_max_w_unscaled_zoomed = gr_screen.max_w_unscaled_zoomed;
2986 			gr_screen.save_max_h_unscaled_zoomed = gr_screen.max_h_unscaled_zoomed;
2987 
2988 			gr_screen.save_center_w = gr_screen.center_w;
2989 			gr_screen.save_center_h = gr_screen.center_h;
2990 
2991 			gr_screen.save_center_offset_x = gr_screen.center_offset_x;
2992 			gr_screen.save_center_offset_y = gr_screen.center_offset_y;
2993 		}
2994 
2995 		if (handle < 0) {
2996 			gr_screen.max_w = gr_screen.save_max_w;
2997 			gr_screen.max_h = gr_screen.save_max_h;
2998 
2999 			gr_screen.max_w_unscaled = gr_screen.save_max_w_unscaled;
3000 			gr_screen.max_h_unscaled = gr_screen.save_max_h_unscaled;
3001 
3002 			gr_screen.max_w_unscaled_zoomed = gr_screen.save_max_w_unscaled_zoomed;
3003 			gr_screen.max_h_unscaled_zoomed = gr_screen.save_max_h_unscaled_zoomed;
3004 
3005 			gr_screen.center_w = gr_screen.save_center_w;
3006 			gr_screen.center_h = gr_screen.save_center_h;
3007 
3008 			gr_screen.center_offset_x = gr_screen.save_center_offset_x;
3009 			gr_screen.center_offset_y = gr_screen.save_center_offset_y;
3010 		} else {
3011 			gr_screen.max_w = entry->bm.w;
3012 			gr_screen.max_h = entry->bm.h;
3013 
3014 			gr_screen.max_w_unscaled = entry->bm.w;
3015 			gr_screen.max_h_unscaled = entry->bm.h;
3016 
3017 			gr_screen.max_w_unscaled_zoomed = entry->bm.w;
3018 			gr_screen.max_h_unscaled_zoomed = entry->bm.h;
3019 
3020 			gr_screen.center_w = entry->bm.w;
3021 			gr_screen.center_h = entry->bm.h;
3022 
3023 			gr_screen.center_offset_x = 0;
3024 			gr_screen.center_offset_y = 0;
3025 		}
3026 
3027 		gr_screen.rendering_to_face = face;
3028 		gr_screen.rendering_to_texture = handle;
3029 
3030 		gr_reset_clip();
3031 
3032 		gr_setup_viewport();
3033 
3034 		return true;
3035 	}
3036 
3037 	return false;
3038 }
3039 
bm_unload(int handle,int clear_render_targets,bool nodebug)3040 int bm_unload(int handle, int clear_render_targets, bool nodebug) {
3041 	bitmap_entry *be;
3042 	bitmap *bmp;
3043 
3044 	if (handle == -1) {
3045 		return -1;
3046 	}
3047 
3048 	be = bm_get_entry(handle);
3049 	bmp = &be->bm;
3050 
3051 	if (!clear_render_targets && ((be->type == BM_TYPE_RENDER_TARGET_STATIC) || (be->type == BM_TYPE_RENDER_TARGET_DYNAMIC))) {
3052 		return -1;
3053 	}
3054 
3055 	if (be->type == BM_TYPE_NONE) {
3056 		return -1;		// Already been released
3057 	}
3058 
3059 	Assert(be->handle == handle);		// INVALID BITMAP HANDLE!
3060 
3061 	// If it is locked, cannot free it.
3062 	if (be->ref_count != 0 && !nodebug) {
3063 		nprintf(("BmpMan", "Tried to unload %s that has a lock count of %d.. not unloading\n", be->filename, be->ref_count));
3064 		return 0;
3065 	}
3066 
3067 	// kind of like ref_count except it gets around the lock/unlock usage problem
3068 	// this gets set for each bm_load() call so we can make sure and not unload it
3069 	// from memory, even if we *can*, until it's really not needed anymore
3070 	if (!Bm_ignore_load_count) {
3071 		if (be->load_count > 0)
3072 			be->load_count--;
3073 
3074 		if (be->load_count != 0 && !nodebug) {
3075 			nprintf(("BmpMan", "Tried to unload %s that has a load count of %d.. not unloading\n", be->filename, be->load_count + 1));
3076 			return 0;
3077 		}
3078 	}
3079 
3080 	// be sure that all frames of an ani are unloaded - taylor
3081 	if (bm_is_anim(be) == true) {
3082 		int i, first = be->info.ani.first_frame;
3083 
3084 		auto first_entry = bm_get_entry(first);
3085 
3086 		// for the unload all case, don't try to unload every frame of every frame
3087 		// all additional frames automatically get unloaded with the first one
3088 		if ((handle != be->info.ani.first_frame) && (first_entry->bm.data == 0))
3089 			return 1;
3090 
3091 		for (i = 0; i < first_entry->info.ani.num_frames; i++) {
3092 			if (!nodebug)
3093 				nprintf(("BmpMan", "Unloading %s frame %d.  %dx%dx%d\n", be->filename, i, bmp->w, bmp->h, bmp->bpp));
3094 			bm_free_data(bm_get_slot(first + i));		// clears flags, bbp, data, etc
3095 		}
3096 	} else {
3097 		if (!nodebug)
3098 			nprintf(("BmpMan", "Unloading %s.  %dx%dx%d\n", be->filename, bmp->w, bmp->h, bmp->bpp));
3099 		bm_free_data(bm_get_slot(handle));		// clears flags, bbp, data, etc
3100 	}
3101 
3102 	return 1;
3103 }
3104 
bm_unload_all()3105 void bm_unload_all() {
3106 	// since bm_unload_all() should only be called from game_shutdown() it should be
3107 	// safe to ignore load_count's and unload anyway
3108 	Bm_ignore_load_count = 1;
3109 	for(auto& block : bm_blocks) {
3110 		for (auto& slot : block) {
3111 			auto& entry = slot.entry;
3112 
3113 			if (entry.type != BM_TYPE_NONE) {
3114 				bm_unload(entry.handle, 1);
3115 			}
3116 		}
3117 	}
3118 
3119 	Bm_ignore_load_count = 0;
3120 }
3121 
bm_unload_fast(int handle,int clear_render_targets)3122 int bm_unload_fast(int handle, int clear_render_targets) {
3123 	bitmap_entry *be;
3124 	bitmap *bmp;
3125 
3126 	be = bm_get_entry(handle);
3127 	bmp = &be->bm;
3128 
3129 	if (be->type == BM_TYPE_NONE) {
3130 		return -1;		// Already been released
3131 	}
3132 
3133 	if (be->type == BM_TYPE_USER) {
3134 		return -1;
3135 	}
3136 
3137 	if (!clear_render_targets && ((be->type == BM_TYPE_RENDER_TARGET_STATIC) || (be->type == BM_TYPE_RENDER_TARGET_DYNAMIC))) {
3138 		return -1;
3139 	}
3140 
3141 	// If it is locked, cannot free it.
3142 	if (be->ref_count != 0) {
3143 		nprintf(("BmpMan", "Tried to unload_fast %s that has a lock count of %d.. not unloading\n", be->filename, be->ref_count));
3144 		return 0;
3145 	}
3146 
3147 	Assert(be->handle == handle);		// INVALID BITMAP HANDLE!
3148 
3149 	// unlike bm_unload(), we handle each frame of an animation separately, for safer use in the graphics API
3150 	nprintf(("BmpMan", "Fast-unloading %s.  %dx%dx%d\n", be->filename, bmp->w, bmp->h, bmp->bpp));
3151 	bm_free_data_fast(handle);		// clears flags, bbp, data, etc
3152 
3153 	return 1;
3154 }
3155 
bm_unlock(int handle)3156 void bm_unlock(int handle) {
3157 	bitmap_entry	*be;
3158 
3159 	Assertion(bm_inited, "bmpman must be initialized before this function can be called!");
3160 
3161 	be = bm_get_entry(handle);
3162 
3163 	be->ref_count--;
3164 	Assert(be->ref_count >= 0);		// Trying to unlock data more times than lock was called!!!
3165 }
3166 
bm_update_memory_used(int n,size_t size)3167 void bm_update_memory_used(int n, size_t size)
3168 {
3169 #ifdef BMPMAN_NDEBUG
3170 	auto entry = bm_get_entry(n);
3171 	Assert( entry->data_size == 0 );
3172 	entry->data_size += size;
3173 	bm_texture_ram += size;
3174 #endif
3175 }
3176 
find_block_of(int n,int start_block)3177 static int find_block_of(int n, int start_block)
3178 {
3179 	Assertion(n < (int) BM_BLOCK_SIZE, "Can not allocate bitmap block with %d slots! Block size is only "
3180 		SIZE_T_ARG
3181 		"!", n, BM_BLOCK_SIZE);
3182 
3183 	int nstart = 0;
3184 
3185 	if (n < 1) {
3186 		Int3();
3187 		return -1;
3188 	}
3189 
3190 	int block_idx;
3191 	for (block_idx = start_block; block_idx < (int)bm_blocks.size(); ++block_idx) {
3192 		auto& block = bm_blocks[block_idx];
3193 
3194 		auto entry_idx = 0;
3195 		auto cnt = 0;
3196 		for (auto& slot : block) {
3197 			auto& entry = slot.entry;
3198 
3199 			if (entry.type == BM_TYPE_NONE) {
3200 				if (cnt == 0) {
3201 					nstart = get_handle(block_idx, entry_idx);
3202 				}
3203 
3204 				cnt++;
3205 			} else {
3206 				cnt = 0;
3207 			}
3208 
3209 			if (cnt == n) {
3210 				return nstart;
3211 			}
3212 
3213 			++entry_idx;
3214 		}
3215 	}
3216 
3217 	// If we are here it means that we could not find a block to store the bitmap blocks in.
3218 	// In that case we just allocate a new block and try to allocate the block in that
3219 	allocate_new_block();
3220 
3221 	// This call is sure to succeed since we now have a contiguous block of size BM_BLOCK_SIZE and n must be less than that
3222 	return find_block_of(n, block_idx);
3223 }
3224 
bm_is_texture_array(const int handle)3225 bool bm_is_texture_array(const int handle) {
3226 	auto entry = bm_get_entry(handle);
3227 
3228 	if (!bm_is_anim(entry)) {
3229 		return true;
3230 	}
3231 
3232 	// This will return the index of the first animation frame
3233 	return entry->info.ani.is_array;
3234 }
3235 
bm_get_base_frame(const int handle,int * num_frames)3236 int bm_get_base_frame(const int handle, int* num_frames) {
3237 	if (handle == -1) {
3238 		if (num_frames != nullptr) {
3239 			*num_frames = 0;
3240 		}
3241 		return -1;
3242 	}
3243 
3244 	// If the bitmap is no animation then num_frames will still be at least 1
3245 	auto animation_begin = bm_get_info(handle, nullptr, nullptr, nullptr, num_frames);
3246 
3247 	if (animation_begin < 0) {
3248 		// Some kind of error
3249 		return -1;
3250 	}
3251 
3252 	if (!bm_is_texture_array(animation_begin)) {
3253 		// If this is not a texture array then we just treat it like a single frame animation
3254 		animation_begin = handle;
3255 		if (num_frames != nullptr) {
3256 			*num_frames = 1;
3257 		}
3258 	}
3259 	return animation_begin;
3260 }
bm_get_array_index(const int handle)3261 int bm_get_array_index(const int handle) {
3262 	return handle - bm_get_base_frame(handle, nullptr);
3263 }
3264 
bmpman_count_bitmaps()3265 int bmpman_count_bitmaps() {
3266 	if (!bm_inited)
3267 		return -1;
3268 
3269 	int total_bitmaps = 0;
3270 	for(auto& block : bm_blocks) {
3271 		for (auto& slot : block) {
3272 			auto& entry = slot.entry;
3273 
3274 			if (entry.type != BM_TYPE_NONE) {
3275 				total_bitmaps++;
3276 			}
3277 		}
3278 	}
3279 
3280 	return total_bitmaps;
3281 }
3282 
bmpman_count_available_slots()3283 int bmpman_count_available_slots() {
3284 	if (!bm_inited)
3285 		return -1;
3286 
3287 	return (int) (bm_blocks.size() * BM_BLOCK_SIZE);
3288 }
3289 
bm_validate_filename(const SCP_string & file,bool single_frame,bool animation)3290 bool bm_validate_filename(const SCP_string& file, bool single_frame, bool animation) {
3291 	Assertion(single_frame || animation, "At least one of single_frame or animation must be true!");
3292 
3293 	if (!VALID_FNAME(file.c_str())) {
3294 		return false;
3295 	}
3296 
3297 	int handle = -1;
3298 	if (single_frame && animation) {
3299 		// Caller has indicated that single frames and animations are valid
3300 		handle = bm_load_either(file.c_str());
3301 	} else if (single_frame) {
3302 		handle = bm_load(file);
3303 	} else {
3304 		handle = bm_load_animation(file.c_str());
3305 	}
3306 	if (handle != -1) {
3307 		bm_release(handle);
3308 		return true;
3309 	}
3310 	return false;
3311 }
bm_to_sdl_surface(int handle)3312 SDL_Surface* bm_to_sdl_surface(int handle) {
3313 	Assertion(bm_is_valid(handle), "%d is no valid bitmap handle!", handle);
3314 
3315 	int w;
3316 	int h;
3317 
3318 	bm_get_info(handle, &w, &h, nullptr, nullptr);
3319 	Uint32 rmask, gmask, bmask, amask;
3320 
3321 #if SDL_BYTEORDER == SDL_BIG_ENDIAN
3322 	rmask = 0x0000ff00;
3323 		gmask = 0x00ff0000;
3324 		bmask = 0xff000000;
3325 		amask = 0x000000ff;
3326 #else
3327 	rmask = 0x00ff0000;
3328 	gmask = 0x0000ff00;
3329 	bmask = 0x000000ff;
3330 	amask = 0xff000000;
3331 #endif
3332 
3333 	SDL_Surface* bitmapSurface = SDL_CreateRGBSurface(0, w, h, 32, rmask, gmask, bmask, amask);
3334 	if (SDL_LockSurface(bitmapSurface) < 0) {
3335 		return nullptr;
3336 	}
3337 	bitmap* bmp = bm_lock(handle, 32, BMP_TEX_XPARENT);
3338 
3339 	memcpy(bitmapSurface->pixels, reinterpret_cast<void*>(bmp->data), static_cast<size_t>(w * h * 4));
3340 
3341 	bm_unlock(handle);
3342 	SDL_UnlockSurface(bitmapSurface);
3343 
3344 	return bitmapSurface;
3345 
3346 }
3347