Attempted fix of audio buffer overrun on intel HDA.
[cantaveria.git] / backend.c
bloba58050ff130b3be7e9bf90842da7f42efb439da6
1 /*
2 Cantaveria - action adventure platform game
3 Copyright (C) 2009 Evan Rinehart
5 This program is free software; you can redistribute it and/or
6 modify it under the terms of the GNU General Public License
7 as published by the Free Software Foundation; either version 2
8 of the License, or (at your option) any later version.
10 This program is distributed in the hope that it will be useful,
11 but WITHOUT ANY WARRANTY; without even the implied warranty of
12 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13 GNU General Public License for more details.
15 You should have received a copy of the GNU General Public License
16 along with this program; if not, write to
18 The Free Software Foundation, Inc.
19 51 Franklin Street, Fifth Floor
20 Boston, MA 02110-1301, USA
23 #include <SDL/SDL.h>
24 #include <SDL/SDL_opengl.h>
26 #include "backend.h"
27 #include "game.h"
28 #include "loader.h"
29 #include "sound.h"
31 SDL_Surface* video;
32 int gl_flag = 0;
33 int W, H;
35 int since(){
36 static int last = 0;
37 int now = SDL_GetTicks();
38 int diff = now - last;
39 last = now;
40 return diff;
43 void delay(int ms){
44 SDL_Delay(ms);
48 int keymap[32];
49 int butmap[MAX_PLAYERS][8];
50 int joymap[MAX_PLAYERS];
52 int keynum(int name){
53 return keymap[name];
56 int butnum(int joy, int name){
57 return butmap[joy][name];
61 void backend_quit(){
62 printf("sdl: quit\n");
63 SDL_LockAudio();
64 SDL_CloseAudio();
65 SDL_Quit();
74 void input(){
76 SDL_Event e;
78 while(SDL_PollEvent(&e) != 0){
79 switch(e.type){
80 case SDL_KEYDOWN:
81 game.handler.keydown(e.key.keysym.sym);
82 break;
83 case SDL_KEYUP:
84 game.handler.keyup(e.key.keysym.sym);
85 break;
86 case SDL_JOYBUTTONDOWN:
87 game.handler.joypress(e.jbutton.which, e.jbutton.button);
88 break;
89 case SDL_JOYBUTTONUP:
90 game.handler.joyrelease(e.jbutton.which, e.jbutton.button);
91 break;
92 case SDL_JOYAXISMOTION:
93 if(e.jaxis.axis == 0){
94 game.handler.joymovex(e.jaxis.which, e.jaxis.value);
96 else if(e.jaxis.axis == 1){
97 game.handler.joymovey(e.jaxis.which, e.jaxis.value);
99 break;
100 case SDL_QUIT:
101 game.end = 1;
102 break;
107 void control(int type, int par1, int par2){
109 switch(type){
110 case KEYDOWN:
111 game.handler.keydown(par1);
112 break;
113 case KEYUP:
114 game.handler.keyup(par1);
115 break;
116 case JOYMOVEX:
117 game.handler.joymovex(par1, par2);
118 break;
119 case JOYMOVEY:
120 game.handler.joymovey(par1, par2);
121 break;
122 case JOYPRESS:
123 game.handler.joypress(par1, par2);
124 break;
125 case JOYRELEASE:
126 game.handler.joyrelease(par1, par2);
127 break;
144 drawing model
146 the backend will draw the current state of the game
147 in the following way.
149 it will render a stage at a given position. a stage is
150 N backgrounds
151 1 background tiles
152 1 wall tiles and
153 1 foreground tiles
155 then it will render the sprites
158 the stage is measured in terms of screens
159 each screen is 20x15 tiles, each tile is 16 pixels square
160 each screen can have one tile set of 256 tiles
163 /***************/
164 /*graphics data*/
165 /***************/
167 struct {
168 char* filename;
169 SDL_Surface* surf;
170 GLuint texture;
171 int w;
172 int h;
173 } gfx[MAX_GFX];
174 int gfx_count = 0;
176 sprite* sprites[MAX_SPRITES];
177 int sprite_count = 0;
179 animation* animations[MAX_ANIMATIONS];
180 int anim_count = 0;
182 int stage_enabled = 0;
184 int screen_offset = 0;
186 struct {
187 int x, y;
188 } camera;
192 /********************/
193 /* drawing routines */
194 /********************/
196 void draw_sprite_sdl(sprite* spr){
197 int x = spr->x - 0 + screen_offset;
198 int y = spr->y - 0;
199 int w = spr->w;
200 int h = spr->h;
202 int X = spr->frame.x;
203 int Y = spr->frame.y;
205 SDL_Surface* surf = gfx[spr->gfxid].surf;
206 SDL_Rect r1 = {X,Y,w,h};
207 SDL_Rect r2 = {x,y,w,h};
208 SDL_BlitSurface(surf,&r1,video,&r2);
211 void draw_sprite_gl(sprite* spr){
212 int x = spr->x - camera.x + screen_offset;
213 int y = spr->y - camera.y;
214 int w = spr->w;
215 int h = spr->h;
217 glBindTexture( GL_TEXTURE_2D, gfx[spr->gfxid].texture );
219 double X0 = spr->frame.x0;
220 double Y0 = spr->frame.y0;
221 double X1 = spr->frame.x1;
222 double Y1 = spr->frame.y1;
224 glBegin( GL_QUADS );
225 glTexCoord2d(X0,Y0);
226 glVertex3f(x,y,0);
228 glTexCoord2d(X1,Y0);
229 glVertex3f(x+w,y,0);
231 glTexCoord2d(X1,Y1);
232 glVertex3f(x+w,y+h,0);
234 glTexCoord2d(X0,Y1);
235 glVertex3f(x,y+h,0);
236 glEnd();
239 void draw_screen_sdl(zone* z, int si, int sj){
240 struct screen* scr = z->screens+si+z->w*sj;
241 SDL_Surface* surf = gfx[z->tileset].surf;
242 int x = si*20*16 - camera.x;
243 int y = sj*15*16 - camera.y;
245 for(int j=0; j < 15; j++){
246 for(int i=0; i < 20; i++){
247 if(x > 320 || y > 240 || x < -16 || y < -16) continue;
248 int gfx = scr->tiles[i][j].gfx;
249 if(gfx != 0){
250 SDL_Rect r1 = {gfx&7,gfx>>3,16,16};
251 SDL_Rect r2 = { x, y,16,16};
252 SDL_BlitSurface(surf,&r1,video,&r2);
254 else{
255 //draw background
257 x += 16;
259 x -= 320;
260 y += 16;
264 void draw_screen_gl(zone* z, int si, int sj){
271 void draw(){
273 if(!gl_flag){
275 SDL_FillRect(video, 0, 0xffffffff);
277 //draw walls and background
278 if(stage_enabled){
279 draw_screen_sdl(zones[0],0,0);
282 //draw sprites
283 for(int i=0; i < sprite_count; i++){
284 draw_sprite_sdl(sprites[i]);
287 //draw foreground tile/sprites
289 SDL_UpdateRect(video,0,0,0,0);
291 else{
293 glClearColor(1.0,1.0,1.0,0.0);
294 glClear( GL_COLOR_BUFFER_BIT );
296 if(stage_enabled){
297 draw_screen_gl(zones[0],0,0);
300 for(int i=0; i < sprite_count; i++){
301 draw_sprite_gl(sprites[i]);
304 SDL_GL_SwapBuffers();
314 /***********/
315 /* utility */
316 /***********/
318 void point_camera(int x, int y){
319 camera.x = x;
320 camera.y = y;
323 void animate_sprites(){
324 for(int i=0; i<sprite_count; i++){
325 sprite* spr = sprites[i];
327 spr->frame_counter += dt;
328 animation* ani = animations[spr->anim];
331 while(spr->frame_counter > ani->frame_lens[spr->current_frame]){
332 spr->frame_counter -= ani->frame_lens[spr->current_frame];
333 spr->current_frame++;
334 if(spr->current_frame == ani->frame_count){
335 spr->current_frame = 0;
337 spr->frame = ani->frames[spr->current_frame];
340 if(spr->update) spr->update(spr, spr->userdata);
347 /********************/
348 /* graphics loading */
349 /********************/
351 SDL_Surface* SDL_NewSurface(int w, int h){
352 char prefix[32] = "SDL_NewSurface";
353 SDL_Surface* tmp = SDL_CreateRGBSurface(SDL_SRCCOLORKEY,w,h,32,0,0,0,0);
354 if(!tmp){
355 out_of_memory(prefix);
358 SDL_Surface* surf = SDL_DisplayFormat(tmp);
359 if(!surf){
360 out_of_memory(prefix);
363 SDL_FreeSurface(tmp);
365 SDL_FillRect(surf,0,0x00ffffff);
367 return surf;
371 load_pixmap filename
372 loads a pixmap file stored in the gfx/ subdir
373 returns an SDL surface in the 24bit format RGBRGBRGB...
375 load_gfx filename
376 returns the gfx id of the graphics with filename
377 may or may not load a new gfx object with load_pixmap
378 in sdl mode this sets up the gfx surface for color key transparency
379 in opengl mode this converts the 24bit to 32bit texture with alpha channel
381 load_sprite filename
382 reads a sprite definition from from the sprites/ subdir
383 loads a new animation structure as defined the file
384 uses load_gfx to get gfx id for animation graphics
385 therefore, may or may not load new graphics
386 returns an sprite id number used to instantiate new sprites
391 SDL_Surface* load_pixmap(char* filename){
392 char path[1024] = "gfx/";
393 strmcat(path, filename, 1024);
395 reader* rd = loader_open(path);
396 if(!rd){
397 return NULL;
400 unsigned char header[18];
401 loader_read(rd, header, 18);
403 int w = header[12] + (header[13]<<8);
404 int h = header[14] + (header[15]<<8);
405 int bpp = header[16];
407 SDL_Surface* surf = SDL_CreateRGBSurface(0,w,h,bpp,
408 0x00ff0000,0x0000ff00,0x000000ff,0xff000000);
409 if(!surf){
410 out_of_memory("load_pixmap");
412 loader_read(rd, surf->pixels, w*(bpp/8)*h);
413 loader_close(rd);
415 return surf;
418 int load_gfx(char* filename){
419 if(gfx_count == MAX_GFX){
420 fatal_error("load_gfx: cannot load any more than %d graphics\n",MAX_GFX);
423 printf("loading %s\n",filename);
426 for(int i=0; i<gfx_count; i++){/*check for already loaded gfx*/
427 if(strcmp(gfx[i].filename, filename)==0){
428 return i;
432 SDL_Surface* src = load_pixmap(filename);
433 if(!src){
434 fatal_error("load_gfx: failed to load %s\n",filename);
437 if(!gl_flag){
438 SDL_Surface* surf = SDL_DisplayFormatAlpha(src);
439 SDL_SetAlpha(surf, 0, 0);
441 Uint32 key = SDL_MapRGB(surf->format, (COLOR_KEY&0xff0000)>>16,
442 (COLOR_KEY&0x00ff00)>>8,
443 (COLOR_KEY&0x0000ff)>>0);
444 SDL_SetColorKey(surf, SDL_SRCCOLORKEY, key);
446 SDL_FreeSurface(src);
449 gfx[gfx_count].filename = strxcpy(filename);
450 gfx[gfx_count].surf = surf;
451 gfx[gfx_count].w = surf->w;
452 gfx[gfx_count].h = surf->h;
454 else {
455 GLuint texture;
457 SDL_Surface* conv = SDL_CreateRGBSurface(0, src->w, src->h, 32,
458 0xff<<16,0xff<<8,0xff<<0,0);
460 SDL_BlitSurface(src, NULL, conv, NULL);
462 int N = 0;
463 int M = 3;
464 Uint8* conv_bytes = conv->pixels;
465 Uint32 key = SDL_MapRGB(src->format,(COLOR_KEY&0xff0000)>>16,
466 (COLOR_KEY&0x00ff00)>>8,
467 (COLOR_KEY&0x0000ff)>>0);
468 for(int i=0; i<src->w; i++){
469 for(int j=0; j<src->h; j++){
470 Uint32 pixel = *((Uint32*)(src->pixels+N));
471 conv_bytes[M] = pixel==key ? SDL_ALPHA_TRANSPARENT : SDL_ALPHA_OPAQUE;
472 N += src->format->BytesPerPixel;
473 M += 4;
477 glGenTextures( 1, &texture );
478 glBindTexture( GL_TEXTURE_2D, texture );
480 glTexParameteri( GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST );
481 glTexParameteri( GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST );
482 glTexParameteri( GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_REPEAT);
483 glTexParameteri( GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_REPEAT);
485 glTexImage2D( GL_TEXTURE_2D, 0, 4, conv->w, conv->h, 0,
486 GL_BGRA, GL_UNSIGNED_BYTE, conv->pixels );
488 gfx[gfx_count].filename = strxcpy(filename);
489 gfx[gfx_count].texture = texture;
490 gfx[gfx_count].w = src->w;
491 gfx[gfx_count].h = src->h;
493 SDL_FreeSurface(conv);
494 SDL_FreeSurface(src);
498 return gfx_count++;
501 int load_sprite(char* filename, int id){
502 printf("loading %s\n",filename);
504 char path[1024] = "sprites/";
505 strncat(path, filename, 1023 - strlen(filename));
507 reader* rd = loader_open(path);
508 if(!rd){
509 return -1;
512 animation* ani = xmalloc(sizeof(animation));
514 char str[1024];
515 int w, h;
516 int frame_count;
517 int loop_mode;
519 loader_scanline(rd,"%s",str);
520 loader_scanline(rd,"%d %d %d %d",&w,&h,&loop_mode,&frame_count);
522 ani->frame_lens = xmalloc(frame_count*sizeof(short));
523 ani->frames = xmalloc(frame_count*sizeof(struct frame));
524 ani->frame_count = frame_count;
526 int g = load_gfx(str);
527 if(g < 0)
528 return -1;
530 ani->gfxid = g;
532 int W = gfx[g].w;
533 int H = gfx[g].h;
534 ani->w = w;
535 ani->h = h;
537 for(int i=0; i < frame_count; i++){
538 int l, x, y;
539 loader_scanline(rd, "%d %d %d", &l, &x, &y);
540 ani->frame_lens[i] = l;
541 if(!gl_flag){
542 ani->frames[i].x = x;
543 ani->frames[i].y = y;
545 else{
546 ani->frames[i].x0 = ((double)x)/W;
547 ani->frames[i].y0 = ((double)y)/H;
548 ani->frames[i].x1 = ((double)(x+w))/W;
549 ani->frames[i].y1 = ((double)(y+h))/W;
553 loader_close(rd);
554 animations[id] = ani;
555 return 0;
559 /********************/
560 /* graphics control */
561 /********************/
563 sprite* enable_sprite(int sprnum){
564 if(!animations[sprnum]){
565 fatal_error("enable_sprite: you just tried to enable sprite with type %d, which does not exist\n",sprnum);
567 if(sprite_count == MAX_SPRITES){
568 /* need a priority based way to create important sprites if full */
569 return NULL;
571 sprite* spr = xmalloc(sizeof(sprite));
572 animation* ani = animations[sprnum];
574 spr->number = sprite_count;
575 spr->frame_counter = 0;
576 spr->current_frame = 0;
577 spr->frame = ani->frames[0];
578 spr->gfxid = ani->gfxid;
579 spr->anim = sprnum;
580 spr->x = 0;
581 spr->y = 0;
582 spr->w = ani->w;
583 spr->h = ani->h;
584 spr->vx = 0;
585 spr->vy = 0;
586 spr->update = NULL;
587 spr->userdata = NULL;
589 sprites[sprite_count++] = spr;
590 return spr;
593 void disable_sprite(sprite* spr){
594 sprite* tmp = sprites[spr->number];
595 sprites[spr->number] = sprites[sprite_count--];
596 free(tmp);
599 sprite* copy_sprite(sprite* spr){
600 if(sprite_count == MAX_SPRITES){
601 /* need way to make important sprites when full */
602 return NULL;
604 sprite* copy = xmalloc(sizeof(sprite));
605 *copy = *spr;
606 sprites[sprite_count++] = copy;
607 return copy;
612 Sint16* lout;
613 Sint16* rout;
614 int buffer_size;
616 extern void process_audio(short lout[], short rout[], int len);
618 void audio_callback(void *userdata, Uint8 *stream, int len){
619 Sint16* out = (Sint16*)stream;
621 process_audio(lout, rout, buffer_size);
623 for(int i=0; i<buffer_size; i++){
624 out[i*2 ] = lout[i];
625 out[i*2 + 1] = rout[i];
630 void backend_init(int argc, char* argv[]){
632 for(int i=0; i<MAX_ANIMATIONS; i++){
633 animations[i] = NULL;
636 if(SDL_Init(SDL_INIT_VIDEO | SDL_INIT_AUDIO | SDL_INIT_JOYSTICK)==-1){
637 report_error("sdl: %s\n",SDL_GetError());
638 exit(-1);
642 /* options */
643 int fullscreen = 0;
644 for(int i=0; i<argc; i++){
645 if(!strcmp(argv[i], "-gl")){
646 gl_flag = 1;
647 continue;
649 if(!strcmp(argv[i], "-f")){
650 fullscreen = 1;
652 if(!strcmp(argv[i], "-h") || !strcmp(argv[i], "--help")){
653 printf("options:\n");
654 printf(" -gl use opengl video mode\n");
655 printf(" -f use fullscreen video mode\n");
656 printf(" -h print this help\n");
657 printf(" -v print version info\n");
658 exit(0);
660 if(!strcmp(argv[i], "-v")){
661 printf("cantaveria (v%d.%d)\n",VERSION_MAJOR,VERSION_MINOR);
662 printf("Copyright 2009 Evan Rinehart\n\n");
664 printf("This program is distributed under the terms of the GNU General\n"
665 "Public License (v2) and comes with ABSOLUTELY NO WARRANTY.\n\n");
667 printf("Send questions, comments, and bugs to evanrinehart@gmail.com\n\n");
669 printf("Send money to:\n");
670 printf("1850 Claiborne St\n");
671 printf("Mandeville, LA 70448\n");
672 printf("United States of America\n\n");
674 printf("Thanks! :)\n");
675 exit(0);
680 /* keymap */
681 keymap[ESCAPE_KEY] = SDLK_ESCAPE;
682 keymap[PAUSE_KEY] = SDLK_PAUSE;
684 keymap[LEFT_KEY] = SDLK_LEFT;
685 keymap[RIGHT_KEY] = SDLK_RIGHT;
686 keymap[UP_KEY] = SDLK_UP;
687 keymap[DOWN_KEY] = SDLK_DOWN;
689 keymap[FIRE_KEY] = SDLK_z;
690 keymap[JUMP_KEY] = SDLK_x;
691 keymap[INVENTORY_KEY] = SDLK_a;
692 keymap[SPECIAL_KEY] = SDLK_s;
694 keymap[L_KEY] = SDLK_q;
695 keymap[R_KEY] = SDLK_w;
696 keymap[START_KEY] = SDLK_RETURN;
697 keymap[SELECT_KEY] = SDLK_TAB;
699 /* joysticks */
700 int N = SDL_NumJoysticks();
701 printf("sdl: detected %d joysticks\n",N);
702 for(int i=0; i<N; i++){
703 if(SDL_JoystickOpen(i)){
704 printf(" joy%d: %s\n",i,SDL_JoystickName(i));
706 else{
707 printf(" joy%d: %s (failed to open)\n",i,SDL_JoystickName(i));
712 /* video */
713 int flags = 0;
715 SDL_WM_SetCaption("cantaveria","cantaveria");
716 SDL_ShowCursor(SDL_DISABLE);
717 const SDL_VideoInfo* vinfo = SDL_GetVideoInfo();
720 if(fullscreen && gl_flag){
721 W = vinfo->current_w;
722 H = vinfo->current_h;
724 else if(gl_flag){
725 //W = 320;
726 //H = 240;
727 W = 640;
728 H = 480;
729 //W = 960;
730 //H = 720;
732 else if(fullscreen){
733 W = 320;
734 H = 240;
736 else{
737 W = 320;
738 H = 240;
741 //double aspect = vinfo->current_w*1.0 / vinfo->current_h;
742 double aspect = ((double)W) / H;
745 if(gl_flag){
746 SDL_GL_SetAttribute(SDL_GL_DOUBLEBUFFER, 1);
747 //SDL_GL_SetAttribute(SDL_GL_ACCELERATED_VISUAL, 1);
748 SDL_GL_SetAttribute(SDL_GL_SWAP_CONTROL, 1);
749 flags |= SDL_OPENGL;
752 if(fullscreen){
753 flags |= SDL_FULLSCREEN;
754 screen_offset = (aspect*SCREEN_H - SCREEN_W)/2;
757 printf("video:\n");
758 printf(" resolution: %d x %d %s\n",W,H,fullscreen?"fullscreen":"windowed");
759 printf(" aspect ratio: %g\n",((double)W)/H);
760 printf(" opengl: %s\n",gl_flag?"yes":"no");
761 printf(" x-offset: %d\n",screen_offset);
763 video = SDL_SetVideoMode(W,H,32,flags);
764 if(video == NULL){
765 report_error("sdl: %s\n",SDL_GetError());
766 exit(-1);
769 printf(" video on\n");
771 if(gl_flag){
772 glEnable(GL_BLEND);
773 glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
775 glEnable( GL_TEXTURE_2D );
776 glClearColor( 0.0f, 0.0f, 0.0f, 0.0f );
778 glViewport( 0, 0, W, H );
780 glClear( GL_COLOR_BUFFER_BIT );
782 glMatrixMode( GL_PROJECTION );
783 glLoadIdentity();
784 if(fullscreen){
785 glOrtho(0.0f, 240*aspect, 240, 0.0f, -1.0f, 1.0f);
787 else{
788 glOrtho(0.0f, 320, 240, 0.0f, -1.0f, 1.0f);
791 glMatrixMode( GL_MODELVIEW );
792 glLoadIdentity();
796 /* setup audio */
797 SDL_AudioSpec audio;
798 audio.freq = SAMPLE_RATE;
799 audio.format = AUDIO_S16;
800 audio.channels = 2;
801 audio.samples = BUFFER_SIZE;
802 audio.callback = audio_callback;
804 SDL_AudioSpec gotten;
806 if(SDL_OpenAudio(&audio, &gotten)<0){
807 report_error("sdl: cannot open audio (%s)\n", SDL_GetError());
808 exit(-1);
811 printf("audio:\n");
812 printf(" sample rate = %d\n",gotten.freq);
813 printf(" channels = %d\n",gotten.channels);
814 printf(" samples = %d\n",gotten.samples);
815 printf(" format = %d\n",gotten.format);
817 if(gotten.format != AUDIO_S16){
818 printf(" WARNING: audio format not AUDIO_S16 :(\n");
821 lout = xmalloc(gotten.samples*2);
822 rout = xmalloc(gotten.samples*2);
823 buffer_size = gotten.samples;
825 printf(" sound on\n");
826 SDL_PauseAudio(0);
829 /* were done here */
831 SDL_Rect** modes;
832 int i;
834 modes = SDL_ListModes(NULL, SDL_FULLSCREEN|SDL_HWSURFACE);
836 if (modes == (SDL_Rect**)0) {
837 printf("No modes available!\n");
838 exit(-1);
841 if (modes == (SDL_Rect**)-1) {
842 printf("All resolutions available.\n");
844 else{
845 printf("Available Modes\n");
846 for (i=0; modes[i]; ++i)
847 printf(" %d x %d\n", modes[i]->w, modes[i]->h);