// $alias aliasname soundname
        private bool ParseAlias()
        {
            // Read aliasname
            if (!SkipWhitespace(true))
            {
                return(false);
            }
            string aliasname = StripTokenQuotes(ReadToken());

            if (string.IsNullOrEmpty(aliasname))
            {
                return(false);
            }

            // Read soundname
            if (!SkipWhitespace(true))
            {
                return(false);
            }
            string soundname = StripTokenQuotes(ReadToken());

            if (string.IsNullOrEmpty(soundname))
            {
                return(false);
            }

            SoundInfo info = GetSoundInfo(soundname);

            // Check for duplicates
            if (sounds.ContainsKey(aliasname))
            {
                LogWarning("$alias name \"" + aliasname + "\" is double-defined");
            }

            // Add to collection
            sounds[aliasname] = info;

            return(true);
        }
        internal void FinishSetup()
        {
            // Check undefined sounds
            List <SoundInfo> toremove = new List <SoundInfo>();

            foreach (SoundInfo sound in sounds.Values)
            {
                if (!IsValid(sound))
                {
                    if (sound.Type == SoundInfo.SoundInfoType.SOUND)
                    {
                        General.ErrorLogger.Add(ErrorType.Warning, ScriptType + " warning: sound \"" + sound.Name + "\" is not defined.");
                    }

                    toremove.Add(sound);
                }
                else
                {
                    // Apply settings from the first child...
                    if (sound.Type == SoundInfo.SoundInfoType.GROUP_RANDOM)
                    {
                        SoundInfo src = sound;
                        do
                        {
                            src = src.Children[0];
                        }while(src.Type != SoundInfo.SoundInfoType.SOUND);

                        if (src.Type == SoundInfo.SoundInfoType.SOUND)
                        {
                            sound.Volume          = src.Volume;
                            sound.Attenuation     = src.Attenuation;
                            sound.MinimumDistance = src.MinimumDistance;
                            sound.MaximumDistance = src.MaximumDistance;
                            sound.Rolloff         = src.Rolloff;
                            sound.RolloffFactor   = src.RolloffFactor;
                        }
                    }

                    // Apply global settings...
                    SoundInfo defprops = new SoundInfo("#DEFAULT_PROPERTIES#");
                    if (sound.Volume == defprops.Volume)
                    {
                        sound.Volume = globalprops.Volume;
                    }
                    if (sound.Attenuation == defprops.Attenuation)
                    {
                        sound.Attenuation = globalprops.Attenuation;
                    }
                    if (sound.MinimumDistance == defprops.MinimumDistance)
                    {
                        sound.MinimumDistance = globalprops.MinimumDistance;
                    }
                    if (sound.MaximumDistance == defprops.MaximumDistance)
                    {
                        sound.MaximumDistance = globalprops.MaximumDistance;
                    }
                    if (sound.Rolloff == defprops.Rolloff)
                    {
                        sound.Rolloff = globalprops.Rolloff;
                    }
                    if (sound.RolloffFactor == defprops.RolloffFactor)
                    {
                        sound.RolloffFactor = globalprops.RolloffFactor;
                    }
                }
            }

            // Connect SoundInfos to AmbientSoundInfos...
            foreach (AmbientSoundInfo info in ambientsounds.Values)
            {
                if (!sounds.ContainsKey(info.SoundName))
                {
                    General.ErrorLogger.Add(ErrorType.Warning, ScriptType + " warning: $ambient sound " + info.Index + " has undefined sound \"" + info.SoundName + "\".");
                    continue;
                }

                info.SetupSound(sounds[info.SoundName]);
            }

            // Remove invalid sounds
            foreach (SoundInfo info in toremove)
            {
                sounds.Remove(info.Name);
            }
        }
        // $rolloff soundname <mindist> <maxdist>
        // $rolloff soundname <type>
        private bool ParseRolloff()
        {
            // Read soundname
            if (!SkipWhitespace(true))
            {
                return(false);
            }
            string    soundname = StripTokenQuotes(ReadToken());
            SoundInfo info      = GetSoundInfo(soundname);

            // Next token can be <type>...
            if (!SkipWhitespace(true))
            {
                return(false);
            }
            string token = ReadToken(false).ToLowerInvariant();

            if (token == "custom" || token == "linear" || token == "log")
            {
                if (token == "linear")
                {
                    // Must be <min distance> <max distance> pair
                    if (!SkipWhitespace(false))
                    {
                        return(false);
                    }
                    if (!ReadSignedInt(ref info.MinimumDistance) || info.MinimumDistance < 0)
                    {
                        ReportError("Expected $rolloff linear <mindist> value");
                        return(false);
                    }

                    if (!SkipWhitespace(false))
                    {
                        return(false);
                    }
                    if (!ReadSignedInt(ref info.MaximumDistance) || info.MaximumDistance < 0)
                    {
                        ReportError("Expected $rolloff linear <maxdist> value");
                        return(false);
                    }
                }
                else if (token == "log")
                {
                    // Must be <min distance> <rolloff factor> pair
                    if (!SkipWhitespace(false))
                    {
                        return(false);
                    }
                    if (!ReadSignedInt(ref info.MinimumDistance) || info.MinimumDistance < 0)
                    {
                        ReportError("Expected $rolloff log <mindist> value");
                        return(false);
                    }

                    if (!SkipWhitespace(false))
                    {
                        return(false);
                    }
                    if (!ReadSignedFloat(ref info.RolloffFactor) || info.RolloffFactor < 0f)
                    {
                        ReportError("Expected $rolloff log <rolloff factor> value");
                        return(false);
                    }
                }

                // Store type
                switch (token)
                {
                case "custom": info.Rolloff = SoundInfo.RolloffType.CUSTOM; break;

                case "linear": info.Rolloff = SoundInfo.RolloffType.LINEAR; break;

                case "log": info.Rolloff = SoundInfo.RolloffType.LOG; break;
                }
            }
            // Must be <mindist> <maxdist> pair
            else
            {
                if (!ReadSignedInt(token, ref info.MinimumDistance) || info.MinimumDistance < 0)
                {
                    ReportError("Expected $rolloff <mindist> value");
                    return(false);
                }

                if (!SkipWhitespace(false))
                {
                    return(false);
                }
                if (!ReadSignedInt(ref info.MaximumDistance) || info.MaximumDistance < 0)
                {
                    ReportError("Expected $rolloff <maxdist> value");
                    return(false);
                }
            }

            return(true);
        }