Exemple #1
0
        public static bool SaveToXml(string filename, WadSounds sounds)
        {
            using (var fileStream = new FileStream(filename, FileMode.Create, FileAccess.Write, FileShare.None))
            {
                var serializer = new XmlSerializer(typeof(WadSounds));
                serializer.Serialize(fileStream, sounds);
            }

            return(true);
        }
Exemple #2
0
        private static WadSounds ReadFromXml(string filename)
        {
            var sounds = new WadSounds();

            using (var fileStream = new FileStream(filename, FileMode.Open, FileAccess.Read, FileShare.None))
            {
                var serializer = new XmlSerializer(typeof(WadSounds));
                sounds = (WadSounds)serializer.Deserialize(fileStream);
            }

            foreach (var soundInfo in sounds.SoundInfos)
            {
                soundInfo.SoundCatalog = filename;
            }

            return(sounds);
        }
Exemple #3
0
        public int SampleCount(LevelSettings settings)
        {
            if (settings.GameVersion.UsesMainSfx() || Samples == null || Samples.Count <= 0)
            {
                return(-1);
            }

            int result = 0;

            foreach (var sample in Samples)
            {
                var path = WadSounds.TryGetSamplePath(settings, sample.FileName);
                if (path != null)
                {
                    result++;
                }
            }
            return(result);
        }
Exemple #4
0
        public static Wad2 LoadFromFile(string fileName, bool withSounds)
        {
            Wad2 result;

            using (var fileStream = new FileStream(fileName, FileMode.Open, FileAccess.Read, FileShare.Read))
                result = LoadFromStream(fileStream);
            result.FileName = fileName;

            // Load additional XML file if it exists
            if (withSounds)
            {
                var xmlFile = Path.ChangeExtension(fileName, "xml");
                if (File.Exists(xmlFile))
                {
                    result.Sounds = WadSounds.ReadFromFile(xmlFile);
                }
            }

            return(result);
        }
Exemple #5
0
        public static void SaveToFile(Wad2 wad, string filename)
        {
            // We save first to a temporary memory stream
            using (var stream = new MemoryStream())
            {
                SaveToStream(wad, stream);

                // Save to temporary file as well, so original wad2 won't vanish in case of crash
                var tempName = filename + ".tmp";
                if (File.Exists(tempName))
                {
                    File.Delete(tempName);
                }

                stream.Seek(0, SeekOrigin.Begin);
                using (var writer = new BinaryWriter(new FileStream(tempName, FileMode.Create, FileAccess.Write, FileShare.None)))
                {
                    var buffer = stream.ToArray();
                    writer.Write(buffer, 0, buffer.Length);
                }

                // Save successful, write temp file over original (if exists)
                if (File.Exists(filename))
                {
                    File.Delete(filename);
                }
                File.Move(tempName, filename);
            }

            // Save sounds to XML file
            if (wad.Sounds.SoundInfos.Count > 0)
            {
                string xmlFilename = Path.Combine(Path.GetDirectoryName(filename), Path.GetFileNameWithoutExtension(filename) + ".xml");
                WadSounds.SaveToXml(xmlFilename, wad.Sounds);
            }
        }
Exemple #6
0
        public static SortedDictionary <int, WadSample> CompileSamples(List <WadSoundInfo> soundMap, LevelSettings settings, bool onlyIndexed, IProgressReporter reporter, out bool samplesMissing)
        {
            var samples = new List <WadSample>();

            foreach (var soundInfo in soundMap)
            {
                if (onlyIndexed && !soundInfo.Indexed)
                {
                    continue;
                }
                foreach (var sample in soundInfo.Samples)
                {
                    samples.Add(sample);
                }
            }

            var loadedSamples = new SortedDictionary <int, WadSample>();

            // Set up maximum buffer sizes and sample rate
            int  maxBufferLength     = 1024 * 256;
            int  warnBufferLength    = 1024 * 256;
            uint supportedSampleRate = 22050;
            uint supportedBitness    = 16;

            switch (settings.GameVersion)
            {
            case TRVersion.Game.TR5Main:
                maxBufferLength  = int.MaxValue;    // Unlimited
                warnBufferLength = int.MaxValue;
                break;

            case TRVersion.Game.TR4:
            case TRVersion.Game.TRNG:
                maxBufferLength = 1024 * 1024;     // Raised TREP limit
                break;

            case TRVersion.Game.TR1:
            case TRVersion.Game.TR2:
                supportedSampleRate = 11025;
                supportedBitness    = 8;
                break;
            }

            var missing = false;

            Parallel.For(0, samples.Count, i =>
            {
                WadSample currentSample = NullSample;
                try
                {
                    string samplePath = WadSounds.TryGetSamplePath(settings, samples[i].FileName);

                    // If sample was found, then load it...
                    if (!string.IsNullOrEmpty(samplePath))
                    {
                        using (var stream = new FileStream(samplePath, FileMode.Open, FileAccess.Read, FileShare.Read))
                        {
                            var buffer = new byte[stream.Length];
                            if (stream.Read(buffer, 0, buffer.Length) != buffer.Length)
                            {
                                throw new EndOfStreamException();
                            }

                            if (settings.GameVersion.UsesMainSfx())
                            {
                                var sampleRate = settings.GameVersion < TRVersion.Game.TR3 ? 11025 : 22050;
                                currentSample  = new WadSample(samplePath, ConvertSampleFormat(buffer, true, (uint)sampleRate));
                            }
                            else
                            {
                                currentSample = new WadSample(samplePath, ConvertSampleFormat(buffer, false));
                            }

                            if (currentSample.SampleRate != supportedSampleRate)
                            {
                                reporter?.ReportWarn("Sample " + samplePath + " has a sample rate of " + currentSample.SampleRate + " which is unsupported for this engine version.");
                            }

                            if (currentSample.ChannelCount > 1)
                            {
                                reporter?.ReportWarn("Sample " + samplePath + " isn't mono. Only mono samples are supported. Crashes may occur.");
                            }

                            if (currentSample.BitsPerSample != supportedBitness)
                            {
                                reporter?.ReportWarn("Sample " + samplePath + " is not " + supportedBitness + "-bit sample and is not supported in this game version. Crashes may occur.");
                            }

                            if (buffer.Length > maxBufferLength)
                            {
                                reporter?.ReportWarn("Sample " + samplePath + " is more than " + maxBufferLength / 1024 + " kbytes long. It is too big for this game version, crashes may occur.");
                            }
                            else if (buffer.Length > warnBufferLength)
                            {
                                reporter?.ReportWarn("Sample " + samplePath + " is more than " + warnBufferLength / 1024 + " kbytes long. It may cause problems without additional measures, such as patching.");
                            }
                        }
                    }
                    // ... otherwise output null sample
                    else
                    {
                        currentSample = WadSample.NullSample;
                        logger.Warn(new FileNotFoundException(), "Unable to find sample '" + samplePath + "'");
                        missing = true;
                    }
                }
                catch (Exception exc)
                {
                    logger.Warn(exc, "Unable to read file '" + samples[i].FileName + "' from provided location.");
                }

                lock (loadedSamples)
                    loadedSamples.Add(i, currentSample);
            });

            if (missing)
            {
                reporter?.ReportWarn("Some samples are missing. Make sure sample paths are specified correctly. Check level settings for details.");
            }

            samplesMissing = missing;
            return(loadedSamples);
        }
Exemple #7
0
 public Wad2()
 {
     Sounds = new WadSounds();
 }
Exemple #8
0
        private static WadSounds ReadFromTxt(string filename)
        {
            var sounds = new WadSounds();

            try
            {
                using (var fileStream = new FileStream(filename, FileMode.Open, FileAccess.Read, FileShare.None))
                {
                    using (var reader = new StreamReader(fileStream))
                    {
                        ushort soundId = 0;
                        while (!reader.EndOfStream)
                        {
                            var s     = reader.ReadLine().Trim();
                            var sound = new WadSoundInfo(soundId);

                            // Get the name (ends with :)
                            int endOfSoundName = s.IndexOf(':');
                            if (endOfSoundName == -1)
                            {
                                soundId++;
                                continue;
                            }

                            sound.Name         = s.Substring(0, endOfSoundName);
                            sound.SoundCatalog = filename;

                            // Get everything else and remove empty tokens
                            var tokens = s.Substring(endOfSoundName + 1).Split(' ', '\t').Where(token => !string.IsNullOrEmpty(token)).ToList();
                            if (tokens.Count <= 1)
                            {
                                soundId++;
                                continue;
                            }

                            // Trim comments
                            int commentTokenIndex = tokens.FindIndex(t => t.StartsWith(";"));
                            if (commentTokenIndex != -1)
                            {
                                tokens.RemoveRange(commentTokenIndex, tokens.Count - commentTokenIndex);
                            }

                            var anyTokenFound = false;
                            for (int i = 0; i < tokens.Count; i++)
                            {
                                var   token = tokens[i];
                                short temp  = 0;

                                if (token.StartsWith("PIT", StringComparison.InvariantCultureIgnoreCase) && short.TryParse(token.Substring(3), out temp))
                                {
                                    sound.PitchFactor = temp;
                                    anyTokenFound     = true;
                                }
                                else if (token.StartsWith("RAD", StringComparison.InvariantCultureIgnoreCase) && short.TryParse(token.Substring(3), out temp))
                                {
                                    sound.RangeInSectors = temp;
                                    anyTokenFound        = true;
                                }
                                else if (token.StartsWith("VOL", StringComparison.InvariantCultureIgnoreCase) && short.TryParse(token.Substring(3), out temp))
                                {
                                    sound.Volume  = temp;
                                    anyTokenFound = true;
                                }
                                else if (token.StartsWith("CH", StringComparison.InvariantCultureIgnoreCase) && short.TryParse(token.Substring(2), out temp))
                                {
                                    sound.Chance  = temp;
                                    anyTokenFound = true;
                                }
                                else if (token.Equals("P", StringComparison.InvariantCultureIgnoreCase))
                                {
                                    sound.RandomizePitch = true;
                                    anyTokenFound        = true;
                                }
                                else if (token.Equals("V", StringComparison.InvariantCultureIgnoreCase))
                                {
                                    sound.RandomizeVolume = true;
                                    anyTokenFound         = true;
                                }
                                else if (token.Equals("N", StringComparison.InvariantCultureIgnoreCase))
                                {
                                    sound.DisablePanning = true;
                                    anyTokenFound        = true;
                                }
                                else if (token.Equals("L", StringComparison.InvariantCultureIgnoreCase))
                                {
                                    sound.LoopBehaviour = WadSoundLoopBehaviour.Looped;
                                    anyTokenFound       = true;
                                }
                                else if (token.Equals("R", StringComparison.InvariantCultureIgnoreCase))
                                {
                                    sound.LoopBehaviour = WadSoundLoopBehaviour.OneShotRewound;
                                    anyTokenFound       = true;
                                }
                                else if (token.Equals("W", StringComparison.InvariantCultureIgnoreCase))
                                {
                                    sound.LoopBehaviour = WadSoundLoopBehaviour.OneShotWait;
                                    anyTokenFound       = true;
                                }
                                else if (token.StartsWith("#g"))
                                {
                                    sound.Global  = true;
                                    anyTokenFound = true;
                                }
                                else if (!token.StartsWith("#") && !anyTokenFound)
                                {
                                    sound.Samples.Add(new WadSample(token + ".wav"));
                                }

                                if (token.StartsWith("#"))
                                {
                                    sound.Indexed = true;
                                }
                            }

                            sounds.SoundInfos.Add(sound);
                            soundId++;
                        }
                    }
                }
            }
            catch (Exception ex)
            {
                return(null);
            }

            return(sounds);
        }
Exemple #9
0
        private static WadSounds ReadFromSfx(string filename)
        {
            var samples = new List <string>();

            // Read version
            int version = 129;

            using (var readerVersion = new BinaryReaderEx(new FileStream(filename, FileMode.Open, FileAccess.Read, FileShare.Read)))
                version = readerVersion.ReadInt32();

            // Read samples
            var samPath = Path.GetDirectoryName(filename) + "\\" + Path.GetFileNameWithoutExtension(filename) + ".sam";

            using (var readerSounds = new StreamReader(new FileStream(samPath, FileMode.Open, FileAccess.Read, FileShare.Read)))
                while (!readerSounds.EndOfStream)
                {
                    samples.Add(readerSounds.ReadLine());
                }

            // Read sounds
            int soundMapSize = 0;

            short[] soundMap;
            var     wadSoundInfos = new List <wad_sound_info>();
            var     soundInfos    = new List <WadSoundInfo>();

            var sfxPath = Path.GetDirectoryName(filename) + "\\" + Path.GetFileNameWithoutExtension(filename) + ".sfx";

            using (var readerSfx = new BinaryReaderEx(new FileStream(sfxPath, FileMode.Open, FileAccess.Read, FileShare.Read)))
            {
                // Try to guess the WAD version
                readerSfx.BaseStream.Seek(740, SeekOrigin.Begin);
                short first  = readerSfx.ReadInt16();
                short second = readerSfx.ReadInt16();
                version = ((first == -1 || second == (first + 1)) ? 130 : 129);
                readerSfx.BaseStream.Seek(0, SeekOrigin.Begin);

                soundMapSize = (version == 130 ? 2048 : 370);
                soundMap     = new short[soundMapSize];
                for (var i = 0; i < soundMapSize; i++)
                {
                    soundMap[i] = readerSfx.ReadInt16();
                }

                var numSounds = readerSfx.ReadUInt32();

                for (var i = 0; i < numSounds; i++)
                {
                    var info = new wad_sound_info();
                    info.Sample          = readerSfx.ReadUInt16();
                    info.Volume          = readerSfx.ReadByte();
                    info.Range           = readerSfx.ReadByte();
                    info.Chance          = readerSfx.ReadByte();
                    info.Pitch           = readerSfx.ReadByte();
                    info.Characteristics = readerSfx.ReadUInt16();
                    wadSoundInfos.Add(info);
                }
            }

            // Convert old data to new format
            for (int i = 0; i < soundMapSize; i++)
            {
                // Check if sound is defined at all
                if (soundMap[i] == -1 || soundMap[i] >= wadSoundInfos.Count)
                {
                    continue;
                }

                // Fill the new sound info
                var oldInfo = wadSoundInfos[soundMap[i]];
                var newInfo = new WadSoundInfo(i);
                newInfo.Name           = TrCatalog.GetOriginalSoundName(TRVersion.Game.TR4, (uint)i);
                newInfo.Volume         = (int)Math.Round(oldInfo.Volume * 100.0f / 255.0f);
                newInfo.RangeInSectors = oldInfo.Range;
                newInfo.Chance         = (int)Math.Round(oldInfo.Chance * 100.0f / 255.0f);
                if (newInfo.Chance == 0)
                {
                    newInfo.Chance = 100;                      // Convert legacy chance value
                }
                newInfo.PitchFactor     = (int)Math.Round((oldInfo.Pitch > 127 ? oldInfo.Pitch - 256 : oldInfo.Pitch) * 100.0f / 128.0f);
                newInfo.RandomizePitch  = ((oldInfo.Characteristics & 0x2000) != 0);
                newInfo.RandomizeVolume = ((oldInfo.Characteristics & 0x4000) != 0);
                newInfo.DisablePanning  = ((oldInfo.Characteristics & 0x1000) != 0);
                newInfo.LoopBehaviour   = (WadSoundLoopBehaviour)(oldInfo.Characteristics & 0x03);
                newInfo.SoundCatalog    = sfxPath;

                // Read all samples linked to this sound info (for example footstep has 4 samples)
                int numSamplesInGroup = (oldInfo.Characteristics & 0x00fc) >> 2;
                for (int j = oldInfo.Sample; j < oldInfo.Sample + numSamplesInGroup; j++)
                {
                    newInfo.Samples.Add(new WadSample(samples[j]));
                }
                soundInfos.Add(newInfo);
            }

            var sounds = new WadSounds(soundInfos);

            return(sounds);
        }
Exemple #10
0
        public static void PlaySample(Level level, WadSample sample, int channel, float volume = 1.0f, float pitch = 1.0f, float pan = 0.0f, int loopCount = 1)
        {
            if (volume <= 0.0f || loopCount <= 0)
            {
                return;
            }

            var disposables = new List <IDisposable>();

            try
            {
                // Load data.
                // If waveform data is loaded into memory, use it to play sound, otherwise find wav file on disk.

                WaveFileReader waveStream;
                MemoryStream   memoryStream;

                if (sample.IsLoaded)
                {
                    memoryStream = disposables.AddAndReturn(new MemoryStream(sample.Data, false));
                    waveStream   = disposables.AddAndReturn(new WaveFileReader(memoryStream));
                }
                else
                {
                    var path = WadSounds.TryGetSamplePath(level.Settings, sample.FileName);
                    if (path == null)
                    {
                        return;
                    }
                    else
                    {
                        waveStream = disposables.AddAndReturn(new WaveFileReader(path));
                    }
                }

                // Apply looping
                ISampleProvider sampleStream;
                if (loopCount <= 1)
                {
                    sampleStream = waveStream.ToSampleProvider();
                }
                else
                {
                    sampleStream = new RepeatedStream(waveStream)
                    {
                        LoopCount = loopCount
                    }
                };

                // Always play sample as 22 khz
                if (sampleStream.WaveFormat.SampleRate != 22050)
                {
                    sampleStream = new PitchedStream {
                        Source = sampleStream, Pitch = 22050.0f / sampleStream.WaveFormat.SampleRate
                    }
                }
                ;

                // Apply panning
                if (pan != 1.0f)
                {
                    sampleStream = new PanningSampleProvider(sampleStream)
                    {
                        Pan = pan
                    }
                }
                ;

                // Apply pitch
                if (pitch != 1.0f)
                {
                    sampleStream = new PitchedStream {
                        Source = sampleStream, Pitch = pitch
                    }
                }
                ;

                // Apply volume
                if (volume != 1.0f)
                {
                    sampleStream = new VolumeSampleProvider(sampleStream)
                    {
                        Volume = volume
                    }
                }
                ;

                // Add some silence to make sure the audio plays out.
                const int latencyInMilliseconds = 200;
                int       latencyInSamples      = (sampleStream.WaveFormat.SampleRate * latencyInMilliseconds * 2) / 1000;
                sampleStream = new OffsetSampleProvider(sampleStream)
                {
                    LeadOutSamples = sampleStream.WaveFormat.Channels * latencyInSamples
                };

                // Play
                _channels[channel] = disposables.AddAndReturn(new WaveOut());
                _channels[channel].Init(sampleStream);
                _channels[channel].PlaybackStopped += (s, e) =>
                {
                    foreach (IDisposable disposable in disposables)
                    {
                        disposable?.Dispose();
                    }
                    _channels[channel] = null;
                    _indices[channel]  = -1;
                };
                _channels[channel].Play();
            }
            catch (Exception ex)
            {
                // Clean up in case of a problem
                foreach (IDisposable disposable in disposables)
                {
                    disposable?.Dispose();
                }

                _logger.Error("Error while playing sample " + sample + ", exception: " + ex);
            }
        }