private void CompileMainSFX(TRVersion.Game version, bool onlyIndexed) { LevelSettings settings; var sounds = Sounds.SoundInfos; if (sounds.Count == 0) { popup.ShowError(soundInfoEditor, "No sounds present. Nothing to compile!"); return; } settings = new LevelSettings() { GameVersion = version }; settings.WadSoundPaths.Clear(); var samplePath = LevelFileDialog.BrowseFolder(this, null, _configuration.SoundTool_LastMainSFXSamplePath, "Choose a path where all samples are stored", null); if (string.IsNullOrEmpty(samplePath)) { return; // User cancelled path selection } settings.WadSoundPaths.Add(new WadSoundPath(samplePath)); _configuration.SoundTool_LastMainSFXSamplePath = samplePath; // Update settings var mainSFXPath = LevelFileDialog.BrowseFile(this, null, _configuration.SoundTool_LastMainSFXPath, "Choose a path to save MAIN.SFX", _fileFormatSfx, null, true); if (string.IsNullOrEmpty(mainSFXPath)) { return; // User cancelled saving } bool missing; var samples = WadSample.CompileSamples(sounds, settings, onlyIndexed, out missing); try { using (var writer = new BinaryWriterEx(new FileStream(mainSFXPath, FileMode.Create, FileAccess.Write, FileShare.None))) { foreach (var sample in samples.Values) { writer.Write(sample.Data, 0, sample.Data.Length); } } var message = "MAIN.SFX compiled successfully!"; if (missing) { message += "\n" + "Some samples weren't found and won't play in game."; } popup.ShowInfo(soundInfoEditor, message); _configuration.SoundTool_LastMainSFXPath = mainSFXPath; } catch (Exception ex) { popup.ShowError(soundInfoEditor, "There was a error writing MAIN.SFX. \nException: " + ex.Message); } }
private void PrepareSoundsData() { // Step 1: create the real list of sounds and indices to compile _finalSoundInfosList = new List <WadSoundInfo>(); if (_level.Settings.GameVersion.UsesMainSfx()) { _finalSelectedSoundsList = new List <int>(_level.Settings.SelectedSounds); } else { _finalSelectedSoundsList = new List <int>(_level.Settings.SelectedAndAvailableSounds); } foreach (var soundInfo in _level.Settings.GlobalSoundMap) { if (_finalSelectedSoundsList.Contains(soundInfo.Id)) { // Check if sound is marked as indexed. If not, exclude it from processing. if (_level.Settings.GameVersion.UsesMainSfx() && !soundInfo.Indexed) { _progressReporter.ReportWarn("Sound info #" + soundInfo.Id + " was selected but was not set as included for TR2-3 MAIN.SFX in SoundTool. It was removed."); _finalSelectedSoundsList.Remove(soundInfo.Id); } else { _finalSoundInfosList.Add(soundInfo); } } } // Step 2: Prepare indices list to be written (needed only for TR2-3). // For that, we iterate through ALL sound infos available and count how many samples // each of them have. Then we build a list of needed sound IDs with appropriate // sample count, which is used later to store indices. // Indices are not necessary for TR4-5, but are for TR2-3 cause main.sfx is used. _finalSoundIndicesList = new List <int>(); int currentIndex = 0; foreach (var sound in _level.Settings.GlobalSoundMap) { // Don't include indices for non-indexed sounds if (_level.Settings.GameVersion.UsesMainSfx() && !sound.Indexed) { continue; } if (_finalSoundInfosList.Contains(sound)) { foreach (var sample in sound.Samples) { _finalSoundIndicesList.Add(currentIndex); currentIndex++; } } else { currentIndex += sound.Samples.Count; } } // HACK: TRNG for some reason remaps certain legacy TR object sounds into extended soundmap array. // There is no other way of guessing it except looking if there is a specific object in any of wads. if (_level.Settings.GameVersion == TRVersion.Game.TRNG) { Action <int, int, int> AddRemappedNGSound = delegate(int moveableTypeToCheck, int originalId, int remappedId) { if (_level.Settings.Wads.Any(w => w.Wad.Moveables.Any(m => (m.Value.Id.TypeId == moveableTypeToCheck)))) { if (!_finalSelectedSoundsList.Contains(remappedId) && _finalSelectedSoundsList.Contains(originalId)) { _progressReporter.ReportWarn("TRNG object with ID " + moveableTypeToCheck + " was found which uses missing hardcoded sound ID " + remappedId + " in embedded soundmap. Trying to remap sound ID from legacy ID (" + originalId + ")."); _finalSelectedSoundsList.Add(remappedId); var oldSound = _finalSoundInfosList.FirstOrDefault(snd => snd.Id == originalId); if (oldSound != null) { var newSound = new WadSoundInfo(oldSound); newSound.Id = remappedId; _finalSoundInfosList.Add(newSound); } } } }; // Motorboat AddRemappedNGSound(465, 308, 1053); AddRemappedNGSound(465, 307, 1055); // Rubber boat AddRemappedNGSound(467, 308, 1423); AddRemappedNGSound(467, 307, 1425); } // Step 3: create the sound map switch (_level.Settings.GameVersion) { case TRVersion.Game.TRNG: _soundMapSize = 2048; break; case TRVersion.Game.TR2: case TRVersion.Game.TR3: case TRVersion.Game.TR4: _soundMapSize = 370; break; case TRVersion.Game.TR5: _soundMapSize = 450; break; case TRVersion.Game.TR5Main: _soundMapSize = 4096; break; default: throw new Exception("Unknown game version " + _level.Settings.GameVersion); } _finalSoundMap = Enumerable.Repeat((short)-1, _soundMapSize).ToArray <short>(); foreach (var sound in _finalSoundInfosList) { if (sound.Id >= _soundMapSize) { _progressReporter.ReportWarn("Sound info #" + sound.Id + " wasn't included because max. ID for this game version is " + _soundMapSize + "."); continue; } _finalSoundMap[sound.Id] = (short)_finalSoundInfosList.IndexOf(sound); } // Samples aren't needed for TR2-3, skip next step if (_level.Settings.GameVersion.UsesMainSfx()) { // Additionally warn user if he uses several sound catalogs which is incompatible with MAIN.SFX workflow. if (_level.Settings.SoundsCatalogs.Count > 1) { _progressReporter.ReportWarn("Multiple sound catalogs can't be used with TR2 and TR3. Results are unpredictable. Remove all sound catalogs but one."); } return; } // Step 4: load samples var loadedSamples = WadSample.CompileSamples(_finalSoundInfosList, _level.Settings, false, _progressReporter); _finalSamplesList = loadedSamples.Values.ToList(); }