1 /*
2 	mngplay
3 
4 	$Date: 2003/12/07 09:45:16 $
5 
6 	Ralph Giles <giles :at: ashlu.bc.ca>
7 
8 	This program my be redistributed under the terms of the
9 	GNU General Public Licence, version 2, or at your preference,
10 	any later version.
11 
12 	(this assuming there's no problem with libmng not being GPL...)
13 
14 
15 	this is an SDL based mng player. the code is very rough;
16 	patches welcome.
17 
18 
19 	GRR 20010708:  added SDL/libmng/zlib/libjpeg version info, mouse-click
20 			handling (alternate quit mode); improved automake setup
21 
22 	Raphael Assenat <raph :at: raphnet.net>
23 	2003/11/26:    added command line options to run in alternate color depths.
24 
25 */
26 
27 #include <stdio.h>
28 #include <stdlib.h>
29 
30 #include <SDL/SDL.h>
31 #include <libmng.h>
32 
33 #include <libgen.h> // basename
34 
35 
36 #define DEFAULT_SDL_VIDEO_DEPTH 32
37 
38 /* structure for keeping track of our mng stream inside the callbacks */
39 typedef struct {
40 	FILE		*file;	   /* pointer to the file we're decoding */
41 	char		*filename; /* pointer to the file's path/name */
42 	SDL_Surface	*surface;  /* SDL display */
43 	mng_uint32	delay;     /* ticks to wait before resuming decode */
44 	int			sdl_video_depth;  /* The depth for SDL_SetVideoMode */
45 } mngstuff;
46 
47 /* callbacks for the mng decoder */
48 
49 /* memory allocation; data must be zeroed */
mymngalloc(mng_uint32 size)50 mng_ptr mymngalloc(mng_uint32 size)
51 {
52 	return (mng_ptr)calloc(1, size);
53 }
54 
55 /* memory deallocation */
mymngfree(mng_ptr p,mng_uint32 size)56 void mymngfree(mng_ptr p, mng_uint32 size)
57 {
58 	free(p);
59 	return;
60 }
61 
mymngopenstream(mng_handle mng)62 mng_bool mymngopenstream(mng_handle mng)
63 {
64 	mngstuff	*mymng;
65 
66 	/* look up our stream struct */
67         mymng = (mngstuff*)mng_get_userdata(mng);
68 
69 	/* open the file */
70 	mymng->file = fopen(mymng->filename, "rb");
71 	if (mymng->file == NULL) {
72 		fprintf(stderr, "unable to open '%s'\n", mymng->filename);
73 		return MNG_FALSE;
74 	}
75 
76 	return MNG_TRUE;
77 }
78 
mymngclosestream(mng_handle mng)79 mng_bool mymngclosestream(mng_handle mng)
80 {
81 	mngstuff	*mymng;
82 
83 	/* look up our stream struct */
84         mymng = (mngstuff*)mng_get_userdata(mng);
85 
86 	/* close the file */
87 	fclose(mymng->file);
88 	mymng->file = NULL;	/* for safety */
89 
90 	return MNG_TRUE;
91 }
92 
93 /* feed data to the decoder */
mymngreadstream(mng_handle mng,mng_ptr buffer,mng_uint32 size,mng_uint32 * bytesread)94 mng_bool mymngreadstream(mng_handle mng, mng_ptr buffer,
95 		mng_uint32 size, mng_uint32 *bytesread)
96 {
97 	mngstuff *mymng;
98 
99 	/* look up our stream struct */
100 	mymng = (mngstuff*)mng_get_userdata(mng);
101 
102 	/* read the requested amount of data from the file */
103 	*bytesread = fread(buffer, 1, size, mymng->file);
104 
105 	return MNG_TRUE;
106 }
107 
108 /* the header's been read. set up the display stuff */
mymngprocessheader(mng_handle mng,mng_uint32 width,mng_uint32 height)109 mng_bool mymngprocessheader(mng_handle mng,
110 		mng_uint32 width, mng_uint32 height)
111 {
112 	mngstuff	*mymng;
113 	SDL_Surface	*screen;
114 	char		title[256];
115 
116 //	fprintf(stderr, "our mng is %dx%d\n", width,height);
117 
118 	/* retreive our user data */
119  	mymng = (mngstuff*)mng_get_userdata(mng);
120 
121 	screen = SDL_SetVideoMode(width,height, mymng->sdl_video_depth, SDL_SWSURFACE);
122 	if (screen == NULL) {
123 		fprintf(stderr, "unable to allocate %dx%d video memory: %s\n",
124 			width, height, SDL_GetError());
125 		return MNG_FALSE;
126 	}
127 
128 	printf("SDL Video Mode: %dx%d bpp=%d\n", width, height, mymng->sdl_video_depth);
129 
130 	/* save the surface pointer */
131 	mymng->surface = screen;
132 
133 	/* set a descriptive window title */
134 	snprintf(title, 256, "mngplay: %s", mymng->filename);
135 	SDL_WM_SetCaption(title, "mngplay");
136 
137 	/* in necessary, lock the drawing surface to the decoder
138 	   can safely fill it. We'll unlock elsewhere before display */
139 	if (SDL_MUSTLOCK(mymng->surface)) {
140 		if ( SDL_LockSurface(mymng->surface) < 0 ) {
141 			fprintf(stderr, "could not lock display surface\n");
142 			exit(1);
143 		}
144 	}
145 
146 /*
147 	printf("RGBA Masks: %08X %08X %08X %08X\n",
148 				mymng->surface->format->Rmask,
149 				mymng->surface->format->Gmask,
150 				mymng->surface->format->Bmask,
151 				mymng->surface->format->Amask);
152 	printf("RGBA Shifts: %08X %08X %08X %08X\n",
153 				mymng->surface->format->Rshift,
154 				mymng->surface->format->Gshift,
155 				mymng->surface->format->Bshift,
156 				mymng->surface->format->Ashift);
157 */
158 	/* Choose a canvas style which matches the SDL_Surface pixel format */
159 	switch(mymng->surface->format->BitsPerPixel)
160 	{
161 		case 32:
162 			if (mymng->surface->format->Amask==0) {
163 				/* No alpha (padding byte) */
164 				if (mymng->surface->format->Bshift==0) {
165 					/* Blue first */
166 					mng_set_canvasstyle(mng, MNG_CANVAS_BGRX8);
167 				} else {
168 					/* Red first */
169 					fprintf(stderr, "No matching mng canvas for sdl pixel format. Colors may be wrong.\n");
170 					mng_set_canvasstyle(mng, MNG_CANVAS_BGRX8);
171 				}
172 			}
173 			else {
174 				/* Real alpha */
175 				if (mymng->surface->format->Bshift==0) {
176 					/* Blue first */
177 					mng_set_canvasstyle(mng, MNG_CANVAS_BGRA8);
178 				} else {
179 					/* Red first */
180 					mng_set_canvasstyle(mng, MNG_CANVAS_RGBA8);
181 				}
182 			}
183 			break;
184 		case 24:
185 			if (mymng->surface->format->Amask==0) {
186 				/* No alpha here should mean true rgb24bit */
187 				if (mymng->surface->format->Bshift==0) {
188 					/* Blue first */
189 					mng_set_canvasstyle(mng, MNG_CANVAS_BGR8);
190 				} else {
191 					/* Red first */
192 					mng_set_canvasstyle(mng, MNG_CANVAS_RGB8);
193 				}
194 			}
195 			else {
196 				/* If there is an alpha and we are in 24 bpp, this must
197 				 * mean rgb5658 */
198 				if (mymng->surface->format->Bshift==0) {
199 					/* Blue first */
200 					mng_set_canvasstyle(mng, MNG_CANVAS_BGRA565);
201 				} else {
202 					/* Red first */
203 					mng_set_canvasstyle(mng, MNG_CANVAS_RGBA565);
204 				}
205 			}
206 			break;
207 		case 16:
208 			if (mymng->surface->format->Bshift==0) {
209 				/* Blue first */
210 				mng_set_canvasstyle(mng, MNG_CANVAS_BGR565);
211 			} else {
212 				/* Red first */
213 				mng_set_canvasstyle(mng, MNG_CANVAS_RGB565);
214 			}
215 			break;
216 		default:
217 			return MNG_FALSE;
218 	}
219 
220 	return MNG_TRUE;
221 }
222 
223 /* return a row pointer for the decoder to fill */
mymnggetcanvasline(mng_handle mng,mng_uint32 line)224 mng_ptr mymnggetcanvasline(mng_handle mng, mng_uint32 line)
225 {
226 	mngstuff	*mymng;
227 	SDL_Surface	*surface;
228 	mng_ptr		row;
229 
230 	/* dereference our structure */
231 	mymng = (mngstuff*)mng_get_userdata(mng);
232 
233 	/* we assume any necessary locking has happened
234 	   outside, in the frame level code */
235 	row = mymng->surface->pixels + mymng->surface->pitch*line;
236 
237 //	fprintf(stderr, "   returning pointer to line %d (%p)\n", line, row);
238 
239 	return (row);
240 }
241 
242 /* timer */
mymnggetticks(mng_handle mng)243 mng_uint32 mymnggetticks(mng_handle mng)
244 {
245 	mng_uint32 ticks;
246 
247 	ticks = (mng_uint32)SDL_GetTicks();
248 //	fprintf(stderr, "  %d\t(returning tick count)\n",ticks);
249 
250 	return(ticks);
251 }
252 
mymngrefresh(mng_handle mng,mng_uint32 x,mng_uint32 y,mng_uint32 w,mng_uint32 h)253 mng_bool mymngrefresh(mng_handle mng, mng_uint32 x, mng_uint32 y,
254 			mng_uint32 w, mng_uint32 h)
255 {
256 	mngstuff	*mymng;
257 	SDL_Rect	frame;
258 
259 	frame.x = x;
260 	frame.y = y;
261 	frame.w = w;
262 	frame.h = h;
263 
264 	/* dereference our structure */
265         mymng = (mngstuff*)mng_get_userdata(mng);
266 
267 	/* if necessary, unlock the display */
268 	if (SDL_MUSTLOCK(mymng->surface)) {
269                 SDL_UnlockSurface(mymng->surface);
270         }
271 
272         /* refresh the screen with the new frame */
273         SDL_UpdateRects(mymng->surface, 1, &frame);
274 
275 	/* in necessary, relock the drawing surface */
276         if (SDL_MUSTLOCK(mymng->surface)) {
277                 if ( SDL_LockSurface(mymng->surface) < 0 ) {
278                         fprintf(stderr, "could not lock display surface\n");
279                         return MNG_FALSE;
280                 }
281         }
282 
283 
284 	return MNG_TRUE;
285 }
286 
287 /* interframe delay callback */
mymngsettimer(mng_handle mng,mng_uint32 msecs)288 mng_bool mymngsettimer(mng_handle mng, mng_uint32 msecs)
289 {
290 	mngstuff	*mymng;
291 
292 //	fprintf(stderr,"  pausing for %d ms\n", msecs);
293 
294 	/* look up our stream struct */
295         mymng = (mngstuff*)mng_get_userdata(mng);
296 
297 	/* set the timer for when the decoder wants to be woken */
298 	mymng->delay = msecs;
299 
300 	return MNG_TRUE;
301 
302 }
303 
mymngerror(mng_handle mng,mng_int32 code,mng_int8 severity,mng_chunkid chunktype,mng_uint32 chunkseq,mng_int32 extra1,mng_int32 extra2,mng_pchar text)304 mng_bool mymngerror(mng_handle mng, mng_int32 code, mng_int8 severity,
305 	mng_chunkid chunktype, mng_uint32 chunkseq,
306 	mng_int32 extra1, mng_int32 extra2, mng_pchar text)
307 {
308 	mngstuff	*mymng;
309 	char		chunk[5];
310 
311         /* dereference our data so we can get the filename */
312         mymng = (mngstuff*)mng_get_userdata(mng);
313 
314 	/* pull out the chuck type as a string */
315 	// FIXME: does this assume unsigned char?
316 	chunk[0] = (char)((chunktype >> 24) & 0xFF);
317 	chunk[1] = (char)((chunktype >> 16) & 0xFF);
318 	chunk[2] = (char)((chunktype >>  8) & 0xFF);
319 	chunk[3] = (char)((chunktype      ) & 0xFF);
320 	chunk[4] = '\0';
321 
322 	/* output the error */
323 	fprintf(stderr, "error playing '%s' chunk %s (%d):\n",
324 		mymng->filename, chunk, chunkseq);
325 	fprintf(stderr, "%s\n", text);
326 
327 	return (0);
328 }
329 
mymngquit(mng_handle mng)330 int mymngquit(mng_handle mng)
331 {
332 	mngstuff	*mymng;
333 
334 	/* dereference our data so we can free it */
335 	mymng = (mngstuff*)mng_get_userdata(mng);
336 
337 	/* cleanup. this will call mymngclosestream */
338         mng_cleanup(&mng);
339 
340 	/* free our data */
341 	free(mymng);
342 
343 	/* quit */
344 	exit(0);
345 }
346 
checkevents(mng_handle mng)347 int checkevents(mng_handle mng)
348 {
349 	SDL_Event	event;
350 
351 	/* check if there's an event pending */
352 	if (!SDL_PollEvent(&event)) {
353 		return 0;	/* no events pending */
354 	}
355 
356 	/* we have an event; process it */
357 	switch (event.type) {
358 		case SDL_QUIT:
359 			mymngquit(mng);	/* quit */
360 			break;
361 		case SDL_MOUSEBUTTONDOWN:
362 		case SDL_MOUSEBUTTONUP:
363 			mymngquit(mng);
364 			break;
365 		case SDL_KEYUP:
366 			switch (event.key.keysym.sym) {
367 				case SDLK_ESCAPE:
368 				case SDLK_q:
369 					mymngquit(mng);
370 					break;
371 			}
372 			/* FALL THROUGH */
373 		default:
374 			return 1;
375 	}
376 
377 	return 0;   /* GRR ADDED:  non-void function */
378 }
379 
main(int argc,char * argv[])380 int main(int argc, char *argv[])
381 {
382 	mngstuff	*mymng;
383 	mng_handle	mng;
384 	SDL_Rect	updaterect;
385 
386 	if (argc < 2) {
387 		const SDL_version *pSDLver = SDL_Linked_Version();
388 
389 		fprintf(stderr, "Usage:  %s mngfile [depth]\n\n", basename(argv[0]));
390 		fprintf(stderr, "        where 'depth' is 15,16,24 or 32\n");
391 		fprintf(stderr,
392 			"  Compiled with SDL %d.%d.%d; using SDL %d.%d.%d.\n",
393 			SDL_MAJOR_VERSION, SDL_MINOR_VERSION, SDL_PATCHLEVEL,
394 			pSDLver->major, pSDLver->minor, pSDLver->patch);
395 		fprintf(stderr, "  Compiled with libmng %s; using libmng %s.\n",
396 			MNG_VERSION_TEXT, mng_version_text());
397 		fprintf(stderr, "  Compiled with zlib %s; using zlib %s.\n",
398 			ZLIB_VERSION, zlib_version);
399 #ifdef JPEG_LIB_VERSION
400 		{
401 			int major = JPEG_LIB_VERSION / 10;
402 			int minor = JPEG_LIB_VERSION % 10;
403 			char minoralpha[2];
404 
405 			if (minor) {
406 				minoralpha[0] = (char)(minor - 1 + 'a');
407 				minoralpha[1] = '\0';
408 			} else
409 				minoralpha[0] = '\0';
410 			fprintf(stderr, "  Compiled with libjpeg %d%s.\n",
411 				major, minoralpha);
412 		}
413 #endif
414 		fprintf(stderr,
415 			"\nPress Esc or Q, or click mouse button, to quit.\n");
416 		exit(1);
417 	}
418 
419 	/* allocate our stream data structure */
420 	mymng = (mngstuff*)calloc(1, sizeof(*mymng));
421 	if (mymng == NULL) {
422 		fprintf(stderr, "could not allocate stream structure.\n");
423 		exit(0);
424 	}
425 
426 	/* pass the name of the file we want to play */
427 	mymng->filename = argv[1];
428 
429 	/* pass the color depth we wish to use */
430 	if (argc>=3) {
431 		mymng->sdl_video_depth = atoi(argv[2]);
432 		switch(mymng->sdl_video_depth) {
433 			case 15:
434 			case 16:
435 			case 24:
436 			case 32:
437 				break;
438 			default:
439 				fprintf(stderr, "Unsupported color depth. Choices are: 15, 16, 24 and 32\n");
440 				exit(1);
441 		}
442 	}
443 	else {
444 		mymng->sdl_video_depth = DEFAULT_SDL_VIDEO_DEPTH;
445 	}
446 
447 	/* set up the mng decoder for our stream */
448         mng = mng_initialize(mymng, mymngalloc, mymngfree, MNG_NULL);
449         if (mng == MNG_NULL) {
450                 fprintf(stderr, "could not initialize libmng.\n");
451                 exit(1);
452         }
453 
454 	/* set the callbacks */
455 	mng_setcb_errorproc(mng, mymngerror);
456 	mng_setcb_openstream(mng, mymngopenstream);
457 	mng_setcb_closestream(mng, mymngclosestream);
458 	mng_setcb_readdata(mng, mymngreadstream);
459 	mng_setcb_gettickcount(mng, mymnggetticks);
460 	mng_setcb_settimer(mng, mymngsettimer);
461 	mng_setcb_processheader(mng, mymngprocessheader);
462 	mng_setcb_getcanvasline(mng, mymnggetcanvasline);
463 	mng_setcb_refresh(mng, mymngrefresh);
464 	/* FIXME: should check for errors here */
465 
466 	/* initialize SDL */
467         if (SDL_Init(SDL_INIT_VIDEO) < 0) {
468                 fprintf(stderr, "%s: Unable to initialize SDL (%s)\n",
469                         argv[0], SDL_GetError());
470                 exit(1);
471         }
472 	/* arrange to call the shutdown routine before we exit */
473         atexit(SDL_Quit);
474 
475 	/* restrict event handling to the relevant bits */
476 	SDL_EventState(SDL_KEYDOWN, SDL_IGNORE); /* keyup only */
477 	SDL_EventState(SDL_MOUSEMOTION, SDL_IGNORE);
478 //	SDL_EventState(SDL_MOUSEBUTTONDOWN, SDL_IGNORE);
479 //	SDL_EventState(SDL_MOUSEBUTTONUP, SDL_IGNORE);
480 
481 //	fprintf(stderr, "playing mng...maybe.\n");
482 
483 	mng_readdisplay(mng);
484 
485 	/* loop though the frames */
486 	while (mymng->delay) {
487 //		fprintf(stderr, "  waiting for %d ms\n", mymng->delay);
488 		SDL_Delay(mymng->delay);
489 
490 		/* reset the delay in case the decoder
491 		   doesn't update it again */
492 		mymng->delay = 0;
493 
494 		mng_display_resume(mng);
495 
496 		/* check for user input (just quit at this point) */
497 		checkevents(mng);
498 	}
499 
500 	/* �hay alguno? pause before quitting */
501 	fprintf(stderr, "pausing before shutdown...\n");
502 	SDL_Delay(1000);
503 
504 	/* cleanup and quit */
505 	mymngquit(mng);
506 }
507 
508