diff --git a/.gitmodules b/.gitmodules new file mode 100644 index 0000000..8c944d8 --- /dev/null +++ b/.gitmodules @@ -0,0 +1,3 @@ +[submodule "Fantome.Libraries.League"] + path = Fantome.Libraries.League + url = https://github.com/LoL-Fantome/Fantome.Libraries.League diff --git a/Fantome.Libraries.League b/Fantome.Libraries.League new file mode 160000 index 0000000..1e32695 --- /dev/null +++ b/Fantome.Libraries.League @@ -0,0 +1 @@ +Subproject commit 1e32695ac269d7f1eb387d37032ba56d937f5d04 diff --git a/Obsidian.sln b/Obsidian.sln index 16c18f3..d6f6f2d 100644 --- a/Obsidian.sln +++ b/Obsidian.sln @@ -1,7 +1,7 @@  Microsoft Visual Studio Solution File, Format Version 12.00 # Visual Studio 15 -VisualStudioVersion = 15.0.26730.15 +VisualStudioVersion = 15.0.27004.2005 MinimumVisualStudioVersion = 10.0.40219.1 Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Obsidian", "Obsidian\Obsidian.csproj", "{7ABEFBE2-A68B-4D8E-A24E-7867C4AD1364}" EndProject diff --git a/Obsidian/App.config b/Obsidian/App.config index 016d28f..8d23437 100644 --- a/Obsidian/App.config +++ b/Obsidian/App.config @@ -1,6 +1,6 @@ - + - + - \ No newline at end of file + diff --git a/Obsidian/MainWindow.xaml b/Obsidian/MainWindow.xaml index b3928dd..9a1feff 100644 --- a/Obsidian/MainWindow.xaml +++ b/Obsidian/MainWindow.xaml @@ -33,7 +33,7 @@ - + diff --git a/Obsidian/MainWindow.xaml.cs b/Obsidian/MainWindow.xaml.cs index 66629f8..2f65886 100644 --- a/Obsidian/MainWindow.xaml.cs +++ b/Obsidian/MainWindow.xaml.cs @@ -1,19 +1,23 @@ -using Fantome.Libraries.League.Helpers.Cryptography; -using Fantome.Libraries.League.Helpers.Utilities; -using Fantome.Libraries.League.IO.BIN; +using Fantome.Libraries.League.Helpers.Utilities; using Fantome.Libraries.League.IO.WAD; +using log4net; +using log4net.Core; using Microsoft.Win32; +using Obsidian.Utils; using Obsidian.Windows; using System; using System.Collections.Generic; +using System.ComponentModel; using System.IO; using System.Linq; -using System.Text; using System.Threading.Tasks; using System.Windows; using System.Windows.Controls; using System.Windows.Data; using System.Windows.Input; +using Newtonsoft.Json.Linq; +using Fantome.Libraries.League.Helpers.Cryptography; +using System.Text; namespace Obsidian { @@ -22,13 +26,36 @@ namespace Obsidian /// public partial class MainWindow : Window { + public Dictionary Config { get; } + private static readonly ILog Logger = LogManager.GetLogger("MainWindow"); public WADFile Wad { get; set; } public WADEntry CurrentlySelectedEntry { get; set; } public static Dictionary StringDictionary { get; set; } = new Dictionary(); public MainWindow() { + AppDomain.CurrentDomain.UnhandledException += CurrentDomain_UnhandledException; + + if (File.Exists("config.json")) + { + this.Config = ConfigUtilities.ReadConfig(); + } + else + { + this.Config = ConfigUtilities.DefaultConfig; + ConfigUtilities.WriteDefaultConfig(); + } + + Logging.InitializeLogger((string)this.Config["LoggingPattern"], this.Config["LogLevel"] as Level); + Logger.Info("Initialized Logger"); + InitializeComponent(); + Logger.Info("Initialized Window"); + } + + private void CurrentDomain_UnhandledException(object sender, UnhandledExceptionEventArgs e) + { + Logging.LogFatal(Logger, "An unhandled exception was thrown, the program will now terminate", (Exception)e.ExceptionObject); } private void image_MouseDown(object sender, MouseButtonEventArgs e) @@ -39,16 +66,39 @@ private void image_MouseDown(object sender, MouseButtonEventArgs e) private void buttonOpenWadFile_Click(object sender, RoutedEventArgs e) { - OpenFileDialog dialog = new OpenFileDialog(); - dialog.Title = "Select the WAD File you want to open"; - dialog.Multiselect = false; - dialog.Filter = "WAD Files (*.wad;*.wad.client)|*.wad;*.wad.client"; + OpenFileDialog dialog = new OpenFileDialog + { + Title = "Select the WAD File you want to open", + Multiselect = false, + Filter = "WAD Files (*.wad;*.wad.client)|*.wad;*.wad.client" + }; if (dialog.ShowDialog() == true) { - this.Wad = new WADFile(dialog.FileName); + try + { + this.Wad?.Dispose(); + this.Wad = new WADFile(dialog.FileName); + } + catch (Exception excp) + { + Logging.LogException(Logger, "Failed to load WAD File: " + dialog.FileName, excp); + return; + } + StringDictionary = new Dictionary(); - GenerateWADStrings(); + + if ((bool)this.Config["GenerateWadDictionary"]) + { + try + { + WADHashGenerator.GenerateWADStrings(Logger, this.Wad, StringDictionary); + } + catch (Exception excp) + { + Logging.LogException(Logger, "Failed to Generate WAD String Dictionary", excp); + } + } this.buttonSaveWadFile.IsEnabled = true; this.buttonImportHashtable.IsEnabled = true; @@ -57,128 +107,81 @@ private void buttonOpenWadFile_Click(object sender, RoutedEventArgs e) this.butonAddFileRedirection.IsEnabled = true; this.CurrentlySelectedEntry = null; this.datagridWadEntries.ItemsSource = this.Wad.Entries; - } - } - private void GenerateWADStrings() - { - foreach (WADEntry wadEntry in this.Wad.Entries.Where(x => x.Type == EntryType.Compressed)) - { - byte[] entryData = wadEntry.GetContent(true); - if (Utilities.GetLeagueFileExtensionType(entryData) == LeagueFileType.BIN) - { - List wadEntryStrings = new List(); - BINFile bin = new BINFile(new MemoryStream(entryData)); - foreach (BINFileEntry binEntry in bin.Entries) - { - foreach (BINFileValue binValue in binEntry.Values.Where(x => x.Type == BINFileValueType.String || x.Value.GetType() == typeof(BINFileValueList))) - { - if (binValue.Type == BINFileValueType.String) - { - wadEntryStrings.Add(binValue.Value as string); - } - else if ( - binValue.Type == BINFileValueType.DoubleTypeList || - binValue.Type == BINFileValueType.LargeStaticTypeList || - binValue.Type == BINFileValueType.List || - binValue.Type == BINFileValueType.List2 || - binValue.Type == BINFileValueType.SmallStaticTypeList) - { - wadEntryStrings.AddRange(GetValueStrings(binValue)); - } - } - } - - using (XXHash64 xxHash = XXHash64.Create()) - { - wadEntryStrings.ForEach(x => - { - if (x != "") - { - string loweredName = x.ToLower(); - ulong hash = BitConverter.ToUInt64(xxHash.ComputeHash(Encoding.ASCII.GetBytes(loweredName)), 0); - if (!StringDictionary.ContainsKey(hash)) - { - StringDictionary.Add(hash, x); - } - } - }); - } - } + Logger.Info("Opened WAD File: " + dialog.FileName); } } - public IEnumerable GetValueStrings(BINFileValue value) + private void buttonSaveWadFile_Click(object sender, RoutedEventArgs e) { - List strings = new List(); - if (value.Type == BINFileValueType.String) + SaveFileDialog dialog = new SaveFileDialog { - strings.Add(value.Value as string); - } - else + Title = "Select the path to save your WAD File", + Filter = "WAD File (*.wad)|*.wad|WAD Client file (*.wad.client)|*.wad.client", + AddExtension = true + }; + + if (dialog.ShowDialog() == true) { - BINFileValueList valueList = value.Value as BINFileValueList; - foreach (BINFileValue binValue in valueList.Entries) + try { - if (binValue.Type == BINFileValueType.String) - { - strings.Add(binValue.Value as string); - } - else if ( - binValue.Type == BINFileValueType.DoubleTypeList || - binValue.Type == BINFileValueType.LargeStaticTypeList || - binValue.Type == BINFileValueType.List || - binValue.Type == BINFileValueType.List2 || - binValue.Type == BINFileValueType.SmallStaticTypeList) - { - foreach (BINFileValue binValue2 in (binValue.Value as BINFileValueList).Entries.Where(x => x.Type == BINFileValueType.String || x.Value.GetType() == typeof(BINFileValueList))) - { - strings.AddRange(GetValueStrings(binValue2)); - } - } + this.Wad.Write(dialog.FileName, (byte)(long)this.Config["WadSaveMajorVersion"], (byte)(long)this.Config["WadSaveMinorVersion"]); + } + catch (Exception excp) + { + Logging.LogException(Logger, "Could not write a WAD File to: " + dialog.FileName, excp); + return; } - } - - return strings.AsEnumerable(); - } - - private void buttonSaveWadFile_Click(object sender, RoutedEventArgs e) - { - SaveFileDialog dialog = new SaveFileDialog(); - dialog.Title = "Select the path to save your WAD File"; - dialog.Filter = "WAD File (*.wad)|*.wad|WAD Client file (*.wad.client)|*.wad.client"; - dialog.AddExtension = true; - if (dialog.ShowDialog() == true) - { - string filePath = dialog.FileName; - this.Wad.Write(filePath); MessageBox.Show("Writing Succesful!", "", MessageBoxButton.OK, MessageBoxImage.Information); + Logger.Info("Successfully written a WAD File to: " + dialog.FileName); } } private void buttonImportHashtable_Click(object sender, RoutedEventArgs e) { - OpenFileDialog dialog = new OpenFileDialog(); - dialog.Title = "Select the Hashtable files you want to load"; - dialog.Multiselect = true; - dialog.Filter = "Hashtable Files (*.hashtable)|*.hashtable"; + OpenFileDialog dialog = new OpenFileDialog + { + Title = "Select the Hashtable files you want to load", + Multiselect = true, + Filter = "Hashtable Files (*.hashtable)|*.hashtable" + }; if (dialog.ShowDialog() == true) { - foreach (string fileName in dialog.FileNames) + try { - foreach (string line in File.ReadAllLines(fileName)) + foreach (string fileName in dialog.FileNames) { - ulong hash = 0; - string[] lineSplit = line.Split(new char[] { ' ' }, StringSplitOptions.RemoveEmptyEntries); - - if (ulong.TryParse(lineSplit[0], out hash) && !StringDictionary.ContainsKey(hash)) + foreach (string line in File.ReadAllLines(fileName)) { - StringDictionary.Add(ulong.Parse(lineSplit[0]), lineSplit[1]); + string[] lineSplit = line.Split(new char[] { ' ' }, StringSplitOptions.RemoveEmptyEntries); + + if (ulong.TryParse(lineSplit[0], out ulong hash) && !StringDictionary.ContainsKey(hash)) + { + StringDictionary.Add(ulong.Parse(lineSplit[0]), lineSplit[1]); + } + else + { + using (XXHash64 xxHash = XXHash64.Create()) + { + ulong key = BitConverter.ToUInt64(xxHash.ComputeHash(Encoding.ASCII.GetBytes(lineSplit[0].ToLower())), 0); + if (!StringDictionary.ContainsKey(key)) + { + StringDictionary.Add(key, lineSplit[0].ToLower()); + } + } + } } + + Logger.Info("Imported Hashtable from: " + fileName); } } + catch (Exception excp) + { + Logging.LogException(Logger, "Failed to Import Hashtable", excp); + return; + } CollectionViewSource.GetDefaultView(this.datagridWadEntries.ItemsSource).Refresh(); } @@ -186,12 +189,12 @@ private void buttonImportHashtable_Click(object sender, RoutedEventArgs e) private void datagridWadEntries_SelectedCellsChanged(object sender, SelectedCellsChangedEventArgs e) { - if (this.datagridWadEntries.SelectedItem != null && this.datagridWadEntries.SelectedItem is WADEntry) + if (this.datagridWadEntries.SelectedItem is WADEntry) { this.buttonRemoveEntry.IsEnabled = true; - if ((this.datagridWadEntries.SelectedItem as WADEntry).Type != EntryType.FileRedirection) + if (((WADEntry)this.datagridWadEntries.SelectedItem).Type != EntryType.FileRedirection) { - this.CurrentlySelectedEntry = this.datagridWadEntries.SelectedItem as WADEntry; + this.CurrentlySelectedEntry = (WADEntry)this.datagridWadEntries.SelectedItem; this.buttonModifyData.IsEnabled = true; } else @@ -199,30 +202,14 @@ private void datagridWadEntries_SelectedCellsChanged(object sender, SelectedCell this.CurrentlySelectedEntry = null; this.buttonModifyData.IsEnabled = false; } - - if (StringDictionary.ContainsKey((this.datagridWadEntries.SelectedItem as WADEntry).XXHash)) - { - this.textBlockSelectedEntryName.Text = StringDictionary[(this.datagridWadEntries.SelectedItem as WADEntry).XXHash]; - } - else - { - this.textBlockSelectedEntryName.Text = ""; - } } - if (this.datagridWadEntries.SelectedItems != null && this.datagridWadEntries.SelectedItems.Cast().ToList().Exists(x => x.Type != EntryType.FileRedirection)) - { - this.buttonExtract.IsEnabled = true; - } - else - { - this.buttonExtract.IsEnabled = false; - } + this.buttonExtract.IsEnabled = this.datagridWadEntries.SelectedItems.Cast().ToList().Exists(x => x.Type != EntryType.FileRedirection); } private void datagridWadEntries_BeginningEdit(object sender, DataGridBeginningEditEventArgs e) { - if ((e.Row.DataContext as WADEntry).Type != EntryType.FileRedirection) + if (((WADEntry)e.Row.DataContext).Type != EntryType.FileRedirection) { e.Cancel = true; } @@ -254,14 +241,32 @@ private void buttonRemoveEntry_Click(object sender, RoutedEventArgs e) private void buttonModifyData_Click(object sender, RoutedEventArgs e) { - OpenFileDialog dialog = new OpenFileDialog(); - dialog.Multiselect = false; - dialog.Title = "Select the File by which you want to replace the current one"; + OpenFileDialog dialog = new OpenFileDialog + { + Multiselect = false, + Title = "Select the File by which you want to replace the current one" + }; if (dialog.ShowDialog() == true) { - this.CurrentlySelectedEntry.EditData(File.ReadAllBytes(dialog.FileName)); - CollectionViewSource.GetDefaultView(this.datagridWadEntries.ItemsSource).Refresh(); + try + { + this.CurrentlySelectedEntry.EditData(File.ReadAllBytes(dialog.FileName)); + CollectionViewSource.GetDefaultView(this.datagridWadEntries.ItemsSource).Refresh(); + } + catch (Exception excp) + { + string entryName = BitConverter.ToString(BitConverter.GetBytes(this.CurrentlySelectedEntry.XXHash)).Replace("-", ""); + if (StringDictionary.ContainsKey(this.CurrentlySelectedEntry.XXHash)) + { + entryName = StringDictionary[this.CurrentlySelectedEntry.XXHash]; + } + + Logging.LogException(Logger, "Failed to modify the data of Entry: " + entryName, excp); + return; + } + + Logger.Info("Modified Data of Entry: " + BitConverter.ToString(BitConverter.GetBytes(this.CurrentlySelectedEntry.XXHash)).Replace("-", "")); MessageBox.Show("Entry Modified Succesfully!", "", MessageBoxButton.OK, MessageBoxImage.Information); } } @@ -272,15 +277,46 @@ private void buttonExtract_Click(object sender, RoutedEventArgs e) if (dialog.ShowDialog() == System.Windows.Forms.DialogResult.OK) { + try + { + ExtractWAD(dialog.SelectedPath, this.datagridWadEntries.SelectedItems.Cast().Where(x => x.Type != EntryType.FileRedirection).ToList()); + } + catch (Exception excp) + { + Logging.LogException(Logger, "Extraction of the currently opened WAD File failed", excp); + } + } + } - foreach (WADEntry entry in this.datagridWadEntries.SelectedItems.Cast().Where(x => x.Type != EntryType.FileRedirection)) + private void ExtractWAD(string selectedPath, List selectedEntries) + { + this.progressBarWadExtraction.Visibility = Visibility.Visible; + this.progressBarWadExtraction.Maximum = selectedEntries.Count; + this.IsEnabled = false; + + BackgroundWorker wadExtractor = new BackgroundWorker + { + WorkerReportsProgress = true + }; + + wadExtractor.ProgressChanged += (sender, args) => + { + this.progressBarWadExtraction.Value = args.ProgressPercentage; + }; + + wadExtractor.DoWork += (sender, e) => + { + Dictionary entries = new Dictionary(); + double progress = 0; + + foreach (WADEntry entry in selectedEntries) { byte[] entryData = entry.GetContent(true); - string entryName = ""; + string entryName; if (StringDictionary.ContainsKey(entry.XXHash)) { entryName = StringDictionary[entry.XXHash]; - Directory.CreateDirectory(string.Format("{0}//{1}", dialog.SelectedPath, System.IO.Path.GetDirectoryName(entryName))); + Directory.CreateDirectory(string.Format("{0}//{1}", selectedPath, Path.GetDirectoryName(entryName))); } else { @@ -288,28 +324,68 @@ private void buttonExtract_Click(object sender, RoutedEventArgs e) entryName += "." + Utilities.GetEntryExtension(Utilities.GetLeagueFileExtensionType(entryData)); } - File.WriteAllBytes(string.Format("{0}//{1}", dialog.SelectedPath, entryName), entryData); + entries.Add(entryName, entryData); + progress += 0.5; + wadExtractor.ReportProgress((int)progress); } + if ((bool)this.Config["ParallelExtraction"]) + { + Parallel.ForEach(entries, (entry) => + { + File.WriteAllBytes(string.Format("{0}//{1}", selectedPath, entry.Key), entry.Value); + progress += 0.5; + wadExtractor.ReportProgress((int)progress); + }); + } + else + { + foreach (KeyValuePair entry in entries) + { + File.WriteAllBytes(string.Format("{0}//{1}", selectedPath, entry.Key), entry.Value); + progress += 0.5; + wadExtractor.ReportProgress((int)progress); + } + } + }; + + wadExtractor.RunWorkerCompleted += (sender, args) => + { + this.IsEnabled = true; + this.progressBarWadExtraction.Value = 0; + this.progressBarWadExtraction.Visibility = Visibility.Hidden; MessageBox.Show("Extraction Succesfull!", "", MessageBoxButton.OK, MessageBoxImage.Information); - } + Logger.Info("WAD Extraction Successfull!"); + }; + + wadExtractor.RunWorkerAsync(); } private void buttonExtractHashtable_Click(object sender, RoutedEventArgs e) { - SaveFileDialog dialog = new SaveFileDialog(); - dialog.Title = "Select the path to save your Hashtable File"; - dialog.Filter = "Hashtable File (*.hashtable)|*.hashtable"; - dialog.AddExtension = true; + SaveFileDialog dialog = new SaveFileDialog + { + Title = "Select the path to save your Hashtable File", + Filter = "Hashtable File (*.hashtable)|*.hashtable", + AddExtension = true + }; if (dialog.ShowDialog() == true) { - List lines = new List(); - foreach (KeyValuePair pair in StringDictionary) + try + { + List lines = new List(); + foreach (KeyValuePair pair in StringDictionary) + { + lines.Add(pair.Key.ToString() + " " + pair.Value); + } + File.WriteAllLines(dialog.FileName, lines.ToArray()); + } + catch (Exception exception) { - lines.Add(pair.Key.ToString() + " " + pair.Value); + Logging.LogException(Logger, "Failed to Extract the current Hashtable", exception); + return; } - File.WriteAllLines(dialog.FileName, lines.ToArray()); MessageBox.Show("Writing Succesful!", "", MessageBoxButton.OK, MessageBoxImage.Information); } diff --git a/Obsidian/Obsidian.csproj b/Obsidian/Obsidian.csproj index 121121a..a5060c4 100644 --- a/Obsidian/Obsidian.csproj +++ b/Obsidian/Obsidian.csproj @@ -8,11 +8,12 @@ WinExe Obsidian Obsidian - v4.7 + v4.6.2 512 {60dc8134-eba5-43b8-bcc9-bb4bc16c2548};{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC} 4 true + AnyCPU @@ -33,7 +34,13 @@ prompt 4 + + icon.ico + + + ..\packages\log4net.2.0.8\lib\net45-full\log4net.dll + ..\packages\Newtonsoft.Json.10.0.3\lib\net45\Newtonsoft.Json.dll @@ -58,6 +65,9 @@ MSBuild:Compile Designer + + + @@ -125,8 +135,8 @@ - - {80F5DF93-19A2-4731-8CD1-982F165D7361} + + {80f5df93-19a2-4731-8cd1-982f165d7361} Fantome.Libraries.League diff --git a/Obsidian/Properties/AssemblyInfo.cs b/Obsidian/Properties/AssemblyInfo.cs index 02ae738..dc68c04 100644 --- a/Obsidian/Properties/AssemblyInfo.cs +++ b/Obsidian/Properties/AssemblyInfo.cs @@ -51,5 +51,5 @@ // You can specify all the values or you can default the Build and Revision Numbers // by using the '*' as shown below: // [assembly: AssemblyVersion("1.0.*")] -[assembly: AssemblyVersion("1.3.0.1")] -[assembly: AssemblyFileVersion("1.0.0.0")] +[assembly: AssemblyVersion("1.4.0.0")] +[assembly: AssemblyFileVersion("1.4.0.0")] diff --git a/Obsidian/Properties/Resources.Designer.cs b/Obsidian/Properties/Resources.Designer.cs index 5b99403..669fed8 100644 --- a/Obsidian/Properties/Resources.Designer.cs +++ b/Obsidian/Properties/Resources.Designer.cs @@ -8,10 +8,10 @@ // //------------------------------------------------------------------------------ -namespace Obsidian.Properties -{ - - +namespace Obsidian.Properties { + using System; + + /// /// A strongly-typed resource class, for looking up localized strings, etc. /// @@ -19,51 +19,43 @@ namespace Obsidian.Properties // class via a tool like ResGen or Visual Studio. // To add or remove a member, edit your .ResX file then rerun ResGen // with the /str option, or rebuild your VS project. - [global::System.CodeDom.Compiler.GeneratedCodeAttribute("System.Resources.Tools.StronglyTypedResourceBuilder", "4.0.0.0")] + [global::System.CodeDom.Compiler.GeneratedCodeAttribute("System.Resources.Tools.StronglyTypedResourceBuilder", "15.0.0.0")] [global::System.Diagnostics.DebuggerNonUserCodeAttribute()] [global::System.Runtime.CompilerServices.CompilerGeneratedAttribute()] - internal class Resources - { - + internal class Resources { + private static global::System.Resources.ResourceManager resourceMan; - + private static global::System.Globalization.CultureInfo resourceCulture; - + [global::System.Diagnostics.CodeAnalysis.SuppressMessageAttribute("Microsoft.Performance", "CA1811:AvoidUncalledPrivateCode")] - internal Resources() - { + internal Resources() { } - + /// /// Returns the cached ResourceManager instance used by this class. /// [global::System.ComponentModel.EditorBrowsableAttribute(global::System.ComponentModel.EditorBrowsableState.Advanced)] - internal static global::System.Resources.ResourceManager ResourceManager - { - get - { - if ((resourceMan == null)) - { + internal static global::System.Resources.ResourceManager ResourceManager { + get { + if (object.ReferenceEquals(resourceMan, null)) { global::System.Resources.ResourceManager temp = new global::System.Resources.ResourceManager("Obsidian.Properties.Resources", typeof(Resources).Assembly); resourceMan = temp; } return resourceMan; } } - + /// /// Overrides the current thread's CurrentUICulture property for all /// resource lookups using this strongly typed resource class. /// [global::System.ComponentModel.EditorBrowsableAttribute(global::System.ComponentModel.EditorBrowsableState.Advanced)] - internal static global::System.Globalization.CultureInfo Culture - { - get - { + internal static global::System.Globalization.CultureInfo Culture { + get { return resourceCulture; } - set - { + set { resourceCulture = value; } } diff --git a/Obsidian/Properties/Settings.Designer.cs b/Obsidian/Properties/Settings.Designer.cs index c07635c..04e4c17 100644 --- a/Obsidian/Properties/Settings.Designer.cs +++ b/Obsidian/Properties/Settings.Designer.cs @@ -8,21 +8,17 @@ // //------------------------------------------------------------------------------ -namespace Obsidian.Properties -{ - - +namespace Obsidian.Properties { + + [global::System.Runtime.CompilerServices.CompilerGeneratedAttribute()] - [global::System.CodeDom.Compiler.GeneratedCodeAttribute("Microsoft.VisualStudio.Editors.SettingsDesigner.SettingsSingleFileGenerator", "11.0.0.0")] - internal sealed partial class Settings : global::System.Configuration.ApplicationSettingsBase - { - + [global::System.CodeDom.Compiler.GeneratedCodeAttribute("Microsoft.VisualStudio.Editors.SettingsDesigner.SettingsSingleFileGenerator", "15.3.0.0")] + internal sealed partial class Settings : global::System.Configuration.ApplicationSettingsBase { + private static Settings defaultInstance = ((Settings)(global::System.Configuration.ApplicationSettingsBase.Synchronized(new Settings()))); - - public static Settings Default - { - get - { + + public static Settings Default { + get { return defaultInstance; } } diff --git a/Obsidian/Utils/ConfigUtilities.cs b/Obsidian/Utils/ConfigUtilities.cs new file mode 100644 index 0000000..479bcbb --- /dev/null +++ b/Obsidian/Utils/ConfigUtilities.cs @@ -0,0 +1,30 @@ +using log4net.Core; +using Newtonsoft.Json; +using System.Collections.Generic; +using System.IO; + +namespace Obsidian.Utils +{ + public static class ConfigUtilities + { + public static readonly Dictionary DefaultConfig = new Dictionary() + { + { "LoggingPattern", "[%utcdate{ABSOLUTE}] | [%-5level] | [%-20logger: (%-4line)] | %message%newline" }, + { "LogLevel", Level.Info }, + { "WadSaveMajorVersion", 2 }, + { "WadSaveMinorVersion", 0 }, + { "GenerateWadDictionary", true }, + { "ParallelExtraction", true } + }; + + public static Dictionary ReadConfig() + { + return JsonConvert.DeserializeObject>(File.ReadAllText("config.json")); + } + + public static void WriteDefaultConfig() + { + File.WriteAllText("config.json", JsonConvert.SerializeObject(DefaultConfig, Formatting.Indented)); + } + } +} diff --git a/Obsidian/Utils/Logging.cs b/Obsidian/Utils/Logging.cs new file mode 100644 index 0000000..98c868e --- /dev/null +++ b/Obsidian/Utils/Logging.cs @@ -0,0 +1,38 @@ +using log4net; +using log4net.Appender; +using log4net.Config; +using log4net.Core; +using log4net.Layout; +using System; +using System.Windows; + +namespace Obsidian.Utils +{ + public static class Logging + { + public static void InitializeLogger(string loggingPattern, Level logLevel) + { + FileAppender appender = new FileAppender + { + Layout = new PatternLayout(loggingPattern), + File = "ObsidianLog.txt", + AppendToFile = true, + Threshold = logLevel + }; + appender.ActivateOptions(); + BasicConfigurator.Configure(appender); + } + + public static void LogException(ILog logger, string message, Exception exception) + { + MessageBox.Show(string.Format("{0}\n{1}\n{2}", message, exception.Message, exception.StackTrace), "", MessageBoxButton.OK, MessageBoxImage.Error); + logger.Error(message, exception); + } + + public static void LogFatal(ILog logger, string message, Exception exception) + { + MessageBox.Show(string.Format("{0}\n{1}\n{2}", message, exception.Message, exception.StackTrace), "", MessageBoxButton.OK, MessageBoxImage.Error); + logger.Fatal(message, exception); + } + } +} diff --git a/Obsidian/Utils/WADHashGenerator.cs b/Obsidian/Utils/WADHashGenerator.cs new file mode 100644 index 0000000..fc6bf02 --- /dev/null +++ b/Obsidian/Utils/WADHashGenerator.cs @@ -0,0 +1,153 @@ +using Fantome.Libraries.League.Helpers.Cryptography; +using Fantome.Libraries.League.Helpers.Utilities; +using Fantome.Libraries.League.IO.BIN; +using Fantome.Libraries.League.IO.WAD; +using log4net; +using System; +using System.Collections.Generic; +using System.IO; +using System.Linq; +using System.Text; + +namespace Obsidian.Utils +{ + public static class WADHashGenerator + { + public static void GenerateWADStrings(ILog logger, WADFile wad, Dictionary stringDictionary) + { + foreach (WADEntry wadEntry in wad.Entries.Where(x => x.Type == EntryType.Compressed)) + { + byte[] entryData = wadEntry.GetContent(true); + if (Utilities.GetLeagueFileExtensionType(entryData) == LeagueFileType.BIN) + { + List wadEntryStrings = new List(); + BINFile bin = null; + + try + { + bin = new BINFile(new MemoryStream(entryData)); + + } + catch + { + logger.Warn("Loading Dictionary Strings from WAD Entry: " + + BitConverter.ToString(BitConverter.GetBytes(wadEntry.XXHash)).Replace("-", "") + + " failed"); + continue; + } + + wadEntryStrings.AddRange(GetLinkedFileStrings(bin.LinkedFiles).Distinct()); + + foreach (BINFileEntry binEntry in bin.Entries) + { + foreach (BINFileValue binValue in binEntry.Values.Where(x => + x.Type == BINFileValueType.String || x.Value.GetType() == typeof(BINFileValueList))) + { + switch (binValue.Type) + { + case BINFileValueType.String: + wadEntryStrings.Add(binValue.Value as string); + break; + case BINFileValueType.PairList: + case BINFileValueType.LargeStaticTypeList: + case BINFileValueType.List: + case BINFileValueType.List2: + case BINFileValueType.SmallStaticTypeList: + wadEntryStrings.AddRange(GetValueStrings(binValue)); + break; + } + } + } + + using (XXHash64 xxHash = XXHash64.Create()) + { + wadEntryStrings.ForEach(x => + { + if (x != "") + { + string loweredName = x.ToLower(); + ulong hash = BitConverter.ToUInt64(xxHash.ComputeHash(Encoding.ASCII.GetBytes(loweredName)), 0); + if (!stringDictionary.ContainsKey(hash)) + { + stringDictionary.Add(hash, x); + } + } + }); + } + } + } + } + + private static IEnumerable GetLinkedFileStrings(List linkedFiles) + { + List characterNames = new List(); + + foreach (string linkedFile in linkedFiles) + { + if (linkedFile.StartsWith("DATA/Characters")) + { + yield return linkedFile; + string withoutStart = linkedFile.Remove(0, 16); + characterNames.Add(withoutStart.Substring(0, withoutStart.IndexOf('/'))); + continue; + } + + string[] strings = linkedFile.Split(new char[] {'_'}, StringSplitOptions.RemoveEmptyEntries); + characterNames.Add(strings[0].Remove(0, 5)); + + string extension = strings[strings.Length - 1].Substring(strings[strings.Length - 1].IndexOf(".", StringComparison.Ordinal)); + strings[strings.Length - 1] = strings[strings.Length - 1].Replace(extension, ""); + + characterNames = characterNames.Distinct().ToList(); + + for (int i = 0; i < characterNames.Count; i++) + { + for (int j = 1; j < strings.Length; j += 2) + { + yield return string.Format("DATA/Characters/{0}/{1}/{2}{3}", characterNames[i], strings[j], strings[j + 1], extension); + } + + yield return string.Format("DATA/Characters/{0}/Skins/Root{1}", characterNames[i], extension); + } + } + } + + private static IEnumerable GetValueStrings(BINFileValue value) + { + List strings = new List(); + if (value.Type == BINFileValueType.String) + { + strings.Add(value.Value as string); + } + else + { + if (value.Value is BINFileValueList valueList) + { + foreach (BINFileValue binValue in valueList.Entries.Where(x => x is BINFileValue)) + { + switch (binValue.Type) + { + case BINFileValueType.String: + strings.Add(binValue.Value as string); + break; + case BINFileValueType.PairList: + case BINFileValueType.LargeStaticTypeList: + case BINFileValueType.List: + case BINFileValueType.List2: + case BINFileValueType.SmallStaticTypeList: + foreach (BINFileValue binValue2 in ((BINFileValueList)binValue.Value).Entries.Where(x => + x is BINFileValue && + ((BINFileValue)x).Type == BINFileValueType.String)) + { + strings.AddRange(GetValueStrings(binValue2)); + } + break; + } + } + } + } + + return strings.AsEnumerable(); + } + } +} diff --git a/Obsidian/ValueConverters/WadNameConverter.cs b/Obsidian/ValueConverters/WadNameConverter.cs index 4789217..b4db573 100644 --- a/Obsidian/ValueConverters/WadNameConverter.cs +++ b/Obsidian/ValueConverters/WadNameConverter.cs @@ -25,9 +25,6 @@ public object Convert(object value, Type targetType, object parameter, CultureIn return finalName; } - public object ConvertBack(object value, Type targetType, object parameter, CultureInfo culture) - { - throw new NotImplementedException(); - } + public object ConvertBack(object value, Type targetType, object parameter, CultureInfo culture) => throw new NotImplementedException(); } } diff --git a/Obsidian/Windows/FileAddWindow.xaml.cs b/Obsidian/Windows/FileAddWindow.xaml.cs index 89b4deb..0d6547a 100644 --- a/Obsidian/Windows/FileAddWindow.xaml.cs +++ b/Obsidian/Windows/FileAddWindow.xaml.cs @@ -26,8 +26,10 @@ private void fileAddWindow_Closed(object sender, EventArgs e) private void buttonSelectFile_Click(object sender, RoutedEventArgs e) { - OpenFileDialog openFileDialog = new OpenFileDialog(); - openFileDialog.Multiselect = false; + OpenFileDialog openFileDialog = new OpenFileDialog + { + Multiselect = false + }; if (openFileDialog.ShowDialog() == System.Windows.Forms.DialogResult.OK) { @@ -42,7 +44,7 @@ private void buttonAddEntry_Click(object sender, RoutedEventArgs e) { this.MainWindow.Wad.AddEntry(this.textboxPath.Text, File.ReadAllBytes(this.textboxFile.Text), this.checkboxCompress.IsChecked.Value); CollectionViewSource.GetDefaultView(this.MainWindow.datagridWadEntries.ItemsSource).Refresh(); - this.Close(); + Close(); } catch (Exception exception) { diff --git a/Obsidian/Windows/FileRedirectionAddWindow.xaml.cs b/Obsidian/Windows/FileRedirectionAddWindow.xaml.cs index 3a33f0d..e986d0e 100644 --- a/Obsidian/Windows/FileRedirectionAddWindow.xaml.cs +++ b/Obsidian/Windows/FileRedirectionAddWindow.xaml.cs @@ -34,7 +34,7 @@ private void buttonAddFileRedirectionEntry_Click(object sender, RoutedEventArgs { this.MainWindow.Wad.AddEntry(this.textboxFile.Text, this.textboxPath.Text); CollectionViewSource.GetDefaultView(this.MainWindow.datagridWadEntries.ItemsSource).Refresh(); - this.Close(); + Close(); } catch (Exception exception) { diff --git a/Obsidian/packages.config b/Obsidian/packages.config index e157ba1..a3df3a8 100644 --- a/Obsidian/packages.config +++ b/Obsidian/packages.config @@ -1,4 +1,5 @@  + \ No newline at end of file