1 #include "globalincs/pstypes.h"
2 #include "globalincs/globals.h"
3 #include "bmpman/bmpman.h"
4 #include "graphics/generic.h"
5 #include "graphics/2d.h"
6 #include "anim/animplay.h"
7 #include "anim/packunpack.h"
8 #define BMPMAN_INTERNAL
9 #include "bmpman/bm_internal.h"
10 #ifdef _WIN32
11 #include <windows.h>	// for MAX_PATH
12 #else
13 #define MAX_PATH	255
14 #endif
15 //#define TIMER
16 #ifdef TIMER
17 #include "io/timer.h"
18 #endif
19 
20 //we check background type to avoid messed up colours for ANI
21 #define ANI_BPP_CHECK		(ga->ani.bg_type == BM_TYPE_PCX) ? 16 : 32
22 
23 // Goober5000
generic_anim_init_and_stream(generic_anim * anim,const char * anim_filename,ubyte bg_type,bool attempt_hi_res)24 int generic_anim_init_and_stream(generic_anim *anim, const char *anim_filename, ubyte bg_type, bool attempt_hi_res)
25 {
26 	int stream_result = -1;
27 	char filename[NAME_LENGTH];
28 	char *p;
29 
30 	Assert(anim != NULL);
31 	Assert(anim_filename != NULL);
32 
33 	// hi-res support
34 	if (attempt_hi_res && (gr_screen.res == GR_1024)) {
35 		// attempt to load a hi-res animation
36 		memset(filename, 0, NAME_LENGTH);
37 		strcpy_s(filename, "2_");
38 		strncat(filename, anim_filename, NAME_LENGTH - 3);
39 
40 		// remove extension
41 		p = strchr(filename, '.');
42 		if(p) {
43 			*p = '\0';
44 		}
45 
46 		// attempt to stream the hi-res ani
47 		generic_anim_init(anim, filename);
48 		anim->ani.bg_type = bg_type;
49 		stream_result = generic_anim_stream(anim);
50 	}
51 
52 	// we failed to stream hi-res, or we aren't running in hi-res, so try low-res
53 	if (stream_result < 0) {
54 		strcpy_s(filename, anim_filename);
55 
56 		// remove extension
57 		p = strchr(filename, '.');
58 		if(p) {
59 			*p = '\0';
60 		}
61 
62 		// attempt to stream the low-res ani
63 		generic_anim_init(anim, filename);
64 		anim->ani.bg_type = bg_type;
65 		stream_result = generic_anim_stream(anim);
66 	}
67 
68 	return stream_result;
69 }
70 
71 // Goober5000
generic_anim_init(generic_anim * ga)72 void generic_anim_init(generic_anim *ga)
73 {
74 	generic_anim_init(ga, NULL);
75 }
76 
77 // Goober5000
generic_anim_init(generic_anim * ga,const char * filename)78 void generic_anim_init(generic_anim *ga, const char *filename)
79 {
80 	if (filename != NULL)
81 		strcpy_s(ga->filename, filename);
82 	else
83 		memset(ga->filename, 0, MAX_FILENAME_LEN);
84 	ga->first_frame = -1;
85 	ga->num_frames = 0;
86 	ga->keyframe = 0;
87 	ga->keyoffset = 0;
88 	ga->current_frame = 0;
89 	ga->previous_frame = -1;
90 	ga->direction = GENERIC_ANIM_DIRECTION_FORWARDS;
91 	ga->done_playing = 0;
92 	ga->total_time = 0.0f;
93 	ga->anim_time = 0.0f;
94 
95 	//we only care about the stuff below if we're streaming
96 	ga->ani.animation = NULL;
97 	ga->ani.instance = NULL;
98 	ga->ani.bg_type = 0;
99 	ga->type = BM_TYPE_NONE;
100 	ga->streaming = 0;
101 	ga->buffer = NULL;
102 	ga->height = 0;
103 	ga->width = 0;
104 	ga->bitmap_id = -1;
105 	ga->use_hud_color = false;
106 }
107 
108 // CommanderDJ - same as generic_anim_init, just with an SCP_string
generic_anim_init(generic_anim * ga,const SCP_string & filename)109 void generic_anim_init(generic_anim *ga, const SCP_string& filename)
110 {
111 	generic_anim_init(ga);
112 	filename.copy(ga->filename, MAX_FILENAME_LEN - 1);
113 }
114 
115 // Goober5000
generic_bitmap_init(generic_bitmap * gb,const char * filename)116 void generic_bitmap_init(generic_bitmap *gb, const char *filename)
117 {
118 	if (filename == NULL) {
119 		gb->filename[0] = '\0';
120 	} else {
121 		strncpy(gb->filename, filename, MAX_FILENAME_LEN - 1);
122 	}
123 
124 	gb->bitmap_id = -1;
125 }
126 
127 // Goober5000
128 // load a generic_anim
129 // return 0 is successful, otherwise return -1
generic_anim_load(generic_anim * ga)130 int generic_anim_load(generic_anim *ga)
131 {
132 	int fps;
133 
134 	if ( !VALID_FNAME(ga->filename) )
135 		return -1;
136 
137 	ga->first_frame = bm_load_animation(ga->filename, &ga->num_frames, &fps, &ga->keyframe);
138 	//mprintf(("generic_anim_load: %s - keyframe = %d\n", ga->filename, ga->keyframe));
139 
140 	if (ga->first_frame < 0)
141 		return -1;
142 
143 	Assert(fps != 0);
144 	ga->total_time = ga->num_frames / (float)fps;
145 	ga->done_playing = 0;
146 	ga->anim_time = 0.0f;
147 
148 	return 0;
149 }
150 
generic_anim_stream(generic_anim * ga)151 int generic_anim_stream(generic_anim *ga)
152 {
153 	CFILE *img_cfp = NULL;
154 	int anim_fps = 0;
155 	char full_path[MAX_PATH];
156 	int size = 0, offset = 0;
157 	const int NUM_TYPES = 2;
158 	const ubyte type_list[NUM_TYPES] = {BM_TYPE_EFF, BM_TYPE_ANI};
159 	const char *ext_list[NUM_TYPES] = {".eff", ".ani"};
160 	int rval = -1;
161 	int bpp;
162 
163 	ga->type = BM_TYPE_NONE;
164 
165 	rval = cf_find_file_location_ext(ga->filename, NUM_TYPES, ext_list, CF_TYPE_ANY, sizeof(full_path) - 1, full_path, &size, &offset, 0);
166 
167 	// could not be found, or is invalid for some reason
168 	if ( (rval < 0) || (rval >= NUM_TYPES) )
169 		return -1;
170 
171 	//make sure we can open it
172 	img_cfp = cfopen_special(full_path, "rb", size, offset, CF_TYPE_ANY);
173 
174 	if (img_cfp == NULL) {
175 		return -1;
176 	}
177 
178 	strcat_s(ga->filename, ext_list[rval]);
179 	ga->type = type_list[rval];
180 	//seek to the end
181 	cfseek(img_cfp, 0, CF_SEEK_END);
182 
183 	cfclose(img_cfp);
184 
185 	//TODO: add streaming EFF
186 	if(ga->type == BM_TYPE_ANI) {
187 		bpp = ANI_BPP_CHECK;
188 		if(ga->use_hud_color)
189 			bpp = 8;
190 		ga->ani.animation = anim_load(ga->filename, CF_TYPE_ANY, 0);
191 		ga->ani.instance = init_anim_instance(ga->ani.animation, bpp);
192 
193 	#ifndef NDEBUG
194 		// for debug of ANI sizes
195 		strcpy_s(ga->ani.animation->name, ga->filename);
196 	#endif
197 
198 		ga->num_frames = ga->ani.animation->total_frames;
199 		anim_fps = ga->ani.animation->fps;
200 		ga->height = ga->ani.animation->height;
201 		ga->width = ga->ani.animation->width;
202 		ga->buffer = ga->ani.instance->frame;
203 		ga->bitmap_id = bm_create(bpp, ga->width, ga->height, ga->buffer, (bpp==8)?BMP_AABITMAP:0);
204 		ga->ani.instance->last_bitmap = -1;
205 
206 		ga->ani.instance->file_offset = ga->ani.animation->file_offset;
207 		ga->ani.instance->data = ga->ani.animation->data;
208 
209 		ga->previous_frame = -1;
210 	}
211 	else {
212 		bpp = 32;
213 		if(ga->use_hud_color)
214 			bpp = 8;
215 		bm_load_and_parse_eff(ga->filename, CF_TYPE_ANY, &ga->num_frames, &anim_fps, &ga->keyframe, 0);
216 		char *p = strrchr( ga->filename, '.' );
217 		if ( p )
218 			*p = 0;
219 		char frame_name[32];
220 		snprintf(frame_name, 32, "%s_0000", ga->filename);
221 		ga->bitmap_id = bm_load(frame_name);
222 		if(ga->bitmap_id < 0) {
223 			mprintf(("Cannot find first frame for eff streaming. eff Filename: %s", ga->filename));
224 			return -1;
225 		}
226 		snprintf(frame_name, 32, "%s_0001", ga->filename);
227 		ga->eff.next_frame = bm_load(frame_name);
228 		bm_get_info(ga->bitmap_id, &ga->width, &ga->height);
229 		ga->previous_frame = 0;
230 	}
231 
232 	// keyframe info
233 	if (ga->type == BM_TYPE_ANI) {
234 		//we only care if there are 2 keyframes - first frame, other frame to jump to for ship/weapons
235 		//mainhall door anis hav every frame as keyframe, so we don't care
236 		//other anis only have the first frame
237 		if(ga->ani.animation->num_keys == 2) {
238 			int key1 = ga->ani.animation->keys[0].frame_num;
239 			int key2 = ga->ani.animation->keys[1].frame_num;
240 
241 			if (key1 < 0 || key1 >= ga->num_frames) key1 = -1;
242 			if (key2 < 0 || key2 >= ga->num_frames) key2 = -1;
243 
244 			// some retail anis have their keyframes reversed
245 			// and some have their keyframes out of bounds
246 			if (key1 >= 0 && key1 >= key2) {
247 				ga->keyframe = ga->ani.animation->keys[0].frame_num;
248 				ga->keyoffset = ga->ani.animation->keys[0].offset;
249 			}
250 			else if (key2 >= 0 && key2 >= key1) {
251 				ga->keyframe = ga->ani.animation->keys[1].frame_num;
252 				ga->keyoffset = ga->ani.animation->keys[1].offset;
253 			}
254 		}
255 	}
256 
257 	ga->streaming = 1;
258 
259 	Assert(anim_fps != 0);
260 	ga->total_time = ga->num_frames / (float) anim_fps;
261 	ga->done_playing = 0;
262 	ga->anim_time = 0.0f;
263 
264 	return 0;
265 }
266 
generic_bitmap_load(generic_bitmap * gb)267 int generic_bitmap_load(generic_bitmap *gb)
268 {
269 	if ( !VALID_FNAME(gb->filename) )
270 		return -1;
271 
272 	gb->bitmap_id = bm_load(gb->filename);
273 
274 	if (gb->bitmap_id < 0)
275 		return -1;
276 
277 	return 0;
278 }
279 
generic_anim_unload(generic_anim * ga)280 void generic_anim_unload(generic_anim *ga)
281 {
282 	if(ga->num_frames > 0) {
283 		if(ga->streaming) {
284 			if(ga->type == BM_TYPE_ANI) {
285 				anim_free(ga->ani.animation);
286 				free_anim_instance(ga->ani.instance);
287 			}
288 			if(ga->type == BM_TYPE_EFF) {
289 				if(ga->eff.next_frame >= 0)
290 					bm_release(ga->eff.next_frame);
291 				if(ga->bitmap_id >= 0)
292 					bm_release(ga->bitmap_id);
293 			}
294 		}
295 		else {
296 			//trying to release the first frame will release ALL frames
297 			bm_release(ga->first_frame);
298 		}
299 		if(ga->buffer) {
300 			bm_release(ga->bitmap_id);
301 		}
302 	}
303 	generic_anim_init(ga, NULL);
304 }
305 
306 //for timer debug, #define TIMER
generic_render_eff_stream(generic_anim * ga)307 void generic_render_eff_stream(generic_anim *ga)
308 {
309 	if(ga->current_frame == ga->previous_frame)
310 		return;
311 	ubyte bpp = 32;
312 	if(ga->use_hud_color)
313 		bpp = 8;
314 	#ifdef TIMER
315 		int start_time = timer_get_fixed_seconds();
316 	#endif
317 
318 	#ifdef TIMER
319 		mprintf(("=========================\n"));
320 		mprintf(("frame: %d\n", ga->current_frame));
321 	#endif
322 		char frame_name[32];
323 		snprintf(frame_name, 32, "%s_%.4d", ga->filename, ga->current_frame);
324 		if(bm_reload(ga->eff.next_frame, frame_name) == ga->eff.next_frame)
325 		{
326 			bitmap* next_frame_bmp = bm_lock(ga->eff.next_frame, bpp, (bpp==8)?BMP_AABITMAP:BMP_TEX_NONCOMP, true);
327 			if(next_frame_bmp->data)
328 				gr_update_texture(ga->bitmap_id, bpp, (ubyte*)next_frame_bmp->data, ga->width, ga->height);
329 			bm_unlock(ga->eff.next_frame);
330 			bm_unload(ga->eff.next_frame, 0, true);
331 		}
332 	#ifdef TIMER
333 		mprintf(("end: %d\n", timer_get_fixed_seconds() - start_time));
334 		mprintf(("=========================\n"));
335 	#endif
336 }
337 
generic_render_ani_stream(generic_anim * ga)338 void generic_render_ani_stream(generic_anim *ga)
339 {
340 	int i;
341 	int bpp = ANI_BPP_CHECK;
342 	if(ga->use_hud_color)
343 		bpp = 8;
344 	#ifdef TIMER
345 		int start_time = timer_get_fixed_seconds();
346 	#endif
347 
348 	if(ga->current_frame == ga->previous_frame)
349 		return;
350 
351 	#ifdef TIMER
352 		mprintf(("=========================\n"));
353 		mprintf(("frame: %d\n", ga->current_frame));
354 	#endif
355 
356 	anim_check_for_palette_change(ga->ani.instance);
357 	// if we're using bitmap polys
358 	BM_SELECT_TEX_FORMAT();
359 	if(ga->direction & GENERIC_ANIM_DIRECTION_BACKWARDS) {
360 		//grab the keyframe - every frame is a keyframe for ANI
361 		if(ga->ani.animation->flags & ANF_STREAMED) {
362 			ga->ani.instance->file_offset = ga->ani.animation->file_offset + ga->ani.animation->keys[ga->current_frame].offset;
363 		} else {
364 			ga->ani.instance->data = ga->ani.animation->data + ga->ani.animation->keys[ga->current_frame].offset;
365 		}
366 		if(ga->ani.animation->flags & ANF_STREAMED) {
367 			ga->ani.instance->file_offset = unpack_frame_from_file(ga->ani.instance, ga->buffer, ga->width * ga->height, (ga->ani.instance->xlate_pal) ? ga->ani.animation->palette_translation : NULL, (bpp==8)?1:0, bpp);
368 		}
369 		else {
370 			ga->ani.instance->data = unpack_frame(ga->ani.instance, ga->ani.instance->data, ga->buffer, ga->width * ga->height, (ga->ani.instance->xlate_pal) ? ga->ani.animation->palette_translation : NULL, (bpp==8)?1:0, bpp);
371 		}
372 	}
373 	else {
374 		//looping back
375 		if((ga->current_frame == 0) || (ga->current_frame < ga->previous_frame)) {
376 			//go back to keyframe if there is one
377 			if(ga->keyframe && (ga->current_frame > 0)) {
378 				if(ga->ani.animation->flags & ANF_STREAMED) {
379 					ga->ani.instance->file_offset = ga->ani.animation->file_offset + ga->keyoffset;
380 				} else {
381 					ga->ani.instance->data = ga->ani.animation->data + ga->keyoffset;
382 				}
383 				ga->previous_frame = ga->keyframe - 1;
384 			}
385 			//go back to the start
386 			else {
387 				ga->ani.instance->file_offset = ga->ani.animation->file_offset;
388 				ga->ani.instance->data = ga->ani.animation->data;
389 				ga->previous_frame = -1;
390 			}
391 		}
392 		#ifdef TIMER
393 				mprintf(("proc: %d\n", timer_get_fixed_seconds() - start_time));
394 				mprintf(("previous frame: %d\n", ga->previous_frame));
395 		#endif
396 		for(i = ga->previous_frame + 1; i <= ga->current_frame; i++) {
397 			if(ga->ani.animation->flags & ANF_STREAMED) {
398 				ga->ani.instance->file_offset = unpack_frame_from_file(ga->ani.instance, ga->buffer, ga->width * ga->height, (ga->ani.instance->xlate_pal) ? ga->ani.animation->palette_translation : NULL, (bpp==8)?1:0, bpp);
399 			}
400 			else {
401 				ga->ani.instance->data = unpack_frame(ga->ani.instance, ga->ani.instance->data, ga->buffer, ga->width * ga->height, (ga->ani.instance->xlate_pal) ? ga->ani.animation->palette_translation : NULL, (bpp==8)?1:0, bpp);
402 			}
403 		}
404 	}
405 	// always go back to screen format
406 	BM_SELECT_SCREEN_FORMAT();
407 	//we need to use this because performance is worse if we flush the gfx card buffer
408 
409 	gr_update_texture(ga->bitmap_id, bpp, ga->buffer, ga->width, ga->height);
410 
411 	//in case we want to check that the frame is actually changing
412 	//mprintf(("frame crc = %08X\n", cf_add_chksum_long(0, ga->buffer, ga->width * ga->height * (bpp >> 3))));
413 	ga->ani.instance->last_bitmap = ga->bitmap_id;
414 
415 	#ifdef TIMER
416 		mprintf(("end: %d\n", timer_get_fixed_seconds() - start_time));
417 		mprintf(("=========================\n"));
418 	#endif
419 }
420 
generic_anim_render(generic_anim * ga,float frametime,int x,int y,bool menu)421 void generic_anim_render(generic_anim *ga, float frametime, int x, int y, bool menu)
422 {
423 	float keytime = 0.0;
424 
425 	if(ga->keyframe)
426 		keytime = (ga->total_time * ((float)ga->keyframe / (float)ga->num_frames));
427 	//don't mess with the frame time if we're paused
428 	if((ga->direction & GENERIC_ANIM_DIRECTION_PAUSED) == 0) {
429 		if(ga->direction & GENERIC_ANIM_DIRECTION_BACKWARDS) {
430 			//keep going forwards if we're in a keyframe loop
431 			if(ga->keyframe && (ga->anim_time >= keytime)) {
432 				ga->anim_time += frametime;
433 				if(ga->anim_time >= ga->total_time) {
434 					ga->anim_time = keytime - 0.001f;
435 					ga->done_playing = 0;
436 				}
437 			}
438 			else {
439 				//playing backwards
440 				ga->anim_time -= frametime;
441 				if((ga->direction & GENERIC_ANIM_DIRECTION_NOLOOP) && ga->anim_time <= 0.0) {
442 					ga->anim_time = 0;		//stop on first frame when playing in reverse
443 				}
444 				else {
445 					while(ga->anim_time <= 0.0)
446 						ga->anim_time += ga->total_time;	//make sure we're always positive, so we can go back to the end
447 				}
448 			}
449 		}
450 		else {
451 			ga->anim_time += frametime;
452 			if(ga->anim_time >= ga->total_time) {
453 				if(ga->direction & GENERIC_ANIM_DIRECTION_NOLOOP) {
454 					ga->anim_time = ga->total_time - 0.001f;		//stop on last frame when playing - if it's equal we jump to the first frame
455 				}
456 				if(!ga->done_playing){
457 					//we've played this at least once
458 					ga->done_playing = 1;
459 				}
460 			}
461 		}
462 	}
463 	if(ga->num_frames > 0)
464 	{
465 		ga->current_frame = 0;
466 		if(ga->done_playing && ga->keyframe) {
467 			ga->anim_time = fmod(ga->anim_time - keytime, ga->total_time - keytime) + keytime;
468 		}
469 		else {
470 			ga->anim_time = fmod(ga->anim_time, ga->total_time);
471 		}
472 		ga->current_frame += fl2i(ga->anim_time * ga->num_frames / ga->total_time);
473 		//sanity check
474 		CLAMP(ga->current_frame, 0, ga->num_frames - 1);
475 		if(ga->streaming) {
476 			//handle streaming - render one frame
477 			//TODO: add EFF streaming
478 			if(ga->type == BM_TYPE_ANI) {
479 				generic_render_ani_stream(ga);
480 			} else {
481 				generic_render_eff_stream(ga);
482 			}
483 			gr_set_bitmap(ga->bitmap_id);
484 		}
485 		else {
486 			gr_set_bitmap(ga->first_frame + ga->current_frame);
487 		}
488 		ga->previous_frame = ga->current_frame;
489 		if(ga->use_hud_color)
490 			gr_aabitmap(x, y, (menu ? GR_RESIZE_MENU : GR_RESIZE_FULL));
491 		else
492 			gr_bitmap(x, y, (menu ? GR_RESIZE_MENU : GR_RESIZE_FULL));
493 	}
494 }
495