From df78f289ae21f768955593c6b4c7931dedfe64f7 Mon Sep 17 00:00:00 2001 From: Daniel Wilkins Date: Sun, 12 Feb 2023 22:47:28 -0800 Subject: [PATCH 01/17] Refactor audio processing --- LanternExtractor/EQ/Sound/ClientSounds.cs | 24 ++++ LanternExtractor/EQ/Sound/EffSndBnk.cs | 48 +++++-- LanternExtractor/EQ/Sound/EffSounds.cs | 150 ++++++++++++-------- LanternExtractor/EQ/Sound/EmissionType.cs | 8 +- LanternExtractor/EQ/Sound/SoundConstants.cs | 9 ++ LanternExtractor/EQ/Sound/SoundEntry.cs | 4 +- LanternExtractor/LanternExtractor.csproj | 2 + LanternExtractor/LanternStrings.cs | 2 +- 8 files changed, 164 insertions(+), 83 deletions(-) create mode 100644 LanternExtractor/EQ/Sound/ClientSounds.cs create mode 100644 LanternExtractor/EQ/Sound/SoundConstants.cs diff --git a/LanternExtractor/EQ/Sound/ClientSounds.cs b/LanternExtractor/EQ/Sound/ClientSounds.cs new file mode 100644 index 0000000..bb40ea9 --- /dev/null +++ b/LanternExtractor/EQ/Sound/ClientSounds.cs @@ -0,0 +1,24 @@ +using System.Collections.Generic; + +namespace LanternExtractor.EQ.Sound +{ + public class ClientSounds + { + // Hardcoded client sounds - verified that no other references exist in Trilogy client + private static Dictionary _clientSounds = new Dictionary + { + { 39, "death_me" }, + { 143, "thunder1" }, + { 144, "thunder2" }, + { 158, "wind_lp1" }, + { 159, "rainloop" }, + { 160, "torch_lp" }, + { 161, "watundlp" }, + }; + + public static string GetClientSound(int index) + { + return _clientSounds.TryGetValue(index, out var soundName) ? soundName : SoundConstants.Unknown; + } + } +} \ No newline at end of file diff --git a/LanternExtractor/EQ/Sound/EffSndBnk.cs b/LanternExtractor/EQ/Sound/EffSndBnk.cs index 2f758d3..9122f07 100644 --- a/LanternExtractor/EQ/Sound/EffSndBnk.cs +++ b/LanternExtractor/EQ/Sound/EffSndBnk.cs @@ -10,11 +10,9 @@ namespace LanternExtractor.EQ.Sound /// public class EffSndBnk { - public readonly List EmitSounds = new List(); - - public readonly List LoopSounds = new List(); - - private readonly string _soundFilePath; + private List _emitSounds = new List(); + private List _loopSounds = new List(); + private string _soundFilePath; public EffSndBnk(string soundFilePath) { @@ -23,15 +21,14 @@ public EffSndBnk(string soundFilePath) public void Initialize() { - List currentList = null; - if (!File.Exists(_soundFilePath)) { return; } + + List currentList = null; string fileText = File.ReadAllText(_soundFilePath); - List parsedLines = TextParser.ParseTextByNewline(fileText); if (parsedLines == null || parsedLines.Count == 0) @@ -41,15 +38,18 @@ public void Initialize() foreach (var line in parsedLines) { + if (string.IsNullOrEmpty(line)) + { + continue; + } + switch (line) { - case "": + case SoundConstants.Emit: + currentList = _emitSounds; continue; - case "EMIT": - currentList = EmitSounds; - continue; - case "LOOP": - currentList = LoopSounds; + case SoundConstants.Loop: + currentList = _loopSounds; continue; default: currentList?.Add(line); @@ -57,5 +57,25 @@ public void Initialize() } } } + + private string GetValueFromList(int index, ref List list) + { + if (index < 0 || index >= list.Count) + { + return SoundConstants.Unknown; + } + + return list[index]; + } + + public string GetEmitSound(int index) + { + return GetValueFromList(index, ref _emitSounds); + } + + public string GetLoopSound(int index) + { + return GetValueFromList(index, ref _loopSounds); + } } } \ No newline at end of file diff --git a/LanternExtractor/EQ/Sound/EffSounds.cs b/LanternExtractor/EQ/Sound/EffSounds.cs index 336b3a6..504e1d6 100644 --- a/LanternExtractor/EQ/Sound/EffSounds.cs +++ b/LanternExtractor/EQ/Sound/EffSounds.cs @@ -1,5 +1,4 @@ -using System; -using System.Collections.Generic; +using System.Collections.Generic; using System.IO; using System.Text; @@ -16,21 +15,21 @@ public class EffSounds /// The sound bank referenced for sound names /// private readonly EffSndBnk _soundBank; - + private readonly string _soundFilePath; - + private readonly List _soundEntries = new List(); - + private readonly List _musicTrackEntries = new List(); - + private const int EntryLengthInBytes = 84; - + public EffSounds(string soundFilePath, EffSndBnk soundBank) { _soundFilePath = soundFilePath; _soundBank = soundBank; } - + public void Initialize() { if (_soundBank == null || !File.Exists(_soundFilePath)) @@ -45,11 +44,11 @@ public void Initialize() LoadMusicTrackNames(zoneShortName); var reader = new BinaryReader(file); - - int fileLength = (int) reader.BaseStream.Length; + int fileLength = (int)reader.BaseStream.Length; if (fileLength % EntryLengthInBytes != 0) { + // File is an incorrect size return; } @@ -77,20 +76,20 @@ public void Initialize() int soundId2 = reader.ReadInt32(); byte soundType = reader.ReadByte(); - newSound.SoundType = (SoundType) soundType; + newSound.SoundType = (SoundType)soundType; if (soundType == 0 || soundType == 2 || soundType == 3) { // Find the sound names EmissionType newSoundEmissionType = newSound.EmissionType; - newSound.SoundIdDay = GetSoundString(soundId1, _soundBank, ref newSoundEmissionType); + newSound.SoundIdDay = soundId1; //GetSoundString(soundId1, _soundBank, ref newSoundEmissionType); EmissionType soundEmissionType = newSound.EmissionType; - newSound.SoundIdNight = GetSoundString(soundId2, _soundBank, ref soundEmissionType); + newSound.SoundIdNight = soundId2; //GetSoundString(soundId2, _soundBank, ref soundEmissionType); } else { - newSound.SoundIdDay = GetMusicTrackName(soundId1); - newSound.SoundIdNight = GetMusicTrackName(soundId1); + newSound.SoundIdDay = soundId1; //GetMusicTrackName(soundId1); + newSound.SoundIdNight = soundId2; //GetMusicTrackName(soundId1); } newSound.UnkPad57 = reader.ReadByte(); @@ -104,19 +103,19 @@ public void Initialize() newSound.FullVolRange = reader.ReadInt32(); newSound.UnkRange80 = reader.ReadInt32(); - if (newSound.SoundIdDay != "" || newSound.SoundIdNight != "") + //if (newSound.SoundIdDay != "" || newSound.SoundIdNight != "") { _soundEntries.Add(newSound); } } } - + private void LoadMusicTrackNames(string zoneShortName) { string[] trackLines = File.ReadAllLines("ClientData/musictracks.txt"); bool isTargetZone = false; - + foreach (string line in trackLines) { if (!isTargetZone) @@ -143,63 +142,82 @@ private void LoadMusicTrackNames(string zoneShortName) /// The client uses specific integer ranges to identify the type. /// There are also a handful of hardcoded sound ids. /// - /// The id index of the sound + /// /// The sound bank in which to look + /// /// The emission type + /// + /// /// The name of the sound - private string GetSoundString(int soundId, EffSndBnk soundBank, ref EmissionType soundType) + private bool TryGetSoundInfo(int soundIdDay, int soundIdNight, EffSndBnk soundBank, out string soundNameDay, + out string soundNameNight) { - if (soundId == 0) + var typeDay = GetEmissionType(soundIdDay); + var typeNight = GetEmissionType(soundIdNight); + soundNameDay = string.Empty; + soundNameNight = string.Empty; + + if (typeDay == EmissionType.None && typeNight == EmissionType.None) { - return string.Empty; + // No sound + return false; } - if (soundId >= 1 && soundId < 32 && soundId < soundBank.EmitSounds.Count) + if (typeDay != typeNight) { - soundType = EmissionType.Emit; - return soundBank.EmitSounds[soundId - 1]; + // Two separate emission types + return false; } - // Hardcoded client sounds - verified that no other references exist in Trilogy client - if (soundId >= 32 && soundId < 162) + if (typeDay != EmissionType.None) { - soundType = EmissionType.Internal; + soundNameDay = GetSoundName(soundIdDay, typeDay); + } - switch (soundId) - { - case 39: - return "death_me"; - case 143: - return "thunder1"; - case 144: - return "thunder2"; - case 158: - return "wind_lp1"; - case 159: - return "rainloop"; - case 160: - return "torch_lp"; - case 161: - return "watundlp"; - } + if (typeNight != EmissionType.None) + { + soundNameNight = GetSoundName(soundIdDay, typeDay); + } + + return true; + } + + private string GetSoundName(int soundId, EmissionType type) + { + switch (type) + { + case EmissionType.Emit: + return _soundBank.GetEmitSound(soundId - 1); + case EmissionType.Loop: + return _soundBank.GetLoopSound(soundId - 162); + case EmissionType.Internal: + return ClientSounds.GetClientSound(soundId); + default: + return SoundConstants.Unknown; + } + } + + private EmissionType GetEmissionType(int soundId) + { + if (soundId <= 0) + { + return EmissionType.None; } - if (soundId < 162 || soundId >= 162 + soundBank.LoopSounds.Count) + if (soundId < 32) { - return string.Empty; + return EmissionType.Emit; } - - soundType = EmissionType.Loop; - return soundBank.LoopSounds[soundId - 161 - 1]; + return soundId < 162 ? EmissionType.Internal : EmissionType.Loop; } - + public void ExportSoundData(string zoneName, string rootFolder) { var soundExport = new StringBuilder(); var musicExport = new StringBuilder(); - foreach (SoundEntry entry in _soundEntries) + foreach (var entry in _soundEntries) { if (entry.SoundType == SoundType.Music) { @@ -224,6 +242,12 @@ public void ExportSoundData(string zoneName, string rootFolder) } else { + if (!TryGetSoundInfo(entry.SoundIdDay, entry.SoundIdNight, _soundBank, out var soundNameDay, + out var soundNameNight, out var emissionType)) + { + continue; + } + soundExport.Append((int)entry.SoundType); soundExport.Append(","); soundExport.Append(entry.PosX); @@ -234,9 +258,9 @@ public void ExportSoundData(string zoneName, string rootFolder) soundExport.Append(","); soundExport.Append(entry.Radius); soundExport.Append(","); - soundExport.Append(entry.SoundIdDay); + soundExport.Append(soundNameDay); soundExport.Append(","); - soundExport.Append(entry.SoundIdNight); + soundExport.Append(soundNameNight); soundExport.Append(","); soundExport.Append(entry.CooldownDay); soundExport.Append(","); @@ -247,24 +271,24 @@ public void ExportSoundData(string zoneName, string rootFolder) } } - string exportPath = rootFolder + zoneName + "/Zone/"; + string exportPath = Path.Combine(rootFolder, zoneName, "Zone/"); - if (soundExport.Length != 0) + if (soundExport.Length > 0) { StringBuilder exportHeader = new StringBuilder(); exportHeader.AppendLine(LanternStrings.ExportHeaderTitle + "Sound Instances"); - exportHeader.AppendLine("# Format: SoundType, PosX, PosY, PosZ, Radius, SoundIdDay, SoundIdNight, CooldownDay, CooldownNight, RandomDelay"); - + exportHeader.AppendLine( + "# Format: SoundType, PosX, PosY, PosZ, Radius, SoundIdDay, SoundIdNight, CooldownDay, CooldownNight, RandomDelay"); Directory.CreateDirectory(exportPath); File.WriteAllText(exportPath + "sound_instances.txt", exportHeader.ToString() + soundExport); } - if (musicExport.Length != 0) + if (musicExport.Length > 0) { StringBuilder exportHeader = new StringBuilder(); exportHeader.AppendLine(LanternStrings.ExportHeaderTitle + "Music Instances"); - exportHeader.AppendLine("# Format: PosX, PosY, PosZ, Radius, SoundIdDay, SoundIdNight, DayLoopCount, NightLoopCount, FadeOutMs"); - + exportHeader.AppendLine( + "# Format: PosX, PosY, PosZ, Radius, MusicIndexDay, MusicIndexNight, DayLoopCount, NightLoopCount, FadeOutMs"); Directory.CreateDirectory(exportPath); File.WriteAllText(exportPath + "music_instances.txt", exportHeader.ToString() + musicExport); } @@ -282,8 +306,8 @@ private string GetMusicTrackName(int index) { return "Unknown"; } - + return _musicTrackEntries[index]; } } -} +} \ No newline at end of file diff --git a/LanternExtractor/EQ/Sound/EmissionType.cs b/LanternExtractor/EQ/Sound/EmissionType.cs index fc604b7..ec0543f 100644 --- a/LanternExtractor/EQ/Sound/EmissionType.cs +++ b/LanternExtractor/EQ/Sound/EmissionType.cs @@ -2,19 +2,21 @@ { public enum EmissionType { + None = 0, + /// /// Emitted sounds - things like bird noises /// - Emit = 0, + Emit = 1, /// /// Looped sounds - things like the ocean or a lake /// - Loop = 1, + Loop = 2, /// /// Sounds that are internal to the client /// - Internal = 2 + Internal = 3 } } \ No newline at end of file diff --git a/LanternExtractor/EQ/Sound/SoundConstants.cs b/LanternExtractor/EQ/Sound/SoundConstants.cs new file mode 100644 index 0000000..e4ff969 --- /dev/null +++ b/LanternExtractor/EQ/Sound/SoundConstants.cs @@ -0,0 +1,9 @@ +namespace LanternExtractor.EQ.Sound +{ + public class SoundConstants + { + public const string Emit = "EMIT"; + public const string Loop = "LOOP"; + public const string Unknown = "Unknown"; + } +} \ No newline at end of file diff --git a/LanternExtractor/EQ/Sound/SoundEntry.cs b/LanternExtractor/EQ/Sound/SoundEntry.cs index 1e13a52..d99c28a 100644 --- a/LanternExtractor/EQ/Sound/SoundEntry.cs +++ b/LanternExtractor/EQ/Sound/SoundEntry.cs @@ -20,8 +20,8 @@ public class SoundEntry public int CooldownNight { get; set; } public int RandomDelay { get; set; } public int Unk44 { get; set; } - public string SoundIdDay { get; set; } - public string SoundIdNight { get; set; } + public int SoundIdDay { get; set; } + public int SoundIdNight { get; set; } public SoundType SoundType { get; set; } public byte UnkPad57 { get; set; } public byte UnkPad58 { get; set; } diff --git a/LanternExtractor/LanternExtractor.csproj b/LanternExtractor/LanternExtractor.csproj index e3380e1..a186d0a 100644 --- a/LanternExtractor/LanternExtractor.csproj +++ b/LanternExtractor/LanternExtractor.csproj @@ -54,9 +54,11 @@ + + diff --git a/LanternExtractor/LanternStrings.cs b/LanternExtractor/LanternStrings.cs index b700550..c276483 100644 --- a/LanternExtractor/LanternStrings.cs +++ b/LanternExtractor/LanternStrings.cs @@ -5,7 +5,7 @@ namespace LanternExtractor /// public static class LanternStrings { - public const string ExportHeaderTitle = "# Lantern Extractor 0.2 - "; + public const string ExportHeaderTitle = "# Lantern Extractor 0.1.7 - "; public const string ExportHeaderFormat = "# Format: "; public const string ObjMaterialHeader = "mtllib "; From b161f91aad3d9a48c5eef0109539130c98e622ff Mon Sep 17 00:00:00 2001 From: Daniel Wilkins Date: Sun, 12 Feb 2023 23:06:22 -0800 Subject: [PATCH 02/17] Cleanup code and remove musictracks.txt --- LanternExtractor/ClientData/musictracks.txt | 595 -------------------- LanternExtractor/EQ/Sound/EffSounds.cs | 58 +- LanternExtractor/LanternExtractor.csproj | 3 - 3 files changed, 4 insertions(+), 652 deletions(-) delete mode 100644 LanternExtractor/ClientData/musictracks.txt diff --git a/LanternExtractor/ClientData/musictracks.txt b/LanternExtractor/ClientData/musictracks.txt deleted file mode 100644 index 196c1c3..0000000 --- a/LanternExtractor/ClientData/musictracks.txt +++ /dev/null @@ -1,595 +0,0 @@ -# Lantern Music Tracks - -#airplane -akanon-2 -airplane_0 -entrance_fanfare-1 -arpeggiated_runs -airplane_1 -heroism-1 - -#akanon -akanon -gfaydark-1 -gfaydark-2 -felwithe_2 -felwithe_0 -felwithe_1 -arena -gypsies - -#befallen -eerie_0 -eerie_1 -eerie_2 -eerie_3 -eerie_4 -eerie_5 -eerie_6 -eerie_7 -eerie_8 -eerie_9 -eerie_10 -eerie_11 - -#blackburrow -eerie_0 -eerie_1 -eerie_2 -eerie_3 -eerie_4 -eerie_5 -eerie_6 -eerie_7 -eerie_8 -eerie_9 -eerie_10 -eerie_11 - -#butcher -entrance_fanfare -entrance_fanfare_1 - -#cauldron -entrance_fanfare -entrance_fanfare_1 -entrance_fanfare_1-1 -entrance_fanfare-1 -sea - -#cobaltscar -cobaltscar -underwater - -#crushbone -gfaydark-4 -airplane_0 -entrance_fanfare-1 -eerie_3 -arpeggiated_runs -neriak_0-1 - -#crystal -erudsxing_0 -gfaydark-1 -cobaltscar -gfaydark - -#damage -damage_0 -damage_1 -damage_2 -damage_3 -damage_4 - -#damage1 -damage_0 -damage_1 -damage_2 -damage_3 -damage_4 - -#damage2 -damage_0 -damage_1 -damage_2 -damage_3 -damage_4 - -#eastkarana -cascade -eastkarana - -#eerie -eerie_0 -eerie_1 -eerie_2 -eerie_3 -eerie_4 -eerie_5 -eerie_6 -eerie_7 -eerie_8 -eerie_9 -eerie_10 -eerie_11 - -#erudnext -felwithe_0 -cascade -karana_river -entrance_fanfare_1-1 -bard -freportn -karana_river-1 -brass_harmonies - -#eudnint -cascade -karana_river -entrance_fanfare_1-1 -bard -freportn -karana_river-1 -brass_harmonies -felwithe_1 - -#erudsxing -erudsxing_0 -erudsxing_1 - -#fearplane -eerie_8 -eerie_9 -eerie_0 -eerie_1 -eerie_2 -eerie_3 -eerie_4 -eerie_5 -eerie_7 - -#felwithea -felwithe_0 -felwithe_1 -felwithe_2 - -#freporte -freeport -lionsmane -fishsale -cottage -gypsies -death_variant -eerie_4 -eerie_5 - -#freportn -harprun -gypsies-1 -gypsies -eqtheme -arena -brass_harmonies -akanon-1 -lionsmane -fishsale -bard -entrance_fanfare_1-1 -cottage -freportn -nightchords -string_fanfare -brass_fanfare - -#freportw -heroism -hogcaller -militia -arena -entrance_fanfare_1-1 -eastkarana -karana_river-1 - -#frozenshadow -eerie_7 -eerie_0 -lavastorm-1 -templeoflife -qeynos_0-1 -gfaydark - -#gfaydark -entrance_fanfare -entrance_fanfare_1 -entrance_fanfare_1-1 -entrance_fanfare-1 -gfaydark -eerie_9 - -#gl -attack_0 -attack_1 -attack_2 -death -airplane_0-1 -gl_5 -gl_6 -crouching -underwater -gl_9 -gl_10 -gl_11 -gl_12 -bard_intro -bard_main -gl_15 -gl_16 -gl_17 -gl_18 -gl_19 -sea -merchant -gfaydark-5 -guildmaster - -#grobb -eerie_0 -eerie_1 -eerie_2 -eerie_3 -eerie_4 -eerie_5 -eerie_6 -eerie_7 -eerie_8 -eerie_9 -eerie_10 -eerie_11 - -#guktop -eerie_0 -eerie_1 -eerie_5 -eerie_7 -arpeggiated_runs -neriak_0-1 -eerie_3 -maidensfancy -hogcaller -entrance_fanfare_1 - -#halas -felwithe_0 -felwithe_1 -felwithe_2 -arena -hogcaller -maidensfancy -lionsmane -fishsale -cottage -eerie_4 - -#hateplane -felwithe_0 -felwithe_2 -felwithe_1 -arpeggiated_runs -bard-2 -neriak_0-1 - -#innothule -entrance_fanfare -entrance_fanfare_1 -eerie_3 -eerie_2 - -#kael -kael_0 -kael_1 -attack_1 -arena - -#kaladima -entrance_fanfare_1 -arena -nightchords -cascade -hogcaller -lionsmane -freportn -heroism -brass_harmonies-1 - -#lavastorm -lavastorm -eerie_1 - -#lfaydark -cascade -gfaydark-2 -entrance_fanfare_1-1 -gypsies-1 -eerie_5 -militia - -#mistmoore -cascade -eastkarana -eerie_1 -entrance_fanfare -felwithe_1 -felwithe_2 -felwithe_0 -neriak_0-1 -karana_river -bard-2 -brass_harmonies -string_fanfare -freportn-1 -nightchords -gfaydark-3 -heroism-1 - -#najena -eerie_0 -eerie_1 -eerie_2 -eerie_3 -eerie_4 -eerie_5 -eerie_6 -eerie_7 -eerie_8 -eerie_9 -eerie_10 -eerie_11 - -#nektulos -lavastorm-1 - -#neriaka -eerie_0 -eerie_1 -eerie_2 -eerie_3 -eerie_4 -eerie_5 -eerie_6 -eerie_7 -eerie_8 -eerie_9 -eerie_10 -eerie_11 -neriak_0 -neriak_1 -neriak_2 -arpeggiated_runs -fishsale -maidensfancy - -#neriakb -eerie_0 -eerie_1 -eerie_2 -eerie_3 -eerie_4 -eerie_5 -eerie_6 -eerie_7 -eerie_8 -eerie_9 -eerie_10 -eerie_11 -neriak_0 -neriak_1 -neriak_2 -arpeggiated_runs -fishsale -maidensfancy - -#neriakc -eerie_0 -eerie_1 -eerie_2 -eerie_3 -eerie_4 -eerie_5 -eerie_6 -eerie_7 -eerie_8 -eerie_9 -eerie_10 -eerie_11 -neriak_0 -neriak_1 -neriak_2 -arpeggiated_runs -fishsale -maidensfancy - -#northkarana -cascade -eastkarana - -#nro -nro -entrance_fanfare_1 -freeport -gypsies -eerie_7 - -#opener -opener_0 - -#opener2 -eastkarana -character_select - -#opener3 -opener_3 -character_select - -#opener4 -eqtheme -character_select - -#paw -eerie_0 -eerie_1 -crouching - -#pickchar -character_select-1 - -#qcat -eerie_0 -eerie_1 -crouching - -#qey2hh1 -cottage -maidensfancy -karana_river - -#qeynos -arena -qeynos_gates -eerie_0 -eerie_1 -templeoflife -bard -eqtheme -gypsies -lionsmane -fishsale -cottage -freeport -qeynos_0 - -#qeynos2 -arena -qeynos_gates -eerie_0 -eerie_1 -templeoflife -bard-1 - -#qeytoqrg -arena -qeytoqrg -eerie_0 -eerie_1 -templeoflife -cottage -maidensfancy -entrance_fanfare - -#qrg -arena -qeytoqrg -templeoflife -qeynos_gates_brass - -#rathemtn -cascade -eastkarana -gypsies-2 -entrance_fanfare_1 -entrance_fanfare_1-1 -eerie_2 -eerie_3 - -#rivervale -rivervale - -#runnyeye -eerie_0 -eerie_1 -crouching - -#skyshrine -gl_12 -bard_intro -skyshrine -beethoven6 -nightchords -sea -gfaydark -gfaydark-2 - -#soldungb -lavastorm -eerie_10 -eerie_11 -eerie_0 -eerie_1 -eerie_2 -eerie_3 -eerie_6-1 - -#southkarana -cascade -eastkarana -eerie_2 -entrance_fanfare -maidensfancy -akanon-1 - -#steamfont -gfaydark-4 -entrance_fanfare -entrance_fanfare_1-1 -akanon-1 -lavastorm -eerie_3 -eerie_5 - -#templeveeshan -templeveeshan -gfaydark-5 -attack_1 -attack_0 - -#thurgadina -templeveeshan -attack_1 -attack_0 -thurgadina_0 -templeoflife -thurgadina_1 - -#thurgadinb -thurgadina_1 -thurgadinb - -#tox -entrance_fanfare_1 -karana_river -gypsies-1 -cascade -karana_river-1 - -#unrest -eastkarana -eerie_2 -eerie_1 -entrance_fanfare -felwithe_1 -felwithe_2 -neriak_0-1 -karana_river -brass_harmonies -string_fanfare -freportn-1 -gfaydark-3 -heroism-1 - -#velketor -velketor_0 -velketor_1 - -#wakening -wakening -gfaydark \ No newline at end of file diff --git a/LanternExtractor/EQ/Sound/EffSounds.cs b/LanternExtractor/EQ/Sound/EffSounds.cs index 504e1d6..011eb55 100644 --- a/LanternExtractor/EQ/Sound/EffSounds.cs +++ b/LanternExtractor/EQ/Sound/EffSounds.cs @@ -38,11 +38,6 @@ public void Initialize() } FileStream file = File.Open(_soundFilePath, FileMode.Open); - - string zoneShortName = Path.GetFileNameWithoutExtension(_soundFilePath).Split('_')[0]; - - LoadMusicTrackNames(zoneShortName); - var reader = new BinaryReader(file); int fileLength = (int)reader.BaseStream.Length; @@ -103,53 +98,24 @@ public void Initialize() newSound.FullVolRange = reader.ReadInt32(); newSound.UnkRange80 = reader.ReadInt32(); - //if (newSound.SoundIdDay != "" || newSound.SoundIdNight != "") + if (newSound.SoundIdDay != 0 || newSound.SoundIdNight != 0) { _soundEntries.Add(newSound); } } } - private void LoadMusicTrackNames(string zoneShortName) - { - string[] trackLines = File.ReadAllLines("ClientData/musictracks.txt"); - - bool isTargetZone = false; - - foreach (string line in trackLines) - { - if (!isTargetZone) - { - if (line == "#" + zoneShortName) - { - isTargetZone = true; - } - - continue; - } - - if (line == string.Empty) - { - break; - } - - _musicTrackEntries.Add(line); - } - } - /// /// Returns the name of the sound based on either the internal reference or the sound back definitions /// The client uses specific integer ranges to identify the type. /// There are also a handful of hardcoded sound ids. /// /// - /// The sound bank in which to look /// - /// The emission type /// /// /// The name of the sound - private bool TryGetSoundInfo(int soundIdDay, int soundIdNight, EffSndBnk soundBank, out string soundNameDay, + private bool TryGetSoundInfo(int soundIdDay, int soundIdNight, out string soundNameDay, out string soundNameNight) { var typeDay = GetEmissionType(soundIdDay); @@ -242,8 +208,8 @@ public void ExportSoundData(string zoneName, string rootFolder) } else { - if (!TryGetSoundInfo(entry.SoundIdDay, entry.SoundIdNight, _soundBank, out var soundNameDay, - out var soundNameNight, out var emissionType)) + if (!TryGetSoundInfo(entry.SoundIdDay, entry.SoundIdNight, out var soundNameDay, + out var soundNameNight)) { continue; } @@ -293,21 +259,5 @@ public void ExportSoundData(string zoneName, string rootFolder) File.WriteAllText(exportPath + "music_instances.txt", exportHeader.ToString() + musicExport); } } - - /// - /// Gets the name of the music track if it exists - /// - /// The zone shortname - /// The index of the track - /// The name of the track - private string GetMusicTrackName(int index) - { - if (index < 0 || index >= _musicTrackEntries.Count) - { - return "Unknown"; - } - - return _musicTrackEntries[index]; - } } } \ No newline at end of file diff --git a/LanternExtractor/LanternExtractor.csproj b/LanternExtractor/LanternExtractor.csproj index a186d0a..8255953 100644 --- a/LanternExtractor/LanternExtractor.csproj +++ b/LanternExtractor/LanternExtractor.csproj @@ -174,9 +174,6 @@ PreserveNewest - - PreserveNewest - PreserveNewest From 0870971586cf78188100d6e7d9df44f8e6247648 Mon Sep 17 00:00:00 2001 From: Daniel Wilkins Date: Wed, 15 Feb 2023 00:31:47 -0800 Subject: [PATCH 03/17] Add music copying --- LanternExtractor/EQ/ArchiveExtractor.cs | 3 +- LanternExtractor/EQ/ClientDataCopier.cs | 2 +- LanternExtractor/EQ/MusicCopier.cs | 45 ++++++++++++++++++++++++ LanternExtractor/EqFileHelper.cs | 10 ++++++ LanternExtractor/LanternExtractor.cs | 4 ++- LanternExtractor/LanternExtractor.csproj | 2 ++ LanternExtractor/ModelExportFormat.cs | 9 +++++ LanternExtractor/Settings.cs | 25 +++++++------ LanternExtractor/settings.txt | 4 +++ 9 files changed, 90 insertions(+), 14 deletions(-) create mode 100644 LanternExtractor/EQ/MusicCopier.cs create mode 100644 LanternExtractor/ModelExportFormat.cs diff --git a/LanternExtractor/EQ/ArchiveExtractor.cs b/LanternExtractor/EQ/ArchiveExtractor.cs index e9b267a..d63bff4 100644 --- a/LanternExtractor/EQ/ArchiveExtractor.cs +++ b/LanternExtractor/EQ/ArchiveExtractor.cs @@ -18,8 +18,7 @@ public static void Extract(string path, string rootFolder, ILogger logger, Setti { return; } - - + string shortName = archiveName.Split('_')[0]; var s3dArchive = new PfsArchive(path, logger); diff --git a/LanternExtractor/EQ/ClientDataCopier.cs b/LanternExtractor/EQ/ClientDataCopier.cs index dbdb957..39c4488 100644 --- a/LanternExtractor/EQ/ClientDataCopier.cs +++ b/LanternExtractor/EQ/ClientDataCopier.cs @@ -6,7 +6,7 @@ namespace LanternExtractor.EQ { public static class ClientDataCopier { - public const string ClientDataDirectory = "clientdata"; + private const string ClientDataDirectory = "clientdata"; public static void Copy(string fileName, string rootFolder, ILogger logger, Settings settings) { diff --git a/LanternExtractor/EQ/MusicCopier.cs b/LanternExtractor/EQ/MusicCopier.cs new file mode 100644 index 0000000..013e24c --- /dev/null +++ b/LanternExtractor/EQ/MusicCopier.cs @@ -0,0 +1,45 @@ +using System.IO; +using System.Linq; +using LanternExtractor.Infrastructure.Logger; + +namespace LanternExtractor.EQ +{ + public static class MusicCopier + { + private const string MusicDirectory = "music"; + + public static void Copy(string shortname, ILogger logger, Settings settings) + { + if (shortname != "music" && shortname != "all") + { + return; + } + + if (!settings.CopyMusic) + { + return; + } + + var xmiFiles = Directory.GetFiles(settings.EverQuestDirectory, "*.*", SearchOption.AllDirectories) + .Where(EqFileHelper.IsMusicFile).ToList(); + var destinationFolder = "Exports/" + MusicDirectory; + + if (!Directory.Exists(destinationFolder)) + { + Directory.CreateDirectory(destinationFolder); + } + + foreach (var xmi in xmiFiles) + { + var fileName = Path.GetFileName(xmi); + var destination = Path.Combine(destinationFolder, fileName); + if (File.Exists(destination)) + { + continue; + } + + File.Copy(xmi, destination); + } + } + } +} \ No newline at end of file diff --git a/LanternExtractor/EqFileHelper.cs b/LanternExtractor/EqFileHelper.cs index f802037..909a8c1 100644 --- a/LanternExtractor/EqFileHelper.cs +++ b/LanternExtractor/EqFileHelper.cs @@ -35,6 +35,16 @@ public static bool IsClientDataFile(string archiveName) { return archiveName == "clientdata"; } + + public static bool IsMusicFile(string filename) + { + return filename.EndsWith(".xmi"); + } + + public static bool IsSpecialCaseExtraction(string archiveName) + { + return archiveName == "clientdata" || archiveName == "music"; + } public static List GetValidEqFilePaths(string directory, string archiveName) { diff --git a/LanternExtractor/LanternExtractor.cs b/LanternExtractor/LanternExtractor.cs index 8afceb7..6258ecc 100644 --- a/LanternExtractor/LanternExtractor.cs +++ b/LanternExtractor/LanternExtractor.cs @@ -57,7 +57,7 @@ private static void Main(string[] args) List eqFiles = EqFileHelper.GetValidEqFilePaths(_settings.EverQuestDirectory, archiveName); eqFiles.Sort(); - if (eqFiles.Count == 0 && !EqFileHelper.IsClientDataFile(archiveName)) + if (eqFiles.Count == 0 && !EqFileHelper.IsSpecialCaseExtraction(archiveName)) { Console.WriteLine("No valid EQ files found for: '" + archiveName + "' at path: " + _settings.EverQuestDirectory); @@ -89,7 +89,9 @@ private static void Main(string[] args) ArchiveExtractor.Extract(file, "Exports/", _logger, _settings); } } + ClientDataCopier.Copy(archiveName, "Exports/", _logger, _settings); + MusicCopier.Copy(archiveName, _logger, _settings); Console.WriteLine($"Extraction complete ({(DateTime.Now - start).TotalSeconds:.00}s)"); } diff --git a/LanternExtractor/LanternExtractor.csproj b/LanternExtractor/LanternExtractor.csproj index 8255953..e3a4c61 100644 --- a/LanternExtractor/LanternExtractor.csproj +++ b/LanternExtractor/LanternExtractor.csproj @@ -51,6 +51,7 @@ + @@ -164,6 +165,7 @@ + diff --git a/LanternExtractor/ModelExportFormat.cs b/LanternExtractor/ModelExportFormat.cs new file mode 100644 index 0000000..c13527c --- /dev/null +++ b/LanternExtractor/ModelExportFormat.cs @@ -0,0 +1,9 @@ +namespace LanternExtractor +{ + public enum ModelExportFormat + { + Intermediate = 0, + Obj = 1, + GlTF = 2 + } +} \ No newline at end of file diff --git a/LanternExtractor/Settings.cs b/LanternExtractor/Settings.cs index 7c94159..e741c36 100644 --- a/LanternExtractor/Settings.cs +++ b/LanternExtractor/Settings.cs @@ -1,25 +1,15 @@ using System; -using System.Collections.Generic; using System.IO; using LanternExtractor.Infrastructure; using LanternExtractor.Infrastructure.Logger; - namespace LanternExtractor { - public enum ModelExportFormat - { - Intermediate = 0, - Obj = 1, - GlTF = 2 - } - /// /// Simple class that parses settings for the extractor /// public class Settings { - /// /// The logger reference for debug output /// @@ -101,6 +91,11 @@ public class Settings /// Additional files that should be copied when extracting with `all` or `clientdata` /// public string ClientDataToCopy { get; private set; } + + /// + /// If enabled, XMI files will be copied to the 'Exports/Music' folder + /// + public bool CopyMusic { get; private set; } /// /// The verbosity of the logger @@ -215,6 +210,16 @@ public void Initialize() { ClientDataToCopy = parsedSettings["ClientDataToCopy"]; } + + if (parsedSettings.ContainsKey("ClientDataToCopy")) + { + ClientDataToCopy = parsedSettings["ClientDataToCopy"]; + } + + if (parsedSettings.ContainsKey("CopyMusic")) + { + CopyMusic = Convert.ToBoolean(parsedSettings["CopyMusic"]); + } if (parsedSettings.ContainsKey("LoggerVerbosity")) { diff --git a/LanternExtractor/settings.txt b/LanternExtractor/settings.txt index 776054a..6792de5 100644 --- a/LanternExtractor/settings.txt +++ b/LanternExtractor/settings.txt @@ -69,6 +69,10 @@ ExportGltfInGlbFormat = false # (Intermediate) ClientDataToCopy = spells.eff,spdat.eff +# If enabled, client XMI files will be copied to the 'Exports/Music' folder +# XMI files can be used in the Lantern client +CopyMusic = true + # The minimum verbosity of the logger # 0 = info, 1 = warnings, 2 = errors LoggerVerbosity = 2 From 9f32e0aba632e44cb689f16b2140e50a3d9a61fe Mon Sep 17 00:00:00 2001 From: Daniel Wilkins Date: Sat, 25 Feb 2023 00:40:52 -0800 Subject: [PATCH 04/17] Add progress on sound reverse engineering --- LanternExtractor/EQ/ArchiveExtractor.cs | 10 +- LanternExtractor/EQ/Sound/ClientSounds.cs | 2 +- LanternExtractor/EQ/Sound/EffSounds.cs | 197 +++++++++++----------- LanternExtractor/EQ/Sound/SoundEntry.cs | 12 +- LanternExtractor/EQ/Sound/SoundType.cs | 2 +- LanternExtractor/EqFileHelper.cs | 20 ++- LanternExtractor/LanternExtractor.cs | 1 - 7 files changed, 125 insertions(+), 119 deletions(-) diff --git a/LanternExtractor/EQ/ArchiveExtractor.cs b/LanternExtractor/EQ/ArchiveExtractor.cs index d63bff4..d13a246 100644 --- a/LanternExtractor/EQ/ArchiveExtractor.cs +++ b/LanternExtractor/EQ/ArchiveExtractor.cs @@ -13,7 +13,7 @@ public static class ArchiveExtractor public static void Extract(string path, string rootFolder, ILogger logger, Settings settings) { string archiveName = Path.GetFileNameWithoutExtension(path); - + if (string.IsNullOrEmpty(archiveName)) { return; @@ -68,7 +68,7 @@ public static void Extract(string path, string rootFolder, ILogger logger, Setti { ExtractArchiveSky(rootFolder, logger, settings, wldFileInArchive, shortName, s3dArchive); } - else if (EqFileHelper.IsCharactersArchive(archiveName)) + else if (EqFileHelper.IsCharacterArchive(archiveName)) { ExtractArchiveCharacters(path, rootFolder, logger, settings, archiveName, wldFileInArchive, shortName, s3dArchive); } @@ -141,7 +141,7 @@ private static void ExtractArchiveZone(string path, string rootFolder, ILogger l zoneObjectsWldFile.Initialize(rootFolder); } - ExtractSoundData(shortName, rootFolder, settings); + ExtractSoundData(shortName, rootFolder, logger, settings); } private static void ExtractArchiveObjects(string path, string rootFolder, ILogger logger, Settings settings, @@ -307,7 +307,7 @@ public static void WriteWldTextures(PfsArchive s3dArchive, WldFile wldFile, stri } } - private static void ExtractSoundData(string shortName, string rootFolder, Settings settings) + private static void ExtractSoundData(string shortName, string rootFolder, ILogger logger, Settings settings) { var sounds = new EffSndBnk(settings.EverQuestDirectory + shortName + "_sndbnk" + LanternStrings.SoundFormatExtension); @@ -315,7 +315,7 @@ private static void ExtractSoundData(string shortName, string rootFolder, Settin var soundEntries = new EffSounds( settings.EverQuestDirectory + shortName + "_sounds" + LanternStrings.SoundFormatExtension, sounds); - soundEntries.Initialize(); + soundEntries.Initialize(logger); soundEntries.ExportSoundData(shortName, rootFolder); } } diff --git a/LanternExtractor/EQ/Sound/ClientSounds.cs b/LanternExtractor/EQ/Sound/ClientSounds.cs index bb40ea9..1470ac2 100644 --- a/LanternExtractor/EQ/Sound/ClientSounds.cs +++ b/LanternExtractor/EQ/Sound/ClientSounds.cs @@ -2,7 +2,7 @@ namespace LanternExtractor.EQ.Sound { - public class ClientSounds + public static class ClientSounds { // Hardcoded client sounds - verified that no other references exist in Trilogy client private static Dictionary _clientSounds = new Dictionary diff --git a/LanternExtractor/EQ/Sound/EffSounds.cs b/LanternExtractor/EQ/Sound/EffSounds.cs index 011eb55..a676048 100644 --- a/LanternExtractor/EQ/Sound/EffSounds.cs +++ b/LanternExtractor/EQ/Sound/EffSounds.cs @@ -1,6 +1,8 @@ using System.Collections.Generic; using System.IO; +using System.Linq; using System.Text; +using LanternExtractor.Infrastructure.Logger; namespace LanternExtractor.EQ.Sound { @@ -30,20 +32,20 @@ public EffSounds(string soundFilePath, EffSndBnk soundBank) _soundBank = soundBank; } - public void Initialize() + public void Initialize(ILogger logger) { if (_soundBank == null || !File.Exists(_soundFilePath)) { return; } - FileStream file = File.Open(_soundFilePath, FileMode.Open); + var file = File.Open(_soundFilePath, FileMode.Open); var reader = new BinaryReader(file); int fileLength = (int)reader.BaseStream.Length; if (fileLength % EntryLengthInBytes != 0) { - // File is an incorrect size + logger.LogError($"Incorrect .eff file - size must be multiple of {EntryLengthInBytes}"); return; } @@ -51,107 +53,102 @@ public void Initialize() for (int i = 0; i < entryCount; ++i) { - var newSound = new SoundEntry(); - newSound.UnkRef00 = reader.ReadInt32(); - newSound.UnkRef04 = reader.ReadInt32(); - newSound.Reserved = reader.ReadInt32(); - newSound.Sequence = reader.ReadInt32(); - - newSound.PosX = reader.ReadSingle(); - newSound.PosY = reader.ReadSingle(); - newSound.PosZ = reader.ReadSingle(); - newSound.Radius = reader.ReadSingle(); - - newSound.CooldownDay = reader.ReadInt32(); - newSound.CooldownNight = reader.ReadInt32(); - newSound.RandomDelay = reader.ReadInt32(); - - newSound.Unk44 = reader.ReadInt32(); - int soundId1 = reader.ReadInt32(); - int soundId2 = reader.ReadInt32(); - - byte soundType = reader.ReadByte(); - newSound.SoundType = (SoundType)soundType; - - if (soundType == 0 || soundType == 2 || soundType == 3) + var newSound = new SoundEntry { - // Find the sound names - EmissionType newSoundEmissionType = newSound.EmissionType; - newSound.SoundIdDay = soundId1; //GetSoundString(soundId1, _soundBank, ref newSoundEmissionType); - EmissionType soundEmissionType = newSound.EmissionType; - newSound.SoundIdNight = soundId2; //GetSoundString(soundId2, _soundBank, ref soundEmissionType); - } - else + UnkRef00 = reader.ReadInt32(), + UnkRef04 = reader.ReadInt32(), + Reserved = reader.ReadInt32(), + Sequence = reader.ReadInt32(), + PosX = reader.ReadSingle(), + PosY = reader.ReadSingle(), + PosZ = reader.ReadSingle(), + Radius = reader.ReadSingle(), + CooldownDay = reader.ReadInt32(), + CooldownNight = reader.ReadInt32(), + RandomDelay = reader.ReadInt32(), + Unk44 = reader.ReadInt32(), + SoundIdDay = reader.ReadInt32(), + SoundIdNight = reader.ReadInt32(), + SoundType = (SoundType)reader.ReadByte(), + UnkPad57 = reader.ReadByte(), + UnkPad58 = reader.ReadByte(), + UnkPad59 = reader.ReadByte(), + AsDistance = reader.ReadInt32(), + UnkRange64 = reader.ReadInt32(), + FadeOutMs = reader.ReadInt32(), + UnkRange72 = reader.ReadInt32(), + FullVolRange = reader.ReadInt32(), + UnkRange80 = reader.ReadInt32() + }; + + _soundEntries.Add(newSound); + + if (newSound.SoundType == SoundType.Music && newSound.FullVolRange != 1000 && newSound.FullVolRange != 0) { - newSound.SoundIdDay = soundId1; //GetMusicTrackName(soundId1); - newSound.SoundIdNight = soundId2; //GetMusicTrackName(soundId1); - } - - newSound.UnkPad57 = reader.ReadByte(); - newSound.UnkPad58 = reader.ReadByte(); - newSound.UnkPad59 = reader.ReadByte(); - - newSound.AsDistance = reader.ReadInt32(); - newSound.UnkRange64 = reader.ReadInt32(); - newSound.FadeOutMs = reader.ReadInt32(); - newSound.UnkRange72 = reader.ReadInt32(); - newSound.FullVolRange = reader.ReadInt32(); - newSound.UnkRange80 = reader.ReadInt32(); - - if (newSound.SoundIdDay != 0 || newSound.SoundIdNight != 0) - { - _soundEntries.Add(newSound); + } } + + var writer = new BinaryWriter(file); + file.Position = 0; + ModifyMusicData(writer); } - /// - /// Returns the name of the sound based on either the internal reference or the sound back definitions - /// The client uses specific integer ranges to identify the type. - /// There are also a handful of hardcoded sound ids. - /// - /// - /// - /// - /// - /// The name of the sound - private bool TryGetSoundInfo(int soundIdDay, int soundIdNight, out string soundNameDay, - out string soundNameNight) + private void ModifyMusicData(BinaryWriter writer) { - var typeDay = GetEmissionType(soundIdDay); - var typeNight = GetEmissionType(soundIdNight); - soundNameDay = string.Empty; - soundNameNight = string.Empty; - - if (typeDay == EmissionType.None && typeNight == EmissionType.None) - { - // No sound - return false; - } - - if (typeDay != typeNight) - { - // Two separate emission types - return false; - } - - if (typeDay != EmissionType.None) - { - soundNameDay = GetSoundName(soundIdDay, typeDay); - } - - if (typeNight != EmissionType.None) - { - soundNameNight = GetSoundName(soundIdDay, typeDay); - } - - return true; + // MUSIC EXPLORATION + /*writer.BaseStream.Position = 16; + writer.Write(0f); + writer.Write(0f); + writer.Write(0f); + writer.Write(0); + writer.BaseStream.Position = 48; + writer.Write(0); + writer.Write(4); + writer.BaseStream.Position = 60; + writer.Write(3); + writer.BaseStream.Position = 68; + writer.Write(1000); + writer.Write(1000); + writer.BaseStream.Position = 76; + writer.Write(500); + writer.BaseStream.Position = 0; + var memoryStream = new MemoryStream(); + writer.BaseStream.CopyTo(memoryStream); + File.WriteAllBytes("arena_sounds.eff", memoryStream.ToArray());*/ + + // SOUND EXPLORATION + /*writer.BaseStream.Position = 16; + writer.Write(0f); + writer.Write(0f); + writer.Write(0f); + writer.Write(0f); + writer.BaseStream.Position = 32; + writer.Write(0); + writer.Write(0); + writer.Write(0); + writer.BaseStream.Position = 76; + writer.Write(100); + writer.BaseStream.Position = 0; + var memoryStream = new MemoryStream(); + writer.BaseStream.CopyTo(memoryStream); + writer.BaseStream.CopyTo(memoryStream); + writer.BaseStream.CopyTo(memoryStream); + File.WriteAllBytes("arena_sounds.eff", memoryStream.ToArray());*/ + + // Sound isolation + /*var memoryStream = new MemoryStream(); + writer.BaseStream.CopyTo(memoryStream); + File.WriteAllBytes("arena_sounds.eff", memoryStream.ToArray().Skip(14 * EntryLengthInBytes).Take(EntryLengthInBytes).ToArray());*/ } - - private string GetSoundName(int soundId, EmissionType type) + + private string GetSoundName(int soundId) { - switch (type) + var emissionType = GetEmissionType(soundId); + switch (emissionType) { + case EmissionType.None: + return string.Empty; case EmissionType.Emit: return _soundBank.GetEmitSound(soundId - 1); case EmissionType.Loop: @@ -208,12 +205,6 @@ public void ExportSoundData(string zoneName, string rootFolder) } else { - if (!TryGetSoundInfo(entry.SoundIdDay, entry.SoundIdNight, out var soundNameDay, - out var soundNameNight)) - { - continue; - } - soundExport.Append((int)entry.SoundType); soundExport.Append(","); soundExport.Append(entry.PosX); @@ -224,9 +215,9 @@ public void ExportSoundData(string zoneName, string rootFolder) soundExport.Append(","); soundExport.Append(entry.Radius); soundExport.Append(","); - soundExport.Append(soundNameDay); + soundExport.Append(GetSoundName(entry.SoundIdDay)); soundExport.Append(","); - soundExport.Append(soundNameNight); + soundExport.Append(GetSoundName(entry.SoundIdNight)); soundExport.Append(","); soundExport.Append(entry.CooldownDay); soundExport.Append(","); @@ -254,7 +245,7 @@ public void ExportSoundData(string zoneName, string rootFolder) StringBuilder exportHeader = new StringBuilder(); exportHeader.AppendLine(LanternStrings.ExportHeaderTitle + "Music Instances"); exportHeader.AppendLine( - "# Format: PosX, PosY, PosZ, Radius, MusicIndexDay, MusicIndexNight, DayLoopCount, NightLoopCount, FadeOutMs"); + "# Format: PosX, PosY, PosZ, Radius, MusicIndexDay, MusicIndexNight, LoopCountDay, LoopCountNight, FadeOutMs"); Directory.CreateDirectory(exportPath); File.WriteAllText(exportPath + "music_instances.txt", exportHeader.ToString() + musicExport); } diff --git a/LanternExtractor/EQ/Sound/SoundEntry.cs b/LanternExtractor/EQ/Sound/SoundEntry.cs index d99c28a..9ec4cd1 100644 --- a/LanternExtractor/EQ/Sound/SoundEntry.cs +++ b/LanternExtractor/EQ/Sound/SoundEntry.cs @@ -7,7 +7,6 @@ /// public class SoundEntry { - public EmissionType EmissionType { get; set; } public int UnkRef00 { get; set; } public int UnkRef04 { get; set; } public int Reserved { get; set; } @@ -33,4 +32,15 @@ public class SoundEntry public int FullVolRange { get; set; } public int UnkRange80 { get; set; } } + + public class MusicData + { + public float PosX { get; set; } + public float PosY { get; set; } + public float PosZ { get; set; } + public float Radius { get; set; } + public int LoopCountDay { get; set; } + public int LoopCountNight { get; set; } + public int FadeOutMs { get; set; } + } } \ No newline at end of file diff --git a/LanternExtractor/EQ/Sound/SoundType.cs b/LanternExtractor/EQ/Sound/SoundType.cs index 7743a76..1445830 100644 --- a/LanternExtractor/EQ/Sound/SoundType.cs +++ b/LanternExtractor/EQ/Sound/SoundType.cs @@ -3,7 +3,7 @@ /// /// Describes the way the sound is heard by the player /// - public enum SoundType : byte + public enum SoundType : int { /// /// Sounds that play at a constant volume diff --git a/LanternExtractor/EqFileHelper.cs b/LanternExtractor/EqFileHelper.cs index 909a8c1..5819db8 100644 --- a/LanternExtractor/EqFileHelper.cs +++ b/LanternExtractor/EqFileHelper.cs @@ -11,7 +11,7 @@ public static bool IsEquipmentArchive(string archiveName) return archiveName.StartsWith("gequip"); } - public static bool IsCharactersArchive(string archiveName) + public static bool IsCharacterArchive(string archiveName) { return archiveName.Contains("_chr") || archiveName.StartsWith("chequip") || archiveName.Contains("_amr"); } @@ -49,19 +49,25 @@ public static bool IsSpecialCaseExtraction(string archiveName) public static List GetValidEqFilePaths(string directory, string archiveName) { archiveName = archiveName.ToLower(); - var validFiles = new List(); + var eqFiles = Directory.GetFiles(directory, "*.*", SearchOption.AllDirectories); if (archiveName == "all") { - validFiles = Directory.GetFiles(directory, "*.*", SearchOption.AllDirectories) - .Where(s => (s.EndsWith(".s3d") || s.EndsWith(".pfs")) && !s.Contains("chequip") && - !s.EndsWith("_lit.s3d")).ToList(); + validFiles = eqFiles.Where(f => (f.EndsWith(".s3d") || f.EndsWith(".pfs")) && !f.Contains("chequip") && + !f.EndsWith("_lit.s3d")).ToList(); + } + if (archiveName == "characters") + { + validFiles = eqFiles.Where(IsCharacterArchive).ToList(); } else if (archiveName == "equipment") { - validFiles = Directory.GetFiles(directory, "*.*", SearchOption.AllDirectories) - .Where(s => (s.EndsWith(".s3d") || s.EndsWith(".pfs")) && s.Contains("gequip")).ToList(); + validFiles = eqFiles.Where(IsEquipmentArchive).ToList(); + } + else if (archiveName == "sounds") + { + validFiles = eqFiles.Where(IsSoundArchive).ToList(); } else if (archiveName.EndsWith(".s3d") || archiveName.EndsWith(".pfs")) { diff --git a/LanternExtractor/LanternExtractor.cs b/LanternExtractor/LanternExtractor.cs index 6258ecc..4dacf7b 100644 --- a/LanternExtractor/LanternExtractor.cs +++ b/LanternExtractor/LanternExtractor.cs @@ -53,7 +53,6 @@ private static void Main(string[] args) } var archiveName = args[0]; - List eqFiles = EqFileHelper.GetValidEqFilePaths(_settings.EverQuestDirectory, archiveName); eqFiles.Sort(); From c07bdab53a721815bbec320634f64238a86eb4a6 Mon Sep 17 00:00:00 2001 From: Dan Wilkins Date: Sat, 22 Apr 2023 16:19:31 -0700 Subject: [PATCH 05/17] Fix incorrect export folder for raw archive contents --- LanternExtractor/EQ/ArchiveExtractor.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/LanternExtractor/EQ/ArchiveExtractor.cs b/LanternExtractor/EQ/ArchiveExtractor.cs index d13a246..4e788df 100644 --- a/LanternExtractor/EQ/ArchiveExtractor.cs +++ b/LanternExtractor/EQ/ArchiveExtractor.cs @@ -30,7 +30,7 @@ public static void Extract(string path, string rootFolder, ILogger logger, Setti if (settings.RawS3dExtract) { - s3dArchive.WriteAllFiles(Path.Combine(rootFolder + shortName, archiveName)); + s3dArchive.WriteAllFiles(Path.Combine(rootFolder, archiveName)); return; } From 1c56279e47c76ef45768a2caedc5a43c28945a3d Mon Sep 17 00:00:00 2001 From: Daniel Wilkins Date: Tue, 13 Jun 2023 23:46:01 -0700 Subject: [PATCH 06/17] Refactor audio handling --- LanternExtractor/EQ/Sound/AudioInstance.cs | 81 ++++++++ LanternExtractor/EQ/Sound/AudioType.cs | 23 +++ LanternExtractor/EQ/Sound/EffSounds.cs | 223 +++++++++------------ LanternExtractor/EQ/Sound/SoundEntry.cs | 46 ----- LanternExtractor/EQ/Sound/SoundTest.cs | 31 +++ LanternExtractor/EQ/Sound/SoundType.cs | 29 --- LanternExtractor/LanternExtractor.csproj | 5 +- 7 files changed, 229 insertions(+), 209 deletions(-) create mode 100644 LanternExtractor/EQ/Sound/AudioInstance.cs create mode 100644 LanternExtractor/EQ/Sound/AudioType.cs delete mode 100644 LanternExtractor/EQ/Sound/SoundEntry.cs create mode 100644 LanternExtractor/EQ/Sound/SoundTest.cs delete mode 100644 LanternExtractor/EQ/Sound/SoundType.cs diff --git a/LanternExtractor/EQ/Sound/AudioInstance.cs b/LanternExtractor/EQ/Sound/AudioInstance.cs new file mode 100644 index 0000000..9b1d9c3 --- /dev/null +++ b/LanternExtractor/EQ/Sound/AudioInstance.cs @@ -0,0 +1,81 @@ +namespace LanternExtractor.EQ.Sound +{ + public abstract class AudioInstance + { + protected AudioInstance(AudioType type, float posX, float posY, float posZ, float radius) + { + AudioType = type; + PosX = posX; + PosY = posY; + PosZ = posZ; + Radius = radius; + } + + public AudioType AudioType { get; } + public float PosX { get; } + public float PosY { get; } + public float PosZ { get; } + public float Radius { get; } + } + + public class MusicInstance : AudioInstance + { + public MusicInstance(AudioType type, float posX, float posY, float posZ, float radius, int trackIndexDay, + int trackIndexNight, int loopCountDay, int loopCountNight, int fadeOutMs) : base(type, posX, posY, posZ, + radius) + { + TrackIndexDay = trackIndexDay; + TrackIndexNight = trackIndexNight; + LoopCountDay = loopCountDay; + LoopCountNight = loopCountNight; + FadeOutMs = fadeOutMs; + } + + public int TrackIndexDay { get; } + public int TrackIndexNight { get; } + public int LoopCountDay { get; } + public int LoopCountNight { get; } + public int FadeOutMs { get; } + } + + public abstract class SoundInstance : AudioInstance + { + public int Volume { get; } + public int SoundId1 { get; } + public int Cooldown1 { get; } + + protected SoundInstance(AudioType type, float posX, float posY, float posZ, float radius, int volume, + int soundId1, int cooldown1) : base(type, posX, posY, posZ, radius) + { + Volume = volume; + SoundId1 = soundId1; + Cooldown1 = cooldown1; + } + } + + public class SoundInstance2d : SoundInstance + { + public int SoundId2 { get; } + public int Cooldown2 { get; } + + public SoundInstance2d(AudioType type, float posX, float posY, float posZ, float radius, int volume, + int soundId1, int cooldown1, int soundId2, int cooldown2) : base(type, posX, posY, posZ, radius, volume, + soundId1, cooldown1) + { + SoundId2 = soundId2; + Cooldown2 = cooldown2; + } + } + + public class SoundInstance3d : SoundInstance + { + public int Multiplier; + + public SoundInstance3d(AudioType type, float posX, float posY, float posZ, float radius, int volume, + int soundId1, int cooldown1, int multiplier) : base(type, posX, posY, posZ, radius, volume, soundId1, + cooldown1) + { + Multiplier = multiplier; + } + } +} \ No newline at end of file diff --git a/LanternExtractor/EQ/Sound/AudioType.cs b/LanternExtractor/EQ/Sound/AudioType.cs new file mode 100644 index 0000000..f58e840 --- /dev/null +++ b/LanternExtractor/EQ/Sound/AudioType.cs @@ -0,0 +1,23 @@ +namespace LanternExtractor.EQ.Sound +{ + /// + /// Describes the way the sound is heard by the player + /// + public enum AudioType : byte + { + /// + /// Sounds that play at a constant volume + /// + Sound2d = 0, + + /// + /// Music instance. Can specify both day and night trackID. + /// + Music = 1, + + /// + /// Sounds that have a falloff - the farther the player is from the center, the quieter it becomes. + /// + Sound3d = 2, + } +} \ No newline at end of file diff --git a/LanternExtractor/EQ/Sound/EffSounds.cs b/LanternExtractor/EQ/Sound/EffSounds.cs index a676048..21a6ab6 100644 --- a/LanternExtractor/EQ/Sound/EffSounds.cs +++ b/LanternExtractor/EQ/Sound/EffSounds.cs @@ -1,6 +1,6 @@ -using System.Collections.Generic; +using System; +using System.Collections.Generic; using System.IO; -using System.Linq; using System.Text; using LanternExtractor.Infrastructure.Logger; @@ -13,18 +13,15 @@ namespace LanternExtractor.EQ.Sound /// public class EffSounds { + public static int EntryLengthInBytes = 84; + /// /// The sound bank referenced for sound names /// private readonly EffSndBnk _soundBank; - + private readonly string _soundFilePath; - - private readonly List _soundEntries = new List(); - - private readonly List _musicTrackEntries = new List(); - - private const int EntryLengthInBytes = 84; + private readonly List _audioInstances = new List(); public EffSounds(string soundFilePath, EffSndBnk soundBank) { @@ -53,95 +50,65 @@ public void Initialize(ILogger logger) for (int i = 0; i < entryCount; ++i) { - var newSound = new SoundEntry + var basePosition = EntryLengthInBytes * i; + reader.BaseStream.Position = basePosition + 16; + float posX = reader.ReadSingle(); + float posY = reader.ReadSingle(); + float posZ = reader.ReadSingle(); + float radius = reader.ReadSingle(); + + reader.BaseStream.Position = basePosition + 56; + + var typeByte = reader.ReadByte(); + + if (!Enum.IsDefined(typeof(AudioType), typeByte)) + { + logger.LogError($"Unable to parse sound type: {typeByte}"); + continue; + } + + var type = (AudioType)typeByte; + reader.BaseStream.Position = basePosition + 48; + int soundId1 = reader.ReadInt32(); + + if (type == AudioType.Music) + { + int soundId2 = reader.ReadInt32(); + reader.BaseStream.Position = basePosition + 60; + int loopCountDay = reader.ReadInt32(); + int loopCountNight = reader.ReadInt32(); + int fadeOutMs = reader.ReadInt32(); + var musicInstance = new MusicInstance(type, posX, posY, posZ, radius, soundId1, soundId2, + loopCountDay, loopCountNight, fadeOutMs); + _audioInstances.Add(musicInstance); + } + else if (type == AudioType.Sound2d) { - UnkRef00 = reader.ReadInt32(), - UnkRef04 = reader.ReadInt32(), - Reserved = reader.ReadInt32(), - Sequence = reader.ReadInt32(), - PosX = reader.ReadSingle(), - PosY = reader.ReadSingle(), - PosZ = reader.ReadSingle(), - Radius = reader.ReadSingle(), - CooldownDay = reader.ReadInt32(), - CooldownNight = reader.ReadInt32(), - RandomDelay = reader.ReadInt32(), - Unk44 = reader.ReadInt32(), - SoundIdDay = reader.ReadInt32(), - SoundIdNight = reader.ReadInt32(), - SoundType = (SoundType)reader.ReadByte(), - UnkPad57 = reader.ReadByte(), - UnkPad58 = reader.ReadByte(), - UnkPad59 = reader.ReadByte(), - AsDistance = reader.ReadInt32(), - UnkRange64 = reader.ReadInt32(), - FadeOutMs = reader.ReadInt32(), - UnkRange72 = reader.ReadInt32(), - FullVolRange = reader.ReadInt32(), - UnkRange80 = reader.ReadInt32() - }; - - _soundEntries.Add(newSound); - - if (newSound.SoundType == SoundType.Music && newSound.FullVolRange != 1000 && newSound.FullVolRange != 0) + int soundId2 = reader.ReadInt32(); + reader.BaseStream.Position = basePosition + 32; + int cooldown1 = reader.ReadInt32(); + int cooldown2 = reader.ReadInt32(); + reader.BaseStream.Position = basePosition + 60; + int volume = reader.ReadInt32(); + var soundInstance = new SoundInstance2d(type, posX, posY, posZ, radius, volume, soundId1, cooldown1, + soundId2, cooldown2); + _audioInstances.Add(soundInstance); + } + else { - + reader.BaseStream.Position = basePosition + 32; + int cooldown1 = reader.ReadInt32(); + reader.BaseStream.Position = basePosition + 60; + int volume = reader.ReadInt32(); + reader.BaseStream.Position = basePosition + 72; + int multiplier = reader.ReadInt32(); + var soundInstance = new SoundInstance3d(type, posX, posY, posZ, radius, volume, soundId1, cooldown1, + multiplier); + _audioInstances.Add(soundInstance); } } - - var writer = new BinaryWriter(file); - file.Position = 0; - ModifyMusicData(writer); } - private void ModifyMusicData(BinaryWriter writer) - { - // MUSIC EXPLORATION - /*writer.BaseStream.Position = 16; - writer.Write(0f); - writer.Write(0f); - writer.Write(0f); - writer.Write(0); - writer.BaseStream.Position = 48; - writer.Write(0); - writer.Write(4); - writer.BaseStream.Position = 60; - writer.Write(3); - writer.BaseStream.Position = 68; - writer.Write(1000); - writer.Write(1000); - writer.BaseStream.Position = 76; - writer.Write(500); - writer.BaseStream.Position = 0; - var memoryStream = new MemoryStream(); - writer.BaseStream.CopyTo(memoryStream); - File.WriteAllBytes("arena_sounds.eff", memoryStream.ToArray());*/ - - // SOUND EXPLORATION - /*writer.BaseStream.Position = 16; - writer.Write(0f); - writer.Write(0f); - writer.Write(0f); - writer.Write(0f); - writer.BaseStream.Position = 32; - writer.Write(0); - writer.Write(0); - writer.Write(0); - writer.BaseStream.Position = 76; - writer.Write(100); - writer.BaseStream.Position = 0; - var memoryStream = new MemoryStream(); - writer.BaseStream.CopyTo(memoryStream); - writer.BaseStream.CopyTo(memoryStream); - writer.BaseStream.CopyTo(memoryStream); - File.WriteAllBytes("arena_sounds.eff", memoryStream.ToArray());*/ - - // Sound isolation - /*var memoryStream = new MemoryStream(); - writer.BaseStream.CopyTo(memoryStream); - File.WriteAllBytes("arena_sounds.eff", memoryStream.ToArray().Skip(14 * EntryLengthInBytes).Take(EntryLengthInBytes).ToArray());*/ - } - private string GetSoundName(int soundId) { var emissionType = GetEmissionType(soundId); @@ -180,51 +147,43 @@ public void ExportSoundData(string zoneName, string rootFolder) var soundExport = new StringBuilder(); var musicExport = new StringBuilder(); - foreach (var entry in _soundEntries) + foreach (var entry in _audioInstances) { - if (entry.SoundType == SoundType.Music) + if (entry.AudioType == AudioType.Music) + { + var music = entry as MusicInstance; + if (music == null) + { + continue; + } + + musicExport.AppendLine(string.Join(",", music.PosX, music.PosY, music.PosZ, music.Radius, + music.TrackIndexDay, music.TrackIndexNight, music.LoopCountDay, music.LoopCountNight, + music.FadeOutMs)); + } + else if (entry.AudioType == AudioType.Sound2d) { - musicExport.Append(entry.PosX); - musicExport.Append(","); - musicExport.Append(entry.PosZ); - musicExport.Append(","); - musicExport.Append(entry.PosY); - musicExport.Append(","); - musicExport.Append(entry.Radius); - musicExport.Append(","); - musicExport.Append(entry.SoundIdDay); - musicExport.Append(","); - musicExport.Append(entry.SoundIdNight); - musicExport.Append(","); - musicExport.Append(entry.AsDistance); // Day loop count - musicExport.Append(","); - musicExport.Append(entry.UnkRange64); // Night loop count - musicExport.Append(","); - musicExport.Append(entry.FadeOutMs); - musicExport.AppendLine(); + var sound2d = entry as SoundInstance2d; + if (sound2d == null) + { + continue; + } + + soundExport.AppendLine(string.Join(",", (byte)sound2d.AudioType, sound2d.PosX, sound2d.PosY, + sound2d.PosZ, sound2d.Radius, GetSoundName(sound2d.SoundId1), GetSoundName(sound2d.SoundId2), + sound2d.Cooldown1, sound2d.Cooldown2, sound2d.Cooldown2, sound2d.Volume)); } else { - soundExport.Append((int)entry.SoundType); - soundExport.Append(","); - soundExport.Append(entry.PosX); - soundExport.Append(","); - soundExport.Append(entry.PosZ); - soundExport.Append(","); - soundExport.Append(entry.PosY); - soundExport.Append(","); - soundExport.Append(entry.Radius); - soundExport.Append(","); - soundExport.Append(GetSoundName(entry.SoundIdDay)); - soundExport.Append(","); - soundExport.Append(GetSoundName(entry.SoundIdNight)); - soundExport.Append(","); - soundExport.Append(entry.CooldownDay); - soundExport.Append(","); - soundExport.Append(entry.CooldownNight); - soundExport.Append(","); - soundExport.Append(entry.RandomDelay); - soundExport.AppendLine(); + var sound3d = entry as SoundInstance3d; + if (sound3d == null) + { + continue; + } + + soundExport.AppendLine(string.Join(",", (byte)sound3d.AudioType, sound3d.PosX, sound3d.PosY, + sound3d.PosZ, sound3d.Radius, GetSoundName(sound3d.SoundId1), sound3d.Cooldown1, sound3d.Volume, + sound3d.Multiplier)); } } diff --git a/LanternExtractor/EQ/Sound/SoundEntry.cs b/LanternExtractor/EQ/Sound/SoundEntry.cs deleted file mode 100644 index 9ec4cd1..0000000 --- a/LanternExtractor/EQ/Sound/SoundEntry.cs +++ /dev/null @@ -1,46 +0,0 @@ -namespace LanternExtractor.EQ.Sound -{ - /// - /// Contains information about a single sound instance in the world - /// Big thanks to Shendare from the EQEmu forums for sharing this information - /// More documentation will come as I verify all of this info - /// - public class SoundEntry - { - public int UnkRef00 { get; set; } - public int UnkRef04 { get; set; } - public int Reserved { get; set; } - public int Sequence { get; set; } - public float PosX { get; set; } - public float PosY { get; set; } - public float PosZ { get; set; } - public float Radius { get; set; } - public int CooldownDay { get; set; } - public int CooldownNight { get; set; } - public int RandomDelay { get; set; } - public int Unk44 { get; set; } - public int SoundIdDay { get; set; } - public int SoundIdNight { get; set; } - public SoundType SoundType { get; set; } - public byte UnkPad57 { get; set; } - public byte UnkPad58 { get; set; } - public byte UnkPad59 { get; set; } - public int AsDistance { get; set; } - public int UnkRange64 { get; set; } - public int FadeOutMs { get; set; } - public int UnkRange72 { get; set; } - public int FullVolRange { get; set; } - public int UnkRange80 { get; set; } - } - - public class MusicData - { - public float PosX { get; set; } - public float PosY { get; set; } - public float PosZ { get; set; } - public float Radius { get; set; } - public int LoopCountDay { get; set; } - public int LoopCountNight { get; set; } - public int FadeOutMs { get; set; } - } -} \ No newline at end of file diff --git a/LanternExtractor/EQ/Sound/SoundTest.cs b/LanternExtractor/EQ/Sound/SoundTest.cs new file mode 100644 index 0000000..c005140 --- /dev/null +++ b/LanternExtractor/EQ/Sound/SoundTest.cs @@ -0,0 +1,31 @@ +using System.IO; +using System.Linq; + +namespace LanternExtractor.EQ.Sound +{ + /// + /// A small helper class useful for sound isolation and testing + /// + public static class SoundTest + { + public static void OutputSingleInstance(BinaryWriter writer, int index, string fileName) + { + var memoryStream = new MemoryStream(); + writer.BaseStream.CopyTo(memoryStream); + File.WriteAllBytes(fileName, + memoryStream.ToArray().Skip(index * EffSounds.EntryLengthInBytes).Take(EffSounds.EntryLengthInBytes).ToArray()); + } + + public static void ModifyInstance(BinaryWriter writer, int index, string fileName) + { + writer.BaseStream.Position = 16; // positions + writer.Write(0f); + writer.Write(0f); + writer.Write(50f); + + var memoryStream = new MemoryStream(); + writer.BaseStream.CopyTo(memoryStream); + File.WriteAllBytes(fileName, memoryStream.ToArray()); + } + } +} \ No newline at end of file diff --git a/LanternExtractor/EQ/Sound/SoundType.cs b/LanternExtractor/EQ/Sound/SoundType.cs deleted file mode 100644 index 1445830..0000000 --- a/LanternExtractor/EQ/Sound/SoundType.cs +++ /dev/null @@ -1,29 +0,0 @@ -namespace LanternExtractor.EQ.Sound -{ - /// - /// Describes the way the sound is heard by the player - /// - public enum SoundType : int - { - /// - /// Sounds that play at a constant volume - /// - NoFalloff = 0, - - /// - /// Background music. Can specify both a daytime and nighttime music via the soundID - /// Music usually has a large fade out delay - /// - Music = 1, - - /// - /// Sounds that have a falloff - the further the player is from the center, the quieter it becomes - /// - Falloff = 2, - - /// - /// Sounds that stay at full volume until the user has wandered outside of the FullVolRange - /// - FullVolumeRange = 3, - } -} \ No newline at end of file diff --git a/LanternExtractor/LanternExtractor.csproj b/LanternExtractor/LanternExtractor.csproj index e3a4c61..73473ab 100644 --- a/LanternExtractor/LanternExtractor.csproj +++ b/LanternExtractor/LanternExtractor.csproj @@ -60,9 +60,10 @@ - - + + + From bc4bf518522475aa286f86a0159359a6a3e7463c Mon Sep 17 00:00:00 2001 From: Nick Gal Date: Tue, 20 Jun 2023 17:43:50 -0700 Subject: [PATCH 07/17] Fix halas female full path --- .../EQ/Wld/Helpers/CharacterFixer.cs | 38 ++++++++++++++----- 1 file changed, 29 insertions(+), 9 deletions(-) diff --git a/LanternExtractor/EQ/Wld/Helpers/CharacterFixer.cs b/LanternExtractor/EQ/Wld/Helpers/CharacterFixer.cs index 5f63067..89db7fb 100644 --- a/LanternExtractor/EQ/Wld/Helpers/CharacterFixer.cs +++ b/LanternExtractor/EQ/Wld/Helpers/CharacterFixer.cs @@ -292,7 +292,7 @@ private void FixShipNames() actor?.MeshReference?.Mesh?.MaterialList?.Name?.Replace("GHOSTSHIP", "GSP") ?? "GSP"; } - if (actor.Name.StartsWith("LAUNCH")) + if (actor.Name.StartsWith("LAUNCH") && (actor?.MeshReference?.Mesh?.Name ?? null) != null) { actor.Name = actor.MeshReference.Mesh.Name.Replace("DMSPRITEDEF", "ACTORDEF"); } @@ -384,13 +384,9 @@ private void FixHalasFemale() return; } - skeleton.BoneMappingClean[7] = "bi_l"; - skeleton.BoneMappingClean[10] = "l_point"; - skeleton.BoneMappingClean[15] = "head_point"; - - skeleton.Skeleton[7].CleanedName = "bi_l"; - skeleton.Skeleton[10].CleanedName = "l_point"; - skeleton.Skeleton[15].CleanedName = "head_point"; + RenameBone(skeleton, 7, "bi_l"); + RenameBone(skeleton, 10, "l_point"); + RenameBone(skeleton, 15, "head_point"); } /// @@ -406,6 +402,11 @@ private void FixHighpassMale() return; } + if (skeleton.Skeleton.Count < 25) + { + return; + } + skeleton.Skeleton[0].Children = new List { 1 @@ -413,5 +414,24 @@ private void FixHighpassMale() skeleton.Skeleton.RemoveAt(24); } + + private void RenameBone(SkeletonHierarchy skeleton, int index, string newBoneName) + { + var oldBoneName = skeleton.BoneMappingClean[index]; + + skeleton.BoneMappingClean[index] = newBoneName; + skeleton.Skeleton[index].CleanedName = newBoneName; + + for (var i = 0; i < skeleton.Skeleton.Count; i++) + { + var fullPath = skeleton.Skeleton[i].CleanedFullPath; + if (!fullPath.Contains(oldBoneName)) + { + continue; + } + + skeleton.Skeleton[i].CleanedFullPath = fullPath.Replace(oldBoneName, newBoneName); + } + } } -} \ No newline at end of file +} From fba375083bc4c1518b838cac62e258b85104fec8 Mon Sep 17 00:00:00 2001 From: Nick Gal Date: Thu, 6 Jul 2023 18:59:44 -0700 Subject: [PATCH 08/17] Add eal parsing --- .gitignore | 1 + LanternExtractor/EQ/ArchiveExtractor.cs | 11 ++-- LanternExtractor/EQ/Sound/EffSounds.cs | 15 +++-- LanternExtractor/EQ/Sound/EnvAudio.cs | 78 ++++++++++++++++++++++++ LanternExtractor/LanternExtractor.csproj | 3 +- 5 files changed, 99 insertions(+), 9 deletions(-) create mode 100644 LanternExtractor/EQ/Sound/EnvAudio.cs diff --git a/.gitignore b/.gitignore index ae4e793..bcf8194 100644 --- a/.gitignore +++ b/.gitignore @@ -6,3 +6,4 @@ LanternExtractor/bin/ *.DS_Store packages/ .vs/ +.vscode/ diff --git a/LanternExtractor/EQ/ArchiveExtractor.cs b/LanternExtractor/EQ/ArchiveExtractor.cs index 4e788df..a3ae39c 100644 --- a/LanternExtractor/EQ/ArchiveExtractor.cs +++ b/LanternExtractor/EQ/ArchiveExtractor.cs @@ -13,12 +13,12 @@ public static class ArchiveExtractor public static void Extract(string path, string rootFolder, ILogger logger, Settings settings) { string archiveName = Path.GetFileNameWithoutExtension(path); - + if (string.IsNullOrEmpty(archiveName)) { return; } - + string shortName = archiveName.Split('_')[0]; var s3dArchive = new PfsArchive(path, logger); @@ -307,14 +307,17 @@ public static void WriteWldTextures(PfsArchive s3dArchive, WldFile wldFile, stri } } - private static void ExtractSoundData(string shortName, string rootFolder, ILogger logger, Settings settings) + private static void ExtractSoundData(string shortName, string rootFolder, ILogger logger, Settings settings) { + var envAudio = EnvAudio.Instance; + envAudio.Load(Path.Combine(settings.EverQuestDirectory, "defaults.dat")); var sounds = new EffSndBnk(settings.EverQuestDirectory + shortName + "_sndbnk" + LanternStrings.SoundFormatExtension); sounds.Initialize(); var soundEntries = new EffSounds( - settings.EverQuestDirectory + shortName + "_sounds" + LanternStrings.SoundFormatExtension, sounds); + settings.EverQuestDirectory + shortName + "_sounds" + LanternStrings.SoundFormatExtension, + sounds, envAudio); soundEntries.Initialize(logger); soundEntries.ExportSoundData(shortName, rootFolder); } diff --git a/LanternExtractor/EQ/Sound/EffSounds.cs b/LanternExtractor/EQ/Sound/EffSounds.cs index 21a6ab6..9fd71f4 100644 --- a/LanternExtractor/EQ/Sound/EffSounds.cs +++ b/LanternExtractor/EQ/Sound/EffSounds.cs @@ -1,4 +1,4 @@ -using System; +using System; using System.Collections.Generic; using System.IO; using System.Text; @@ -19,14 +19,15 @@ public class EffSounds /// The sound bank referenced for sound names /// private readonly EffSndBnk _soundBank; - + private readonly EnvAudio _envAudio; private readonly string _soundFilePath; private readonly List _audioInstances = new List(); - public EffSounds(string soundFilePath, EffSndBnk soundBank) + public EffSounds(string soundFilePath, EffSndBnk soundBank, EnvAudio envAudio) { _soundFilePath = soundFilePath; _soundBank = soundBank; + _envAudio = envAudio; } public void Initialize(ILogger logger) @@ -127,6 +128,12 @@ private string GetSoundName(int soundId) } } + private float GetEalSoundVolume(int soundId) + { + var soundName = GetSoundName(soundId); + return _envAudio.GetVolumeLinear(soundName); + } + private EmissionType GetEmissionType(int soundId) { if (soundId <= 0) @@ -210,4 +217,4 @@ public void ExportSoundData(string zoneName, string rootFolder) } } } -} \ No newline at end of file +} diff --git a/LanternExtractor/EQ/Sound/EnvAudio.cs b/LanternExtractor/EQ/Sound/EnvAudio.cs new file mode 100644 index 0000000..9d49abe --- /dev/null +++ b/LanternExtractor/EQ/Sound/EnvAudio.cs @@ -0,0 +1,78 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.IO; +using EalTools; + +namespace LanternExtractor.EQ.Sound +{ + public class EnvAudio + { + public static EnvAudio Instance => _instance; + private static readonly EnvAudio _instance = new EnvAudio(); + + public EalData Data { get; private set; } + + private string _ealFilePath; + private EalFile _ealFile; + private Dictionary _sourceLevels; + + static EnvAudio() + { + } + + private EnvAudio() + { + } + + public bool Load(string ealFilePath) + { + if (_ealFilePath == ealFilePath) + { + return false; + } + + _ealFilePath = ealFilePath; + + if (_ealFilePath == null || !File.Exists(_ealFilePath)) + { + return false; + } + + _ealFile = new EalFile(_ealFilePath); + + if (!_ealFile.Initialize()) + { + return false; + } + + Data = _ealFile.Data; + + if (Data == null) + { + return false; + } + + _sourceLevels = Data.SourceModels.ToDictionary( + s => Path.GetFileNameWithoutExtension(s.Name).ToLower(), + s => s.SourceAttributes.EaxAttributes.DirectPathLevel + ); + + return true; + } + + public int GetVolumeEq(string soundFile) + { + var volume = 0; + _sourceLevels?.TryGetValue(soundFile, out volume); + return volume; + } + + public float GetVolumeLinear(string soundFile) + { + var volumeEq = GetVolumeEq(soundFile); + var linear = MathF.Pow(10.0f, volumeEq / 2000.0f); + return Math.Clamp(linear, 0f, 1f); + } + } +} diff --git a/LanternExtractor/LanternExtractor.csproj b/LanternExtractor/LanternExtractor.csproj index 82f0b26..67a1e45 100644 --- a/LanternExtractor/LanternExtractor.csproj +++ b/LanternExtractor/LanternExtractor.csproj @@ -1,4 +1,4 @@ - + Exe net6.0 @@ -27,6 +27,7 @@ + 0.9.8 From 8a02fb9b20b208c906ef4110dcc5d8c58238d334 Mon Sep 17 00:00:00 2001 From: Nick Gal Date: Fri, 7 Jul 2023 20:01:52 -0700 Subject: [PATCH 09/17] Handle eqzip's crc --- LanternExtractor/EQ/Pfs/PfsArchive.cs | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/LanternExtractor/EQ/Pfs/PfsArchive.cs b/LanternExtractor/EQ/Pfs/PfsArchive.cs index fda1b24..2aec495 100644 --- a/LanternExtractor/EQ/Pfs/PfsArchive.cs +++ b/LanternExtractor/EQ/Pfs/PfsArchive.cs @@ -92,7 +92,9 @@ public bool Initialize() inflatedSize += inflatedLength; } - if (crc == 0x61580AC9) + // EQZip saved archives use 0xFFFFFFFFU for filenames + // https://github.com/Shendare/EQZip/blob/b181ec7658ea9880984d58271cbab924ab8dd702/EQArchive.cs#L517 + if (crc == 0x61580AC9 || (crc == 0xFFFFFFFFU && fileNames.Count == 0)) { var dictionaryStream = new MemoryStream(fileBytes); var dictionary = new BinaryReader(dictionaryStream); @@ -227,4 +229,4 @@ public void RenameFile(string originalName, string newName) _fileNameReference[newName] = file; } } -} \ No newline at end of file +} From 79f7f1e60156d0252ac098eabad9042a43926bd9 Mon Sep 17 00:00:00 2001 From: Nick Gal Date: Fri, 7 Jul 2023 22:04:14 -0700 Subject: [PATCH 10/17] Rework libgdiplus hacks --- LanternExtractor/Infrastructure/EqBmp.cs | 121 ++++++++++++++++++ .../Infrastructure/ImageWriter.cs | 82 ++---------- 2 files changed, 134 insertions(+), 69 deletions(-) create mode 100644 LanternExtractor/Infrastructure/EqBmp.cs diff --git a/LanternExtractor/Infrastructure/EqBmp.cs b/LanternExtractor/Infrastructure/EqBmp.cs new file mode 100644 index 0000000..cc043ae --- /dev/null +++ b/LanternExtractor/Infrastructure/EqBmp.cs @@ -0,0 +1,121 @@ +using System; +using System.Drawing; +using System.Drawing.Imaging; +using System.IO; +using System.Linq; + +namespace LanternExtractor.Infrastructure +{ + /// + /// Wrapper over Bitmap with various hacks to make libgdiplus + /// consistently create transparent pngs on MacOS & Linux + /// + public class EqBmp + { + private static readonly bool _needsGdipHacks = Environment.OSVersion.Platform == PlatformID.MacOSX || + Environment.OSVersion.Platform == PlatformID.Unix; + private static bool _hasCheckedForPaletteFlagsField; + private static System.Reflection.FieldInfo _paletteFlagsField = null; + + public PixelFormat PixelFormat => _bitmap.PixelFormat; + + private readonly Bitmap _bitmap; + private readonly ColorPalette _palette; + + public EqBmp(Stream stream) + { + SetPaletteFlagsField(); + + _bitmap = new Bitmap(stream); + _palette = _bitmap.Palette; + } + + public void WritePng(string outputFilePath) + { + _bitmap.Save(outputFilePath, ImageFormat.Png); + } + + public void MakeMagentaTransparent() + { + _bitmap.MakeTransparent(Color.Magenta); + if (_needsGdipHacks) + { + // https://github.com/ mono/libgdiplus/commit/bf9a1440b7bfea704bf2cb771f5c2b5c09e7bcfa + _bitmap.MakeTransparent(Color.FromArgb(0, Color.Magenta)); + } + } + + public void MakePaletteTransparent(int transparentIndex) + { + if (_needsGdipHacks) + { + // https://github.com/mono/libgdiplus/issues/702 + _paletteFlagsField?.SetValue(_palette, _palette.Flags | (int)PaletteFlags.HasAlpha); + } + + var transparentColor = GetTransparentPaletteColor(); + _palette.Entries[transparentIndex] = transparentColor; + _bitmap.Palette = _palette; + + if (_needsGdipHacks) + { + // Due to a bug with the libgdiplus implementation of System.Drawing, setting a color palette + // entry to transparent does not work. The workaround is to ensure that the transparent + // key is unique and then use MakeTransparent() + _bitmap.MakeTransparent(transparentColor); + } + } + + private Color GetTransparentPaletteColor() + { + var transparencyColor = Color.FromArgb(0, 0, 0, 0); + + if (!_needsGdipHacks) + { + return transparencyColor; + } + + var random = new Random(); + var foundUnique = false; + + while (!foundUnique) + { + foundUnique = _palette.Entries.All(e => e != transparencyColor); + transparencyColor = Color.FromArgb(0, random.Next(256), random.Next(256), random.Next(256)); + } + + return transparencyColor; + } + + // https://github.com/Robmaister/SharpFont/blob/422bdab059dd8e594b4b061a3b53152e71342ce2/Source/SharpFont.GDI/FTBitmapExtensions.cs + // https://github.com/Robmaister/SharpFont/pull/136 + private static void SetPaletteFlagsField() + { + if (!_needsGdipHacks || _hasCheckedForPaletteFlagsField) + { + return; + } + + _hasCheckedForPaletteFlagsField = true; + + // The field needed may be named "flags" or "_flags", dependin on the version of Mono. To be thorough, check for the first Name that contains "lags". + var fields = typeof(ColorPalette).GetFields(System.Reflection.BindingFlags.Instance | System.Reflection.BindingFlags.NonPublic); + + for (int i = 0; i < fields.Length; i++) + { + if (fields[i].Name.Contains("lags")) + { + _paletteFlagsField = fields[i]; + break; + } + } + } + + enum PaletteFlags + { + HasAlpha = 0x0001, + GrayScale = 0x0002, + HalfTone = 0x0004, + } + } +} diff --git a/LanternExtractor/Infrastructure/ImageWriter.cs b/LanternExtractor/Infrastructure/ImageWriter.cs index ac9471f..2625509 100644 --- a/LanternExtractor/Infrastructure/ImageWriter.cs +++ b/LanternExtractor/Infrastructure/ImageWriter.cs @@ -31,8 +31,6 @@ public static void WriteImageAsPng(byte[] bytes, string filePath, string fileNam private static void WriteBmpAsPng(byte[] bytes, string filePath, string fileName, bool isMasked, bool rotate, ILogger logger) { - var byteStream = new MemoryStream(bytes); - if (string.IsNullOrEmpty(filePath)) { return; @@ -40,11 +38,12 @@ private static void WriteBmpAsPng(byte[] bytes, string filePath, string fileName Directory.CreateDirectory(filePath); - Bitmap image; + EqBmp image; + var byteStream = new MemoryStream(bytes); try { - image = new Bitmap(byteStream); + image = new EqBmp(byteStream); } catch (Exception e) { @@ -60,76 +59,21 @@ private static void WriteBmpAsPng(byte[] bytes, string filePath, string fileName fileName = "canwall1.png"; } - Bitmap cloneBitmap; - - if (isMasked) + switch (image.PixelFormat) { - cloneBitmap = image.Clone(new Rectangle(0, 0, image.Width, image.Height), - PixelFormat.Format8bppIndexed); - - int paletteIndex = GetPaletteIndex(fileName); - var palette = cloneBitmap.Palette; - - if (Environment.OSVersion.Platform != PlatformID.MacOSX && - Environment.OSVersion.Platform != PlatformID.Unix) - { - palette.Entries[paletteIndex] = Color.FromArgb(0, 0, 0, 0); - cloneBitmap.Palette = palette; - } - else - { - // Due to a bug with the MacOS implementation of System.Drawing, setting a color palette value to - // transparent does not work. The workaround is to ensure that the first palette value (the transparent - // key) is unique and then use MakeTransparent() - Color transparencyColor = palette.Entries[paletteIndex]; - bool isUnique = false; - - while (!isUnique) - { - isUnique = true; - - for (var i = 1; i < cloneBitmap.Palette.Entries.Length; i++) - { - Color paletteValue = cloneBitmap.Palette.Entries[i]; - - if (paletteValue == transparencyColor) - { - Random random = new Random(); - transparencyColor = Color.FromArgb(random.Next(256), random.Next(256), random.Next(256)); - isUnique = false; - break; - } - } - } - - palette.Entries[paletteIndex] = transparencyColor; - cloneBitmap.Palette = palette; - cloneBitmap.MakeTransparent(transparencyColor); - - // For some reason, this now has to be done to ensure the pixels are actually set to transparent - // Another head scratching MacOS bug - for (int i = 0; i < cloneBitmap.Width; ++i) + case PixelFormat.Format8bppIndexed: + if (isMasked) { - for (int j = 0; j < cloneBitmap.Height; ++j) - { - if (cloneBitmap.GetPixel(i, j) == transparencyColor) - { - cloneBitmap.SetPixel(i, j, Color.FromArgb(0, 0, 0, 0)); - } - } + var paletteIndex = GetPaletteIndex(fileName); + image.MakePaletteTransparent(paletteIndex); } - } - } - else - { - cloneBitmap = image.Clone(new Rectangle(0, 0, image.Width, image.Height), PixelFormat.Format32bppArgb); - if (image.PixelFormat != PixelFormat.Format8bppIndexed) - { - cloneBitmap.MakeTransparent(Color.Magenta); - } + break; + default: + image.MakeMagentaTransparent(); + break; } - cloneBitmap.Save(Path.Combine(filePath, fileName), ImageFormat.Png); + image.WritePng(Path.Combine(filePath, fileName)); } private static void WriteDdsAsPng(byte[] bytes, string filePath, string fileName) From 8e318b631d2928c6be54e29f4024c0309d8b33f3 Mon Sep 17 00:00:00 2001 From: Owen Raccuglia Date: Sun, 9 Jul 2023 10:17:27 -0400 Subject: [PATCH 11/17] Open EAL files without locking the file for other readers --- LanternExtractor/EQ/Sound/EnvAudio.cs | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/LanternExtractor/EQ/Sound/EnvAudio.cs b/LanternExtractor/EQ/Sound/EnvAudio.cs index 9d49abe..b56e5a0 100644 --- a/LanternExtractor/EQ/Sound/EnvAudio.cs +++ b/LanternExtractor/EQ/Sound/EnvAudio.cs @@ -39,7 +39,9 @@ public bool Load(string ealFilePath) return false; } - _ealFile = new EalFile(_ealFilePath); + // Allow other threads to open the same file for reading + _ealFile = new EalFile(new FileStream( + _ealFilePath, FileMode.Open, FileAccess.Read, FileShare.Read)); if (!_ealFile.Initialize()) { From 643f1dcbb86b23b2ce49f2c5d72ab6cf1a60bd0b Mon Sep 17 00:00:00 2001 From: Daniel Wilkins Date: Mon, 17 Jul 2023 23:58:46 -0700 Subject: [PATCH 12/17] Add EAL sound volume and remaining known data --- LanternExtractor/EQ/Sound/AudioInstance.cs | 32 +++++---- LanternExtractor/EQ/Sound/EffSounds.cs | 82 ++++++++++++++-------- LanternExtractor/EQ/Sound/EnvAudio.cs | 9 ++- 3 files changed, 77 insertions(+), 46 deletions(-) diff --git a/LanternExtractor/EQ/Sound/AudioInstance.cs b/LanternExtractor/EQ/Sound/AudioInstance.cs index 9b1d9c3..7b1bbd8 100644 --- a/LanternExtractor/EQ/Sound/AudioInstance.cs +++ b/LanternExtractor/EQ/Sound/AudioInstance.cs @@ -40,30 +40,34 @@ public MusicInstance(AudioType type, float posX, float posY, float posZ, float r public abstract class SoundInstance : AudioInstance { - public int Volume { get; } - public int SoundId1 { get; } + public string Sound1 { get; } + public float Volume1 { get; } public int Cooldown1 { get; } + public int CooldownRandom { get; } - protected SoundInstance(AudioType type, float posX, float posY, float posZ, float radius, int volume, - int soundId1, int cooldown1) : base(type, posX, posY, posZ, radius) + protected SoundInstance(AudioType type, float posX, float posY, float posZ, float radius, float volume1, + string sound1, int cooldown1, int cooldownRandom) : base(type, posX, posY, posZ, radius) { - Volume = volume; - SoundId1 = soundId1; + Sound1 = sound1; + Volume1 = volume1; Cooldown1 = cooldown1; + CooldownRandom = cooldownRandom; } } public class SoundInstance2d : SoundInstance { - public int SoundId2 { get; } + public string Sound2 { get; } + public float Volume2 { get; } public int Cooldown2 { get; } - public SoundInstance2d(AudioType type, float posX, float posY, float posZ, float radius, int volume, - int soundId1, int cooldown1, int soundId2, int cooldown2) : base(type, posX, posY, posZ, radius, volume, - soundId1, cooldown1) + public SoundInstance2d(AudioType type, float posX, float posY, float posZ, float radius, float volume1, + string sound1, int cooldown1, string sound2, int cooldown2, int cooldownRandom, float volume2) : base(type, posX, posY, posZ, radius, volume1, + sound1, cooldown1, cooldownRandom) { - SoundId2 = soundId2; + Sound2 = sound2; Cooldown2 = cooldown2; + Volume2 = volume2; } } @@ -71,9 +75,9 @@ public class SoundInstance3d : SoundInstance { public int Multiplier; - public SoundInstance3d(AudioType type, float posX, float posY, float posZ, float radius, int volume, - int soundId1, int cooldown1, int multiplier) : base(type, posX, posY, posZ, radius, volume, soundId1, - cooldown1) + public SoundInstance3d(AudioType type, float posX, float posY, float posZ, float radius, float volume, + string sound1, int cooldown1, int cooldownRandom, int multiplier) : base(type, posX, posY, posZ, radius, volume, sound1, + cooldown1, cooldownRandom) { Multiplier = multiplier; } diff --git a/LanternExtractor/EQ/Sound/EffSounds.cs b/LanternExtractor/EQ/Sound/EffSounds.cs index 9fd71f4..26b083c 100644 --- a/LanternExtractor/EQ/Sound/EffSounds.cs +++ b/LanternExtractor/EQ/Sound/EffSounds.cs @@ -19,6 +19,7 @@ public class EffSounds /// The sound bank referenced for sound names /// private readonly EffSndBnk _soundBank; + private readonly EnvAudio _envAudio; private readonly string _soundFilePath; private readonly List _audioInstances = new List(); @@ -71,6 +72,7 @@ public void Initialize(ILogger logger) var type = (AudioType)typeByte; reader.BaseStream.Position = basePosition + 48; int soundId1 = reader.ReadInt32(); + string sound1 = GetSoundName(soundId1); if (type == AudioType.Music) { @@ -86,25 +88,33 @@ public void Initialize(ILogger logger) else if (type == AudioType.Sound2d) { int soundId2 = reader.ReadInt32(); + string sound2 = GetSoundName(soundId2); reader.BaseStream.Position = basePosition + 32; int cooldown1 = reader.ReadInt32(); int cooldown2 = reader.ReadInt32(); + int cooldownRandom = reader.ReadInt32(); reader.BaseStream.Position = basePosition + 60; - int volume = reader.ReadInt32(); - var soundInstance = new SoundInstance2d(type, posX, posY, posZ, radius, volume, soundId1, cooldown1, - soundId2, cooldown2); + int volume1Raw = reader.ReadInt32(); + float volume1 = GetEalSoundVolume(sound1, volume1Raw); + int volume2Raw = reader.ReadInt32(); + float volume2 = GetEalSoundVolume(sound2, volume2Raw); + var soundInstance = new SoundInstance2d(type, posX, posY, posZ, radius, volume1, sound1, cooldown1, + sound2, cooldown2, cooldownRandom, volume2); _audioInstances.Add(soundInstance); } else { reader.BaseStream.Position = basePosition + 32; int cooldown1 = reader.ReadInt32(); + reader.BaseStream.Position = basePosition + 40; + int cooldownRandom = reader.ReadInt32(); reader.BaseStream.Position = basePosition + 60; - int volume = reader.ReadInt32(); + int volumeRaw = reader.ReadInt32(); + float volume = GetEalSoundVolume(sound1, volumeRaw); reader.BaseStream.Position = basePosition + 72; int multiplier = reader.ReadInt32(); - var soundInstance = new SoundInstance3d(type, posX, posY, posZ, radius, volume, soundId1, cooldown1, - multiplier); + var soundInstance = new SoundInstance3d(type, posX, posY, posZ, radius, volume, sound1, + cooldown1, cooldownRandom, multiplier); _audioInstances.Add(soundInstance); } } @@ -128,10 +138,14 @@ private string GetSoundName(int soundId) } } - private float GetEalSoundVolume(int soundId) + private float GetEalSoundVolume(string soundName, int volumeRaw) { - var soundName = GetSoundName(soundId); - return _envAudio.GetVolumeLinear(soundName); + if (volumeRaw > 0) + { + volumeRaw = -volumeRaw; + } + + return volumeRaw == 0 ? _envAudio.GetVolumeLinear(soundName) : _envAudio.GetVolumeLinear(volumeRaw); } private EmissionType GetEmissionType(int soundId) @@ -151,15 +165,15 @@ private EmissionType GetEmissionType(int soundId) public void ExportSoundData(string zoneName, string rootFolder) { - var soundExport = new StringBuilder(); + var sound2dExport = new StringBuilder(); + var sound3dExport = new StringBuilder(); var musicExport = new StringBuilder(); foreach (var entry in _audioInstances) { if (entry.AudioType == AudioType.Music) { - var music = entry as MusicInstance; - if (music == null) + if (!(entry is MusicInstance music)) { continue; } @@ -170,51 +184,59 @@ public void ExportSoundData(string zoneName, string rootFolder) } else if (entry.AudioType == AudioType.Sound2d) { - var sound2d = entry as SoundInstance2d; - if (sound2d == null) + if (!(entry is SoundInstance2d sound2d)) { continue; } - soundExport.AppendLine(string.Join(",", (byte)sound2d.AudioType, sound2d.PosX, sound2d.PosY, - sound2d.PosZ, sound2d.Radius, GetSoundName(sound2d.SoundId1), GetSoundName(sound2d.SoundId2), - sound2d.Cooldown1, sound2d.Cooldown2, sound2d.Cooldown2, sound2d.Volume)); + sound2dExport.AppendLine(string.Join(",", sound2d.PosX, sound2d.PosY, + sound2d.PosZ, sound2d.Radius, sound2d.Sound1, sound2d.Sound2, + sound2d.Cooldown1, sound2d.Cooldown2, sound2d.CooldownRandom, sound2d.Volume1, sound2d.Volume2)); } else { - var sound3d = entry as SoundInstance3d; - if (sound3d == null) + if (!(entry is SoundInstance3d sound3d)) { continue; } - soundExport.AppendLine(string.Join(",", (byte)sound3d.AudioType, sound3d.PosX, sound3d.PosY, - sound3d.PosZ, sound3d.Radius, GetSoundName(sound3d.SoundId1), sound3d.Cooldown1, sound3d.Volume, + sound3dExport.AppendLine(string.Join(",", sound3d.PosX, sound3d.PosY, + sound3d.PosZ, sound3d.Radius, sound3d.Sound1, sound3d.Cooldown1, sound3d.CooldownRandom, sound3d.Volume1, sound3d.Multiplier)); } } string exportPath = Path.Combine(rootFolder, zoneName, "Zone/"); - if (soundExport.Length > 0) + if (musicExport.Length > 0) { StringBuilder exportHeader = new StringBuilder(); - exportHeader.AppendLine(LanternStrings.ExportHeaderTitle + "Sound Instances"); + exportHeader.AppendLine(LanternStrings.ExportHeaderTitle + "Music Instances"); exportHeader.AppendLine( - "# Format: SoundType, PosX, PosY, PosZ, Radius, SoundIdDay, SoundIdNight, CooldownDay, CooldownNight, RandomDelay"); + "# Format: PosX, PosY, PosZ, Radius, MusicIndexDay, MusicIndexNight, LoopCountDay, LoopCountNight, FadeOutMs"); Directory.CreateDirectory(exportPath); - File.WriteAllText(exportPath + "sound_instances.txt", exportHeader.ToString() + soundExport); + File.WriteAllText(exportPath + "music_instances.txt", exportHeader.ToString() + musicExport); } - if (musicExport.Length > 0) + if (sound2dExport.Length > 0) { StringBuilder exportHeader = new StringBuilder(); - exportHeader.AppendLine(LanternStrings.ExportHeaderTitle + "Music Instances"); + exportHeader.AppendLine(LanternStrings.ExportHeaderTitle + "Sound 2D Instances"); exportHeader.AppendLine( - "# Format: PosX, PosY, PosZ, Radius, MusicIndexDay, MusicIndexNight, LoopCountDay, LoopCountNight, FadeOutMs"); + "# Format: PosX, PosY, PosZ, Radius, SoundNameDay, SoundNameNight, CooldownDay, CooldownNight, CooldownRandom, VolumeDay, VolumeNight"); Directory.CreateDirectory(exportPath); - File.WriteAllText(exportPath + "music_instances.txt", exportHeader.ToString() + musicExport); + File.WriteAllText(exportPath + "sound2d_instances.txt", exportHeader.ToString() + sound2dExport); + } + + if (sound3dExport.Length > 0) + { + StringBuilder exportHeader = new StringBuilder(); + exportHeader.AppendLine(LanternStrings.ExportHeaderTitle + "Sound 3D Instances"); + exportHeader.AppendLine( + "# Format: PosX, PosY, PosZ, Radius, SoundName, Cooldown, CooldownRandom, Volume, Multiplier"); + Directory.CreateDirectory(exportPath); + File.WriteAllText(exportPath + "sound3d_instances.txt", exportHeader.ToString() + sound3dExport); } } } -} +} \ No newline at end of file diff --git a/LanternExtractor/EQ/Sound/EnvAudio.cs b/LanternExtractor/EQ/Sound/EnvAudio.cs index b56e5a0..60ee7bb 100644 --- a/LanternExtractor/EQ/Sound/EnvAudio.cs +++ b/LanternExtractor/EQ/Sound/EnvAudio.cs @@ -63,7 +63,7 @@ public bool Load(string ealFilePath) return true; } - public int GetVolumeEq(string soundFile) + private int GetVolumeEq(string soundFile) { var volume = 0; _sourceLevels?.TryGetValue(soundFile, out volume); @@ -73,7 +73,12 @@ public int GetVolumeEq(string soundFile) public float GetVolumeLinear(string soundFile) { var volumeEq = GetVolumeEq(soundFile); - var linear = MathF.Pow(10.0f, volumeEq / 2000.0f); + return GetVolumeLinear(volumeEq); + } + + public float GetVolumeLinear(int directAudioLevel) + { + var linear = MathF.Pow(10.0f, directAudioLevel / 2000.0f); return Math.Clamp(linear, 0f, 1f); } } From f3d8542d1bc0eabfb306d858ed253e6de24b92e4 Mon Sep 17 00:00:00 2001 From: Daniel Wilkins Date: Tue, 18 Jul 2023 23:27:22 -0700 Subject: [PATCH 13/17] Fix export position of audio instances --- LanternExtractor/EQ/Sound/EffSounds.cs | 12 ++++++------ LanternExtractor/EQ/Sound/SoundTest.cs | 10 +++++++--- 2 files changed, 13 insertions(+), 9 deletions(-) diff --git a/LanternExtractor/EQ/Sound/EffSounds.cs b/LanternExtractor/EQ/Sound/EffSounds.cs index 26b083c..8bbd0da 100644 --- a/LanternExtractor/EQ/Sound/EffSounds.cs +++ b/LanternExtractor/EQ/Sound/EffSounds.cs @@ -44,7 +44,7 @@ public void Initialize(ILogger logger) if (fileLength % EntryLengthInBytes != 0) { - logger.LogError($"Incorrect .eff file - size must be multiple of {EntryLengthInBytes}"); + logger.LogError($"Invalid .eff file - size must be multiple of {EntryLengthInBytes}"); return; } @@ -178,7 +178,7 @@ public void ExportSoundData(string zoneName, string rootFolder) continue; } - musicExport.AppendLine(string.Join(",", music.PosX, music.PosY, music.PosZ, music.Radius, + musicExport.AppendLine(string.Join(",", music.PosX, music.PosZ, music.PosY, music.Radius, music.TrackIndexDay, music.TrackIndexNight, music.LoopCountDay, music.LoopCountNight, music.FadeOutMs)); } @@ -189,8 +189,8 @@ public void ExportSoundData(string zoneName, string rootFolder) continue; } - sound2dExport.AppendLine(string.Join(",", sound2d.PosX, sound2d.PosY, - sound2d.PosZ, sound2d.Radius, sound2d.Sound1, sound2d.Sound2, + sound2dExport.AppendLine(string.Join(",", sound2d.PosX, sound2d.PosZ, + sound2d.PosY, sound2d.Radius, sound2d.Sound1, sound2d.Sound2, sound2d.Cooldown1, sound2d.Cooldown2, sound2d.CooldownRandom, sound2d.Volume1, sound2d.Volume2)); } else @@ -200,8 +200,8 @@ public void ExportSoundData(string zoneName, string rootFolder) continue; } - sound3dExport.AppendLine(string.Join(",", sound3d.PosX, sound3d.PosY, - sound3d.PosZ, sound3d.Radius, sound3d.Sound1, sound3d.Cooldown1, sound3d.CooldownRandom, sound3d.Volume1, + sound3dExport.AppendLine(string.Join(",", sound3d.PosX, sound3d.PosZ, + sound3d.PosY, sound3d.Radius, sound3d.Sound1, sound3d.Cooldown1, sound3d.CooldownRandom, sound3d.Volume1, sound3d.Multiplier)); } } diff --git a/LanternExtractor/EQ/Sound/SoundTest.cs b/LanternExtractor/EQ/Sound/SoundTest.cs index c005140..b70bccf 100644 --- a/LanternExtractor/EQ/Sound/SoundTest.cs +++ b/LanternExtractor/EQ/Sound/SoundTest.cs @@ -10,19 +10,23 @@ public static class SoundTest { public static void OutputSingleInstance(BinaryWriter writer, int index, string fileName) { + writer.BaseStream.Position = 0; var memoryStream = new MemoryStream(); writer.BaseStream.CopyTo(memoryStream); - File.WriteAllBytes(fileName, - memoryStream.ToArray().Skip(index * EffSounds.EntryLengthInBytes).Take(EffSounds.EntryLengthInBytes).ToArray()); + var bytes = memoryStream.ToArray().Skip(index * EffSounds.EntryLengthInBytes) + .Take(EffSounds.EntryLengthInBytes).ToArray(); + File.WriteAllBytes(fileName, bytes); } - public static void ModifyInstance(BinaryWriter writer, int index, string fileName) + public static void ModifyInstance(BinaryWriter writer, string fileName) { writer.BaseStream.Position = 16; // positions writer.Write(0f); writer.Write(0f); writer.Write(50f); + + writer.BaseStream.Position = 0; var memoryStream = new MemoryStream(); writer.BaseStream.CopyTo(memoryStream); File.WriteAllBytes(fileName, memoryStream.ToArray()); From 9fd53ced8095dec0c778cd9f22e59aeec7f00fa7 Mon Sep 17 00:00:00 2001 From: Nick Gal Date: Fri, 21 Jul 2023 08:56:07 -0700 Subject: [PATCH 14/17] Fallback to defaults.eal for newer clients --- LanternExtractor/EQ/ArchiveExtractor.cs | 8 +++++++- LanternExtractor/EQ/Sound/EnvAudio.cs | 13 ++++++++----- 2 files changed, 15 insertions(+), 6 deletions(-) diff --git a/LanternExtractor/EQ/ArchiveExtractor.cs b/LanternExtractor/EQ/ArchiveExtractor.cs index a3ae39c..1a7f7cc 100644 --- a/LanternExtractor/EQ/ArchiveExtractor.cs +++ b/LanternExtractor/EQ/ArchiveExtractor.cs @@ -310,10 +310,16 @@ public static void WriteWldTextures(PfsArchive s3dArchive, WldFile wldFile, stri private static void ExtractSoundData(string shortName, string rootFolder, ILogger logger, Settings settings) { var envAudio = EnvAudio.Instance; - envAudio.Load(Path.Combine(settings.EverQuestDirectory, "defaults.dat")); + var ealFilePath = Path.Combine(settings.EverQuestDirectory, "defaults.dat"); + if (!envAudio.Load(ealFilePath)) + { + envAudio.Load(Path.ChangeExtension(ealFilePath, ".eal")); + } + var sounds = new EffSndBnk(settings.EverQuestDirectory + shortName + "_sndbnk" + LanternStrings.SoundFormatExtension); sounds.Initialize(); + var soundEntries = new EffSounds( settings.EverQuestDirectory + shortName + "_sounds" + LanternStrings.SoundFormatExtension, diff --git a/LanternExtractor/EQ/Sound/EnvAudio.cs b/LanternExtractor/EQ/Sound/EnvAudio.cs index 60ee7bb..a058fbc 100644 --- a/LanternExtractor/EQ/Sound/EnvAudio.cs +++ b/LanternExtractor/EQ/Sound/EnvAudio.cs @@ -13,6 +13,7 @@ public class EnvAudio public EalData Data { get; private set; } + private bool _loaded; private string _ealFilePath; private EalFile _ealFile; private Dictionary _sourceLevels; @@ -29,16 +30,16 @@ public bool Load(string ealFilePath) { if (_ealFilePath == ealFilePath) { - return false; + return _loaded; } - _ealFilePath = ealFilePath; - - if (_ealFilePath == null || !File.Exists(_ealFilePath)) + if (ealFilePath == null || !File.Exists(ealFilePath)) { return false; } + _ealFilePath = ealFilePath; + // Allow other threads to open the same file for reading _ealFile = new EalFile(new FileStream( _ealFilePath, FileMode.Open, FileAccess.Read, FileShare.Read)); @@ -60,7 +61,9 @@ public bool Load(string ealFilePath) s => s.SourceAttributes.EaxAttributes.DirectPathLevel ); - return true; + _loaded = true; + + return _loaded; } private int GetVolumeEq(string soundFile) From 9e67b3ca09589326f93967ea0a3baceeb1c91ff8 Mon Sep 17 00:00:00 2001 From: Daniel Wilkins Date: Mon, 14 Aug 2023 21:36:45 -0700 Subject: [PATCH 15/17] Rework export settings and fix archive location --- LanternExtractor/EQ/ArchiveExtractor.cs | 29 ++-- LanternExtractor/EQ/Wld/WldFile.cs | 7 - LanternExtractor/EqFileHelper.cs | 181 ++++++++++++++++-------- LanternExtractor/Settings.cs | 81 +++++------ LanternExtractor/settings.txt | 6 +- 5 files changed, 180 insertions(+), 124 deletions(-) diff --git a/LanternExtractor/EQ/ArchiveExtractor.cs b/LanternExtractor/EQ/ArchiveExtractor.cs index 1a7f7cc..082e2ed 100644 --- a/LanternExtractor/EQ/ArchiveExtractor.cs +++ b/LanternExtractor/EQ/ArchiveExtractor.cs @@ -36,15 +36,15 @@ public static void Extract(string path, string rootFolder, ILogger logger, Setti // For non WLD files, we can just extract their contents // Used for pure texture archives (e.g. bmpwad.s3d) and sound archives (e.g. snd1.pfs) - // The difference between this and the raw export is that it will convert images to .png + // The difference between this and the raw export is that it will convert images to PNG if (!s3dArchive.IsWldArchive) { WriteS3dTextures(s3dArchive, rootFolder + shortName, logger); - if (EqFileHelper.IsSoundArchive(archiveName)) + if (EqFileHelper.IsUsedSoundArchive(archiveName)) { - var soundFolder = settings.ExportSoundsToSingleFolder ? "sounds" : shortName; - WriteS3dSounds(s3dArchive, rootFolder + soundFolder, logger); + WriteS3dSounds(s3dArchive, + Path.Combine(rootFolder, settings.ExportSoundsToSingleFolder ? "sounds" : shortName), logger); } return; @@ -70,7 +70,8 @@ public static void Extract(string path, string rootFolder, ILogger logger, Setti } else if (EqFileHelper.IsCharacterArchive(archiveName)) { - ExtractArchiveCharacters(path, rootFolder, logger, settings, archiveName, wldFileInArchive, shortName, s3dArchive); + ExtractArchiveCharacters(path, rootFolder, logger, settings, archiveName, wldFileInArchive, shortName, + s3dArchive); } else if (EqFileHelper.IsObjectsArchive(archiveName)) { @@ -82,7 +83,6 @@ public static void Extract(string path, string rootFolder, ILogger logger, Setti } MissingTextureFixer.Fix(archiveName); - } private static void ExtractArchiveZone(string path, string rootFolder, ILogger logger, Settings settings, @@ -104,7 +104,8 @@ private static void ExtractArchiveZone(string path, string rootFolder, ILogger l if (litWldLightsFileInArchive != null) { var lightsWldFile = - new WldFileLights(litWldLightsFileInArchive, shortName, WldType.Lights, logger, settings, wldFileLit); + new WldFileLights(litWldLightsFileInArchive, shortName, WldType.Lights, logger, settings, + wldFileLit); lightsWldFile.Initialize(rootFolder); } } @@ -120,6 +121,7 @@ private static void ExtractArchiveZone(string path, string rootFolder, ILogger l wldFile.RootFolder = rootFolder; wldFile.ShortName = shortName; } + InitializeWldAndWriteTextures(wldFile, rootFolder, rootFolder + shortName + "/Zone/Textures/", s3dArchive, settings, logger); @@ -200,7 +202,8 @@ private static void ExtractArchiveCharacters(string path, string rootFolder, ILo s3dArchive, settings, logger); } - private static void ExtractArchiveSky(string rootFolder, ILogger logger, Settings settings, PfsFile wldFileInArchive, + private static void ExtractArchiveSky(string rootFolder, ILogger logger, Settings settings, + PfsFile wldFileInArchive, string shortName, PfsArchive s3dArchive) { var wldFile = new WldFileZone(wldFileInArchive, shortName, WldType.Sky, logger, settings); @@ -213,10 +216,10 @@ private static void ExtractArchiveEquipment(string rootFolder, ILogger logger, S { var wldFile = new WldFileEquipment(wldFileInArchive, shortName, WldType.Equipment, logger, settings); var exportPath = rootFolder + - (settings.ExportEquipmentToSingleFolder && - settings.ModelExportFormat == ModelExportFormat.Intermediate - ? "equipment/Textures/" - : shortName + "/Textures/"); + (settings.ExportEquipmentToSingleFolder && + settings.ModelExportFormat == ModelExportFormat.Intermediate + ? "equipment/Textures/" + : shortName + "/Textures/"); InitializeWldAndWriteTextures(wldFile, rootFolder, exportPath, s3dArchive, settings, logger); } @@ -328,4 +331,4 @@ private static void ExtractSoundData(string shortName, string rootFolder, ILogge soundEntries.ExportSoundData(shortName, rootFolder); } } -} +} \ No newline at end of file diff --git a/LanternExtractor/EQ/Wld/WldFile.cs b/LanternExtractor/EQ/Wld/WldFile.cs index 98394b2..b61a2e1 100644 --- a/LanternExtractor/EQ/Wld/WldFile.cs +++ b/LanternExtractor/EQ/Wld/WldFile.cs @@ -23,11 +23,6 @@ public abstract class WldFile public WldType WldType => _wldType; - /// - /// The link between fragment types and fragment classes - /// - private Dictionary> _fragmentBuilder; - /// /// A link of indices to fragments /// @@ -159,8 +154,6 @@ public virtual bool Initialize(string rootFolder, bool exportData = true) ParseStringHash(WldStringDecoder.DecodeString(stringHash)); - long readPosition = 0; - for (int i = 0; i < fragmentCount; ++i) { uint fragSize = reader.ReadUInt32(); diff --git a/LanternExtractor/EqFileHelper.cs b/LanternExtractor/EqFileHelper.cs index 5819db8..4519262 100644 --- a/LanternExtractor/EqFileHelper.cs +++ b/LanternExtractor/EqFileHelper.cs @@ -6,73 +6,70 @@ namespace LanternExtractor { public static class EqFileHelper { - public static bool IsEquipmentArchive(string archiveName) + public static List GetValidEqFilePaths(string directory, string archiveName) { - return archiveName.StartsWith("gequip"); - } + archiveName = archiveName.ToLower(); + var eqFiles = Directory.GetFiles(directory, "*.*", SearchOption.AllDirectories); + List validFiles; - public static bool IsCharacterArchive(string archiveName) - { - return archiveName.Contains("_chr") || archiveName.StartsWith("chequip") || archiveName.Contains("_amr"); - } + switch (archiveName) + { + case "all": + validFiles = GetAllValidFiles(eqFiles); + break; + case "zones": + validFiles = GetValidZoneFiles(eqFiles); + break; + case "characters": + validFiles = GetValidCharacterFiles(eqFiles); + break; + case "equipment": + validFiles = GetValidEquipmentFiles(eqFiles); + break; + case "sounds": + validFiles = GetValidSoundFiles(eqFiles); + break; + default: + { + validFiles = GetValidFiles(archiveName, directory); + break; + } + } - public static bool IsObjectsArchive(string archiveName) - { - return archiveName.Contains("_obj"); + return validFiles; } - public static bool IsSkyArchive(string archiveName) + private static List GetValidEquipmentFiles(string[] eqFiles) { - return archiveName == "sky"; + return eqFiles.Where(x => IsEquipmentArchive(Path.GetFileName(x))).ToList(); } - public static bool IsSoundArchive(string archiveName) + private static List GetAllValidFiles(string[] eqFiles) { - return archiveName.StartsWith("snd"); + return eqFiles.Where(x => IsValidArchive(Path.GetFileName(x))).ToList(); } - public static bool IsClientDataFile(string archiveName) + private static List GetValidZoneFiles(string[] eqFiles) { - return archiveName == "clientdata"; + return eqFiles.Where(x => IsZoneArchive(Path.GetFileName(x))).ToList(); } - - public static bool IsMusicFile(string filename) + + private static List GetValidCharacterFiles(string[] eqFiles) { - return filename.EndsWith(".xmi"); + return eqFiles.Where(x => IsCharacterArchive(Path.GetFileName(x))).ToList(); } - public static bool IsSpecialCaseExtraction(string archiveName) + private static List GetValidSoundFiles(string[] eqFiles) { - return archiveName == "clientdata" || archiveName == "music"; + return eqFiles.Where(x => IsSoundArchive(Path.GetFileName(x))).ToList(); } - public static List GetValidEqFilePaths(string directory, string archiveName) + private static List GetValidFiles(string archiveName, string directory) { - archiveName = archiveName.ToLower(); var validFiles = new List(); - var eqFiles = Directory.GetFiles(directory, "*.*", SearchOption.AllDirectories); - - if (archiveName == "all") - { - validFiles = eqFiles.Where(f => (f.EndsWith(".s3d") || f.EndsWith(".pfs")) && !f.Contains("chequip") && - !f.EndsWith("_lit.s3d")).ToList(); - } - if (archiveName == "characters") - { - validFiles = eqFiles.Where(IsCharacterArchive).ToList(); - } - else if (archiveName == "equipment") - { - validFiles = eqFiles.Where(IsEquipmentArchive).ToList(); - } - else if (archiveName == "sounds") + if (archiveName.EndsWith(".s3d") || archiveName.EndsWith(".pfs")) { - validFiles = eqFiles.Where(IsSoundArchive).ToList(); - } - else if (archiveName.EndsWith(".s3d") || archiveName.EndsWith(".pfs")) - { - string archivePath = directory + archiveName; - + string archivePath = Path.Combine(directory, archiveName); if (File.Exists(archivePath)) { validFiles.Add(archivePath); @@ -80,38 +77,36 @@ public static List GetValidEqFilePaths(string directory, string archiveN } else { - // If it's the shortname of a PFS file, we assume it's standalone - used for sound files - string archivePath = directory + archiveName + ".pfs"; + string archivePath = Path.Combine(directory, $"{archiveName}.pfs"); if (File.Exists(archivePath)) { validFiles.Add(archivePath); - return validFiles; } - // Try and find all associated files with the shortname - can theoretically be a non zone file - string mainArchivePath = directory + archiveName + ".s3d"; + // Try and find all associated files with the shortname - can theoretically be a non-zone file + string mainArchivePath = Path.Combine(directory, $"{archiveName}.s3d"); if (File.Exists(mainArchivePath)) { validFiles.Add(mainArchivePath); } // Some zones have additional object archives for things added past their initial release - // None of them contain fragments that are linked to other related archives. - string extensionObjectsArchivePath = directory + archiveName + "_2_obj.s3d"; + // No archives contain cross archive fragment references + string extensionObjectsArchivePath = Path.Combine(directory, $"{archiveName}_2_obj.s3d"); if (File.Exists(extensionObjectsArchivePath)) { validFiles.Add(extensionObjectsArchivePath); } - string objectsArchivePath = directory + archiveName + "_obj.s3d"; + string objectsArchivePath = Path.Combine(directory, $"{archiveName}_obj.s3d"); if (File.Exists(objectsArchivePath)) { validFiles.Add(objectsArchivePath); } - string charactersArchivePath = directory + archiveName + "_chr.s3d"; + string charactersArchivePath = Path.Combine(directory, $"{archiveName}_chr.s3d"); if (File.Exists(charactersArchivePath)) { validFiles.Add(charactersArchivePath); @@ -120,7 +115,7 @@ public static List GetValidEqFilePaths(string directory, string archiveN // Some zones have additional character archives for things added past their initial release // None of them contain fragments that are linked to other related archives. // "qeynos" must be excluded because both qeynos and qeynos2 are used as shortnames - string extensionCharactersArchivePath = directory + archiveName + "2_chr.s3d"; + string extensionCharactersArchivePath = Path.Combine(directory, $"{archiveName}2_chr.s3d"); if (File.Exists(extensionCharactersArchivePath) && archiveName != "qeynos") { validFiles.Add(extensionCharactersArchivePath); @@ -129,5 +124,79 @@ public static List GetValidEqFilePaths(string directory, string archiveN return validFiles; } + + private static bool IsValidArchive(string archiveName) + { + return archiveName.EndsWith(".s3d") || archiveName.EndsWith(".pfs") && !archiveName.Contains("chequip") && + !archiveName.EndsWith("_lit.s3d"); + } + + private static bool IsZoneArchive(string archiveName) + { + return IsValidArchive(archiveName) && !IsEquipmentArchive(archiveName) && !IsSkyArchive(archiveName) && + !IsBitmapArchive(archiveName) && !IsCharacterArchive(archiveName); + } + + public static bool IsEquipmentArchive(string archiveName) + { + return archiveName.StartsWith("gequip"); + } + + public static bool IsCharacterArchive(string archiveName) + { + return archiveName.Contains("_chr") || archiveName.StartsWith("chequip") || + archiveName.Contains("_amr"); + } + + public static bool IsObjectsArchive(string archiveName) + { + return archiveName.Contains("_obj"); + } + + public static bool IsSkyArchive(string archiveName) + { + return archiveName == "sky"; + } + + public static bool IsBitmapArchive(string archiveName) + { + return archiveName.StartsWith("bmpwad"); + } + + public static bool IsSoundArchive(string archiveName) + { + return archiveName.StartsWith("snd"); + } + + public static bool IsClientDataFile(string archiveName) + { + return archiveName == "clientdata"; + } + + public static bool IsMusicFile(string filename) + { + return filename.EndsWith(".xmi"); + } + + public static bool IsSpecialCaseExtraction(string archiveName) + { + return archiveName == "clientdata" || archiveName == "music"; + } + + public static bool IsUsedSoundArchive(string archiveName) + { + if (!IsSoundArchive(archiveName)) + { + return false; + } + + // Trilogy client does not use archives higher than snd9 + if (int.TryParse(archiveName.Substring(archiveName.Length - 2), out _)) + { + return false; + } + + return true; + } } -} +} \ No newline at end of file diff --git a/LanternExtractor/Settings.cs b/LanternExtractor/Settings.cs index e741c36..5aac280 100644 --- a/LanternExtractor/Settings.cs +++ b/LanternExtractor/Settings.cs @@ -45,20 +45,11 @@ public class Settings /// Sets the desired model export format /// public ModelExportFormat ModelExportFormat { get; private set; } - - /// - /// Sets the desired model export format - /// + public bool ExportCharactersToSingleFolder { get; private set; } - - /// - /// Sets the desired model export format - /// + public bool ExportEquipmentToSingleFolder { get; private set; } - - /// - /// Export all sound files to a single folder - /// + public bool ExportSoundsToSingleFolder { get; private set; } /// @@ -142,88 +133,88 @@ public void Initialize() return; } - if (parsedSettings.ContainsKey("EverQuestDirectory")) + if (parsedSettings.TryGetValue("EverQuestDirectory", out var setting)) { - EverQuestDirectory = parsedSettings["EverQuestDirectory"]; + EverQuestDirectory = setting; // Ensure the path ends with a / EverQuestDirectory = Path.GetFullPath(EverQuestDirectory + "/"); } - if (parsedSettings.ContainsKey("RawS3DExtract")) + if (parsedSettings.TryGetValue("RawS3DExtract", out var parsedSetting)) { - RawS3dExtract = Convert.ToBoolean(parsedSettings["RawS3DExtract"]); + RawS3dExtract = Convert.ToBoolean(parsedSetting); } - if (parsedSettings.ContainsKey("ExportZoneMeshGroups")) + if (parsedSettings.TryGetValue("ExportZoneMeshGroups", out var setting1)) { - ExportZoneMeshGroups = Convert.ToBoolean(parsedSettings["ExportZoneMeshGroups"]); + ExportZoneMeshGroups = Convert.ToBoolean(setting1); } - if (parsedSettings.ContainsKey("ExportHiddenGeometry")) + if (parsedSettings.TryGetValue("ExportHiddenGeometry", out var parsedSetting1)) { - ExportHiddenGeometry = Convert.ToBoolean(parsedSettings["ExportHiddenGeometry"]); + ExportHiddenGeometry = Convert.ToBoolean(parsedSetting1); } - if (parsedSettings.ContainsKey("ExportZoneWithObjects")) + if (parsedSettings.TryGetValue("ExportZoneWithObjects", out var setting2)) { - ExportZoneWithObjects = Convert.ToBoolean(parsedSettings["ExportZoneWithObjects"]); + ExportZoneWithObjects = Convert.ToBoolean(setting2); } - if (parsedSettings.ContainsKey("ModelExportFormat")) + if (parsedSettings.TryGetValue("ModelExportFormat", out var parsedSetting2)) { - var exportFormatSetting = (ModelExportFormat)Convert.ToInt32(parsedSettings["ModelExportFormat"]); + var exportFormatSetting = (ModelExportFormat)Convert.ToInt32(parsedSetting2); ModelExportFormat = exportFormatSetting; } - if (parsedSettings.ContainsKey("ExportCharacterToSingleFolder")) + if (parsedSettings.TryGetValue("ExportCharacterToSingleFolder", out var setting3)) { - ExportCharactersToSingleFolder = Convert.ToBoolean(parsedSettings["ExportCharacterToSingleFolder"]); + ExportCharactersToSingleFolder = Convert.ToBoolean(setting3); } - if (parsedSettings.ContainsKey("ExportEquipmentToSingleFolder")) + if (parsedSettings.TryGetValue("ExportEquipmentToSingleFolder", out var parsedSetting3)) { - ExportEquipmentToSingleFolder = Convert.ToBoolean(parsedSettings["ExportEquipmentToSingleFolder"]); + ExportEquipmentToSingleFolder = Convert.ToBoolean(parsedSetting3); } - - if (parsedSettings.ContainsKey("ExportSoundsToSingleFolder")) + + if (parsedSettings.TryGetValue("ExportSoundsToSingleFolder", out var setting4)) { - ExportSoundsToSingleFolder = Convert.ToBoolean(parsedSettings["ExportSoundsToSingleFolder"]); + ExportSoundsToSingleFolder = Convert.ToBoolean(setting4); } - if (parsedSettings.ContainsKey("ExportAllAnimationFrames")) + if (parsedSettings.TryGetValue("ExportAllAnimationFrames", out var parsedSetting4)) { - ExportAllAnimationFrames = Convert.ToBoolean(parsedSettings["ExportAllAnimationFrames"]); + ExportAllAnimationFrames = Convert.ToBoolean(parsedSetting4); } - if (parsedSettings.ContainsKey("ExportGltfVertexColors")) + if (parsedSettings.TryGetValue("ExportGltfVertexColors", out var setting5)) { - ExportGltfVertexColors = Convert.ToBoolean(parsedSettings["ExportGltfVertexColors"]); + ExportGltfVertexColors = Convert.ToBoolean(setting5); } - if (parsedSettings.ContainsKey("ExportGltfInGlbFormat")) + if (parsedSettings.TryGetValue("ExportGltfInGlbFormat", out var parsedSetting5)) { - ExportGltfInGlbFormat = Convert.ToBoolean(parsedSettings["ExportGltfInGlbFormat"]); + ExportGltfInGlbFormat = Convert.ToBoolean(parsedSetting5); } - if (parsedSettings.ContainsKey("ClientDataToCopy")) + if (parsedSettings.TryGetValue("ClientDataToCopy", out var setting6)) { - ClientDataToCopy = parsedSettings["ClientDataToCopy"]; + ClientDataToCopy = setting6; } - if (parsedSettings.ContainsKey("ClientDataToCopy")) + if (parsedSettings.TryGetValue("ClientDataToCopy", out var parsedSetting6)) { - ClientDataToCopy = parsedSettings["ClientDataToCopy"]; + ClientDataToCopy = parsedSetting6; } - if (parsedSettings.ContainsKey("CopyMusic")) + if (parsedSettings.TryGetValue("CopyMusic", out var setting7)) { - CopyMusic = Convert.ToBoolean(parsedSettings["CopyMusic"]); + CopyMusic = Convert.ToBoolean(setting7); } - if (parsedSettings.ContainsKey("LoggerVerbosity")) + if (parsedSettings.TryGetValue("LoggerVerbosity", out var parsedSetting7)) { - LoggerVerbosity = Convert.ToInt32(parsedSettings["LoggerVerbosity"]); + LoggerVerbosity = Convert.ToInt32(parsedSetting7); } } } diff --git a/LanternExtractor/settings.txt b/LanternExtractor/settings.txt index 6792de5..592ac32 100644 --- a/LanternExtractor/settings.txt +++ b/LanternExtractor/settings.txt @@ -6,8 +6,8 @@ EverQuestDirectory = C:/EverQuest # Extract the contents of the S3D archive as is? -# If true, this will export all contents as they exist in the archives to their original formats. -# If false, this will extract contents, process WLD files and convert images to .png +# If true, this will export all contents as they exist in the archives in their original formats. +# If false, this will extract contents, process WLD files and convert images to PNG. RawS3DExtract = false # Sets the desired export format for models @@ -37,7 +37,7 @@ ExportCharacterToSingleFolder = true ExportEquipmentToSingleFolder = true # If true, will export all sounds to the 'sounds' folder -# Note, some sounds with conflicting names will be overwritten +# Some sounds with conflicting names will be overwritten ExportSoundsToSingleFolder = true # If true and using OBJ export format, this will dump an OBJ for each animation frame. From d02e40f2309547974aca942622a8c28f31d552a0 Mon Sep 17 00:00:00 2001 From: Nick Gal Date: Sun, 3 Sep 2023 10:56:20 -0700 Subject: [PATCH 16/17] Remove trailing spaces in gequip5 it10503 --- LanternExtractor/EQ/Wld/Helpers/FragmentNameCleaner.cs | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/LanternExtractor/EQ/Wld/Helpers/FragmentNameCleaner.cs b/LanternExtractor/EQ/Wld/Helpers/FragmentNameCleaner.cs index 8df4857..ac2cf36 100644 --- a/LanternExtractor/EQ/Wld/Helpers/FragmentNameCleaner.cs +++ b/LanternExtractor/EQ/Wld/Helpers/FragmentNameCleaner.cs @@ -28,13 +28,13 @@ public static string CleanName(WldFragment fragment, bool toLower = true) { cleanedName = cleanedName.Replace(_prefixes[fragment.GetType()], string.Empty); } - + if(toLower) { cleanedName = cleanedName.ToLower(); } - - return cleanedName; + + return cleanedName.Trim(); } } -} \ No newline at end of file +} From e8e8c7ab8d851cc0abe79accb18ebc3284b589ae Mon Sep 17 00:00:00 2001 From: Nick Gal Date: Sat, 16 Sep 2023 17:35:19 -0700 Subject: [PATCH 17/17] Fix textures in new wld zones --- LanternExtractor/EQ/Wld/Fragments/Mesh.cs | 22 +++++++++++----------- 1 file changed, 11 insertions(+), 11 deletions(-) diff --git a/LanternExtractor/EQ/Wld/Fragments/Mesh.cs b/LanternExtractor/EQ/Wld/Fragments/Mesh.cs index f8d7494..669ba5b 100644 --- a/LanternExtractor/EQ/Wld/Fragments/Mesh.cs +++ b/LanternExtractor/EQ/Wld/Fragments/Mesh.cs @@ -45,7 +45,7 @@ public class Mesh : WldFragment /// The vertices of the mesh /// public List Vertices { get; set; } - + /// /// The normals of the mesh /// @@ -55,7 +55,7 @@ public class Mesh : WldFragment /// The polygon indices of the mesh /// public List Indices { get; private set; } - + public List Colors { get; set; } /// @@ -123,9 +123,9 @@ public override void Initialize(int index, int size, byte[] data, // Seems to be related to lighting models? (torches, etc.) if (unknownDword1 != 0 || unknownDword2 != 0 || unknownDword3 != 0) { - + } - + MaxDistance = Reader.ReadSingle(); MinPosition = new vec3(Reader.ReadSingle(), Reader.ReadSingle(), Reader.ReadSingle()); MaxPosition = new vec3(Reader.ReadSingle(), Reader.ReadSingle(), Reader.ReadSingle()); @@ -145,7 +145,7 @@ public override void Initialize(int index, int size, byte[] data, short vertexTextureCount = Reader.ReadInt16(); short size9 = Reader.ReadInt16(); float scale = 1.0f / (1 << Reader.ReadInt16()); - + for (int i = 0; i < vertexCount; ++i) { Vertices.Add(new vec3(Reader.ReadInt16() * scale, Reader.ReadInt16() * scale, @@ -156,7 +156,7 @@ public override void Initialize(int index, int size, byte[] data, { if (isNewWldFormat) { - TextureUvCoordinates.Add(new vec2(Reader.ReadInt32() / 256.0f, Reader.ReadInt32() / 256.0f)); + TextureUvCoordinates.Add(new vec2(Reader.ReadSingle(), Reader.ReadSingle())); } else { @@ -179,7 +179,7 @@ public override void Initialize(int index, int size, byte[] data, int g = colorBytes[1]; int r = colorBytes[2]; int a = colorBytes[3]; - + Colors.Add(new Color( r, g, b, a)); } @@ -202,7 +202,7 @@ public override void Initialize(int index, int size, byte[] data, Vertex3 = Reader.ReadInt16(), }); } - + MobPieces = new Dictionary(); int mobStart = 0; @@ -224,7 +224,7 @@ public override void Initialize(int index, int size, byte[] data, MaterialGroups = new List(); StartTextureIndex = Int32.MaxValue; - + for (int i = 0; i < polygonTextureCount; ++i) { var group = new RenderGroup(); @@ -247,7 +247,7 @@ public override void Initialize(int index, int size, byte[] data, { Reader.BaseStream.Position += 12; } - + // In some rare cases, the number of uvs does not match the number of vertices if (Vertices.Count != TextureUvCoordinates.Count) { @@ -291,4 +291,4 @@ public void ClearCollision() ExportSeparateCollision = true; } } -} \ No newline at end of file +}