Crossfire Client, Trunk  R18666
/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 13995 2010-10-14 04:01:47Z kbulgrien $";
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     strncpy(path, getenv("HOME"), sizeof(path) - 1);
00353     strncat(path, USER_CONFIG_FILE, sizeof(path) - 1);
00354     CONVERT_FILESPEC_TO_OS_FORMAT(path);
00355     user_config_file = (char *) malloc(strlen(path));
00356     if (user_config_file)
00357         strcpy(user_config_file, path);
00358     else
00359         return -1;
00360 
00361     strncpy(path, getenv("HOME"), sizeof(path) - 1);
00362     strncat(path, USER_SOUNDS_FILE, sizeof(path) - 1);
00363     CONVERT_FILESPEC_TO_OS_FORMAT(path);
00364     user_sounds_file = (char *) malloc(strlen(path));
00365     if (user_sounds_file)
00366         strcpy(user_sounds_file, path);
00367     else
00368         return -1;
00369 
00370     strncpy(path, getenv("HOME"), sizeof(path) - 1);
00371     strncat(path, USER_SOUNDS_PATH, sizeof(path) - 1);
00372     CONVERT_FILESPEC_TO_OS_FORMAT(path);
00373     user_sounds_path = (char *) malloc(strlen(path));
00374     if (user_sounds_path)
00375         strcpy(user_sounds_path, path);
00376     else
00377         return -1;
00378 
00379     strncpy(path, CLIENT_SOUNDS_PATH, sizeof(path) - 1);
00380     CONVERT_FILESPEC_TO_OS_FORMAT(path);
00381     client_sounds_path = (char *) malloc(strlen(path));
00382     if (client_sounds_path)
00383         strcpy(client_sounds_path, path);
00384     else
00385         return -1;
00386 
00387     buffers = (char *) malloc(settings.buffers * settings.buflen);
00388     if (!buffers)
00389         return -1;
00390 
00391     sounds_in_buffer = (int *) calloc(settings.buffers, sizeof(int));
00392     if (!sounds_in_buffer)
00393         return -1;
00394 
00395     if (init_audio())
00396         return -1;
00397 
00398     if (sign)
00399         zerolevel = 0;
00400     else
00401         zerolevel = bit8 ? 0x80 : 0x00;
00402 
00403     memset(buffers, zerolevel, settings.buflen * settings.buffers);
00404 
00405 #ifdef SOUND_DEBUG
00406     fprintf( stderr, "bits: %i, ", bit8 ? 8 : 16);
00407     fprintf( stderr, "%s, ", sign ? "signed" : "unsigned");
00408     fprintf( stderr, "%s, ", stereo ? "stereo" : "mono");
00409     fprintf( stderr, "freq: %i, ", frequency);
00410     fprintf( stderr, "smpl_size: %i, ", sample_size);
00411     fprintf( stderr, "0level: %i\n", zerolevel);
00412 #endif
00413 
00414     for (i = 0; i < MAX_SOUNDS; i++) {
00415         normal_sounds[i].filename = NULL;
00416         spell_sounds[i].filename = NULL;
00417         normal_sounds[i].size = -1;
00418         spell_sounds[i].size = -1;
00419     }
00420     default_normal.filename = NULL;
00421     default_spell.filename = NULL;
00422 
00423     i = 0;
00424     if (!(fp = fopen(user_sounds_file, "r"))) {
00425         fprintf(stderr,
00426             "Unable to open %s - using built-in defaults\n",
00427                 user_sounds_file);
00428         for (; i < sizeof(def_sounds) / sizeof(char*); i++) {
00429             strcpy(buf, def_sounds[i]);
00430             parse_sound_line(buf, i);
00431         }
00432     } else while (fgets(buf, 511, fp) != NULL) {
00433         buf[511] = '\0';
00434         parse_sound_line(buf, ++i);
00435     }
00436     /* Note in both cases below, we leave the symbolic name untouched. */
00437     for (i = 0; i < MAX_SOUNDS; i++) {
00438         if (!normal_sounds[i].filename) {
00439             normal_sounds[i].filename = default_normal.filename;
00440             normal_sounds[i].volume = default_normal.volume;
00441         }
00442         if (!spell_sounds[i].filename) {
00443             spell_sounds[i].filename = default_spell.filename;
00444             spell_sounds[i].volume = default_spell.volume;
00445         }
00446         normal_sounds[i].data = NULL;
00447         spell_sounds[i].data = NULL;
00448     }
00449     return 0;
00450 }
00451 
00461 int sound_to_soundnum(const char *name, uint8 type) {
00462 
00463     fprintf(stderr, "name=%s type=%d\n", name, type);
00468     return 1;
00469 }
00470 
00480 int type_to_soundtype(uint8 type) {
00481 
00482 #ifdef SOUND_DEBUG
00483     fprintf(stderr,
00484         "Converted type %d to legacy type %d.\n", type, (type == 2) ? 2 : 1);
00485 #endif
00486 
00489     return (type == 2) ? 2 : 1;
00490 }
00491 
00519 int StdinCmd(char *data, int len) {
00520     char* dptr;                         /* Pointer used when parsing data */
00521     char* fptr;
00522     char* sound = NULL;                 /* Points to a sound or music name */
00523     char* source = NULL;
00524     char  soundfile[MAXSOCKBUF];
00525     int   sourcelen;
00526     int   soundlen;
00527     int   spacelen;
00528     int   type = 0;
00529     int   dir = 0;
00530     int   vol = 0;
00531     int   x = 0;
00532     int   y = 0;
00533     int   i = 0;
00534 
00535     fptr = soundfile;
00536     dptr = strtok(data, "\"");
00537     /*
00538      * Is data a blank line (ending with LF) or is it a quoted, empty string?
00539      */
00540     if (dptr == NULL) {
00541         fprintf(stderr, "Sound/music command does not contain any data.\n");
00542         return -1;
00543     }
00544     /*
00545      * If the first character is not a quote character, a sound command is
00546      * expected.
00547      */
00548     if (data[0] != '\"') {
00549         /*
00550          * There are 5 numeric values expected and required.  Technically, if
00551          * cfsndserv was new, and the client old, 4 might be present, but the
00552          * player does not attempt to support old clients.
00553          */
00554         i = sscanf(dptr, "%d %d %d %d %d", &x, &y, &dir, &vol, &type);
00555 
00556         if ((i != 5)
00557         ||  (dir < 0)
00558         ||  (dir > 8)
00559         ||  (vol < 0)
00560         ||  (vol > 100)
00561         ||  (type < 1)) {
00562             /*
00563              * There is not much point in trying to work with data that does
00564              * not fit some basic rules known at the time of development.
00565              */
00566             fprintf(stderr, "Unrecognized sound command data format.\n");
00567 #ifdef SOUND_DEBUG
00568             fprintf(stderr,
00569                 "(%d valid items read) x=%d y=%d dir=%d vol=%d type=%d\n",
00570                     i, x, y, dir, vol, type);
00571 #endif
00572             return -1;
00573         }
00574     }
00575     /*
00576      * Below this point, when type == 0, a music command is expected, and when
00577      * type != 0, a sound command is required.
00578      */
00579     if (type) {
00580         /*
00581          * dptr points to the numerics already read, so advance to the string
00582          * following the first quote delimiter.  A sound source name is
00583          * expected.
00584          */
00585         dptr = strtok(NULL, "\"");
00586         if (dptr == NULL) {
00587             fprintf(stderr, "Sound command is missing sound/source names.\n");
00588             return -1;
00589         }
00590         source = dptr;
00591         sourcelen = strlen(dptr);
00592        /*
00593         * Verify there is whitespace between source and sound names.
00594         */
00595         dptr = strtok(NULL, "\"");
00596         if (dptr == NULL) {
00597              fprintf(stderr, "Sound command is missing the sound name.\n");
00598              return -1;
00599         }
00600         spacelen = strlen(dptr);
00601         for (i = 0; i < spacelen; i++) {
00602             if (dptr[i] != ' ' && dptr[i] != '\t') {
00603                 fprintf(stderr, "Invalid characters after source name.\n");
00604                 return -1;
00605             }
00606         }
00607         /*
00608          * Advance the data pointer to the following sound name.
00609          */
00610          dptr = strtok(NULL, "\"");
00611          if (dptr == NULL) {
00612              fprintf(stderr, "Sound command is missing the sound name.\n");
00613              return -1;
00614         }
00615     }
00616     /*
00617      * Record the sound or music name here (type determines which it is).
00618      */
00619     sound = dptr;
00620     soundlen = strlen(dptr);
00621     /*
00622      * If there was a trailing quote after the sound or music name, there will
00623      * be a null there now, and sound[soundlen] should point to the character
00624      * just before another null at data[len-1] (that terminates the command).
00625      */
00626     i = sound - data + soundlen + 1 + 1;
00627     if (i - 1 == len) {
00628         fprintf(stderr, "Sound or music name does not end with a quote.\n");
00629         return -1;
00630     }
00631     if (i > len) {
00632         fprintf(stderr,
00633             "Invalid data after sound/music name (a quoted string needed)\n");
00634         return -1;
00635     }
00636 
00637     if (type) {
00638         /*
00639          * Play sound effect here.
00640          */
00641 #ifdef SOUND_DEBUG
00642         fprintf(stderr, "Playing sound "
00643             "%d,%d dir=%d vol=%d type=%d source=\"%s\" sound=\"%s\"\n",
00644                 x, y, dir, vol, type, source, sound);
00645 #endif
00646         play_sound(sound_to_soundnum(sound, type),
00647                    type_to_soundtype(type), x, y);
00648         return 0;
00649     } else {
00650         /*
00651          * Play music here.
00652          */
00653 #ifdef SOUND_DEBUG
00654         fprintf(stderr,
00655             "Playing music \"%s\"\n", sound);
00656 #endif
00657         play_music(sound);
00658     }
00659 
00660     return 0;
00661 }
00662 
00668 int write_settings(void) {
00669     FILE *f;
00670 
00671     f = fopen(user_config_file, "w");
00672     if (!f)
00673         return -1;
00674 
00675     fprintf(f, "# Crossfire sound server settings\n");
00676     fprintf(f, "# Please note, that not everything will work\n\n");
00677     fprintf(f, "stereo: %i\n", settings.stereo);
00678     fprintf(f, "bits: %i\n", settings.bit8?8:16);
00679     fprintf(f, "signed: %i\n", settings.sign);
00680     fprintf(f, "frequency: %i\n", settings.frequency);
00681     fprintf(f, "buffers: %i\n", settings.buffers);
00682     fprintf(f, "buflen: %i\n", settings.buflen);
00683     fprintf(f, "simultaneously: %i\n", settings.simultaneously);
00684     /* fprintf(f,"device: %s\n",settings.audiodev); */
00685     fclose(f);
00686     return 0;
00687 }
00688 
00694 int read_settings(void) {
00695     char linebuf[1024];
00696     FILE *f;
00697 
00698     if (user_config_file == NULL)
00699         return 0;
00700 
00701     f = fopen(user_config_file, "r");
00702     if (!f)
00703         return -1;
00704 
00705     while(fgets(linebuf, 1023, f) != NULL) {
00706         linebuf[1023] = 0;
00707         /* Strip off the newline */
00708         linebuf[strlen(linebuf) - 1] = 0;
00709 
00710         if (strncmp(linebuf, "stereo:", strlen("stereo:")) == 0)
00711             settings.stereo = atoi(linebuf + strlen("stereo:")) ? 1 : 0;
00712         else if (strncmp(linebuf, "bits:", strlen("bits:")) == 0)
00713             settings.bit8 = (atoi(linebuf + strlen("bits:"))==8) ? 1 : 0;
00714         else if (strncmp(linebuf, "signed:", strlen("signed:")) == 0)
00715             settings.sign = atoi(linebuf + strlen("signed:")) ? 1 : 0;
00716         else if (strncmp(linebuf, "buffers:", strlen("buffers:")) == 0)
00717             settings.buffers = atoi(linebuf + strlen("buffers:"));
00718         else if (strncmp(linebuf, "buflen:", strlen("buflen:")) == 0)
00719             settings.buflen = atoi(linebuf + strlen("buflen:"));
00720         else if (strncmp(linebuf, "frequency:", strlen("frequency:")) == 0)
00721             settings.frequency = atoi(linebuf + strlen("frequency:"));
00722         else if (strncmp(linebuf, "simultaneously:", strlen("simultaneously:")) == 0)
00723             settings.simultaneously = atoi(linebuf + strlen("simultaneously:"));
00724 #if 0
00725         else if (strncmp(linebuf,"device: ",strlen("device: "))==0)
00726                 settings.audiodev=strdup_local(linebuf+strlen("device: "));
00727 #endif
00728     }
00729     fclose(f);
00730     return 0;
00731 }
00732