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