1 /***************************************************************************
2                           stk.c  -  description
3                              -------------------
4     begin                : Thu Oct 12 2002
5     copyright            : (C) 2002 by Michael Speck
6     email                : kulkanie@gmx.net
7  ***************************************************************************/
8 
9 /***************************************************************************
10  *                                                                         *
11  *   This program is free software; you can redistribute it and/or modify  *
12  *   it under the terms of the GNU General Public License as published by  *
13  *   the Free Software Foundation; either version 2 of the License, or     *
14  *   (at your option) any later version.                                   *
15  *                                                                         *
16  ***************************************************************************/
17 
18 #include <string.h>
19 #include <stdarg.h>
20 #include <stdlib.h>
21 #include <png.h>
22 #include "stk.h"
23 
24 //#define STK_DEBUG
25 
26 /*
27 ====================================================================
28 LOCAL
29 ====================================================================
30 */
31 
32 int stk_quit_request = 0;
33 SDL_Surface *stk_display = 0;
34 enum { STK_UPDATE_RECT_LIMIT = 200 };
35 SDL_Rect stk_update_rects[STK_UPDATE_RECT_LIMIT];
36 int stk_update_rect_count = 0;
37 int stk_display_use_fade = 1; /* fading allowed? */
38 SDL_Cursor *stk_empty_cursor = 0;
39 SDL_Cursor *stk_standard_cursor = 0;
40 char *stk_surface_path = 0;
41 char *stk_audio_path = 0;
42 SDL_Rect stk_srect, stk_drect;
43 int stk_old_alpha = 0;
44 int stk_audio_ok = 0;
45 #ifdef AUDIO_ENABLED
46 int stk_audio_mixchannel_count = MIX_CHANNELS;
47 #else
48 int stk_audio_mixchannel_count = 0;
49 #endif
50 int stk_audio_sound_enabled = 1;
51 int stk_audio_sound_volume = 127;
52 int stk_audio_buffer_size = 512;
53 int stk_screenshot_id = 1;
54 
55 /* Load a PNG type image from an SDL datasource */
56 #define IMG_SetError	SDL_SetError
png_read_data(png_structp ctx,png_bytep area,png_size_t size)57 static void png_read_data(png_structp ctx, png_bytep area, png_size_t size)
58 {
59 	SDL_RWops *src;
60 
61 	src = (SDL_RWops *)png_get_io_ptr(ctx);
62 	SDL_RWread(src, area, size, 1);
63 }
IMG_LoadPNG_RW(SDL_RWops * src)64 static SDL_Surface *IMG_LoadPNG_RW(SDL_RWops *src)
65 {
66 	SDL_Surface *volatile surface;
67 	png_structp png_ptr;
68 	png_infop info_ptr;
69 	png_uint_32 width, height;
70 	int bit_depth, color_type, interlace_type;
71 	Uint32 Rmask;
72 	Uint32 Gmask;
73 	Uint32 Bmask;
74 	Uint32 Amask;
75 	SDL_Palette *palette;
76 	png_bytep *volatile row_pointers;
77 	int row, i;
78 	volatile int ckey = -1;
79 	png_color_16 *transv;
80 
81 	/* Initialize the data we will clean up when we're done */
82 	png_ptr = NULL; info_ptr = NULL; row_pointers = NULL; surface = NULL;
83 
84 	/* Check to make sure we have something to do */
85 	if ( ! src ) {
86 		goto done;
87 	}
88 
89 	/* Create the PNG loading context structure */
90 	png_ptr = png_create_read_struct(PNG_LIBPNG_VER_STRING,
91 					  NULL,NULL,NULL);
92 	if (png_ptr == NULL){
93 		IMG_SetError("Couldn't allocate memory for PNG file");
94 		goto done;
95 	}
96 
97 	 /* Allocate/initialize the memory for image information.  REQUIRED. */
98 	info_ptr = png_create_info_struct(png_ptr);
99 	if (info_ptr == NULL) {
100 		IMG_SetError("Couldn't create image information for PNG file");
101 		goto done;
102 	}
103 
104 	/* Set error handling if you are using setjmp/longjmp method (this is
105 	 * the normal method of doing things with libpng).  REQUIRED unless you
106 	 * set up your own error handlers in png_create_read_struct() earlier.
107 	 */
108 	if ( setjmp(png_jmpbuf(png_ptr)) ) {
109 		IMG_SetError("Error reading the PNG file.");
110 		goto done;
111 	}
112 
113 	/* Set up the input control */
114 	png_set_read_fn(png_ptr, src, png_read_data);
115 
116 	/* Read PNG header info */
117 	png_read_info(png_ptr, info_ptr);
118 	png_get_IHDR(png_ptr, info_ptr, &width, &height, &bit_depth,
119 			&color_type, &interlace_type, NULL, NULL);
120 
121 	/* tell libpng to strip 16 bit/color files down to 8 bits/color */
122 	png_set_strip_16(png_ptr) ;
123 
124 	/* Extract multiple pixels with bit depths of 1, 2, and 4 from a single
125 	 * byte into separate bytes (useful for paletted and grayscale images).
126 	 */
127 	png_set_packing(png_ptr);
128 
129 	/* scale greyscale values to the range 0..255 */
130 	if(color_type == PNG_COLOR_TYPE_GRAY)
131 		png_set_expand(png_ptr);
132 
133 	/* For images with a single "transparent colour", set colour key;
134 	   if more than one index has transparency, or if partially transparent
135 	   entries exist, use full alpha channel */
136 	if (png_get_valid(png_ptr, info_ptr, PNG_INFO_tRNS)) {
137 	        int num_trans;
138 		Uint8 *trans;
139 		png_get_tRNS(png_ptr, info_ptr, &trans, &num_trans,
140 			     &transv);
141 		if(color_type == PNG_COLOR_TYPE_PALETTE) {
142 		    /* Check if all tRNS entries are opaque except one */
143 		    int i, t = -1;
144 		    for(i = 0; i < num_trans; i++)
145 			if(trans[i] == 0) {
146 			    if(t >= 0)
147 				break;
148 			    t = i;
149 			} else if(trans[i] != 255)
150 			    break;
151 		    if(i == num_trans) {
152 			/* exactly one transparent index */
153 			ckey = t;
154 		    } else {
155 			/* more than one transparent index, or translucency */
156 			png_set_expand(png_ptr);
157 		    }
158 		} else
159 		    ckey = 0; /* actual value will be set later */
160 	}
161 
162 	if ( color_type == PNG_COLOR_TYPE_GRAY_ALPHA )
163 		png_set_gray_to_rgb(png_ptr);
164 
165 	png_read_update_info(png_ptr, info_ptr);
166 
167 	png_get_IHDR(png_ptr, info_ptr, &width, &height, &bit_depth,
168 			&color_type, &interlace_type, NULL, NULL);
169 
170 	/* Allocate the SDL surface to hold the image */
171 	Rmask = Gmask = Bmask = Amask = 0 ;
172 	if ( color_type != PNG_COLOR_TYPE_PALETTE ) {
173 		if ( SDL_BYTEORDER == SDL_LIL_ENDIAN ) {
174 			Rmask = 0x000000FF;
175 			Gmask = 0x0000FF00;
176 			Bmask = 0x00FF0000;
177 			Amask = (png_get_channels(png_ptr, info_ptr) == 4) ? 0xFF000000 : 0;
178 		} else {
179 		        int s = (png_get_channels(png_ptr, info_ptr) == 4) ? 0 : 8;
180 			Rmask = 0xFF000000 >> s;
181 			Gmask = 0x00FF0000 >> s;
182 			Bmask = 0x0000FF00 >> s;
183 			Amask = 0x000000FF >> s;
184 		}
185 	}
186 	surface = SDL_AllocSurface(SDL_SWSURFACE, width, height,
187 			bit_depth*png_get_channels(png_ptr, info_ptr), Rmask,Gmask,Bmask,Amask);
188 	if ( surface == NULL ) {
189 		IMG_SetError("Out of memory");
190 		goto done;
191 	}
192 
193 	if(ckey != -1) {
194 	        if(color_type != PNG_COLOR_TYPE_PALETTE)
195 			/* FIXME: Should these be truncated or shifted down? */
196 		        ckey = SDL_MapRGB(surface->format,
197 			                 (Uint8)transv->red,
198 			                 (Uint8)transv->green,
199 			                 (Uint8)transv->blue);
200 	        SDL_SetColorKey(surface, SDL_SRCCOLORKEY, ckey);
201 	}
202 
203 	/* Create the array of pointers to image data */
204 	row_pointers = (png_bytep*) malloc(sizeof(png_bytep)*height);
205 	if ( (row_pointers == NULL) ) {
206 		IMG_SetError("Out of memory");
207 		SDL_FreeSurface(surface);
208 		surface = NULL;
209 		goto done;
210 	}
211 	for (row = 0; row < (int)height; row++) {
212 		row_pointers[row] = (png_bytep)
213 				(Uint8 *)surface->pixels + row*surface->pitch;
214 	}
215 
216 	/* Read the entire image in one go */
217 	png_read_image(png_ptr, row_pointers);
218 
219 	/* read rest of file, get additional chunks in info_ptr - REQUIRED */
220 	png_read_end(png_ptr, info_ptr);
221 
222 	/* Load the palette, if any */
223 	palette = surface->format->palette;
224 	if ( palette ) {
225 	    if(color_type == PNG_COLOR_TYPE_GRAY) {
226 		palette->ncolors = 256;
227 		for(i = 0; i < 256; i++) {
228 		    palette->colors[i].r = i;
229 		    palette->colors[i].g = i;
230 		    palette->colors[i].b = i;
231 		}
232 	    } else {
233 		png_colorp file_palette;
234 		int file_num_palette;
235 		png_get_PLTE(png_ptr, info_ptr, &file_palette,
236 			     &file_num_palette);
237 		if (file_num_palette > 0 ) {
238 		    palette->ncolors = file_num_palette;
239 		    for( i=0; i<file_num_palette; ++i ) {
240 		        palette->colors[i].b = file_palette[i].blue;
241 		        palette->colors[i].g = file_palette[i].green;
242 		        palette->colors[i].r = file_palette[i].red;
243 		    }
244 		}
245 	    }
246 	}
247 
248 done:	/* Clean up and return */
249 	png_destroy_read_struct(&png_ptr, info_ptr ? &info_ptr : (png_infopp)0,
250 								(png_infopp)0);
251 	if ( row_pointers ) {
252 		free(row_pointers);
253 	}
254 	return(surface);
255 }
256 
png_load(char * path)257 static SDL_Surface *png_load( char *path )
258 {
259     SDL_RWops *src;
260     SDL_Surface *surf;
261     if ( ( src = SDL_RWFromFile(path, "rb") ) == 0 ) {
262         fprintf( stderr, "%s\n", SDL_GetError() );
263         return 0;
264     }
265     else {
266         surf = IMG_LoadPNG_RW( src );
267         SDL_RWclose( src );
268         return surf;
269     }
270 }
271 
272 /*
273 ====================================================================
274 Close display and call SDL_Quit.
275 ====================================================================
276 */
stk_quit(void)277 static void stk_quit( void )
278 {
279     stk_surface_free( &stk_display );
280     stk_cursor_free( &stk_empty_cursor );
281     if ( stk_surface_path ) {
282         free( stk_surface_path );
283         stk_surface_path = 0;
284     }
285     printf( "STK finalized\n" );
286     SDL_Quit();
287     printf( "SDL finalized\n" );
288 }
289 
290 /*
291 ====================================================================
292 PUBLIC
293 ====================================================================
294 */
295 
296 /* GENERAL */
297 
298 /*
299 ====================================================================
300 Initiate SDL, build a default cursor and install the clean
301 up function stk_quit().
302 ====================================================================
303 */
stk_init(int flags)304 void stk_init( int flags )
305 {
306     /* remove sound flag if disabled */
307 #ifndef WITH_SOUND
308     if ( flags & SDL_INIT_AUDIO )
309         flags = flags & ~SDL_INIT_AUDIO;
310 #endif
311     /* init SDL */
312     if ( SDL_Init( flags ) < 0 )
313         STK_ABORT( SDL_GetError() );
314     /* enable unicode */
315     SDL_EnableUNICODE( 1 );
316     /* set cleanup function */
317     atexit( stk_quit );
318     /* set current directory as path for surfaces */
319     stk_surface_set_path( "." );
320     /* set current directory as path for sounds */
321     stk_audio_set_path( "." );
322     /* create empty cursor */
323     stk_empty_cursor = stk_cursor_create( 16, 16, 8, 8,
324                                   "                "
325                                   "                "
326                                   "                "
327                                   "                "
328                                   "                "
329                                   "                "
330                                   "                "
331                                   "                "
332                                   "                "
333                                   "                "
334                                   "                "
335                                   "                "
336                                   "                "
337                                   "                "
338                                   "                "
339                                   "                " );
340     stk_standard_cursor = SDL_GetCursor();
341 }
342 
343 /*
344 ====================================================================
345 Block until either a key or button was pressed.
346 ====================================================================
347 */
stk_wait_for_input(void)348 void stk_wait_for_input( void )
349 {
350     SDL_Event event;
351     while ( 1 ) {
352         SDL_WaitEvent(&event);
353         if (event.type == SDL_QUIT) {
354             stk_quit_request = 1;
355             return;
356         }
357         if (event.type == SDL_KEYDOWN || event.type == SDL_MOUSEBUTTONDOWN)
358             return;
359     }
360 }
361 
362 /* SCREEN */
363 
364 /*
365 ====================================================================
366 Open a display with the passed settings. Depth may be modified due
367 to Xserver settings and if the resolution is completely
368 unavailable 640 x 480 x 16 x SDL_SWSURFACE is opened.
369 If the display is already open it is closed and re-opened.
370 There is no function to close the display as this is handled by the
371 stk_quit() function installed by stk_init().
372 The display can be accessed by
373   extern SDL_Surface *stk_display;
374 This funtion returns True if the wanted width and height are
375 available, False if not and it aborts when no display is found.
376 ====================================================================
377 */
stk_display_open(int flags,int width,int height,int depth)378 int stk_display_open( int flags, int width, int height, int depth )
379 {
380 #ifdef STK_DEBUG
381     SDL_PixelFormat	*format;
382 #endif
383     if ( stk_display ) {
384         /* if display is already open check if the requested
385            resolution is already the current one */
386         if ( stk_display->w == width )
387             if ( stk_display->h == height )
388                 if ( stk_display->flags == flags )
389                     return 1;
390         /* close old display */
391         stk_surface_free( &stk_display );
392     }
393     else { /* load window icon on first time setup */
394 	SDL_Surface *icon;
395 	icon = png_load(SRC_DIR "/gfx/win_icon.png");
396 	SDL_SetColorKey(icon, SDL_SRCCOLORKEY, 0);
397 	SDL_WM_SetIcon(icon, NULL);
398     }
399 
400     /* open new display */
401     if ( ( depth = SDL_VideoModeOK( width, height, depth, flags ) ) == 0 ) {
402         fprintf( stderr, "Requested mode %ix%i %s unavailable\n",
403                  width, height, (flags&SDL_FULLSCREEN)?"Fullscreen":"Window" );
404         stk_display = SDL_SetVideoMode( 640, 480, 16, SDL_SWSURFACE );
405         return 0;
406     }
407     else
408         if ( ( stk_display = SDL_SetVideoMode( width, height, depth, flags ) ) == 0 )
409             STK_ABORT( SDL_GetError() );
410 
411 #ifdef STK_DEBUG
412     printf( "set display %ix%i %s\n",
413             width, height, (flags&SDL_FULLSCREEN)?"Fullscreen":"Window" );
414 
415     format = stk_display->format;
416     printf("video mode format:\n");
417     printf("Masks: R=%i, G=%i, B=%i\n",
418         format->Rmask, format->Gmask, format->Bmask);
419     printf("LShft: R=%i, G=%i, B=%i\n",
420         format->Rshift, format->Gshift, format->Bshift);
421     printf("RShft: R=%i, G=%i, B=%i\n",
422         format->Rloss, format->Gloss, format->Bloss);
423     printf("BBP: %i\n", format->BitsPerPixel);
424     printf("-----\n");
425 #endif
426 
427     return 1;
428 }
429 
430 /*
431 ====================================================================
432 Add an update rectangle that will be updated by
433 stk_display_update(). Regardless of clipping the rectangles
434 must fit the screen else it'll raise an X error.
435 If NULL is passed as 'rect' the whole screen is stored for update.
436 ====================================================================
437 */
stk_display_store_rect(SDL_Rect * rect)438 void stk_display_store_rect( SDL_Rect *rect )
439 {
440     if ( stk_update_rect_count < STK_UPDATE_RECT_LIMIT ) {
441         if ( rect ) {
442             /* for safety this check is kept although it should be
443                unnescessary */
444             if ( rect->x < 0 ) {
445                 rect->w += rect->x;
446                 rect->x = 0;
447             }
448             if ( rect->y < 0 ) {
449                 rect->h += rect->y;
450                 rect->y = 0;
451             }
452             if ( rect->x + rect->w > stk_display->w )
453                 rect->w = stk_display->w - rect->x;
454             if ( rect->y + rect->h > stk_display->h )
455                 rect->h = stk_display->h - rect->y;
456             if ( rect->w > 0 && rect->h > 0 )
457                 stk_update_rects[stk_update_rect_count++] = *rect;
458         }
459         else
460             stk_update_rect_count = STK_UPDATE_RECT_LIMIT;
461     }
462 }
463 
464 /*
465 ====================================================================
466 Store the destination rectangle of the last blit operation. SDL
467 modified this rectangle to the actually updated clipping region.
468 ====================================================================
469 */
stk_display_store_drect(void)470 void stk_display_store_drect( void )
471 {
472     if ( stk_update_rect_count < STK_UPDATE_RECT_LIMIT )
473         stk_update_rects[stk_update_rect_count++] = stk_drect;
474 }
475 
476 /*
477 ====================================================================
478 Either update all gathered rects or simply the full screen.
479 In any case the stored regions are cleared.
480 ====================================================================
481 */
stk_display_update(int type)482 void stk_display_update( int type )
483 {
484     if ( type == STK_UPDATE_ALL ||
485          stk_update_rect_count == STK_UPDATE_RECT_LIMIT )
486         SDL_UpdateRect( stk_display, 0, 0, 0, 0 );
487     else
488         SDL_UpdateRects( stk_display, stk_update_rect_count,
489                          stk_update_rects );
490     stk_update_rect_count = 0;
491 }
492 
493 /*
494 ====================================================================
495 Fade the current contents of the display either in or out. 'time'
496 is the time in milliseconds the fading is supposed to take.
497 ====================================================================
498 */
stk_display_fade(int type,int time)499 void stk_display_fade( int type, int time )
500 {
501     SDL_Surface *buffer = 0;
502     float alpha;
503     float alpha_change; /* per ms */
504     int leave = 0;
505     int ms;
506 
507     if ( stk_quit_request ) return;
508 
509     if ( !stk_display_use_fade ) {
510         if ( type == STK_FADE_IN )
511             stk_display_update( STK_UPDATE_ALL );
512         else {
513             stk_surface_fill( stk_display, 0, 0, -1, -1, 0x0 );
514             stk_display_update( STK_UPDATE_ALL );
515         }
516     }
517 
518     /* get screen contents */
519     buffer = stk_surface_create( SDL_SWSURFACE, stk_display->w, stk_display->h );
520     SDL_SetColorKey( buffer, 0, 0 );
521     stk_surface_blit( stk_display, 0, 0, -1, -1, buffer, 0, 0 );
522 
523     /* compute alpha and alpha change */
524     if ( type == STK_FADE_OUT ) {
525         alpha = 255;
526         alpha_change = -255.0 / time;
527     }
528     else {
529         alpha = 0;
530         alpha_change = 255.0 / time;
531     }
532 
533     /* fade */
534     stk_timer_reset();
535     while ( !leave ) {
536         ms = stk_timer_get_time();
537         alpha += alpha_change * ms;
538         if ( type == STK_FADE_IN && alpha >= 255 ) break;
539         if ( type == STK_FADE_OUT && alpha <= 0 ) break;
540         /* update */
541         stk_surface_fill( stk_display, 0, 0, -1, -1, 0x0 );
542         SDL_SetAlpha( buffer, SDL_SRCALPHA, (int)alpha );
543         stk_surface_blit( buffer, 0, 0, -1, -1, stk_display, 0, 0);
544         stk_display_update( STK_UPDATE_ALL );
545     }
546 
547     /* update screen */
548     SDL_SetAlpha( buffer, 0, 0 );
549     if ( type == STK_FADE_IN )
550         stk_surface_blit( buffer, 0, 0, -1, -1, stk_display, 0, 0 );
551     else
552         stk_surface_fill( stk_display, 0, 0, -1, -1, 0x0 );
553     stk_display_update( STK_UPDATE_ALL );
554     stk_surface_free( &buffer );
555 }
556 
557 
558 /*
559 ====================================================================
560 Take a screenshot and save it to screenshot[index].bmp in the
561 current directory.
562 ====================================================================
563 */
stk_display_take_screenshot()564 void stk_display_take_screenshot()
565 {
566     char str[32];
567     snprintf( str, 32, "screenshot%i.bmp", stk_screenshot_id++ );
568     SDL_SaveBMP( stk_display, str );
569 }
570 
571 /*
572 ====================================================================
573 Switch fullscreen/windowed for current resolution.
574 ====================================================================
575 */
stk_display_apply_fullscreen(int fullscreen)576 void stk_display_apply_fullscreen( int fullscreen )
577 {
578     int flags = stk_display->flags;
579     if ( fullscreen && stk_display->flags & SDL_FULLSCREEN )
580         return;
581     if ( !fullscreen && !(stk_display->flags & SDL_FULLSCREEN ) )
582         return;
583     if ( fullscreen )
584         flags |= SDL_FULLSCREEN;
585     else
586         flags &= ~SDL_FULLSCREEN;
587     stk_display_open( flags, stk_display->w, stk_display->h,
588         stk_display->format->BitsPerPixel );
589 }
590 
591 /* SURFACE */
592 
593 /*
594 ====================================================================
595 If stk_surface_load() is called with a relative path this prefix
596 is added. Default is '.'; 'path' is copied.
597 ====================================================================
598 */
stk_surface_set_path(char * path)599 void stk_surface_set_path( char *path )
600 {
601 #ifdef STK_DEBUG
602     printf( "surface prefix set to: %s\n", path );
603 #endif
604     if ( stk_surface_path )
605         free( stk_surface_path );
606     if ( ( stk_surface_path = strdup( path ) ) == 0 )
607         STK_ABORT( "Out Of Memory" )
608 }
609 
is_path_relative(char * path)610 static int is_path_relative(char *path)
611 {
612     int is_relative;
613 #ifdef _WIN32
614     is_relative = ((*path != '\\') && ((strchr(path,':') == NULL)));
615 #else
616     is_relative = (*path != '/');
617 #endif
618     return is_relative;
619 }
620 
621 
622 /*
623 ====================================================================
624 Load a surface from a path. If it's not an absolute directory
625 (starting with '/') the prefix passed in stk_surface_set_path()
626 is prepended.
627 If a video mode was set the surface will be converted to its
628 format to speed up blitting. As resource either BMP or PNG may
629 be provided.
630 If SDL_NONFATAL is passed a warning is displayed and the function
631 returns NULL else it will exit the program with an error.
632 ====================================================================
633 */
stk_surface_load(int flags,char * format,...)634 SDL_Surface* stk_surface_load( int flags, char *format, ... )
635 {
636     SDL_Surface *surface = 0;
637     SDL_Surface *converted_surface = 0;
638     char path[512], *ptr;
639     va_list args;
640 
641     /* build full path */
642     memset( path, 0, 512 ); ptr = path;
643     if ( is_path_relative(format) && stk_surface_path ) {
644         sprintf( path, "%s/", stk_surface_path );
645         ptr = path + strlen( path );
646     }
647     va_start( args, format );
648     vsnprintf( ptr, 511 - (path-ptr), format, args );
649     va_end( args );
650 #ifdef STK_DEBUG
651     printf( "loading %s\n", path );
652 #endif
653 
654     /* load surface */
655     ptr = path + strlen( path ) - 3;
656     if ( !strncmp( ptr, "png", 3 ) )
657         /* as png */
658         surface = png_load( path );
659     else
660         /* as bmp */
661         surface = SDL_LoadBMP( path );
662     if ( surface == 0 ) {
663         if ( flags & SDL_NONFATAL ) {
664             fprintf( stderr, "%s\n", SDL_GetError() );
665             return 0;
666         }
667         else
668             STK_ABORT( SDL_GetError() )
669     }
670 
671     /* convert if display open */
672     if ( stk_display ) {
673         converted_surface = SDL_DisplayFormat( surface );
674         if ( converted_surface == 0 ) {
675             fprintf( stderr, "Conversion of %s failed: %s\n",
676                      path, SDL_GetError() );
677             return surface;
678         }
679         else {
680             SDL_FreeSurface( surface );
681             SDL_SetColorKey(
682                 converted_surface, SDL_SRCCOLORKEY, 0x0 );
683             SDL_SetAlpha( converted_surface, 0, 0 );
684             return converted_surface;
685         }
686     }
687     else {
688 #ifdef STK_DEBUG
689             printf( "  not converted\n" );
690 #endif
691         return surface;
692     }
693 }
694 
695 /*
696 ====================================================================
697 Create a surface with the given size and the format of the
698 video mode which must be set.
699 The default color key of the surface is black and no alpha.
700 ====================================================================
701 */
stk_surface_create(int flags,int width,int height)702 SDL_Surface* stk_surface_create( int flags, int width, int height )
703 {
704     SDL_Surface *surface = 0;
705     if ( !stk_display ) {
706         if ( flags & SDL_NONFATAL ) {
707             fprintf( stderr,
708                 "Can't create surface as no display is open\n" );
709             return 0;
710         }
711         else
712             STK_ABORT(
713                 "Can't create surface as no display is open\n" );
714     }
715     surface = SDL_CreateRGBSurface( flags, width, height,
716                   stk_display->format->BitsPerPixel,
717                   stk_display->format->Rmask,
718                   stk_display->format->Gmask,
719                   stk_display->format->Bmask,
720                   stk_display->format->Amask );
721     if ( surface == 0 ) {
722         if ( flags & SDL_NONFATAL ) {
723             fprintf( stderr, "%s\n", SDL_GetError() );
724             return 0;
725         }
726         else
727             STK_ABORT( SDL_GetError() );
728     }
729     SDL_SetColorKey( surface, SDL_SRCCOLORKEY, 0x0 );
730     SDL_SetAlpha( surface, 0, 0 );
731 #ifdef STK_DEBUG
732     printf( "surface %ix%i created\n", width, height );
733 #endif
734     return surface;
735 }
736 
737 /*
738 ====================================================================
739 Free the memory of a surface if not NULL and reset the pointer
740 to NULL.
741 ====================================================================
742 */
stk_surface_free(SDL_Surface ** surface)743 void stk_surface_free( SDL_Surface **surface )
744 {
745     if ( *surface ) {
746         SDL_FreeSurface( *surface );
747         *surface = 0;
748     }
749 }
750 
751 /*
752 ====================================================================
753 Blit retangle from 'src' to 'dest' with current alpha of 'src'.
754 If 'sw' is -1 the full source width is used, 'sh' analogue.
755 stk_display_store_drect() can be used to store the update rect
756 of the blitted surface.
757 ====================================================================
758 */
stk_surface_blit(SDL_Surface * src,int sx,int sy,int sw,int sh,SDL_Surface * dest,int dx,int dy)759 void stk_surface_blit(
760     SDL_Surface *src, int sx, int sy, int sw, int sh,
761     SDL_Surface *dest, int dx, int dy )
762 {
763     /* build rectangles */
764     stk_drect.x = dx; stk_drect.y = dy;
765     stk_drect.w = (sw==-1)?src->w:sw;
766     stk_drect.h = (sh==-1)?src->h:sh;
767     stk_srect.x = sx; stk_srect.y = sy;
768     stk_srect.w = stk_drect.w;
769     stk_srect.h = stk_drect.h;
770     /* blit */
771     SDL_BlitSurface( src, &stk_srect, dest, &stk_drect );
772 }
773 
774 /*
775 ====================================================================
776 Different from stk_surface_blit() this function temporarily
777 overwrites 'src's alpha value. It is not recommended to use
778 this function if performance is important as it is slower
779 than an SDL_SetAlpha() combined with stk_surface_blit().
780 ====================================================================
781 */
stk_surface_alpha_blit(SDL_Surface * src,int sx,int sy,int sw,int sh,SDL_Surface * dest,int dx,int dy,int alpha)782 void stk_surface_alpha_blit(
783     SDL_Surface *src, int sx, int sy, int sw, int sh,
784     SDL_Surface *dest, int dx, int dy, int alpha )
785 {
786     if ( src->flags & SDL_SRCALPHA )
787         stk_old_alpha = src->format->alpha;
788     else
789         stk_old_alpha = -1;
790     SDL_SetAlpha( src, SDL_SRCALPHA, alpha );
791     stk_surface_blit( src, sx,sy,sw,sh, dest, dx,dy );
792     if ( stk_old_alpha == -1 )
793         SDL_SetAlpha( src, 0, 0 );
794     else
795         SDL_SetAlpha( src, SDL_SRCALPHA,
796                       stk_old_alpha );
797 }
798 
799 /*
800 ====================================================================
801 Fill a rectangle of the surface with a given color of the format
802 0xRRGGBB. 'dw' == -1 and 'dh' == -1 have the same effect as in
803 stk_surface_blit().
804 ====================================================================
805 */
stk_surface_fill(SDL_Surface * dest,int dx,int dy,int dw,int dh,int color)806 void stk_surface_fill(
807     SDL_Surface *dest, int dx, int dy, int dw, int dh,
808     int color )
809 {
810     /* build rectangle */
811     stk_drect.x = dx; stk_drect.y = dy;
812     stk_drect.w = (dw==-1)?dest->w:dw;
813     stk_drect.h = (dh==-1)?dest->h:dh;
814     /* fill surface */
815     SDL_FillRect( dest, &stk_drect,
816         SDL_MapRGB( stk_display->format,
817                     color >> 16,
818                     (color >> 8) & 0xFF,
819                     color & 0xFF) );
820 }
821 
822 /*
823 ====================================================================
824 Set the clipping region of a surface. All blit operations into
825 this surface will only have effect within the clipping region.
826 'dw' == -1 and 'dh' == -1 have the same effect as in
827 stk_surface_blit().
828 ====================================================================
829 */
stk_surface_clip(SDL_Surface * dest,int dx,int dy,int dw,int dh)830 void stk_surface_clip(
831     SDL_Surface *dest, int dx, int dy, int dw, int dh )
832 {
833     /* build rectangle */
834     stk_drect.x = dx; stk_drect.y = dy;
835     stk_drect.w = (dw<=0)?dest->w:dw;
836     stk_drect.h = (dh<=0)?dest->h:dh;
837     /* clip */
838     SDL_SetClipRect( dest, &stk_drect );
839 }
840 
841 /*
842 ====================================================================
843 Lock/unlock surface for direct access.
844 ====================================================================
845 */
846 #define stk_surface_lock( surface ) \
847  if (SDL_MUSTLOCK((surface))) SDL_LockSurface((surface))
848 #define stk_surface_unlock( surface ) \
849  if (SDL_MUSTLOCK((surface))) SDL_UnlockSurface((surface))
850 
851 /*
852 ====================================================================
853 Get or set a pixel from/to a surface. This time the pixel must
854 already be in SDL format.
855 ====================================================================
856 */
stk_surface_set_pixel(SDL_Surface * dest,int dx,int dy,Uint32 pixel)857 void stk_surface_set_pixel(
858     SDL_Surface *dest, int dx, int dy, Uint32 pixel )
859 {
860     memcpy( dest->pixels + dy * dest->pitch +
861             dx * dest->format->BytesPerPixel,
862             &pixel, dest->format->BytesPerPixel );
863 }
stk_surface_get_pixel(SDL_Surface * src,int sx,int sy)864 Uint32 stk_surface_get_pixel( SDL_Surface *src, int sx, int sy )
865 {
866     Uint32 pixel = 0;
867     memcpy( &pixel,
868             src->pixels + sy * src->pitch +
869             sx * src->format->BytesPerPixel,
870             src->format->BytesPerPixel );
871     return pixel;
872 }
873 
874 /*
875 ====================================================================
876 Convert part of the surface to gray and if 'dark' is set cut the
877 brightness in half. 'dw' == -1 and 'dh' == -1 have the same
878 effect as in tk_surface_blit().
879 ====================================================================
880 */
stk_surface_gray(SDL_Surface * dest,int dx,int dy,int dw,int dh,int dark)881 void stk_surface_gray(
882     SDL_Surface *dest, int dx, int dy, int dw, int dh, int dark )
883 {
884     int i, j, y_offset, x_offset;
885     Uint32 pixel, temp;
886     Uint8 red, green, blue, gray;
887     /* adjust size */
888     if ( dw == -1 ) dw = dest->w;
889     if ( dh == -1 ) dh = dest->h;
890     /* gray */
891     y_offset = dest->pitch * dy;
892     for ( j = dy; j < dy + dh; j++ ) {
893         for ( i = dx, x_offset = dx * dest->format->BytesPerPixel;
894               i < dx + dw;
895               i++, x_offset += dest->format->BytesPerPixel ) {
896             pixel = 0;
897             /* get pixel */
898             memcpy( &pixel,
899                     dest->pixels +
900                     y_offset + x_offset,
901                     dest->format->BytesPerPixel );
902             /* extract colors */
903             temp=pixel & dest->format->Rmask;
904             temp=temp>>dest->format->Rshift;
905             temp=temp<<dest->format->Rloss;
906             red=(Uint8)temp;
907             temp=pixel & dest->format->Gmask;
908             temp=temp>>dest->format->Gshift;
909             temp=temp<<dest->format->Gloss;
910             green=(Uint8)temp;
911             temp=pixel & dest->format->Bmask;
912             temp=temp>>dest->format->Bshift;
913             temp=temp<<dest->format->Bloss;
914             blue=(Uint8)temp;
915             /* build new color */
916             gray = (red + green + green + blue) >> (2 + dark);
917             pixel = SDL_MapRGB( dest->format, gray, gray, gray );
918             /* replace */
919             memcpy( dest->pixels +
920                     y_offset + x_offset,
921                     &pixel,
922                     dest->format->BytesPerPixel );
923         }
924         y_offset += dest->pitch;
925     }
926 }
927 
928 /*
929 ====================================================================
930 Add a 3D frame to surface and dark to contents. The frame border
931 is 'border' thick.
932 ====================================================================
933 */
stk_surface_add_3dframe(SDL_Surface * dest,int dx,int dy,int dw,int dh,int border)934 void stk_surface_add_3dframe(
935     SDL_Surface *dest, int dx, int dy, int dw, int dh, int border )
936 {
937     printf( "not implemented yet!\n" );
938 }
939 
940 /*
941 ====================================================================
942 Fill the surface with the wallpaper (clipped)
943 ====================================================================
944 */
stk_surface_apply_wallpaper(SDL_Surface * dest,int dx,int dy,int dw,int dh,SDL_Surface * wallpaper,int alpha)945 void stk_surface_apply_wallpaper(
946     SDL_Surface *dest, int dx, int dy, int dw, int dh,
947     SDL_Surface *wallpaper, int alpha )
948 {
949     int i, j;
950     /* adjust dw and dh */
951     if ( dw == -1 ) dw = dest->w;
952     if ( dh == -1 ) dh = dest->h;
953     stk_surface_clip( dest, dx, dy, dw, dh );
954     /* apply */
955     for ( i = 0; i < dw; i += wallpaper->w )
956         for ( j = 0; j < dh; j += wallpaper->h )
957             stk_surface_alpha_blit( wallpaper, 0, 0, -1, -1,
958                               dest, dx + i, dy + j, alpha );
959     stk_surface_clip( dest, 0,0,-1,-1 );
960 }
961 
962 /*
963 ====================================================================
964 Apply a frame to the surface. The frame resource provides the eight
965 square-like parts for the frame composed as a little frame: corners
966 and sides.
967 'dw' == -1 and 'dh' == -1 have the same effect as in
968 stk_surface_blit().
969 Returns the size of the border.
970 ====================================================================
971 */
stk_surface_apply_frame(SDL_Surface * dest,int dx,int dy,int dw,int dh,SDL_Surface * frame)972 int stk_surface_apply_frame(
973     SDL_Surface *dest, int dx, int dy, int dw, int dh,
974     SDL_Surface *frame )
975 {
976     int tile_size = frame->w / 3;
977     int i;
978     /* adjust dw and dh */
979     if ( dw == -1 ) dw = dest->w;
980     if ( dh == -1 ) dh = dest->h;
981     /* horizontal middle parts */
982     for ( i = dx; i < dx + dw - tile_size; i += tile_size ) {
983         stk_surface_blit( frame, tile_size, 0,
984                           tile_size, tile_size,
985                           dest, i, dy );
986         stk_surface_blit( frame, tile_size, tile_size << 1,
987                           tile_size, tile_size,
988                           dest, i, dy + dh - tile_size );
989     }
990     /* vertical middle parts */
991     for ( i = dy; i < dy + dh - tile_size; i += tile_size ) {
992         stk_surface_blit( frame, 0, tile_size,
993                           tile_size, tile_size,
994                           dest, dx, i );
995         stk_surface_blit( frame, tile_size << 1, tile_size,
996                           tile_size, tile_size,
997                           dest, dx + dw - tile_size, i );
998     }
999     /* corners */
1000     stk_surface_blit( frame, 0, 0, tile_size, tile_size,
1001                       dest, dx, dy );
1002     stk_surface_blit( frame, tile_size << 1, 0,
1003                       tile_size, tile_size,
1004                       dest, dx + dw - tile_size, dy );
1005     stk_surface_blit( frame, 0, tile_size << 1,
1006                       tile_size, tile_size,
1007                       dest, dx, dy + dh - tile_size );
1008     stk_surface_blit( frame, tile_size << 1, tile_size << 1,
1009                       tile_size, tile_size,
1010                       dest, dx + dw - tile_size,
1011                       dy + dh - tile_size );
1012     return tile_size;
1013 }
1014 
1015 /* FIXED FONT */
1016 
1017 /*
1018 ====================================================================
1019 Load a fixed font which is simply a surface containing the ASCII
1020 characters from 32 (blank) to 96 (whatever) where all characters
1021 have the same width.
1022 ====================================================================
1023 */
stk_font_load(int flags,char * format,...)1024 StkFont* stk_font_load( int flags, char *format, ... )
1025 {
1026     char path[512];
1027     va_list args;
1028     StkFont *font = calloc( 1, sizeof ( StkFont ) );
1029     if ( font == 0 )
1030         STK_ABORT( "Out Of Memory" );
1031     /* build path */
1032     memset( path, 0, 512 );
1033     va_start( args, format );
1034     vsnprintf( path, 511, format, args );
1035     va_end( args );
1036     /* load surface */
1037     font->surface = stk_surface_load( flags, path );
1038     if ( font == 0 ) {
1039         if ( flags & SDL_NONFATAL ) {
1040             fprintf( stderr, "%s\n", SDL_GetError() );
1041             return 0;
1042         }
1043         else
1044             STK_ABORT( SDL_GetError() );
1045     }
1046     /* do the rest */
1047     font->height = font->surface->h;
1048     font->width = font->surface->w / 96;
1049     return font;
1050 }
1051 
1052 /*
1053 ====================================================================
1054 Free a font if not NULL and reset pointer to NULL.
1055 ====================================================================
1056 */
stk_font_free(StkFont ** font)1057 void stk_font_free( StkFont **font )
1058 {
1059     if ( *font ) {
1060         stk_surface_free( &(*font)->surface );
1061         free(*font); *font = 0;
1062     }
1063 }
1064 
1065 /*
1066 ====================================================================
1067 Write string to surface. If 'alpha' is -1 the font is displayed
1068 with the current alpha value else the new alpha is used and kept.
1069 stk_display_store_drect() can be used to store the update rect
1070 of the written string.
1071 ====================================================================
1072 */
stk_font_write(StkFont * font,SDL_Surface * dest,int dx,int dy,int alpha,char * string)1073 void stk_font_write(
1074     StkFont *font, SDL_Surface *dest, int dx, int dy,
1075     int alpha, char *string )
1076 {
1077     int	x, i, width = 0;
1078 
1079     /* get pixel width of string */
1080     width = stk_font_string_width( font, string );
1081 
1082     /* ajust dx,dy to alignment */
1083     if ( font->align & STK_FONT_ALIGN_CENTER_X )
1084         dx -= width >> 1;
1085     else
1086         if ( font->align & STK_FONT_ALIGN_RIGHT )
1087             dx -= width;
1088     if ( font->align & STK_FONT_ALIGN_CENTER_Y )
1089         dy -= (font->height >> 1 ) + 1;
1090     else
1091         if ( font->align & STK_FONT_ALIGN_BOTTOM )
1092             dy -= font->height;
1093 
1094     /* draw characters */
1095     if ( alpha != -1 )
1096         SDL_SetAlpha( font->surface, SDL_SRCALPHA, alpha );
1097     for ( i = 0, x = dx; i < strlen( string );
1098           i++, x += font->width ) {
1099         stk_surface_blit(
1100             font->surface, font->width * (string[i] - 32), 0,
1101             font->width, font->height,
1102             dest, x, dy );
1103     }
1104 
1105     /* fake destination rectangle to full string */
1106     stk_drect.x = dx; stk_drect.y = dy;
1107     stk_drect.w = width; stk_drect.h = font->height;
1108     if (stk_drect.x < 0) {
1109         stk_drect.w += stk_drect.x;
1110         stk_drect.x = 0;
1111     }
1112     if (stk_drect.y < 0) {
1113         stk_drect.h += stk_drect.y;
1114         stk_drect.y = 0;
1115     }
1116     if (stk_drect.x + stk_drect.w >= stk_display->w)
1117         stk_drect.w = stk_display->w - stk_drect.x;
1118     if (stk_drect.y + stk_drect.h >= stk_display->h)
1119         stk_drect.h = stk_display->h - stk_drect.y;
1120 }
1121 
1122 /* CURSOR */
1123 
1124 /*
1125 ====================================================================
1126 Build an SDL cursor with the given hotspot. The resource mask
1127 contains 'b' for black 'w' for white and ' ' for transparent.
1128 ====================================================================
1129 */
stk_cursor_create(int width,int height,int hot_x,int hot_y,char * source)1130 SDL_Cursor* stk_cursor_create(
1131     int width, int height, int hot_x, int hot_y, char *source )
1132 {
1133     unsigned char *mask = 0, *data = 0;
1134     SDL_Cursor *cursor = 0;
1135     int i, j, k;
1136     unsigned char data_byte, mask_byte;
1137     int pot;
1138     /* create mask&data */
1139     mask = calloc( width * height / 8, sizeof ( char ) );
1140     data = calloc( width * height / 8, sizeof ( char ) );
1141     /* convert */
1142     k = 0;
1143     for (j = 0; j < width * height; j += 8, k++) {
1144         pot = 1;
1145         data_byte = mask_byte = 0;
1146         /* create byte */
1147         for (i = 7; i >= 0; i--) {
1148             switch ( source[j + i] ) {
1149                 case 'b':
1150                     data_byte += pot;
1151                 case 'w':
1152                     mask_byte += pot;
1153                     break;
1154             }
1155             pot *= 2;
1156         }
1157         /* add to mask */
1158         data[k] = data_byte;
1159         mask[k] = mask_byte;
1160     }
1161     /* create and return cursor */
1162     cursor = SDL_CreateCursor( data, mask, width, height, hot_x, hot_y );
1163     free( mask );
1164     free( data );
1165     return cursor;
1166 }
1167 
1168 /*
1169 ====================================================================
1170 Free a cursor if not NULL and reset pointer to NULL.
1171 ====================================================================
1172 */
stk_cursor_free(SDL_Cursor ** cursor)1173 void stk_cursor_free( SDL_Cursor **cursor )
1174 {
1175     if ( *cursor ) {
1176         SDL_FreeCursor( *cursor );
1177         *cursor = 0;
1178     }
1179 }
1180 
1181 /* TIMER */
1182 
1183 int stk_timer_last_call = 0;
1184 
1185 /*
1186 ====================================================================
1187 Reset the timer.
1188 ====================================================================
1189 */
stk_timer_reset(void)1190 void stk_timer_reset( void )
1191 {
1192     stk_timer_last_call = SDL_GetTicks();
1193 }
1194 
1195 /*
1196 ====================================================================
1197 Get the time since last call or reset.
1198 ====================================================================
1199 */
stk_timer_get_time(void)1200 int stk_timer_get_time( void )
1201 {
1202     int ms, current = SDL_GetTicks();
1203     if ( current == stk_timer_last_call )
1204         SDL_Delay(1);
1205     current = SDL_GetTicks();
1206     ms = current - stk_timer_last_call;
1207     stk_timer_last_call = current;
1208     return ms;
1209 }
1210 
1211 /* AUDIO */
1212 
1213 /*
1214 ====================================================================
1215 Open mixer. If this fails an error message is
1216 displayed (program doesn't abort) and all stk_audio and stk_sound
1217 functions have no effect.
1218   'frequency': 11025, 22050, 44100 ...
1219   'format': MIX_DEFAULT_FORMAT is recommended
1220   'channels': mono(1) or stereo(2)
1221   'chunksize': mix buffer size (1024 Bytes recommended)
1222 Passing 0 for an argument means to use the SDL default:
1223   22050Hz in format AUDIO_S16LSB stereo with 1024 bytes chunksize
1224 stk_audio_open() installs stk_audio_close() as exit function.
1225 There are 8 channels available for mixing.
1226 ====================================================================
1227 */
stk_audio_open(int frequency,Uint16 format,int channels,int chunksize)1228 int stk_audio_open(
1229     int frequency, Uint16 format, int channels, int chunksize )
1230 {
1231 #ifdef AUDIO_ENABLED
1232     if ( frequency == 0 ) frequency = MIX_DEFAULT_FREQUENCY;
1233     if ( format == 0 ) format = MIX_DEFAULT_FORMAT;
1234     if ( channels == 0 ) channels = 2;
1235     if ( chunksize == 0 ) chunksize = stk_audio_buffer_size;
1236     if ( Mix_OpenAudio(
1237              frequency, format, channels, chunksize ) < 0 ) {
1238         fprintf( stderr, "%s\n", SDL_GetError() );
1239         stk_audio_ok = 0;
1240         return 0;
1241     }
1242     stk_audio_ok = 1;
1243     atexit( stk_audio_close );
1244     return 1;
1245 #else
1246     stk_audio_ok = 0;
1247     return 0;
1248 #endif
1249 }
1250 
1251 /*
1252 ====================================================================
1253 Close mixer if previously opened.
1254 ====================================================================
1255 */
stk_audio_close(void)1256 void stk_audio_close( void )
1257 {
1258 #ifdef AUDIO_ENABLED
1259     if ( stk_audio_ok ) {
1260         Mix_CloseAudio();
1261         stk_audio_ok = 0;
1262     }
1263 #endif
1264     printf( "Audio finalized\n" );
1265 }
1266 
1267 /*
1268 ====================================================================
1269 If stk_sound_load() is called with a relative path this prefix
1270 is added. Default is '.'; 'path' is copied.
1271 ====================================================================
1272 */
stk_audio_set_path(char * path)1273 void stk_audio_set_path( char *path )
1274 {
1275 #ifdef STK_DEBUG
1276     printf( "sound prefix set to: %s\n", path );
1277 #endif
1278     if ( stk_audio_path )
1279         free( stk_audio_path );
1280     if ( ( stk_audio_path = strdup( path ) ) == 0 )
1281         STK_ABORT( "Out Of Memory" )
1282 }
1283 
1284 /*
1285 ====================================================================
1286 Enable/disable sound. If disabled stk_sound_play () has no effect.
1287 ====================================================================
1288 */
stk_audio_enable_sound(int enable)1289 void stk_audio_enable_sound( int enable )
1290 {
1291     stk_audio_sound_enabled = enable;
1292 }
1293 
1294 /*
1295 ====================================================================
1296 Set default volume of all sounds: 0 - 128
1297 ====================================================================
1298 */
stk_audio_set_sound_volume(int volume)1299 void stk_audio_set_sound_volume( int volume )
1300 {
1301     if ( !stk_audio_ok ) return;
1302     if ( volume < 0 ) volume = 0;
1303     if ( volume > 127 ) volume = 127;
1304     stk_audio_sound_volume = volume;
1305 #ifdef AUDIO_ENABLED
1306     Mix_Volume( -1, volume ); /* all sound channels */
1307 #endif
1308 }
1309 
1310 /*
1311 ====================================================================
1312 Fade out a specific sound channel. If 'channel is -1 all
1313 channels fade out. 'ms' is the time the fading shall take.
1314 ====================================================================
1315 */
stk_audio_fade_out(int channel,int ms)1316 void stk_audio_fade_out( int channel, int ms )
1317 {
1318 #ifdef AUDIO_ENABLED
1319     if ( stk_audio_ok )
1320         Mix_FadeOutChannel( channel, ms );
1321 #endif
1322 }
1323 
1324 /* SOUND */
1325 
1326 /*
1327 ====================================================================
1328 Load a sound from a path. If it's not an absolute directory
1329 (starting with '/') the prefix passed in stk_sound_set_path()
1330 is prepended. Loading sounds is non-fatal thus if a sound
1331 cannot be found it is created but empty. In this case
1332 a warning is displayed.
1333 The volume is set to the default set by
1334 stk_audio_set_sound_volume(). Sounds with an equal 'channel'
1335 will share it so if a new sound is played the old one breaks up
1336 thus channel should be used to group sounds.
1337 If channel is -1 the first available channel is used
1338 to play sound.
1339 ====================================================================
1340 */
stk_sound_load(int channel,char * format,...)1341 StkSound *stk_sound_load( int channel, char *format, ... )
1342 {
1343     StkSound *sound;
1344 #ifdef AUDIO_ENABLED
1345     char path[512], *ptr;
1346     va_list args;
1347 
1348     /* build full path */
1349     memset( path, 0, 512 ); ptr = path;
1350     if ( is_path_relative(format)  && stk_audio_path ) {
1351         sprintf( path, "%s/", stk_audio_path );
1352         ptr = path + strlen( path );
1353     }
1354     va_start( args, format );
1355     vsnprintf( ptr, 511 - (path-ptr), format, args );
1356     va_end( args );
1357 #ifdef STK_DEBUG
1358     printf( "loading %s\n", path );
1359 #endif
1360 
1361     /* load sound */
1362     if ( ( sound = calloc( 1, sizeof( StkSound ) ) ) == 0 )
1363         STK_ABORT( "Out Of Memory" );
1364     if ( ( sound->chunk = Mix_LoadWAV( path ) ) == 0 )
1365         fprintf( stderr, "Couldn't open %s (%s)\n",
1366             path, SDL_GetError() );
1367 #else
1368     if ( ( sound = calloc( 1, sizeof( StkSound ) ) ) == 0 )
1369         STK_ABORT( "Out Of Memory" );
1370 #endif
1371     sound->volume = stk_audio_sound_volume;
1372     sound->channel = channel;
1373 
1374     /* done */
1375     return sound;
1376 }
1377 
1378 /*
1379 ====================================================================
1380 Free the memory of a sound if not NULL and reset the pointer
1381 to NULL.
1382 ====================================================================
1383 */
stk_sound_free(StkSound ** sound)1384 void stk_sound_free( StkSound **sound )
1385 {
1386     if ( *sound ) {
1387 #ifdef AUDIO_ENABLED
1388         if ( (*sound)->chunk )
1389             Mix_FreeChunk( (*sound)->chunk );
1390 #endif
1391         free( *sound );
1392     }
1393 }
1394 
1395 /*
1396 ====================================================================
1397 Set channel/volume of sound.
1398 ====================================================================
1399 */
stk_sound_set_volume(StkSound * sound,int volume)1400 void stk_sound_set_volume( StkSound *sound, int volume )
1401 {
1402     if ( volume < 0 ) volume = 0;
1403     if ( volume > 128 ) volume = 128;
1404     sound->volume = volume;
1405 }
stk_sound_set_channel(StkSound * sound,int channel)1406 void stk_sound_set_channel( StkSound *sound, int channel )
1407 {
1408     if ( channel < 0 ) channel = 0;
1409     if ( channel > stk_audio_mixchannel_count )
1410         channel = stk_audio_mixchannel_count;
1411     sound->channel = channel;
1412 }
1413 
1414 /*
1415 ====================================================================
1416 Play a sound.
1417 ====================================================================
1418 */
stk_sound_play(StkSound * sound)1419 void stk_sound_play( StkSound *sound )
1420 {
1421     int channel;
1422 #ifdef AUDIO_ENABLED
1423     if ( stk_audio_ok && stk_audio_sound_enabled ) {
1424         /* if channel is -1 use first free channel */
1425         channel = sound->channel;
1426         if ( channel == -1 )
1427             channel = Mix_GroupAvailable(-1);
1428         Mix_Volume( channel, stk_audio_sound_volume );
1429         Mix_PlayChannel( channel, sound->chunk, 0 );
1430     }
1431 #endif
1432 }
1433 /*
1434 ====================================================================
1435 Play a sound at horizontal position x.
1436 ====================================================================
1437 */
stk_sound_play_x(int x,StkSound * sound)1438 void stk_sound_play_x( int x, StkSound *sound )
1439 {
1440     int channel;
1441 #ifdef AUDIO_ENABLED
1442     if ( stk_audio_ok && stk_audio_sound_enabled ) {
1443         x = (x - 40) * 255 / (640 - 40 - 40);
1444         if (x < 0) x = 0;
1445         if (x > 255) x = 255;
1446         /* if channel is -1 use first free channel */
1447         channel = sound->channel;
1448         if ( channel == -1 )
1449             channel = Mix_GroupAvailable(-1);
1450         Mix_SetPanning( channel, 255 - x, x );
1451         Mix_Volume( channel, stk_audio_sound_volume );
1452         Mix_PlayChannel( channel, sound->chunk, 0 );
1453     }
1454 #endif
1455 }
1456 
1457 /*
1458 ====================================================================
1459 Fade out the channel of this sound.
1460 ====================================================================
1461 */
stk_sound_fade_out(StkSound * sound,int ms)1462 void stk_sound_fade_out( StkSound *sound, int ms )
1463 {
1464 #ifdef AUDIO_ENABLED
1465     if ( stk_audio_ok )
1466         Mix_FadeOutChannel( sound->channel, ms );
1467 #endif
1468 }
1469