Crossfire Client, Branch  R11627
porting.c
Go to the documentation of this file.
00001 /*
00002  * static char *rcsid_sdl_c =
00003  *   "$Id: porting.c 8077 2008-01-03 12:07:59Z ryo_saeba $";
00004  */
00005 
00006 /*
00007     CrossFire, A Multiplayer game for X-windows
00008 
00009     Copyright (C) 2001 Mark Wedel & Crossfire Development Team
00010     Copyright (C) 1992 Frank Tore Johansen
00011 
00012     This program is free software; you can redistribute it and/or modify
00013     it under the terms of the GNU General Public License as published by
00014     the Free Software Foundation; either version 2 of the License, or
00015     (at your option) any later version.
00016 
00017     This program is distributed in the hope that it will be useful,
00018     but WITHOUT ANY WARRANTY; without even the implied warranty of
00019     MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
00020     GNU General Public License for more details.
00021 
00022     You should have received a copy of the GNU General Public License
00023     along with this program; if not, write to the Free Software
00024     Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
00025 
00026     The author can be reached via e-mail to crossfire-devel@real-time.com
00027 */
00028 
00029 #include <config.h>
00030 #include <ctype.h>
00031 #include <time.h>
00032 
00033 #ifdef WIN32
00034 #include <sys/stat.h>
00035 #include <stdio.h>
00036 #include <mmsystem.h>
00037 #include "client.h"
00038 #include "soundsdef.h"
00039 
00040 #ifdef HAVE_SDL
00041 #include <math.h>
00042 #include "sdl_mixer.h"
00043 #pragma comment( lib, "sdl.lib" )
00044 #pragma comment( lib, "sdl_mixer.lib" )
00045 #endif /* HAVE_SDL */
00046 
00047 #define MAX_SOUNDS 1024
00048 #define SOUND_NORMAL 0
00049 #define SOUND_SPELL 1
00050 
00051 typedef struct Sound_Info {
00052         char *filename;
00053         char *symbolic;
00054         unsigned char volume;
00055         int size;
00056 #ifdef HAVE_SDL
00057         Mix_Chunk *data;
00058 #else
00059     char* data;
00060 #endif
00061 } Sound_Info;
00062 
00063 Sound_Info normal_sounds[MAX_SOUNDS], spell_sounds[MAX_SOUNDS],
00064         default_normal, default_spell;
00065 
00066 
00067 #define SOUND_DECREASE 0.1
00068 
00069 /* Parse a line from the sound config file.
00070  */
00071 static void parse_sound_line(char *line, int lineno)
00072 {
00073         static int readtype=0, lastnum=0;
00074         int newnum, len;
00075         char *cp,*volume,*symbolic,*cp1,filename[512];
00076 
00077         if (line[0]=='#' || line[0]=='\n') return;
00078 
00079         if ( !strcmp(line,"Standard Sounds:\n")) {
00080                 lastnum = 0;
00081                 readtype = 1;
00082                 return;
00083         }
00084         if ( !strcmp(line,"Spell Sounds:\n")) {
00085                 lastnum = 0;
00086                 readtype = 2;
00087                 return;
00088         }
00089         if ( !readtype ) {
00090                 fprintf(stderr,"Got input without finding section header yet:\n%d:%s\n",
00091                         lineno, line);
00092                 return;
00093         }
00094 
00095         if (line[strlen(line)-1]=='\n') {
00096                 line[strlen(line)-1]='\0';
00097         }
00098 
00099         len = strcspn(line," \t");
00100         line[len]='\0';
00101         cp = line+len+1;
00102 
00103         /* Skip all whitespace for the next field */
00104         while(*cp != '\0' && (*cp==' ' || *cp=='\t')) {
00105                 cp++;
00106         }
00107 
00108         volume=cp;
00109 
00110         /* No symbolic name or number - that is ok */
00111         cp1=cp;
00112         if (!(cp=strchr(cp1,' ')) && !(cp=strchr(cp1,'\t'))) {
00113                 newnum=lastnum+1;
00114                 symbolic=NULL;
00115         } else {
00116                 /* We think we have a symbolic name */
00117                 /* Don't need to nulterm the volume, since we atoi it anyways */
00118                 while(*cp != '\0' && (*cp==' ' || *cp=='\t')) {
00119                         cp++;
00120                 }
00121                 symbolic = cp;
00122                 /* Some symbolic names are double quote protected.  If, do some
00123                  * special processing.  We strip off the quotes.
00124                  */
00125                 if ( *symbolic=='"') {
00126                         symbolic++;
00127                         for (cp=symbolic; *cp != '\0' && *cp !='"'; cp++ );
00128                         *cp = '\0';
00129                         cp++;
00130                 }
00131                 /* Let's try to find the sound number now */
00132                 cp1 = cp;
00133                 if ( !(cp=strchr(cp1,' ')) && !(cp=strchr(cp1,'\t'))) {
00134                         newnum=lastnum+1;
00135                 } else {
00136                         *cp++='\0';
00137                         while(*cp!='\0' && (*cp==' ' || *cp=='\t')) {
00138                                 cp++;
00139                         }
00140                         if ( isdigit(*cp)) {
00141                                 newnum=atoi(cp);
00142                         } else {
00143                                 newnum=lastnum+1;
00144                         }
00145                 }
00146         }
00147         if ( newnum < 0 || newnum >MAX_SOUNDS ) {
00148                 fprintf(stderr,"Invalid sound number %d, line %d, buf %s\n",
00149                         newnum, lineno, line );
00150                 return;
00151         }
00152 
00153         strcpy(filename, line);
00154 
00155         if ( symbolic && !strcmp(symbolic,"DEFAULT")) {
00156                 if (readtype==1) {
00157                         default_normal.filename=strdup(filename);
00158                         default_normal.volume = atoi(volume);
00159                 } else if (readtype==2) {
00160                         default_spell.filename = strdup(filename);
00161                         default_spell.volume = atoi(volume);
00162                 }
00163                 return;
00164         } else {
00165                 if ( readtype==1 ) {
00166                         normal_sounds[newnum].filename = strdup(filename);
00167                         normal_sounds[newnum].volume = atoi(volume);
00168                         if ( symbolic ) {
00169                                 normal_sounds[newnum].symbolic = strdup(symbolic);
00170                         } else {
00171                                 normal_sounds[newnum].symbolic = NULL;
00172                         }
00173                 } else if (readtype==2) {
00174                         spell_sounds[newnum].filename = strdup(filename);
00175                         spell_sounds[newnum].volume = atoi(volume);
00176                         if ( symbolic ) {
00177                                 spell_sounds[newnum].symbolic = strdup(symbolic);
00178                         } else {
00179                                 spell_sounds[newnum].symbolic = NULL;
00180                         }
00181                 }
00182                 lastnum = newnum;
00183         }
00184 }
00185 
00186 void load_sounds_file( )
00187     {
00188     int i;
00189         FILE *fp;
00190         char path[256], buf[512];
00191 
00192     for ( i=0;i<MAX_SOUNDS; i++ ) {
00193                 normal_sounds[i].filename = NULL;
00194                 spell_sounds[i].filename = NULL;
00195                 normal_sounds[i].size = -1;
00196                 spell_sounds[i].size = -1;
00197         }
00198         default_normal.filename = NULL;
00199         default_spell.filename = NULL;
00200 
00201         sprintf(path,"%s/sounds", getenv("HOME"));
00202         i=0;
00203         if ( !(fp=fopen(path,"r"))) {
00204                 fprintf(stderr,"Unable to open %s - will use built in defaults\n", path);
00205                 for (; i<sizeof(def_sounds)/sizeof(char*); i++ ) {
00206                         strcpy(buf,def_sounds[i]);
00207                         parse_sound_line(buf,i);
00208                 }
00209         } else {
00210                 while(fgets(buf,511,fp)!=NULL) {
00211                         buf[511] = '\0';
00212                         parse_sound_line(buf,++i);
00213                 }
00214         fclose( fp );
00215         }
00216         /* Note in both cases below, we leave the symbolic name untouched */
00217         for ( i=0; i<MAX_SOUNDS; i++ ) {
00218                 if ( !normal_sounds[i].filename ) {
00219                         normal_sounds[i].filename = strdup( default_normal.filename );
00220                         normal_sounds[i].volume = default_normal.volume;
00221                 }
00222                 if ( !spell_sounds[i].filename ) {
00223                         spell_sounds[i].filename= strdup( default_spell.filename );
00224                         spell_sounds[i].volume = default_spell.volume;
00225                 }
00226                 normal_sounds[i].data = NULL;
00227                 spell_sounds[i].volume = 0;
00228         }
00229     }
00230 
00231 #ifdef HAVE_SDL  /* HAVE_SDL */
00232 
00233 #include <SDL.h>
00234 #include <SDL_Mixer.h>
00235 
00236 /* mixer variables */
00237 char *buffers = NULL;
00238 int *sounds_in_buffer=NULL;
00239 int current_buffer=0; /* Next buffer we will write out */
00240 int first_free_buffer=0; /* So we know when to stop playing sounds */
00241 
00242 /* sound device parameters */
00243 int stereo=0,bit8=0,sample_size=0,frequency=0,sign=0,zerolevel=0;
00244 
00245 struct sound_settings{
00246         int stereo,bit8,sign,frequency,buffers,buflen,simultaneously;
00247 } settings={0,1,0,11025,100,1024,4};
00248 
00249 
00250 /* Background music */
00251 Mix_Music *music = NULL;
00252 
00253 
00254 /* Initialize SDL sound.
00255  */
00256 int init_sounds()
00257 {
00258 #ifdef DEBUG
00259         fprintf( stderr,"Settings: bits: %i, ",settings.bit8?8:16);
00260         fprintf( stderr,"%s, ",settings.sign?"signed":"unsigned");
00261         fprintf( stderr,"%s, ",settings.stereo?"stereo":"mono");
00262         fprintf( stderr,"frequency: %i\n ",settings.frequency);
00263 #endif
00264 
00265         /* If SDL audio wasn't init'ed, init it. */
00266         if( SDL_WasInit(SDL_INIT_AUDIO) == 0) {
00267                 int audio_rate = 22050;
00268                 uint16 audio_format = AUDIO_S16;
00269                 int audio_channels = 2;
00270                 int audio_buffers = 4096;
00271 
00272                 if( SDL_InitSubSystem(SDL_INIT_AUDIO) < 0)
00273                 {
00274                         fprintf( stderr, "Could not initialize SDL audio: %s\n", SDL_GetError());
00275                         return -1;
00276                 }
00277 
00278                 /* init SDL_mixer */
00279                 if (Mix_OpenAudio(audio_rate, audio_format, audio_channels, audio_buffers)) {
00280                         fprintf(stderr,"SDL_MIXER: Unable to open audio: %s\n", SDL_GetError());
00281                         return -1;
00282                 } else {
00283                         int numtimesopened;
00284                         char format_str[8];
00285                         numtimesopened = Mix_QuerySpec(&audio_rate, &audio_format, &audio_channels);
00286 
00287                         sprintf(format_str,"unknown");
00288                         switch(audio_format) {
00289                         case AUDIO_U8: sprintf(format_str,"U8"); break;
00290                         case AUDIO_S8: sprintf(format_str,"S8"); break;
00291                         case AUDIO_U16LSB: sprintf(format_str,"U16LSB"); break;
00292                         case AUDIO_S16LSB: sprintf(format_str,"S16LSB"); break;
00293                         case AUDIO_U16MSB: sprintf(format_str,"U16MSB"); break;
00294                         case AUDIO_S16MSB: sprintf(format_str,"S16MSB"); break;
00295                         }
00296                         fprintf(stderr,"SDL_MIXER: Using SDL Mixer for audio [ %d kHz | %d channels | Audio Format %s ].\n",
00297                                 audio_rate, audio_channels, format_str );
00298                         /*
00299                          * start playing the background music
00300                          * Possibly have different background music for each map?
00301                          */
00302                         Mix_HaltChannel(-1);
00303                         Mix_HaltMusic();
00304 
00305                         if ( music == NULL ) {
00306                                 music = Mix_LoadMUS("sfx/backg1.wav");
00307                                 if ( music ) {
00308                                         Mix_PlayMusic(music,-1);
00309                                 }
00310                         } else {
00311                                 Mix_PlayMusic(music,-1);
00312                         }
00313                 }
00314         }
00315 
00316 
00317         buffers = (char *)malloc(settings.buffers * settings.buflen );
00318         if ( !buffers ) {
00319                 return -1;
00320         }
00321         sounds_in_buffer = (int*)calloc(settings.buffers,sizeof(int));
00322         if ( !sounds_in_buffer ) {
00323                 return -1;
00324         }
00325 
00326         if ( sign ) {
00327                 zerolevel = 0;
00328         } else {
00329                 zerolevel = bit8?0x80:0x8000;
00330         }
00331 
00332         memset(buffers,zerolevel,settings.buflen * settings.buffers);
00333 
00334 
00335 #ifdef DEBUG
00336         fprintf( stderr,"bits: %i, ",bit8?8:16);
00337         fprintf( stderr,"%s, ",sign?"signed":"unsigned");
00338         fprintf( stderr,"%s, ",stereo?"stereo":"mono");
00339         fprintf( stderr,"freq: %i, ",frequency);
00340         fprintf( stderr,"smpl_size: %i, ",sample_size);
00341         fprintf( stderr,"0level: %i\n",zerolevel);
00342 #endif
00343 
00344     load_sounds_file( );
00345         PlaySound(NULL,NULL,SND_ASYNC);
00346 
00347         return 0;
00348 }
00349 
00350 /* Plays sound 'soundnum', soundtype is 0 for normal sounds, 1 for
00351  * spell sounds.  This might get extended in the futur.  x,y are offset
00352  * (assumed from player) to play sound.  This information is used to
00353  * determine value and left vs right speaker balance.
00354  * This doesn't really play a sound, rather it just adds it to
00355  * the buffer to be played later on.
00356  */
00357 
00358 static void play_sound(int soundnum, int soundtype, sint8 x, sint8 y )
00359 {
00360         Sound_Info *si;
00361         double dist;
00362         int index;
00363 
00364         if (!use_config[CONFIG_SOUND]) return;
00365 
00366         if ( soundnum>=MAX_SOUNDS || soundnum<0 ) {
00367                 fprintf(stderr,"Invalid sound number: %d\n", soundnum );
00368                 return;
00369         }
00370         if (soundtype == SOUND_NORMAL ) {
00371                 si = &normal_sounds[soundnum];
00372         } else if ( soundtype == SOUND_SPELL ) {
00373                 si = &spell_sounds[soundnum];
00374         } else {
00375                 fprintf(stderr,"Unknown soundtype: %d\n", soundtype);
00376                 return;
00377         }
00378 
00379         if (!si->data) {
00380                 si->data = Mix_LoadWAV(si->filename);
00381                 if ( !si->data ) {
00382                         fprintf(stderr, "SDL_MIXER: Couldn't load %s: %s\n", si->filename, SDL_GetError());
00383                 }
00384         }
00385         if (si->data) {
00386                 int playchannel;
00387         int angle;
00388 
00389                 playchannel = 0;
00390                 si->data->volume = si->volume;
00391                 for ( index=0; index<MIX_CHANNELS; index++ ) {
00392                         if ( !Mix_Playing(index) ) {
00393                                 playchannel = index;
00394                                 break;
00395                         }
00396                 }
00397                 dist = sqrt(x*x+y*y);
00398         if ( x == 0 )
00399             {
00400             angle = ( y < 0 ) ? 0 : 180;
00401             }
00402         else
00403             {
00404             angle = ( asin( ( double )x / ( double )dist ) ) * 180. / 3.14159;
00405             if ( y < 0 )
00406                 angle = - angle;
00407             }
00408 
00409                 if ( Mix_Playing(playchannel) ) {
00410                         Mix_HaltChannel(playchannel);
00411                 }
00412 
00413                 /*Mix_SetDistance(playchannel,dist);*/
00414         Mix_SetPosition( playchannel, angle, dist );
00415 
00416                 Mix_PlayChannel(playchannel,si->data,0);
00417 
00418                 return;
00419         }
00420 
00421         /*
00422          * Load the sound if it is not loaded yet
00423          *
00424          */
00425 /*      if ( !si->data ) {
00426                 FILE *f;
00427                 struct stat sbuf;
00428                 fprintf(stderr,"Loading file: %s\n",si->filename );
00429                 if (stat(si->filename,&sbuf)) {
00430                         perror(si->filename);
00431                         return;
00432                 }
00433                 si->size=sbuf.st_size;
00434                 if ( si->size<=0) {
00435                         return;
00436                 }
00437                 if ( si->size * sample_size > settings.buflen*(settings.buffers-1)) {
00438                         fprintf(stderr,"Sound %s too long (%i > %i)\n",si->filename,si->size,
00439                                 settings.buflen*(settings.buffers-1)/sample_size);
00440                         return;
00441                 }
00442                 si->data=(unsigned char*)malloc(si->size);
00443                 f=fopen(si->filename,"r");
00444                 if ( !f ) {
00445                         perror(si->filename);
00446                         return;
00447                 }
00448                 fread(si->data,1,si->size,f);
00449                 fclose(f);
00450         }
00451 #ifdef DEBUG
00452         fprintf(stderr,"Playing sound %i (%s), volume %i, x,y=%d,%d\n",soundnum,si->symbolic,si->volume,x,y);
00453 #endif
00454 
00455         PlaySound(NULL,NULL,SND_ASYNC);
00456 
00457         PlaySound(si->data,NULL,SND_ASYNC | SND_MEMORY );
00458 
00459         return;
00460     */
00461 }
00462 
00463 void SoundCmd(unsigned char *data, int len)
00464 {
00465         int num, type;
00466     sint8 x, y;
00467 
00468     if (len!=5) {
00469         fprintf(stderr,"Got invalid length on sound command: %d\n", len);
00470         return;
00471     }
00472     x = data[0];
00473     y = data[1];
00474     num = GetShort_String(data+2);
00475     type = data[4];
00476 
00477 #ifdef DEBUG
00478     fprintf(stderr,"Playing sound %d (type %d), offset %d, %x\n",
00479             num, type, x ,y);
00480 #endif
00481 
00482     play_sound(num, type, x, y);
00483 }
00484 
00485 #else  /* HAVE_SDL */
00486 /* No SDL, let's use dumb PlaySound (better than nothing).
00487  */
00488 
00489 int init_sounds()
00490 {
00491     LOG(LOG_INFO,"init_sounds","using regular Windows PlaySound");
00492         PlaySound(NULL,NULL,SND_ASYNC);
00493     load_sounds_file( );
00494         return 0;
00495 }
00496 
00497 void SoundCmd(unsigned char *data, int len)
00498 {
00499         int num, type;
00500     Sound_Info* si;
00501 
00502     if (len!=5) {
00503         LOG(LOG_WARNING,"SoundCmd(win)","Got invalid length on sound command: %d\n", len);
00504         return;
00505     }
00506     num = GetShort_String(data+2);
00507     type = data[4];
00508 
00509         if (type == SOUND_NORMAL ) {
00510                 si = &normal_sounds[ num ];
00511         } else if ( type == SOUND_SPELL ) {
00512                 si = &spell_sounds[ num ];
00513     } else {
00514         LOG(LOG_WARNING,"SoundCmd(win)","invalid sound type %d",type);
00515         return;
00516     }
00517 
00518     if ( !si->filename )
00519         /* Already tried to load sound, failed, let's stop here. */
00520         return;
00521 
00522     if ( !si->data )
00523         {
00524         /* Let's try to load the sound */
00525         FILE* fsound;
00526         struct stat sbuf;
00527 
00528                 if ( ( stat( si->filename, &sbuf ) == -1 ) || ( ( fsound = fopen( si->filename, "rb" ) ) == NULL ) )
00529             {
00530             // Failed to load it, clear name & such so we don't try again.
00531             LOG( LOG_WARNING, "SoundCmd(win)", "Can't open sound %s", si->filename );
00532                         perror( si->filename );
00533             free( si->filename );
00534             si->filename = NULL;
00535                         return;
00536                     }
00537 
00538                 si->size=sbuf.st_size;
00539         si->data = malloc( si->size );
00540         fread( si->data, si->size, 1, fsound );
00541         fclose( fsound );
00542         }
00543 
00544     PlaySound( si->data, NULL, SND_ASYNC | SND_MEMORY | SND_NOWAIT);
00545 }
00546 
00547 #endif /* HAVE_SDL */
00548 
00549 
00550 /* The only gettimeofday calls appears to be ones added for purposes of
00551  * timing the map redraws, so for practical purposes, it should just
00552  * always return 0 with no real harm.
00553  */
00554 #include <winsock.h>
00555 void gettimeofday(struct timeval *tv, void* unused)
00556 {
00557         memset(tv, 0, sizeof(struct timeval));
00558 }
00559 
00560 
00561 /* This is functionally equivalent to Sleep(x) under win32.
00562 void Sleep(long SleepMilliseconds)
00563 {
00564     static struct timeval t;
00565     t.tv_sec = SleepMilliseconds/1000;
00566     t.tv_usec = (SleepMilliseconds %1000000) *1000;
00567 
00568     select(0, NULL, NULL, NULL, &t);
00569 }
00570 */
00571 
00572 int strncasecmp(const char *s1, const char *s2, int n)
00573 {
00574   register char c1, c2;
00575 
00576   while (*s1 && *s2 && n) {
00577     c1 = tolower(*s1);
00578     c2 = tolower(*s2);
00579     if (c1 != c2)
00580       return (c1 - c2);
00581     s1++;
00582     s2++;
00583     n--;
00584   }
00585   if (!n)
00586     return(0);
00587   return (int) (*s1 - *s2);
00588 }
00589 
00590 int strcasecmp(const char *s1, const char*s2)
00591 {
00592   register char c1, c2;
00593 
00594   while (*s1 && *s2) {
00595     c1 = tolower(*s1);
00596     c2 = tolower(*s2);
00597     if (c1 != c2)
00598       return (c1 - c2);
00599     s1++;
00600     s2++;
00601   }
00602   if (*s1=='\0' && *s2=='\0')
00603         return 0;
00604   return (int) (*s1 - *s2);
00605 }
00606 #endif /* WIN32 */