Crossfire Client, Trunk  R18925
/home/leaf/crossfire/client/trunk/sound-src/common.c
Go to the documentation of this file.
00001 const char * rcsid_sound_src_common_c =
00002     "$Id: common.c 18770 2013-07-14 23:41:43Z partmedia $";
00003 /*
00004     Crossfire client, a client program for the crossfire program.
00005 
00006     Copyright (C) 2001 Mark Wedel & Crossfire Development Team
00007 
00008     This program is free software; you can redistribute it and/or modify
00009     it under the terms of the GNU General Public License as published by
00010     the Free Software Foundation; either version 2 of the License, or
00011     (at your option) any later version.
00012 
00013     This program is distributed in the hope that it will be useful,
00014     but WITHOUT ANY WARRANTY; without even the implied warranty of
00015     MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
00016     GNU General Public License for more details.
00017 
00018     You should have received a copy of the GNU General Public License
00019     along with this program; if not, write to the Free Software
00020     Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
00021 
00022     The author can be reached via e-mail to crossfire-devel@real-time.com
00023 */
00024 
00029 #include "config.h"
00030 
00031 #include <stdio.h>
00032 #include <stdlib.h>
00033 
00034 #ifdef HAVE_STRING_H
00035 #include <string.h>
00036 #endif
00037 
00038 #include <ctype.h>
00039 
00040 #include "client-types.h"
00041 #include "newclient.h"
00042 #include "client.h"
00043 
00044 #include "def_sounds.h"
00045 #include "common.h"
00046 
00047 Sound_Info normal_sounds[MAX_SOUNDS];
00048 Sound_Info spell_sounds[MAX_SOUNDS];
00049 Sound_Info default_normal;
00050 Sound_Info default_spell;
00051 
00052 char *client_sounds_path = NULL;        /* Client sound file folder         */
00053 char *user_sounds_path   = NULL;        /* User sound file folder           */
00054 char *user_sounds_file   = NULL;        /* User sound mappings              */
00055 char *user_config_file   = NULL;        /* User sndconfig file.             */
00056 
00057 char *buffers = NULL;
00058 
00059 /*
00060  * Sound device parameters.  See also sound_settings.
00061  */
00062 int stereo = 0;
00063 int bit8 = 0;
00064 int sample_size = 0;
00065 int frequency = 0;
00066 int sign = 0;
00067 int zerolevel = 0;
00068 
00098 static void parse_sound_line(char *line, int lineno) {
00099     static int readtype=0;              
00104     static int lastnum=0;               
00107     int        newnum, len;
00108     char      *cp, *volume, *symbolic, *cp1, filename[512];
00109 
00110     if (line[0] == '#' || line[0] == '\n')
00111         return;
00112 
00113     if (!strcmp(line, "Standard Sounds:\n")) {
00114         lastnum = 0;
00115         readtype = 1;
00116         return;
00117     }
00118 
00119     if (!strcmp(line, "Spell Sounds:\n")) {
00120         lastnum = 0;
00121         readtype = 2;
00122         return;
00123     }
00124     if (!readtype) {
00125 #ifdef SOUND_DEBUG
00126         fprintf(stderr,
00127             "parse_sound_line: Ignored file header:\n%d:%s\n", lineno, line);
00128 #endif
00129         return;
00130     }
00131     /*
00132      * Change the LF delimiter at the end of the line to a null terminator.
00133      */
00134     if (line[strlen(line)-1] == '\n')
00135         line[strlen(line)-1] = '\0';
00136     /*
00137      * Convert the first whitespace found to a null terminator.
00138      */
00139     len = strcspn(line, " \t");
00140     line[len] = '\0';
00141     cp = line + len + 1;
00142     /*
00143      * Skip all the following whitespace to locate the next field, and save a
00144      * pointer to the volume data.
00145      */
00146     while (*cp != '\0' && (*cp == ' ' || *cp == '\t'))
00147         cp++;
00148     volume = cp;
00149     /*
00150      * There is no need to null terminate the volume since it is processed
00151      * with atoi.
00152      *
00153      * Next, check to see if the unprocessed portion of the line has any
00154      * whitespace following the default volume.
00155      */
00156     cp1 = cp;
00157     if (!(cp = strchr(cp1, ' ')) && !(cp = strchr(cp1, '\t'))) {
00158         /*
00159          * If not, there cannot be a sound number, and any data left is an
00160          * unused symbolic name, so the sound number is auto-assigned.
00161          */
00162         newnum = lastnum + 1;
00163         symbolic = NULL;
00164     } else {
00165         /*
00166          * Since there is more whitespace, there might be a symbolic name and
00167          * sound number.  Ignore any additional whitespace following the
00168          * volume, and treat the next character as the beginning of a symbolic
00169          * name.
00170          */
00171         while (*cp != '\0' && (*cp == ' ' || *cp == '\t'))
00172             cp++;
00173         symbolic = cp;
00174         /*
00175          * Some symbolic names are double-quoted to allow them to contain
00176          * whitespace.  If a quote starts the name, advance the name pointer
00177          * to effectively strip the quote, and convert the final quote to a
00178          * null terminator.
00179          */
00180         if (*symbolic == '"') {
00181             symbolic++;
00182             for (cp = symbolic; *cp != '\0' && *cp != '"'; cp++);
00183             *cp = '\0';
00184             cp++;
00185         }
00186         /*
00187          * cp is either the beginning of an unquoted symbolic name or is
00188          * pointing to the whitespace between a quoted symbolic name and the
00189          * sound number.
00190          */
00191         cp1 = cp;
00192         if (!(cp = strchr(cp1, ' ')) && !(cp = strchr(cp1, '\t'))) {
00193             /*
00194              * There is no more whitespace on the line.  If the name was
00195              * quoted, there should have been whitespace following that cp was
00196              * pointing to, so there cannot be a sound number present.  On the
00197              * other hand, if the name was not quoted, cp would point at the
00198              * symbolic name and whitespace should follow if a sound number is
00199              * present.  Either way, the sound number must be auto-assigned.
00200              */
00201             newnum = lastnum + 1;
00202         } else {
00203             /*
00204              * If there was whitespace left, cp points to it now, whether or
00205              * not the symbolic name was quoted.  A sound number should follow
00206              * the whitespace.  First, try to null terminate the prior data,
00207              * then skip all subsequent whitespace, and point at what should
00208              * be the sound number.  If numeric data is found, read the sound
00209              * number, otherwise auto-assign the sound number.  This is a bit
00210              * dodgy as invalid data is silently ignored.
00211              */
00212             *cp++ = '\0';
00213             while (*cp != '\0' && (*cp == ' ' || *cp == '\t'))
00214                 cp++;
00215             if (isdigit(*cp))
00216                 newnum = atoi(cp);
00217             else
00218                 newnum = lastnum + 1;
00219         }
00220     }
00221     if (newnum < 0 || newnum > MAX_SOUNDS) {
00222         fprintf(stderr,
00223             "Invalid sound number %d, line %d, buf %s\n",
00224                 newnum, lineno, line);
00225         return;
00226     }
00227     /*
00228      * Compatibility processing for older files and/or the SDL_mixer setup.
00229      * If the filename ends in .au, convert the ending to a more appropriate
00230      * one as .au files are not distributed by the project.
00231      *
00232      * Use .raw instead of .au for most sound setups, as this is what has been
00233      * supported by the clients for a long time.
00234      *
00235      * As SDL_mixer does not support .raw, change the extension to .ogg for
00236      * systems other than Windows, or .wav for Windows.  Technically, it would
00237      * be okay to use either .wav or .ogg whatever the platform, so it is a
00238      * FIXME in that it would probably be best for the file extension to be a
00239      * configurable option.
00240      *
00241      * Overriding the content of the sound file is a bit of a kludge, but
00242      * allows legacy .crossfire/sound files to work with the current client.
00243      * The dodgy part is that if someone looks in the file, it will not
00244      * necessarily indicate the actual file being played.
00245      */
00246     strcpy(filename, line);
00247     cp = filename + strlen(filename) - 3;
00248     if (!strcmp(cp, ".au"))
00249         strcpy(cp, ".raw");
00250 #ifdef SDL_SOUND
00251     cp = filename + strlen(filename) - 4;
00252     if (!strcmp(cp, ".raw"))
00253 #ifndef WIN32
00254         strcpy(cp, ".ogg");
00255 #else
00256         strcpy(cp, ".wav");
00257 #endif
00258 #endif
00259     /*
00260      * One symbolic name is used: DEFAULT.  If it is found, the sound file
00261      * becomes the default sound for any undefined sound number, so set the
00262      * appropriate default, and ignore any sound number that may follow.
00263      */
00264     if (symbolic && !strcmp(symbolic, "DEFAULT")) {
00265         if (readtype == 1) {
00266             /*
00267              * Standard Sounds
00268              */
00269             default_normal.filename = strdup_local(filename);
00270             default_normal.volume = atoi(volume);
00271         } else if (readtype == 2) {
00272             /*
00273              * Spell Sounds
00274              */
00275             default_spell.filename = strdup_local(filename);
00276             default_spell.volume = atoi(volume);
00277         }
00278         return;
00279     }
00280     /*
00281      * The only way for processing to reach this point is if valid sound data
00282      * was found in a section.  Process it according to the section it is in.
00283      */
00284     if (readtype == 1) {
00285         /*
00286          * Standard Sounds
00287          */
00288         normal_sounds[newnum].filename = strdup_local(filename);
00289         normal_sounds[newnum].volume = atoi(volume);
00290         if (symbolic)
00291             normal_sounds[newnum].symbolic = strdup_local(symbolic);
00292         else
00293             normal_sounds[newnum].symbolic = NULL;
00294     } else if (readtype == 2) {
00295         /*
00296          * Spell Sounds
00297          */
00298         spell_sounds[newnum].filename = strdup_local(filename);
00299         spell_sounds[newnum].volume = atoi(volume);
00300         if (symbolic)
00301             spell_sounds[newnum].symbolic = strdup_local(symbolic);
00302         else
00303             spell_sounds[newnum].symbolic = NULL;
00304     }
00305     /*
00306      * Retain the assigned sound number for possible use in subsquent data
00307      * lines.
00308      */
00309     lastnum = newnum;
00310 }
00311 
00329 int init_sounds(void) {
00330     FILE *fp;
00331     char  path[MAXSOCKBUF];
00332     char  buf[512];
00333     int   i;
00334 
00335 #ifdef SOUND_DEBUG
00336     fprintf( stderr, "Settings: bits: %i, ", settings.bit8 ? 8 : 16);
00337     fprintf( stderr, "%s, ",settings.sign ? "signed" : "unsigned");
00338     fprintf( stderr, "%s, ",settings.stereo ? "stereo" : "mono");
00339     fprintf( stderr, "frequency: %i, ", settings.frequency);
00340     fprintf( stderr, "device: %s\n", settings.audiodev);
00341 #endif
00342 
00343     /*
00344      * Force the last char of the buffer to null in case strn* cuts off the
00345      * terminating null while copying file information.
00346      */
00347     path[sizeof(path) - 1] = '\0';
00348     /*
00349      * Initialize paths to various sound system resources.  Bail if any of
00350      * the buffer allocations fail.
00351      */
00352     if (getenv("HOME") == NULL) {
00353         fprintf(stderr,
00354                 "error: couldn't read $HOME environmental variable\n"
00355                 "Please run again with $HOME set to something reasonable.\n");
00356         return -1;
00357     }
00358 
00359     strncpy(path, getenv("HOME"), sizeof(path) - 1);
00360     strncat(path, USER_CONFIG_FILE, sizeof(path) - 1);
00361     CONVERT_FILESPEC_TO_OS_FORMAT(path);
00362     user_config_file = (char *) malloc(strlen(path));
00363     if (user_config_file)
00364         strcpy(user_config_file, path);
00365     else
00366         return -1;
00367 
00368     strncpy(path, getenv("HOME"), sizeof(path) - 1);
00369     strncat(path, USER_SOUNDS_FILE, sizeof(path) - 1);
00370     CONVERT_FILESPEC_TO_OS_FORMAT(path);
00371     user_sounds_file = (char *) malloc(strlen(path));
00372     if (user_sounds_file)
00373         strcpy(user_sounds_file, path);
00374     else
00375         return -1;
00376 
00377     strncpy(path, getenv("HOME"), sizeof(path) - 1);
00378     strncat(path, USER_SOUNDS_PATH, sizeof(path) - 1);
00379     CONVERT_FILESPEC_TO_OS_FORMAT(path);
00380     user_sounds_path = (char *) malloc(strlen(path));
00381     if (user_sounds_path)
00382         strcpy(user_sounds_path, path);
00383     else
00384         return -1;
00385 
00386     strncpy(path, CLIENT_SOUNDS_PATH, sizeof(path) - 1);
00387     CONVERT_FILESPEC_TO_OS_FORMAT(path);
00388     client_sounds_path = (char *) malloc(strlen(path));
00389     if (client_sounds_path)
00390         strcpy(client_sounds_path, path);
00391     else
00392         return -1;
00393 
00394     buffers = (char *) malloc(settings.buffers * settings.buflen);
00395     if (!buffers)
00396         return -1;
00397 
00398     sounds_in_buffer = (int *) calloc(settings.buffers, sizeof(int));
00399     if (!sounds_in_buffer)
00400         return -1;
00401 
00402     if (init_audio())
00403         return -1;
00404 
00405     if (sign)
00406         zerolevel = 0;
00407     else
00408         zerolevel = bit8 ? 0x80 : 0x00;
00409 
00410     memset(buffers, zerolevel, settings.buflen * settings.buffers);
00411 
00412 #ifdef SOUND_DEBUG
00413     fprintf( stderr, "bits: %i, ", bit8 ? 8 : 16);
00414     fprintf( stderr, "%s, ", sign ? "signed" : "unsigned");
00415     fprintf( stderr, "%s, ", stereo ? "stereo" : "mono");
00416     fprintf( stderr, "freq: %i, ", frequency);
00417     fprintf( stderr, "smpl_size: %i, ", sample_size);
00418     fprintf( stderr, "0level: %i\n", zerolevel);
00419 #endif
00420 
00421     for (i = 0; i < MAX_SOUNDS; i++) {
00422         normal_sounds[i].filename = NULL;
00423         spell_sounds[i].filename = NULL;
00424         normal_sounds[i].size = -1;
00425         spell_sounds[i].size = -1;
00426     }
00427     default_normal.filename = NULL;
00428     default_spell.filename = NULL;
00429 
00430     i = 0;
00431     if (!(fp = fopen(user_sounds_file, "r"))) {
00432         fprintf(stderr,
00433             "Unable to open %s - using built-in defaults\n",
00434                 user_sounds_file);
00435         for (; i < sizeof(def_sounds) / sizeof(char*); i++) {
00436             strcpy(buf, def_sounds[i]);
00437             parse_sound_line(buf, i);
00438         }
00439     } else while (fgets(buf, 511, fp) != NULL) {
00440         buf[511] = '\0';
00441         parse_sound_line(buf, ++i);
00442     }
00443     /* Note in both cases below, we leave the symbolic name untouched. */
00444     for (i = 0; i < MAX_SOUNDS; i++) {
00445         if (!normal_sounds[i].filename) {
00446             normal_sounds[i].filename = default_normal.filename;
00447             normal_sounds[i].volume = default_normal.volume;
00448         }
00449         if (!spell_sounds[i].filename) {
00450             spell_sounds[i].filename = default_spell.filename;
00451             spell_sounds[i].volume = default_spell.volume;
00452         }
00453         normal_sounds[i].data = NULL;
00454         spell_sounds[i].data = NULL;
00455     }
00456     return 0;
00457 }
00458 
00468 int sound_to_soundnum(const char *name, uint8 type) {
00469 
00470     fprintf(stderr, "name=%s type=%d\n", name, type);
00475     return 1;
00476 }
00477 
00487 int type_to_soundtype(uint8 type) {
00488 
00489 #ifdef SOUND_DEBUG
00490     fprintf(stderr,
00491         "Converted type %d to legacy type %d.\n", type, (type == 2) ? 2 : 1);
00492 #endif
00493 
00496     return (type == 2) ? 2 : 1;
00497 }
00498 
00526 int StdinCmd(char *data, int len) {
00527     char* dptr;                         /* Pointer used when parsing data */
00528     char* fptr;
00529     char* sound = NULL;                 /* Points to a sound or music name */
00530     char* source = NULL;
00531     char  soundfile[MAXSOCKBUF];
00532     int   sourcelen;
00533     int   soundlen;
00534     int   spacelen;
00535     int   type = 0;
00536     int   dir = 0;
00537     int   vol = 0;
00538     int   x = 0;
00539     int   y = 0;
00540     int   i = 0;
00541 
00542     fptr = soundfile;
00543     dptr = strtok(data, "\"");
00544     /*
00545      * Is data a blank line (ending with LF) or is it a quoted, empty string?
00546      */
00547     if (dptr == NULL) {
00548         fprintf(stderr, "Sound/music command does not contain any data.\n");
00549         return -1;
00550     }
00551     /*
00552      * If the first character is not a quote character, a sound command is
00553      * expected.
00554      */
00555     if (data[0] != '\"') {
00556         /*
00557          * There are 5 numeric values expected and required.  Technically, if
00558          * cfsndserv was new, and the client old, 4 might be present, but the
00559          * player does not attempt to support old clients.
00560          */
00561         i = sscanf(dptr, "%d %d %d %d %d", &x, &y, &dir, &vol, &type);
00562 
00563         if ((i != 5)
00564         ||  (dir < 0)
00565         ||  (dir > 8)
00566         ||  (vol < 0)
00567         ||  (vol > 100)
00568         ||  (type < 1)) {
00569             /*
00570              * There is not much point in trying to work with data that does
00571              * not fit some basic rules known at the time of development.
00572              */
00573             fprintf(stderr, "Unrecognized sound command data format.\n");
00574 #ifdef SOUND_DEBUG
00575             fprintf(stderr,
00576                 "(%d valid items read) x=%d y=%d dir=%d vol=%d type=%d\n",
00577                     i, x, y, dir, vol, type);
00578 #endif
00579             return -1;
00580         }
00581     }
00582     /*
00583      * Below this point, when type == 0, a music command is expected, and when
00584      * type != 0, a sound command is required.
00585      */
00586     if (type) {
00587         /*
00588          * dptr points to the numerics already read, so advance to the string
00589          * following the first quote delimiter.  A sound source name is
00590          * expected.
00591          */
00592         dptr = strtok(NULL, "\"");
00593         if (dptr == NULL) {
00594             fprintf(stderr, "Sound command is missing sound/source names.\n");
00595             return -1;
00596         }
00597         source = dptr;
00598         sourcelen = strlen(dptr);
00599        /*
00600         * Verify there is whitespace between source and sound names.
00601         */
00602         dptr = strtok(NULL, "\"");
00603         if (dptr == NULL) {
00604              fprintf(stderr, "Sound command is missing the sound name.\n");
00605              return -1;
00606         }
00607         spacelen = strlen(dptr);
00608         for (i = 0; i < spacelen; i++) {
00609             if (dptr[i] != ' ' && dptr[i] != '\t') {
00610                 fprintf(stderr, "Invalid characters after source name.\n");
00611                 return -1;
00612             }
00613         }
00614         /*
00615          * Advance the data pointer to the following sound name.
00616          */
00617          dptr = strtok(NULL, "\"");
00618          if (dptr == NULL) {
00619              fprintf(stderr, "Sound command is missing the sound name.\n");
00620              return -1;
00621         }
00622     }
00623     /*
00624      * Record the sound or music name here (type determines which it is).
00625      */
00626     sound = dptr;
00627     soundlen = strlen(dptr);
00628     /*
00629      * If there was a trailing quote after the sound or music name, there will
00630      * be a null there now, and sound[soundlen] should point to the character
00631      * just before another null at data[len-1] (that terminates the command).
00632      */
00633     i = sound - data + soundlen + 1 + 1;
00634     if (i - 1 == len) {
00635         fprintf(stderr, "Sound or music name does not end with a quote.\n");
00636         return -1;
00637     }
00638     if (i > len) {
00639         fprintf(stderr,
00640             "Invalid data after sound/music name (a quoted string needed)\n");
00641         return -1;
00642     }
00643 
00644     if (type) {
00645         /*
00646          * Play sound effect here.
00647          */
00648 #ifdef SOUND_DEBUG
00649         fprintf(stderr, "Playing sound "
00650             "%d,%d dir=%d vol=%d type=%d source=\"%s\" sound=\"%s\"\n",
00651                 x, y, dir, vol, type, source, sound);
00652 #endif
00653         play_sound(sound_to_soundnum(sound, type),
00654                    type_to_soundtype(type), x, y);
00655         return 0;
00656     } else {
00657         /*
00658          * Play music here.
00659          */
00660 #ifdef SOUND_DEBUG
00661         fprintf(stderr,
00662             "Playing music \"%s\"\n", sound);
00663 #endif
00664         play_music(sound);
00665     }
00666 
00667     return 0;
00668 }
00669 
00675 int write_settings(void) {
00676     FILE *f;
00677 
00678     f = fopen(user_config_file, "w");
00679     if (!f)
00680         return -1;
00681 
00682     fprintf(f, "# Crossfire sound server settings\n");
00683     fprintf(f, "# Please note, that not everything will work\n\n");
00684     fprintf(f, "stereo: %i\n", settings.stereo);
00685     fprintf(f, "bits: %i\n", settings.bit8?8:16);
00686     fprintf(f, "signed: %i\n", settings.sign);
00687     fprintf(f, "frequency: %i\n", settings.frequency);
00688     fprintf(f, "buffers: %i\n", settings.buffers);
00689     fprintf(f, "buflen: %i\n", settings.buflen);
00690     fprintf(f, "simultaneously: %i\n", settings.simultaneously);
00691     /* fprintf(f,"device: %s\n",settings.audiodev); */
00692     fclose(f);
00693     return 0;
00694 }
00695 
00701 int read_settings(void) {
00702     char linebuf[1024];
00703     FILE *f;
00704 
00705     if (user_config_file == NULL)
00706         return 0;
00707 
00708     f = fopen(user_config_file, "r");
00709     if (!f)
00710         return -1;
00711 
00712     while(fgets(linebuf, 1023, f) != NULL) {
00713         linebuf[1023] = 0;
00714         /* Strip off the newline */
00715         linebuf[strlen(linebuf) - 1] = 0;
00716 
00717         if (strncmp(linebuf, "stereo:", strlen("stereo:")) == 0)
00718             settings.stereo = atoi(linebuf + strlen("stereo:")) ? 1 : 0;
00719         else if (strncmp(linebuf, "bits:", strlen("bits:")) == 0)
00720             settings.bit8 = (atoi(linebuf + strlen("bits:"))==8) ? 1 : 0;
00721         else if (strncmp(linebuf, "signed:", strlen("signed:")) == 0)
00722             settings.sign = atoi(linebuf + strlen("signed:")) ? 1 : 0;
00723         else if (strncmp(linebuf, "buffers:", strlen("buffers:")) == 0)
00724             settings.buffers = atoi(linebuf + strlen("buffers:"));
00725         else if (strncmp(linebuf, "buflen:", strlen("buflen:")) == 0)
00726             settings.buflen = atoi(linebuf + strlen("buflen:"));
00727         else if (strncmp(linebuf, "frequency:", strlen("frequency:")) == 0)
00728             settings.frequency = atoi(linebuf + strlen("frequency:"));
00729         else if (strncmp(linebuf, "simultaneously:", strlen("simultaneously:")) == 0)
00730             settings.simultaneously = atoi(linebuf + strlen("simultaneously:"));
00731 #if 0
00732         else if (strncmp(linebuf,"device: ",strlen("device: "))==0)
00733                 settings.audiodev=strdup_local(linebuf+strlen("device: "));
00734 #endif
00735     }
00736     fclose(f);
00737     return 0;
00738 }
00739