From 263447dc85e934f0231b7d136c36c2ef03490452 Mon Sep 17 00:00:00 2001 From: TOM_RUS Date: Sun, 14 Feb 2016 23:04:01 +0300 Subject: [PATCH 01/17] Fix --- CASCExplorer/CASCViewHelper.cs | 65 +++++++++++++++------------------- CASCExplorer/DB2Reader.cs | 7 ++-- CASCExplorer/DB3Reader.cs | 7 ++-- CASCExplorer/MainForm.cs | 18 ++++++---- CascLib/CASCEntry.cs | 5 +-- CascLib/CASCHandler.cs | 29 ++++++--------- CascLib/CASCHandlerLite.cs | 2 +- CascLib/WowRootHandler.cs | 15 ++++---- 8 files changed, 65 insertions(+), 83 deletions(-) diff --git a/CASCExplorer/CASCViewHelper.cs b/CASCExplorer/CASCViewHelper.cs index af4442ad..edf653e5 100644 --- a/CASCExplorer/CASCViewHelper.cs +++ b/CASCExplorer/CASCViewHelper.cs @@ -19,6 +19,7 @@ class CASCViewHelper private ExtractProgress extractProgress; private CASCHandler _casc; private CASCFolder _root; + private CASCFolder _currentFolder; private CASCEntrySorter Sorter = new CASCEntrySorter(); private ScanForm scanForm; private NumberFormatInfo sizeNumberFmt = new NumberFormatInfo() @@ -41,11 +42,14 @@ public CASCFolder Root get { return _root; } } - public void ExtractFiles(NoFlickerListView filesList) + public CASCFolder CurrentFolder { - CASCFolder folder = filesList.Tag as CASCFolder; + get { return _currentFolder; } + } - if (folder == null) + public void ExtractFiles(NoFlickerListView filesList) + { + if (_currentFolder == null) return; if (!filesList.HasSelection) @@ -54,7 +58,7 @@ public void ExtractFiles(NoFlickerListView filesList) if (extractProgress == null) extractProgress = new ExtractProgress(); - var files = folder.GetFiles(filesList.SelectedIndices.Cast()).ToList(); + var files = _currentFolder.GetFiles(filesList.SelectedIndices.Cast()).ToList(); extractProgress.SetExtractData(_casc, files); extractProgress.ShowDialog(); } @@ -76,7 +80,7 @@ await Task.Run(() => foreach (var file in installFiles) { - _casc.ExtractFile(_casc.Encoding.GetEntry(file.MD5).Key, "data\\" + build + "\\install_files", file.Name); + _casc.ExtractFile(_casc.Encoding.GetEntry(file.MD5).Key, Path.Combine("data", build, "install_files"), file.Name); progress.Report((int)(++numDone / (float)numFiles * 100.0f)); } @@ -223,8 +227,9 @@ public void UpdateListView(CASCFolder baseEntry, NoFlickerListView fileList, str baseEntry.Entries = baseEntry.EntriesMirror.Where(v => v.Value is CASCFolder || (v.Value is CASCFile && wildcard.IsMatch(v.Value.Name))). OrderBy(v => v.Value, Sorter).ToDictionary(pair => pair.Key, pair => pair.Value); + _currentFolder = baseEntry; + // Update - fileList.Tag = baseEntry; fileList.VirtualListSize = 0; fileList.VirtualListSize = baseEntry.Entries.Count; @@ -332,15 +337,13 @@ public void SetSort(int column) public void GetSize(NoFlickerListView fileList) { - CASCFolder folder = fileList.Tag as CASCFolder; - - if (folder == null) + if (_currentFolder == null) return; if (!fileList.HasSelection) return; - var files = folder.GetFiles(fileList.SelectedIndices.Cast()); + var files = _currentFolder.GetFiles(fileList.SelectedIndices.Cast()); long size = files.Sum(f => (long)f.GetSize(_casc)); @@ -349,15 +352,13 @@ public void GetSize(NoFlickerListView fileList) public void PreviewFile(NoFlickerListView fileList) { - CASCFolder folder = fileList.Tag as CASCFolder; - - if (folder == null) + if (_currentFolder == null) return; if (!fileList.HasSingleSelection) return; - var file = folder.Entries.ElementAt(fileList.SelectedIndex).Value as CASCFile; + var file = _currentFolder.Entries.ElementAt(fileList.SelectedIndex).Value as CASCFile; var extension = Path.GetExtension(file.Name); @@ -427,15 +428,15 @@ private void PreviewBlp(CASCFile file) } } - public void CreateListViewItem(RetrieveVirtualItemEventArgs e, CASCFolder folder) + public void CreateListViewItem(RetrieveVirtualItemEventArgs e) { - if (folder == null) + if (_currentFolder == null) return; - if (e.ItemIndex < 0 || e.ItemIndex >= folder.Entries.Count) + if (e.ItemIndex < 0 || e.ItemIndex >= _currentFolder.Entries.Count) return; - ICASCEntry entry = folder.Entries.ElementAt(e.ItemIndex).Value; + ICASCEntry entry = _currentFolder.Entries.ElementAt(e.ItemIndex).Value; var localeFlags = LocaleFlags.None; var contentFlags = ContentFlags.None; @@ -449,10 +450,7 @@ public void CreateListViewItem(RetrieveVirtualItemEventArgs e, CASCFolder folder { var enc = _casc.Encoding.GetEntry(rootInfosLocale.First().MD5); - if (enc != null) - size = enc.Size.ToString("N", sizeNumberFmt); - else - size = "0"; + size = enc?.Size.ToString("N", sizeNumberFmt) ?? "0"; foreach (var rootInfo in rootInfosLocale) { @@ -471,10 +469,7 @@ public void CreateListViewItem(RetrieveVirtualItemEventArgs e, CASCFolder folder { var enc = _casc.Encoding.GetEntry(installInfos.First().MD5); - if (enc != null) - size = enc.Size.ToString("N", sizeNumberFmt); - else - size = "0"; + size = enc?.Size.ToString("N", sizeNumberFmt) ?? "0"; //foreach (var rootInfo in rootInfosLocale) //{ @@ -501,17 +496,15 @@ public void CreateListViewItem(RetrieveVirtualItemEventArgs e, CASCFolder folder public void Cleanup() { - OnCleanup?.Invoke(); - Sorter.CASC = null; + _currentFolder = null; _root = null; - if (_casc != null) - { - _casc.Clear(); - _casc = null; - } + _casc?.Clear(); + _casc = null; + + OnCleanup?.Invoke(); } public void Search(NoFlickerListView fileList, SearchForVirtualItemEventArgs e) @@ -520,8 +513,6 @@ public void Search(NoFlickerListView fileList, SearchForVirtualItemEventArgs e) bool searchUp = false; int SelectedIndex = fileList.SelectedIndex; - CASCFolder folder = fileList.Tag as CASCFolder; - var comparisonType = ignoreCase ? StringComparison.InvariantCultureIgnoreCase : StringComparison.InvariantCulture; @@ -530,7 +521,7 @@ public void Search(NoFlickerListView fileList, SearchForVirtualItemEventArgs e) { for (var i = SelectedIndex - 1; i >= 0; --i) { - var op = folder.Entries.ElementAt(i).Value.Name; + var op = _currentFolder.Entries.ElementAt(i).Value.Name; if (op.IndexOf(e.Text, comparisonType) != -1) { e.Index = i; @@ -542,7 +533,7 @@ public void Search(NoFlickerListView fileList, SearchForVirtualItemEventArgs e) { for (int i = SelectedIndex + 1; i < fileList.Items.Count; ++i) { - var op = folder.Entries.ElementAt(i).Value.Name; + var op = _currentFolder.Entries.ElementAt(i).Value.Name; if (op.IndexOf(e.Text, comparisonType) != -1) { e.Index = i; diff --git a/CASCExplorer/DB2Reader.cs b/CASCExplorer/DB2Reader.cs index a84e6e6c..a959766c 100644 --- a/CASCExplorer/DB2Reader.cs +++ b/CASCExplorer/DB2Reader.cs @@ -129,10 +129,9 @@ public bool HasRow(int index) public DB2Row GetRow(int index) { - if (!m_index.ContainsKey(index)) - return null; - - return m_index[index]; + DB2Row row; + m_index.TryGetValue(index, out row); + return row; } public IEnumerator> GetEnumerator() diff --git a/CASCExplorer/DB3Reader.cs b/CASCExplorer/DB3Reader.cs index dec7a5c9..4a6da3a7 100644 --- a/CASCExplorer/DB3Reader.cs +++ b/CASCExplorer/DB3Reader.cs @@ -204,10 +204,9 @@ public bool HasRow(int index) public DB3Row GetRow(int index) { - if (!m_index.ContainsKey(index)) - return null; - - return m_index[index]; + DB3Row row; + m_index.TryGetValue(index, out row); + return row; } public IEnumerator> GetEnumerator() diff --git a/CASCExplorer/MainForm.cs b/CASCExplorer/MainForm.cs index 8223112e..3dce8019 100644 --- a/CASCExplorer/MainForm.cs +++ b/CASCExplorer/MainForm.cs @@ -149,7 +149,7 @@ private void listView1_ColumnClick(object sender, ColumnClickEventArgs e) { viewHelper.SetSort(e.Column); - CASCFolder folder = fileList.Tag as CASCFolder; + CASCFolder folder = viewHelper.CurrentFolder; if (folder == null) return; @@ -159,7 +159,7 @@ private void listView1_ColumnClick(object sender, ColumnClickEventArgs e) private void listView1_RetrieveVirtualItem(object sender, RetrieveVirtualItemEventArgs e) { - viewHelper.CreateListViewItem(e, fileList.Tag as CASCFolder); + viewHelper.CreateListViewItem(e); } private void listView1_MouseDoubleClick(object sender, MouseEventArgs e) @@ -190,7 +190,7 @@ private void listView1_KeyDown(object sender, KeyEventArgs e) private bool NavigateFolder() { // Current folder - CASCFolder folder = fileList.Tag as CASCFolder; + CASCFolder folder = viewHelper.CurrentFolder; if (folder == null) return false; @@ -222,7 +222,7 @@ private void extractToolStripMenuItem_Click(object sender, EventArgs e) private void contextMenuStrip1_Opening(object sender, CancelEventArgs e) { extractToolStripMenuItem.Enabled = fileList.HasSelection; - copyNameToolStripMenuItem.Enabled = (fileList.HasSelection && (fileList.Tag as CASCFolder).GetFiles(fileList.SelectedIndices.Cast(), false).Count() > 0) || false; + copyNameToolStripMenuItem.Enabled = (fileList.HasSelection && viewHelper.CurrentFolder.GetFiles(fileList.SelectedIndices.Cast(), false).Count() > 0) || false; getSizeToolStripMenuItem.Enabled = fileList.HasSelection; } @@ -234,7 +234,7 @@ private void aboutToolStripMenuItem_Click(object sender, EventArgs e) private void copyNameToolStripMenuItem_Click(object sender, EventArgs e) { - CASCFolder folder = fileList.Tag as CASCFolder; + CASCFolder folder = viewHelper.CurrentFolder; if (folder == null) return; @@ -313,6 +313,12 @@ private void contentFlagsToolStripMenuItem_Click(object sender, EventArgs e) private void Cleanup() { fileList.VirtualListSize = 0; + + if (folderTree.Nodes.Count > 0) + { + folderTree.Nodes[0].Tag = null; + } + folderTree.Nodes.Clear(); CDNBuildsToolStripMenuItem.Enabled = false; @@ -436,7 +442,7 @@ private void exportListfileToolStripMenuItem_Click(object sender, EventArgs e) private void filterToolStripTextBox_TextChanged(object sender, EventArgs e) { - viewHelper.UpdateListView(fileList.Tag as CASCFolder, fileList, filterToolStripTextBox.Text); + viewHelper.UpdateListView(viewHelper.CurrentFolder, fileList, filterToolStripTextBox.Text); } private void openStorageToolStripButton_Click(object sender, EventArgs e) diff --git a/CascLib/CASCEntry.cs b/CascLib/CASCEntry.cs index 2a3f0137..0b3d150b 100644 --- a/CascLib/CASCEntry.cs +++ b/CascLib/CASCEntry.cs @@ -141,10 +141,7 @@ public int GetSize(CASCHandler casc) { var encoding = casc.GetEncodingEntry(hash); - if (encoding != null) - return encoding.Size; - - return 0; + return encoding?.Size ?? 0; } public int CompareTo(ICASCEntry other, int col, CASCHandler casc) diff --git a/CascLib/CASCHandler.cs b/CascLib/CASCHandler.cs index 7463df06..1f8adc30 100644 --- a/CascLib/CASCHandler.cs +++ b/CascLib/CASCHandler.cs @@ -137,7 +137,7 @@ public bool FileExists(string file) public bool FileExists(ulong hash) { var rootInfos = RootHandler.GetAllEntries(hash); - return rootInfos != null && rootInfos.Any(); + return rootInfos?.Any() ?? false; } public EncodingEntry GetEncodingEntry(ulong hash) @@ -214,7 +214,7 @@ public void SaveFileTo(ulong hash, string extractPath, string fullName) public void Clear() { - CDNIndex.Clear(); + CDNIndex?.Clear(); CDNIndex = null; foreach (var stream in DataStreams) @@ -222,29 +222,20 @@ public void Clear() DataStreams.Clear(); - EncodingHandler.Clear(); + EncodingHandler?.Clear(); EncodingHandler = null; - if (InstallHandler != null) - { - InstallHandler.Clear(); - InstallHandler = null; - } + InstallHandler?.Clear(); + InstallHandler = null; - if (LocalIndex != null) - { - LocalIndex.Clear(); - LocalIndex = null; - } + LocalIndex?.Clear(); + LocalIndex = null; - RootHandler.Clear(); + RootHandler?.Clear(); RootHandler = null; - if (DownloadHandler != null) - { - DownloadHandler.Clear(); - DownloadHandler = null; - } + DownloadHandler?.Clear(); + DownloadHandler = null; } } } diff --git a/CascLib/CASCHandlerLite.cs b/CascLib/CASCHandlerLite.cs index 5f9228ad..19ac81f7 100644 --- a/CascLib/CASCHandlerLite.cs +++ b/CascLib/CASCHandlerLite.cs @@ -62,8 +62,8 @@ private CASCHandlerLite(CASCConfig config, LocaleFlags locale, BackgroundWorkerE } RootHandler.Clear(); - EncodingHandler.Clear(); RootHandler = null; + EncodingHandler.Clear(); EncodingHandler = null; GC.Collect(); diff --git a/CascLib/WowRootHandler.cs b/CascLib/WowRootHandler.cs index 3e3d0864..1a09e279 100644 --- a/CascLib/WowRootHandler.cs +++ b/CascLib/WowRootHandler.cs @@ -302,16 +302,16 @@ public override void LoadListFile(string path, BackgroundWorkerEx worker = null) if (LoadPreHashedListFile("listfile.bin", path, worker)) return; - if (!File.Exists(path)) - { - Logger.WriteLine("WowRootHandler: list file missing!"); - return; - } - using (var _ = new PerfCounter("WowRootHandler::LoadListFile()")) { worker?.ReportProgress(0, "Loading \"listfile\"..."); + if (!File.Exists(path)) + { + Logger.WriteLine("WowRootHandler: list file missing!"); + return; + } + Logger.WriteLine("WowRootHandler: loading file names..."); Dictionary> dirData = new Dictionary>(StringComparer.OrdinalIgnoreCase); @@ -437,8 +437,7 @@ public override void Clear() FileDataStore.Clear(); FileDataStoreReverse.Clear(); UnknownFiles.Clear(); - if (Root != null) - Root.Entries.Clear(); + Root?.Entries.Clear(); CASCFile.FileNames.Clear(); } From 373bef3019100625031bce17774fd1190219a718 Mon Sep 17 00:00:00 2001 From: TOM_RUS Date: Mon, 15 Feb 2016 02:51:48 +0300 Subject: [PATCH 02/17] Fix --- CASCConsole/Program.cs | 2 +- CascLib/CASCHandler.cs | 16 ++++------------ CascLib/DBCReader.cs | 39 ++++++++++++++++----------------------- 3 files changed, 21 insertions(+), 36 deletions(-) diff --git a/CASCConsole/Program.cs b/CASCConsole/Program.cs index 4bc8a4c1..1c1f90a9 100644 --- a/CASCConsole/Program.cs +++ b/CASCConsole/Program.cs @@ -45,7 +45,7 @@ static void Main(string[] args) Console.WriteLine("Loaded."); - Console.WriteLine("Extract params:", pattern, dest, locale); + Console.WriteLine("Extract params:"); Console.WriteLine(" Pattern: {0}", pattern); Console.WriteLine(" Destination: {0}", dest); Console.WriteLine(" LocaleFlags: {0}", locale); diff --git a/CascLib/CASCHandler.cs b/CascLib/CASCHandler.cs index 1f8adc30..f55061fe 100644 --- a/CascLib/CASCHandler.cs +++ b/CascLib/CASCHandler.cs @@ -118,14 +118,10 @@ public bool FileExists(int fileDataId) { WowRootHandler rh = Root as WowRootHandler; - if (rh != null) - { - var hash = rh.GetHashByFileDataId(fileDataId); - - return FileExists(hash); - } + if (rh == null) + return false; - return false; + return FileExists(rh.GetHashByFileDataId(fileDataId)); } public bool FileExists(string file) @@ -161,11 +157,7 @@ public override Stream OpenFile(int fileDataId) WowRootHandler rh = Root as WowRootHandler; if (rh != null) - { - var hash = rh.GetHashByFileDataId(fileDataId); - - return OpenFile(hash); - } + return OpenFile(rh.GetHashByFileDataId(fileDataId)); if (CASCConfig.ThrowOnFileNotFound) throw new FileNotFoundException("FileData: " + fileDataId.ToString()); diff --git a/CascLib/DBCReader.cs b/CascLib/DBCReader.cs index 38fd66bc..fb872b62 100644 --- a/CascLib/DBCReader.cs +++ b/CascLib/DBCReader.cs @@ -21,31 +21,24 @@ public DBCRow(DBCReader reader, byte[] data) public T GetField(int field) { - try - { - object retVal; + object retVal; - switch (Type.GetTypeCode(typeof(T))) - { - case TypeCode.String: - int start = BitConverter.ToInt32(m_data, field * 4), len = 0; - while (m_reader.StringTable[start + len] != 0) - len++; - retVal = Encoding.UTF8.GetString(m_reader.StringTable, start, len); - return (T)retVal; - case TypeCode.Int32: - retVal = BitConverter.ToInt32(m_data, field * 4); - return (T)retVal; - case TypeCode.Single: - retVal = BitConverter.ToSingle(m_data, field * 4); - return (T)retVal; - default: - return default(T); - } - } - catch + switch (Type.GetTypeCode(typeof(T))) { - return default(T); + case TypeCode.String: + int start = BitConverter.ToInt32(m_data, field * 4), len = 0; + while (m_reader.StringTable[start + len] != 0) + len++; + retVal = Encoding.UTF8.GetString(m_reader.StringTable, start, len); + return (T)retVal; + case TypeCode.Int32: + retVal = BitConverter.ToInt32(m_data, field * 4); + return (T)retVal; + case TypeCode.Single: + retVal = BitConverter.ToSingle(m_data, field * 4); + return (T)retVal; + default: + return default(T); } } } From 570a308c7f4745ee25a1c4e93c50e9710ec18f53 Mon Sep 17 00:00:00 2001 From: TOM_RUS Date: Mon, 15 Feb 2016 07:09:40 +0300 Subject: [PATCH 03/17] Hopefully it still work... --- CASCConsole/Program.cs | 3 +- CASCExplorer/CASCViewHelper.cs | 34 +++++++----- CASCExplorer/InitForm.cs | 3 + CASCExplorer/MainForm.cs | 6 +- CascLib/BLTEHandler.cs | 18 +++--- CascLib/CASCConfig.cs | 20 +++---- CascLib/CASCEntry.cs | 24 ++++---- CascLib/CASCHandler.cs | 7 --- CascLib/CASCHandlerBase.cs | 16 +++--- CascLib/CASCHandlerLite.cs | 4 +- CascLib/CDNIndexHandler.cs | 19 +++++-- CascLib/CascLib.csproj | 2 +- CascLib/D3RootHandler.cs | 6 +- CascLib/DownloadHandler.cs | 8 +-- CascLib/EncodingHandler.cs | 14 ++--- CascLib/Extensions.cs | 55 +++++++++++++++++++ CascLib/InstallHandler.cs | 4 +- CascLib/LocalIndexHandler.cs | 32 ++++++++--- ...yteArrayComparer.cs => MD5HashComparer.cs} | 21 ++++--- CascLib/MNDXRootHandler.cs | 4 +- CascLib/OWRootHandler.cs | 12 ++-- CascLib/RootHandlerBase.cs | 1 - CascLib/WowRootHandler.cs | 9 ++- 23 files changed, 208 insertions(+), 114 deletions(-) rename CascLib/{ByteArrayComparer.cs => MD5HashComparer.cs} (53%) diff --git a/CASCConsole/Program.cs b/CASCConsole/Program.cs index 1c1f90a9..7678f435 100644 --- a/CASCConsole/Program.cs +++ b/CASCConsole/Program.cs @@ -3,6 +3,7 @@ using System; using System.ComponentModel; using System.IO; +using System.Linq; using System.Text.RegularExpressions; namespace CASCConsole @@ -53,7 +54,7 @@ static void Main(string[] args) Wildcard wildcard = new Wildcard(pattern, true, RegexOptions.IgnoreCase); - foreach (var file in root.GetFiles()) + foreach (var file in CASCFolder.GetFiles(root.Entries.Select(kv => kv.Value))) { if (wildcard.IsMatch(file.FullName)) { diff --git a/CASCExplorer/CASCViewHelper.cs b/CASCExplorer/CASCViewHelper.cs index edf653e5..7c5c3e45 100644 --- a/CASCExplorer/CASCViewHelper.cs +++ b/CASCExplorer/CASCViewHelper.cs @@ -20,6 +20,7 @@ class CASCViewHelper private CASCHandler _casc; private CASCFolder _root; private CASCFolder _currentFolder; + private List _displayedEntries; private CASCEntrySorter Sorter = new CASCEntrySorter(); private ScanForm scanForm; private NumberFormatInfo sizeNumberFmt = new NumberFormatInfo() @@ -47,6 +48,11 @@ public CASCFolder CurrentFolder get { return _currentFolder; } } + public List DisplayedEntries + { + get { return _displayedEntries; } + } + public void ExtractFiles(NoFlickerListView filesList) { if (_currentFolder == null) @@ -58,7 +64,7 @@ public void ExtractFiles(NoFlickerListView filesList) if (extractProgress == null) extractProgress = new ExtractProgress(); - var files = _currentFolder.GetFiles(filesList.SelectedIndices.Cast()).ToList(); + var files = CASCFolder.GetFiles(_displayedEntries, filesList.SelectedIndices.Cast()).ToList(); extractProgress.SetExtractData(_casc, files); extractProgress.ShowDialog(); } @@ -172,7 +178,7 @@ await Task.Run(() => if (unknownFolder == null) return; - IEnumerable files = unknownFolder.GetFiles(null, true); + IEnumerable files = CASCFolder.GetFiles(unknownFolder.Entries.Select(kv => kv.Value), null, true); int numTotal = files.Count(); int numDone = 0; @@ -224,14 +230,14 @@ public void UpdateListView(CASCFolder baseEntry, NoFlickerListView fileList, str Wildcard wildcard = new Wildcard(filter, false, RegexOptions.IgnoreCase); // Sort - baseEntry.Entries = baseEntry.EntriesMirror.Where(v => v.Value is CASCFolder || (v.Value is CASCFile && wildcard.IsMatch(v.Value.Name))). - OrderBy(v => v.Value, Sorter).ToDictionary(pair => pair.Key, pair => pair.Value); + _displayedEntries = baseEntry.Entries.Where(v => v.Value is CASCFolder || (v.Value is CASCFile && wildcard.IsMatch(v.Value.Name))). + OrderBy(v => v.Value, Sorter).Select(kv => kv.Value).ToList(); _currentFolder = baseEntry; // Update fileList.VirtualListSize = 0; - fileList.VirtualListSize = baseEntry.Entries.Count; + fileList.VirtualListSize = _displayedEntries.Count; if (fileList.VirtualListSize > 0) { @@ -343,7 +349,7 @@ public void GetSize(NoFlickerListView fileList) if (!fileList.HasSelection) return; - var files = _currentFolder.GetFiles(fileList.SelectedIndices.Cast()); + var files = CASCFolder.GetFiles(_displayedEntries, fileList.SelectedIndices.Cast()); long size = files.Sum(f => (long)f.GetSize(_casc)); @@ -358,7 +364,7 @@ public void PreviewFile(NoFlickerListView fileList) if (!fileList.HasSingleSelection) return; - var file = _currentFolder.Entries.ElementAt(fileList.SelectedIndex).Value as CASCFile; + var file = _displayedEntries[fileList.SelectedIndex] as CASCFile; var extension = Path.GetExtension(file.Name); @@ -433,10 +439,10 @@ public void CreateListViewItem(RetrieveVirtualItemEventArgs e) if (_currentFolder == null) return; - if (e.ItemIndex < 0 || e.ItemIndex >= _currentFolder.Entries.Count) + if (e.ItemIndex < 0 || e.ItemIndex >= _displayedEntries.Count) return; - ICASCEntry entry = _currentFolder.Entries.ElementAt(e.ItemIndex).Value; + ICASCEntry entry = _displayedEntries[e.ItemIndex]; var localeFlags = LocaleFlags.None; var contentFlags = ContentFlags.None; @@ -501,6 +507,8 @@ public void Cleanup() _currentFolder = null; _root = null; + _displayedEntries?.Clear(); + _casc?.Clear(); _casc = null; @@ -521,7 +529,7 @@ public void Search(NoFlickerListView fileList, SearchForVirtualItemEventArgs e) { for (var i = SelectedIndex - 1; i >= 0; --i) { - var op = _currentFolder.Entries.ElementAt(i).Value.Name; + var op = _displayedEntries[i].Name; if (op.IndexOf(e.Text, comparisonType) != -1) { e.Index = i; @@ -533,7 +541,7 @@ public void Search(NoFlickerListView fileList, SearchForVirtualItemEventArgs e) { for (int i = SelectedIndex + 1; i < fileList.Items.Count; ++i) { - var op = _currentFolder.Entries.ElementAt(i).Value.Name; + var op = _displayedEntries[i].Name; if (op.IndexOf(e.Text, comparisonType) != -1) { e.Index = i; @@ -547,7 +555,7 @@ public void ExportListFile() { using (StreamWriter sw = new StreamWriter("listfile_export.txt")) { - foreach (var file in Root.GetFiles(null, true).OrderBy(f => f.FullName, StringComparer.OrdinalIgnoreCase)) + foreach (var file in CASCFolder.GetFiles(_root.Entries.Select(kv => kv.Value), null, true).OrderBy(f => f.FullName, StringComparer.OrdinalIgnoreCase)) sw.WriteLine(file.FullName); } } @@ -557,7 +565,7 @@ public void ExtractCASCSystemFiles() if (_casc == null) return; - var files = new Dictionary() + var files = new Dictionary() { { "root", _casc.Encoding.GetEntry(_casc.Config.RootMD5).Key }, { "install", _casc.Encoding.GetEntry(_casc.Config.InstallMD5).Key }, diff --git a/CASCExplorer/InitForm.cs b/CASCExplorer/InitForm.cs index 74ea6349..7a4dae56 100644 --- a/CASCExplorer/InitForm.cs +++ b/CASCExplorer/InitForm.cs @@ -1,4 +1,5 @@ using CASCExplorer.Properties; +using System; using System.ComponentModel; using System.IO; using System.Windows.Forms; @@ -55,6 +56,8 @@ private void backgroundWorker1_DoWork(object sender, DoWorkEventArgs e) var fldr = casc.Root.SetFlags(Settings.Default.LocaleFlags, Settings.Default.ContentFlags); casc.Root.MergeInstall(casc.Install); + GC.Collect(); + e.Result = new object[] { casc, fldr }; } diff --git a/CASCExplorer/MainForm.cs b/CASCExplorer/MainForm.cs index 3dce8019..e5c8bb5c 100644 --- a/CASCExplorer/MainForm.cs +++ b/CASCExplorer/MainForm.cs @@ -199,7 +199,7 @@ private bool NavigateFolder() return false; // Selected folder - CASCFolder baseEntry = folder.Entries.ElementAt(fileList.SelectedIndex).Value as CASCFolder; + CASCFolder baseEntry = viewHelper.DisplayedEntries[fileList.SelectedIndex] as CASCFolder; if (baseEntry == null) return false; @@ -222,7 +222,7 @@ private void extractToolStripMenuItem_Click(object sender, EventArgs e) private void contextMenuStrip1_Opening(object sender, CancelEventArgs e) { extractToolStripMenuItem.Enabled = fileList.HasSelection; - copyNameToolStripMenuItem.Enabled = (fileList.HasSelection && viewHelper.CurrentFolder.GetFiles(fileList.SelectedIndices.Cast(), false).Count() > 0) || false; + copyNameToolStripMenuItem.Enabled = (fileList.HasSelection && CASCFolder.GetFiles(viewHelper.DisplayedEntries, fileList.SelectedIndices.Cast(), false).Count() > 0) || false; getSizeToolStripMenuItem.Enabled = fileList.HasSelection; } @@ -242,7 +242,7 @@ private void copyNameToolStripMenuItem_Click(object sender, EventArgs e) if (!fileList.HasSelection) return; - var files = folder.GetFiles(fileList.SelectedIndices.Cast(), false).Select(f => f.FullName); + var files = CASCFolder.GetFiles(viewHelper.DisplayedEntries, fileList.SelectedIndices.Cast(), false).Select(f => f.FullName); string temp = string.Join(Environment.NewLine, files); diff --git a/CascLib/BLTEHandler.cs b/CascLib/BLTEHandler.cs index 9e134a52..29f576eb 100644 --- a/CascLib/BLTEHandler.cs +++ b/CascLib/BLTEHandler.cs @@ -22,7 +22,7 @@ class DataBlock { public int CompSize; public int DecompSize; - public byte[] Hash; + public MD5Hash Hash; public byte[] Data; } @@ -37,7 +37,7 @@ class BLTEHandler : IDisposable private const byte ENCRYPTION_ARC4 = 0x41; private const int BLTE_MAGIC = 0x45544c42; - public BLTEHandler(Stream stream, byte[] md5) + public BLTEHandler(Stream stream, MD5Hash md5) { _reader = new BinaryReader(stream, Encoding.ASCII, true); Parse(md5); @@ -65,7 +65,7 @@ public MemoryStream OpenFile(bool leaveOpen = false) return _memStream; } - private void Parse(byte[] md5) + private void Parse(MD5Hash md5) { int size = (int)_reader.BaseStream.Length; @@ -87,8 +87,8 @@ private void Parse(byte[] md5) byte[] newHash = _md5.ComputeHash(_reader.ReadBytes(headerSize > 0 ? headerSize : size)); - if (!newHash.EqualsTo(md5)) - throw new Exception("data corrupted"); + if (!md5.EqualsTo(newHash)) + throw new BLTEDecoderException("data corrupted"); _reader.BaseStream.Position = oldPos; } @@ -126,13 +126,13 @@ private void Parse(byte[] md5) { block.CompSize = _reader.ReadInt32BE(); block.DecompSize = _reader.ReadInt32BE(); - block.Hash = _reader.ReadBytes(16); + block.Hash = _reader.Read(); } else { block.CompSize = size - 8; block.DecompSize = size - 8 - 1; - block.Hash = null; + block.Hash = default(MD5Hash); } blocks[i] = block; @@ -146,11 +146,11 @@ private void Parse(byte[] md5) block.Data = _reader.ReadBytes(block.CompSize); - if (block.Hash != null && CASCConfig.ValidateData) + if (!block.Hash.IsZeroed() && CASCConfig.ValidateData) { byte[] blockHash = _md5.ComputeHash(block.Data); - if (!blockHash.EqualsTo(block.Hash)) + if (!block.Hash.EqualsTo(blockHash)) throw new BLTEDecoderException("MD5 mismatch"); } diff --git a/CascLib/CASCConfig.cs b/CascLib/CASCConfig.cs index b69f686a..80e9ffe1 100644 --- a/CascLib/CASCConfig.cs +++ b/CascLib/CASCConfig.cs @@ -258,29 +258,29 @@ public static CASCConfig LoadLocalStorageConfig(string basePath) public string Product { get; private set; } - public byte[] RootMD5 + public MD5Hash RootMD5 { - get { return _Builds[ActiveBuild]["root"][0].ToByteArray(); } + get { return _Builds[ActiveBuild]["root"][0].ToByteArray().ToMD5(); } } - public byte[] DownloadMD5 + public MD5Hash DownloadMD5 { - get { return _Builds[ActiveBuild]["download"][0].ToByteArray(); } + get { return _Builds[ActiveBuild]["download"][0].ToByteArray().ToMD5(); } } - public byte[] InstallMD5 + public MD5Hash InstallMD5 { - get { return _Builds[ActiveBuild]["install"][0].ToByteArray(); } + get { return _Builds[ActiveBuild]["install"][0].ToByteArray().ToMD5(); } } - public byte[] EncodingMD5 + public MD5Hash EncodingMD5 { - get { return _Builds[ActiveBuild]["encoding"][0].ToByteArray(); } + get { return _Builds[ActiveBuild]["encoding"][0].ToByteArray().ToMD5(); } } - public byte[] EncodingKey + public MD5Hash EncodingKey { - get { return _Builds[ActiveBuild]["encoding"][1].ToByteArray(); } + get { return _Builds[ActiveBuild]["encoding"][1].ToByteArray().ToMD5(); } } public string BuildUID diff --git a/CascLib/CASCEntry.cs b/CascLib/CASCEntry.cs index 0b3d150b..a7826292 100644 --- a/CascLib/CASCEntry.cs +++ b/CascLib/CASCEntry.cs @@ -17,12 +17,10 @@ public class CASCFolder : ICASCEntry private string _name; public Dictionary Entries { get; set; } - public Dictionary EntriesMirror { get; private set; } public CASCFolder(string name) { Entries = new Dictionary(StringComparer.OrdinalIgnoreCase); - EntriesMirror = new Dictionary(StringComparer.OrdinalIgnoreCase); _name = name; } @@ -43,23 +41,25 @@ public ICASCEntry GetEntry(string name) return entry; } - public IEnumerable GetFiles(IEnumerable selection = null, bool recursive = true) + public static IEnumerable GetFiles(IEnumerable entries, IEnumerable selection = null, bool recursive = true) { if (selection != null) { foreach (int index in selection) { - var entry = Entries.ElementAt(index); + var entry = entries.ElementAt(index); - if (entry.Value is CASCFile) + if (entry is CASCFile) { - yield return entry.Value as CASCFile; + yield return entry as CASCFile; } else { if (recursive) { - foreach (var file in (entry.Value as CASCFolder).GetFiles()) + var folder = entry as CASCFolder; + + foreach (var file in GetFiles(folder.Entries.Select(kv => kv.Value))) { yield return file; } @@ -69,17 +69,19 @@ public IEnumerable GetFiles(IEnumerable selection = null, bool re } else { - foreach (var entry in Entries) + foreach (var entry in entries) { - if (entry.Value is CASCFile) + if (entry is CASCFile) { - yield return entry.Value as CASCFile; + yield return entry as CASCFile; } else { if (recursive) { - foreach (var file in (entry.Value as CASCFolder).GetFiles()) + var folder = entry as CASCFolder; + + foreach (var file in GetFiles(folder.Entries.Select(kv => kv.Value))) { yield return file; } diff --git a/CascLib/CASCHandler.cs b/CascLib/CASCHandler.cs index f55061fe..4bb5894a 100644 --- a/CascLib/CASCHandler.cs +++ b/CascLib/CASCHandler.cs @@ -4,13 +4,6 @@ namespace CASCExplorer { - public class IndexEntry - { - public int Index; - public int Offset; - public int Size; - } - public sealed class CASCHandler : CASCHandlerBase { private EncodingHandler EncodingHandler; diff --git a/CascLib/CASCHandlerBase.cs b/CascLib/CASCHandlerBase.cs index b50a1b93..82e27deb 100644 --- a/CascLib/CASCHandlerBase.cs +++ b/CascLib/CASCHandlerBase.cs @@ -47,7 +47,7 @@ public CASCHandlerBase(CASCConfig config, BackgroundWorkerEx worker) public abstract Stream OpenFile(string name); public abstract Stream OpenFile(ulong hash); - public Stream OpenFile(byte[] key) + public Stream OpenFile(MD5Hash key) { try { @@ -62,7 +62,7 @@ public Stream OpenFile(byte[] key) } } - private Stream OpenFileOnline(byte[] key) + private Stream OpenFileOnline(MD5Hash key) { IndexEntry idxInfo = CDNIndex.GetIndexInfo(key); @@ -84,7 +84,7 @@ private Stream OpenFileOnline(byte[] key) } } - private Stream OpenFileLocal(byte[] key) + private Stream OpenFileLocal(MD5Hash key) { Stream stream = GetLocalDataStream(key); @@ -94,7 +94,7 @@ private Stream OpenFileLocal(byte[] key) } } - private Stream GetLocalDataStream(byte[] key) + private Stream GetLocalDataStream(MD5Hash key) { IndexEntry idxInfo = LocalIndex.GetIndexInfo(key); @@ -109,7 +109,7 @@ private Stream GetLocalDataStream(byte[] key) byte[] md5 = reader.ReadBytes(16); Array.Reverse(md5); - if (!md5.EqualsTo(key)) + if (!key.EqualsTo(md5)) throw new Exception("local data corrupted"); int size = reader.ReadInt32(); @@ -127,7 +127,7 @@ private Stream GetLocalDataStream(byte[] key) } } - public void ExtractFile(byte[] key, string path, string name) + public void ExtractFile(MD5Hash key, string path, string name) { try { @@ -142,7 +142,7 @@ public void ExtractFile(byte[] key, string path, string name) } } - private void ExtractFileOnline(byte[] key, string path, string name) + private void ExtractFileOnline(MD5Hash key, string path, string name) { IndexEntry idxInfo = CDNIndex.GetIndexInfo(key); @@ -164,7 +164,7 @@ private void ExtractFileOnline(byte[] key, string path, string name) } } - private void ExtractFileLocal(byte[] key, string path, string name) + private void ExtractFileLocal(MD5Hash key, string path, string name) { Stream stream = GetLocalDataStream(key); diff --git a/CascLib/CASCHandlerLite.cs b/CascLib/CASCHandlerLite.cs index 19ac81f7..a6c00cf9 100644 --- a/CascLib/CASCHandlerLite.cs +++ b/CascLib/CASCHandlerLite.cs @@ -6,7 +6,7 @@ namespace CASCExplorer { public sealed class CASCHandlerLite : CASCHandlerBase { - private Dictionary HashToKey = new Dictionary(); + private Dictionary HashToKey = new Dictionary(); private Dictionary FileDataIdToHash = new Dictionary(); private CASCHandlerLite(CASCConfig config, LocaleFlags locale, BackgroundWorkerEx worker) : base(config, worker) @@ -132,7 +132,7 @@ public override Stream OpenFile(string name) public override Stream OpenFile(ulong hash) { - byte[] key; + MD5Hash key; if (HashToKey.TryGetValue(hash, out key)) return OpenFile(key); diff --git a/CascLib/CDNIndexHandler.cs b/CascLib/CDNIndexHandler.cs index 24207d73..dda7820d 100644 --- a/CascLib/CDNIndexHandler.cs +++ b/CascLib/CDNIndexHandler.cs @@ -5,10 +5,17 @@ namespace CASCExplorer { + public class IndexEntry + { + public int Index; + public int Offset; + public int Size; + } + public class CDNIndexHandler { - private static readonly ByteArrayComparer comparer = new ByteArrayComparer(); - private readonly Dictionary CDNIndexData = new Dictionary(comparer); + private static readonly MD5HashComparer comparer = new MD5HashComparer(); + private readonly Dictionary CDNIndexData = new Dictionary(comparer); private CASCConfig CASCConfig; private BackgroundWorkerEx worker; @@ -61,10 +68,10 @@ private void ParseIndex(Stream stream, int i) for (int j = 0; j < count; ++j) { - byte[] key = br.ReadBytes(16); + MD5Hash key = br.Read(); if (key.IsZeroed()) // wtf? - key = br.ReadBytes(16); + key = br.Read(); if (key.IsZeroed()) // wtf? throw new Exception("key.IsZeroed()"); @@ -150,7 +157,7 @@ public Stream OpenDataFile(IndexEntry entry) } } - public Stream OpenDataFileDirect(byte[] key) + public Stream OpenDataFileDirect(MD5Hash key) { var keyStr = key.ToHexString().ToLower(); @@ -192,7 +199,7 @@ public static Stream OpenFileDirect(string url) } } - public IndexEntry GetIndexInfo(byte[] key) + public IndexEntry GetIndexInfo(MD5Hash key) { IndexEntry result; diff --git a/CascLib/CascLib.csproj b/CascLib/CascLib.csproj index a759ecfc..ccaad07b 100644 --- a/CascLib/CascLib.csproj +++ b/CascLib/CascLib.csproj @@ -53,7 +53,7 @@ - + diff --git a/CascLib/D3RootHandler.cs b/CascLib/D3RootHandler.cs index 155ac9f4..149b2d0f 100644 --- a/CascLib/D3RootHandler.cs +++ b/CascLib/D3RootHandler.cs @@ -9,7 +9,7 @@ namespace CASCExplorer class D3RootEntry { public int Type; - public byte[] MD5; + public MD5Hash MD5; public int SNO; public int FileIndex; public string Name; @@ -19,7 +19,7 @@ public static D3RootEntry Read(int type, BinaryReader s) D3RootEntry e = new D3RootEntry(); e.Type = type; - e.MD5 = s.ReadBytes(16); + e.MD5 = s.Read(); if (type == 0 || type == 1) // has SNO id { @@ -60,7 +60,7 @@ public D3RootHandler(BinaryReader stream, BackgroundWorkerEx worker, CASCHandler for (int j = 0; j < count; j++) { - byte[] md5 = stream.ReadBytes(16); + MD5Hash md5 = stream.Read(); string name = stream.ReadCString(); var entries = new List(); diff --git a/CascLib/DownloadHandler.cs b/CascLib/DownloadHandler.cs index cfb7e3e1..d3457561 100644 --- a/CascLib/DownloadHandler.cs +++ b/CascLib/DownloadHandler.cs @@ -21,8 +21,8 @@ public class DownloadTag public class DownloadHandler { - private static readonly ByteArrayComparer comparer = new ByteArrayComparer(); - private readonly Dictionary DownloadData = new Dictionary(comparer); + private static readonly MD5HashComparer comparer = new MD5HashComparer(); + private readonly Dictionary DownloadData = new Dictionary(comparer); Dictionary Tags = new Dictionary(); public int Count @@ -48,7 +48,7 @@ public DownloadHandler(BinaryReader stream, BackgroundWorkerEx worker) for (int i = 0; i < numFiles; i++) { - byte[] key = stream.ReadBytes(0x10); + MD5Hash key = stream.Read(); byte[] unk = stream.ReadBytes(0xA); @@ -87,7 +87,7 @@ public void Dump() } } - public DownloadEntry GetEntry(byte[] key) + public DownloadEntry GetEntry(MD5Hash key) { DownloadEntry entry; DownloadData.TryGetValue(key, out entry); diff --git a/CascLib/EncodingHandler.cs b/CascLib/EncodingHandler.cs index b23b8d1a..8a295ed6 100644 --- a/CascLib/EncodingHandler.cs +++ b/CascLib/EncodingHandler.cs @@ -6,13 +6,13 @@ namespace CASCExplorer public class EncodingEntry { public int Size; - public byte[] Key; + public MD5Hash Key; } public class EncodingHandler { - private static readonly ByteArrayComparer comparer = new ByteArrayComparer(); - private readonly Dictionary EncodingData = new Dictionary(comparer); + private static readonly MD5HashComparer comparer = new MD5HashComparer(); + private readonly Dictionary EncodingData = new Dictionary(comparer); private const int CHUNK_SIZE = 4096; @@ -55,7 +55,7 @@ public EncodingHandler(BinaryReader stream, BackgroundWorkerEx worker) while ((keysCount = stream.ReadUInt16()) != 0) { int fileSize = stream.ReadInt32BE(); - byte[] md5 = stream.ReadBytes(16); + MD5Hash md5 = stream.Read(); EncodingEntry entry = new EncodingEntry(); entry.Size = fileSize; @@ -63,7 +63,7 @@ public EncodingHandler(BinaryReader stream, BackgroundWorkerEx worker) // how do we handle multiple keys? for (int ki = 0; ki < keysCount; ++ki) { - byte[] key = stream.ReadBytes(16); + MD5Hash key = stream.Read(); // use first key for now if (ki == 0) @@ -111,7 +111,7 @@ public EncodingHandler(BinaryReader stream, BackgroundWorkerEx worker) // string block till the end of file } - public IEnumerable> Entries + public IEnumerable> Entries { get { @@ -120,7 +120,7 @@ public IEnumerable> Entries } } - public EncodingEntry GetEntry(byte[] md5) + public EncodingEntry GetEntry(MD5Hash md5) { EncodingEntry result; EncodingData.TryGetValue(md5, out result); diff --git a/CascLib/Extensions.cs b/CascLib/Extensions.cs index 1b818dc5..18d4f8ee 100644 --- a/CascLib/Extensions.cs +++ b/CascLib/Extensions.cs @@ -116,6 +116,61 @@ public static string ToBinaryString(this BitArray bits) return sb.ToString(); } + + public static unsafe bool EqualsTo(this MD5Hash key, MD5Hash other) + { + for (var i = 0; i < 16; ++i) + if (key.Value[i] != other.Value[i]) + return false; + return true; + } + + public static unsafe bool EqualsTo(this MD5Hash key, byte[] array) + { + if (array.Length != 16) + return false; + + MD5Hash other; + + fixed (byte* ptr = array) + other = *(MD5Hash*)ptr; + + for (var i = 0; i < 16; ++i) + if (key.Value[i] != other.Value[i]) + return false; + return true; + } + + public static unsafe string ToHexString(this MD5Hash key) + { + byte[] array = new byte[16]; + + fixed (byte* aptr = array) + { + *(MD5Hash*)aptr = key; + } + + return array.ToHexString(); + } + + public static unsafe bool IsZeroed(this MD5Hash key) + { + for (var i = 0; i < 16; ++i) + if (key.Value[i] != 0) + return false; + return true; + } + + public static unsafe MD5Hash ToMD5(this byte[] array) + { + if (array.Length != 16) + throw new ArgumentException("array size != 16"); + + fixed (byte* ptr = array) + { + return *(MD5Hash*)ptr; + } + } } public static class CStringExtensions diff --git a/CascLib/InstallHandler.cs b/CascLib/InstallHandler.cs index 5c63281a..35140048 100644 --- a/CascLib/InstallHandler.cs +++ b/CascLib/InstallHandler.cs @@ -8,7 +8,7 @@ namespace CASCExplorer public class InstallEntry { public string Name; - public byte[] MD5; + public MD5Hash MD5; public int Size; public List Tags; @@ -66,7 +66,7 @@ public InstallHandler(BinaryReader stream, BackgroundWorkerEx worker) { InstallEntry entry = new InstallEntry(); entry.Name = stream.ReadCString(); - entry.MD5 = stream.ReadBytes(16); + entry.MD5 = stream.Read(); entry.Size = stream.ReadInt32BE(); InstallData.Add(entry); diff --git a/CascLib/LocalIndexHandler.cs b/CascLib/LocalIndexHandler.cs index 50ed391d..2e68c711 100644 --- a/CascLib/LocalIndexHandler.cs +++ b/CascLib/LocalIndexHandler.cs @@ -7,8 +7,8 @@ namespace CASCExplorer { public class LocalIndexHandler { - private static readonly ByteArrayComparer comparer = new ByteArrayComparer(); - private readonly Dictionary LocalIndexData = new Dictionary(comparer); + private static readonly MD5HashComparer comparer = new MD5HashComparer(); + private readonly Dictionary LocalIndexData = new Dictionary(comparer); public int Count { @@ -45,7 +45,7 @@ public static LocalIndexHandler Initialize(CASCConfig config, BackgroundWorkerEx return handler; } - private void ParseIndex(string idx) + private unsafe void ParseIndex(string idx) { using (var fs = new FileStream(idx, FileMode.Open, FileAccess.Read, FileShare.ReadWrite)) using (var br = new BinaryReader(fs)) @@ -62,15 +62,32 @@ private void ParseIndex(string idx) int numBlocks = dataLen / 18; + //byte[] buf = new byte[8]; + for (int i = 0; i < numBlocks; i++) { IndexEntry info = new IndexEntry(); - byte[] key = br.ReadBytes(9); + byte[] keyBytes = br.ReadBytes(9); + Array.Resize(ref keyBytes, 16); + + MD5Hash key; + + fixed (byte *ptr = keyBytes) + key = *(MD5Hash*)ptr; + byte indexHigh = br.ReadByte(); int indexLow = br.ReadInt32BE(); info.Index = (indexHigh << 2 | (byte)((indexLow & 0xC0000000) >> 30)); info.Offset = (indexLow & 0x3FFFFFFF); + + //for (int j = 3; j < 8; j++) + // buf[7 - j] = br.ReadByte(); + + //long val = BitConverter.ToInt64(buf, 0); + //info.Index = (int)(val / 0x40000000); + //info.Offset = (int)(val % 0x40000000); + info.Size = br.ReadInt32(); // duplicate keys wtf... @@ -111,12 +128,13 @@ private static List GetIdxFiles(CASCConfig config) return latestIdx; } - public IndexEntry GetIndexInfo(byte[] key) + public unsafe IndexEntry GetIndexInfo(MD5Hash key) { - byte[] temp = key.Copy(9); + ulong* ptr = (ulong*)&key; + ptr[1] &= 0xFF; IndexEntry result; - if (!LocalIndexData.TryGetValue(temp, out result)) + if (!LocalIndexData.TryGetValue(key, out result)) Logger.WriteLine("LocalIndexHandler: missing index: {0}", key.ToHexString()); return result; diff --git a/CascLib/ByteArrayComparer.cs b/CascLib/MD5HashComparer.cs similarity index 53% rename from CascLib/ByteArrayComparer.cs rename to CascLib/MD5HashComparer.cs index e90fdab1..8da7f272 100644 --- a/CascLib/ByteArrayComparer.cs +++ b/CascLib/MD5HashComparer.cs @@ -2,35 +2,34 @@ namespace CASCExplorer { - class ByteArrayComparer : IEqualityComparer + class MD5HashComparer : IEqualityComparer { const uint FnvPrime32 = 16777619; const uint FnvOffset32 = 2166136261; - public bool Equals(byte[] x, byte[] y) + public unsafe bool Equals(MD5Hash x, MD5Hash y) { - if (x.Length != y.Length) - return false; - - for (int i = 0; i < x.Length; ++i) - if (x[i] != y[i]) + for (int i = 0; i < 16; ++i) + if (x.Value[i] != y.Value[i]) return false; return true; } - public int GetHashCode(byte[] obj) + public int GetHashCode(MD5Hash obj) { return To32BitFnv1aHash(obj); } - private int To32BitFnv1aHash(byte[] toHash) + private unsafe int To32BitFnv1aHash(MD5Hash toHash) { uint hash = FnvOffset32; - foreach (var chunk in toHash) + uint* ptr = (uint*)&toHash; + + for (int i = 0; i < 4; i++) { - hash ^= chunk; + hash ^= ptr[i]; hash *= FnvPrime32; } diff --git a/CascLib/MNDXRootHandler.cs b/CascLib/MNDXRootHandler.cs index a9ffbc1c..068903c3 100644 --- a/CascLib/MNDXRootHandler.cs +++ b/CascLib/MNDXRootHandler.cs @@ -43,7 +43,7 @@ class CASC_ROOT_ENTRY_MNDX { public int Flags; // High 8 bits: Flags, low 24 bits: package index //[MarshalAs(UnmanagedType.ByValArray, SizeConst = 0x10)] - public byte[] MD5; // Encoding key for the file + public MD5Hash MD5; // Encoding key for the file public int FileSize; // Uncompressed file size, in bytes public CASC_ROOT_ENTRY_MNDX Next; @@ -146,7 +146,7 @@ public MNDXRootHandler(BinaryReader stream, BackgroundWorkerEx worker) prevEntry = entry; entry.Flags = stream.ReadInt32(); - entry.MD5 = stream.ReadBytes(16); + entry.MD5 = stream.Read(); entry.FileSize = stream.ReadInt32(); mndxRootEntries.Add(i, entry); diff --git a/CascLib/OWRootHandler.cs b/CascLib/OWRootHandler.cs index 38ab8b5b..950eec8f 100644 --- a/CascLib/OWRootHandler.cs +++ b/CascLib/OWRootHandler.cs @@ -10,7 +10,7 @@ public class OWRootHandler : RootHandlerBase { private readonly Dictionary RootData = new Dictionary(); - public OWRootHandler(BinaryReader stream, BackgroundWorkerEx worker, CASCHandler casc) + public unsafe OWRootHandler(BinaryReader stream, BackgroundWorkerEx worker, CASCHandler casc) { worker?.ReportProgress(0, "Loading \"root\"..."); @@ -30,7 +30,7 @@ public OWRootHandler(BinaryReader stream, BackgroundWorkerEx worker, CASCHandler { // add apm file for dev purposes ulong fileHash1 = Hasher.ComputeHash(name); - byte[] md5 = filedata[0].ToByteArray(); + MD5Hash md5 = filedata[0].ToByteArray().ToMD5(); RootData[fileHash1] = new RootEntry() { MD5 = md5, Block = RootBlock.Empty }; CASCFile.FileNames[fileHash1] = name; @@ -55,7 +55,7 @@ public OWRootHandler(BinaryReader stream, BackgroundWorkerEx worker, CASCHandler s.Position += 0x2C; // skip unknown int size = br.ReadInt32(); // size (matches size in encoding file) s.Position += 8; // skip unknown - byte[] md5_2 = br.ReadBytes(16); + MD5Hash md5_2 = br.Read(); EncodingEntry enc2 = casc.Encoding.GetEntry(md5_2); @@ -87,13 +87,17 @@ public OWRootHandler(BinaryReader stream, BackgroundWorkerEx worker, CASCHandler return LocaleFlags.All; }; + MD5Hash key; + foreach (var entry in casc.Encoding.Entries) { DownloadEntry dl = casc.Download.GetEntry(entry.Value.Key); if (dl != null) { - string fakeName = "unknown" + "/" + entry.Key[0].ToString("X2") + "/" + entry.Key.ToHexString(); + key = entry.Key; + + string fakeName = "unknown" + "/" + key.Value[0].ToString("X2") + "/" + entry.Key.ToHexString(); var locales = dl.Tags.Where(tag => tag.Value.Type == 4).Select(tag => tag2locale(tag.Key)); diff --git a/CascLib/RootHandlerBase.cs b/CascLib/RootHandlerBase.cs index acc9ec35..ac9c4629 100644 --- a/CascLib/RootHandlerBase.cs +++ b/CascLib/RootHandlerBase.cs @@ -57,7 +57,6 @@ protected void CreateSubTree(CASCFolder root, ulong filehash, string file) } folder.Entries[entryName] = entry; - folder.EntriesMirror[entryName] = entry; } folder = entry as CASCFolder; diff --git a/CascLib/WowRootHandler.cs b/CascLib/WowRootHandler.cs index 1a09e279..5f521820 100644 --- a/CascLib/WowRootHandler.cs +++ b/CascLib/WowRootHandler.cs @@ -47,11 +47,16 @@ public class RootBlock public LocaleFlags LocaleFlags; } + public unsafe struct MD5Hash + { + public fixed byte Value[16]; + } + public class RootEntry { public RootBlock Block; public int FileDataId; - public byte[] MD5; + public MD5Hash MD5; public override string ToString() { @@ -104,7 +109,7 @@ public WowRootHandler(BinaryReader stream, BackgroundWorkerEx worker) for (var i = 0; i < count; ++i) { - entries[i].MD5 = stream.ReadBytes(16); + entries[i].MD5 = stream.Read(); ulong hash = stream.ReadUInt64(); From 733588e7c54f920c3680c809e84892e8029012ae Mon Sep 17 00:00:00 2001 From: TOM_RUS Date: Mon, 15 Feb 2016 07:13:57 +0300 Subject: [PATCH 04/17] Fix --- CascLib/Extensions.cs | 16 ---------------- 1 file changed, 16 deletions(-) diff --git a/CascLib/Extensions.cs b/CascLib/Extensions.cs index 18d4f8ee..58079ba6 100644 --- a/CascLib/Extensions.cs +++ b/CascLib/Extensions.cs @@ -89,14 +89,6 @@ public static bool EqualsToIgnoreLength(this byte[] array, byte[] other) return true; } - public static bool IsZeroed(this byte[] array) - { - for (var i = 0; i < array.Length; ++i) - if (array[i] != 0) - return false; - return true; - } - public static byte[] Copy(this byte[] array, int len) { byte[] ret = new byte[len]; @@ -117,14 +109,6 @@ public static string ToBinaryString(this BitArray bits) return sb.ToString(); } - public static unsafe bool EqualsTo(this MD5Hash key, MD5Hash other) - { - for (var i = 0; i < 16; ++i) - if (key.Value[i] != other.Value[i]) - return false; - return true; - } - public static unsafe bool EqualsTo(this MD5Hash key, byte[] array) { if (array.Length != 16) From 07a356ecb578ac8b10037e0f2b86277063b1a59c Mon Sep 17 00:00:00 2001 From: TOM_RUS Date: Mon, 15 Feb 2016 09:57:48 +0300 Subject: [PATCH 05/17] Fix --- CASCExplorer/CASCViewHelper.cs | 71 ++++++++++++++++++++-------------- CascLib/CASCEntry.cs | 13 +++---- CascLib/CASCHandler.cs | 48 +++++++---------------- CascLib/CASCHandlerBase.cs | 12 +++--- CascLib/CASCHandlerLite.cs | 10 ++--- CascLib/D3RootHandler.cs | 27 +++++++------ CascLib/EncodingHandler.cs | 10 ++--- CascLib/Extensions.cs | 10 +++-- CascLib/MNDXRootHandler.cs | 12 +++--- CascLib/MultiDictionary.cs | 6 +-- CascLib/OWRootHandler.cs | 17 ++++---- CascLib/WowRootHandler.cs | 54 ++++++++++---------------- 12 files changed, 138 insertions(+), 152 deletions(-) diff --git a/CASCExplorer/CASCViewHelper.cs b/CASCExplorer/CASCViewHelper.cs index 7c5c3e45..3678e393 100644 --- a/CASCExplorer/CASCViewHelper.cs +++ b/CASCExplorer/CASCViewHelper.cs @@ -86,7 +86,10 @@ await Task.Run(() => foreach (var file in installFiles) { - _casc.ExtractFile(_casc.Encoding.GetEntry(file.MD5).Key, Path.Combine("data", build, "install_files"), file.Name); + EncodingEntry enc; + + if (_casc.Encoding.GetEntry(file.MD5, out enc)) + _casc.ExtractFile(enc.Key, Path.Combine("data", build, "install_files"), file.Name); progress.Report((int)(++numDone / (float)numFiles * 100.0f)); } @@ -182,12 +185,14 @@ await Task.Run(() => int numTotal = files.Count(); int numDone = 0; + WowRootHandler wowRoot = _casc.Root as WowRootHandler; + foreach (var unknownEntry in files) { CASCFile unknownFile = unknownEntry as CASCFile; string name; - if (idToName.TryGetValue(_casc.Root.GetEntries(unknownFile.Hash).First().FileDataId, out name)) + if (idToName.TryGetValue(wowRoot.GetFileDataIdByHash(unknownFile.Hash), out name)) unknownFile.FullName = name; else { @@ -454,16 +459,16 @@ public void CreateListViewItem(RetrieveVirtualItemEventArgs e) if (rootInfosLocale.Any()) { - var enc = _casc.Encoding.GetEntry(rootInfosLocale.First().MD5); + EncodingEntry enc; - size = enc?.Size.ToString("N", sizeNumberFmt) ?? "0"; - - foreach (var rootInfo in rootInfosLocale) + if (_casc.Encoding.GetEntry(rootInfosLocale.First().MD5, out enc)) { - if (rootInfo.Block != null) + size = enc.Size.ToString("N", sizeNumberFmt) ?? "0"; + + foreach (var rootInfo in rootInfosLocale) { - localeFlags |= rootInfo.Block.LocaleFlags; - contentFlags |= rootInfo.Block.ContentFlags; + localeFlags |= rootInfo.LocaleFlags; + contentFlags |= rootInfo.ContentFlags; } } } @@ -473,18 +478,21 @@ public void CreateListViewItem(RetrieveVirtualItemEventArgs e) if (installInfos.Any()) { - var enc = _casc.Encoding.GetEntry(installInfos.First().MD5); - - size = enc?.Size.ToString("N", sizeNumberFmt) ?? "0"; - - //foreach (var rootInfo in rootInfosLocale) - //{ - // if (rootInfo.Block != null) - // { - // localeFlags |= rootInfo.Block.LocaleFlags; - // contentFlags |= rootInfo.Block.ContentFlags; - // } - //} + EncodingEntry enc; + + if (_casc.Encoding.GetEntry(installInfos.First().MD5, out enc)) + { + size = enc.Size.ToString("N", sizeNumberFmt) ?? "0"; + + //foreach (var rootInfo in rootInfosLocale) + //{ + // if (rootInfo.Block != null) + // { + // localeFlags |= rootInfo.Block.LocaleFlags; + // contentFlags |= rootInfo.Block.ContentFlags; + // } + //} + } } } } @@ -565,13 +573,20 @@ public void ExtractCASCSystemFiles() if (_casc == null) return; - var files = new Dictionary() - { - { "root", _casc.Encoding.GetEntry(_casc.Config.RootMD5).Key }, - { "install", _casc.Encoding.GetEntry(_casc.Config.InstallMD5).Key }, - { "encoding", _casc.Config.EncodingKey }, - { "download", _casc.Encoding.GetEntry(_casc.Config.DownloadMD5).Key } - }; + EncodingEntry enc; + + var files = new Dictionary(); + + files.Add("encoding", _casc.Config.EncodingKey); + + if (_casc.Encoding.GetEntry(_casc.Config.RootMD5, out enc)) + files.Add("root", enc.Key); + + if (_casc.Encoding.GetEntry(_casc.Config.InstallMD5, out enc)) + files.Add("install", enc.Key); + + if (_casc.Encoding.GetEntry(_casc.Config.DownloadMD5, out enc)) + files.Add("download", enc.Key); foreach (var file in files) { diff --git a/CascLib/CASCEntry.cs b/CascLib/CASCEntry.cs index a7826292..644ce443 100644 --- a/CascLib/CASCEntry.cs +++ b/CascLib/CASCEntry.cs @@ -141,9 +141,8 @@ public ulong Hash public int GetSize(CASCHandler casc) { - var encoding = casc.GetEncodingEntry(hash); - - return encoding?.Size ?? 0; + EncodingEntry enc; + return casc.GetEncodingEntry(hash, out enc) ? enc.Size : 0; } public int CompareTo(ICASCEntry other, int col, CASCHandler casc) @@ -165,8 +164,8 @@ public int CompareTo(ICASCEntry other, int col, CASCHandler casc) { var e1 = casc.Root.GetEntries(Hash); var e2 = casc.Root.GetEntries(other.Hash); - var flags1 = e1.Any() ? e1.First().Block.LocaleFlags : LocaleFlags.None; - var flags2 = e2.Any() ? e2.First().Block.LocaleFlags : LocaleFlags.None; + var flags1 = e1.Any() ? e1.First().LocaleFlags : LocaleFlags.None; + var flags2 = e2.Any() ? e2.First().LocaleFlags : LocaleFlags.None; result = flags1.CompareTo(flags2); } break; @@ -174,8 +173,8 @@ public int CompareTo(ICASCEntry other, int col, CASCHandler casc) { var e1 = casc.Root.GetEntries(Hash); var e2 = casc.Root.GetEntries(other.Hash); - var flags1 = e1.Any() ? e1.First().Block.ContentFlags : ContentFlags.None; - var flags2 = e2.Any() ? e2.First().Block.ContentFlags : ContentFlags.None; + var flags1 = e1.Any() ? e1.First().ContentFlags : ContentFlags.None; + var flags2 = e2.Any() ? e2.First().ContentFlags : ContentFlags.None; result = flags1.CompareTo(flags2); } break; diff --git a/CascLib/CASCHandler.cs b/CascLib/CASCHandler.cs index 4bb5894a..67819e77 100644 --- a/CascLib/CASCHandler.cs +++ b/CascLib/CASCHandler.cs @@ -80,10 +80,7 @@ private CASCHandler(CASCConfig config, BackgroundWorkerEx worker) : base(config, } } - public static CASCHandler OpenStorage(CASCConfig config, BackgroundWorkerEx worker = null) - { - return Open(worker, config); - } + public static CASCHandler OpenStorage(CASCConfig config, BackgroundWorkerEx worker = null) => Open(worker, config); public static CASCHandler OpenLocalStorage(string basePath, BackgroundWorkerEx worker = null) { @@ -117,32 +114,25 @@ public bool FileExists(int fileDataId) return FileExists(rh.GetHashByFileDataId(fileDataId)); } - public bool FileExists(string file) - { - var hash = Hasher.ComputeHash(file); - return FileExists(hash); - } + public bool FileExists(string file) => FileExists(Hasher.ComputeHash(file)); - public bool FileExists(ulong hash) - { - var rootInfos = RootHandler.GetAllEntries(hash); - return rootInfos?.Any() ?? false; - } + public bool FileExists(ulong hash) => RootHandler.GetAllEntries(hash).Any(); - public EncodingEntry GetEncodingEntry(ulong hash) + public bool GetEncodingEntry(ulong hash, out EncodingEntry enc) { var rootInfos = RootHandler.GetEntries(hash); if (rootInfos.Any()) - return EncodingHandler.GetEntry(rootInfos.First().MD5); + return EncodingHandler.GetEntry(rootInfos.First().MD5, out enc); if ((CASCConfig.LoadFlags & LoadFlags.Install) != 0) { var installInfos = Install.GetEntries().Where(e => Hasher.ComputeHash(e.Name) == hash); if (installInfos.Any()) - return EncodingHandler.GetEntry(installInfos.First().MD5); + return EncodingHandler.GetEntry(installInfos.First().MD5, out enc); } - return null; + enc = default(EncodingEntry); + return false; } public override Stream OpenFile(int fileDataId) @@ -157,18 +147,13 @@ public override Stream OpenFile(int fileDataId) return null; } - public override Stream OpenFile(string name) - { - var hash = Hasher.ComputeHash(name); - - return OpenFile(hash); - } + public override Stream OpenFile(string name) => OpenFile(Hasher.ComputeHash(name)); public override Stream OpenFile(ulong hash) { - EncodingEntry encInfo = GetEncodingEntry(hash); + EncodingEntry encInfo; - if (encInfo != null) + if (GetEncodingEntry(hash, out encInfo)) return OpenFile(encInfo.Key); if (CASCConfig.ThrowOnFileNotFound) @@ -176,18 +161,13 @@ public override Stream OpenFile(ulong hash) return null; } - public void SaveFileTo(string fullName, string extractPath) - { - var hash = Hasher.ComputeHash(fullName); - - SaveFileTo(hash, extractPath, fullName); - } + public void SaveFileTo(string fullName, string extractPath) => SaveFileTo(Hasher.ComputeHash(fullName), extractPath, fullName); public void SaveFileTo(ulong hash, string extractPath, string fullName) { - EncodingEntry encInfo = GetEncodingEntry(hash); + EncodingEntry encInfo; - if (encInfo != null) + if (GetEncodingEntry(hash, out encInfo)) { ExtractFile(encInfo.Key, extractPath, fullName); return; diff --git a/CascLib/CASCHandlerBase.cs b/CascLib/CASCHandlerBase.cs index 82e27deb..66ae6042 100644 --- a/CascLib/CASCHandlerBase.cs +++ b/CascLib/CASCHandlerBase.cs @@ -176,9 +176,9 @@ private void ExtractFileLocal(MD5Hash key, string path, string name) protected static BinaryReader OpenInstallFile(EncodingHandler enc, CASCHandlerBase casc) { - var encInfo = enc.GetEntry(casc.Config.InstallMD5); + EncodingEntry encInfo; - if (encInfo == null) + if (!enc.GetEntry(casc.Config.InstallMD5, out encInfo)) throw new FileNotFoundException("encoding info for install file missing!"); //ExtractFile(encInfo.Key, ".", "install"); @@ -188,9 +188,9 @@ protected static BinaryReader OpenInstallFile(EncodingHandler enc, CASCHandlerBa protected BinaryReader OpenDownloadFile(EncodingHandler enc, CASCHandlerBase casc) { - var encInfo = enc.GetEntry(casc.Config.DownloadMD5); + EncodingEntry encInfo; - if (encInfo == null) + if (!enc.GetEntry(casc.Config.DownloadMD5, out encInfo)) throw new FileNotFoundException("encoding info for download file missing!"); //ExtractFile(encInfo.Key, ".", "download"); @@ -200,9 +200,9 @@ protected BinaryReader OpenDownloadFile(EncodingHandler enc, CASCHandlerBase cas protected BinaryReader OpenRootFile(EncodingHandler enc, CASCHandlerBase casc) { - var encInfo = enc.GetEntry(casc.Config.RootMD5); + EncodingEntry encInfo; - if (encInfo == null) + if (!enc.GetEntry(casc.Config.RootMD5, out encInfo)) throw new FileNotFoundException("encoding info for root file missing!"); //ExtractFile(encInfo.Key, ".", "root"); diff --git a/CascLib/CASCHandlerLite.cs b/CascLib/CASCHandlerLite.cs index a6c00cf9..da50b654 100644 --- a/CascLib/CASCHandlerLite.cs +++ b/CascLib/CASCHandlerLite.cs @@ -28,7 +28,7 @@ private CASCHandlerLite(CASCConfig config, LocaleFlags locale, BackgroundWorkerE Logger.WriteLine("CASCHandlerLite: loading root data..."); - RootHandlerBase RootHandler; + WowRootHandler RootHandler; using (var _ = new PerfCounter("new RootHandler()")) { @@ -46,16 +46,16 @@ private CASCHandlerLite(CASCConfig config, LocaleFlags locale, BackgroundWorkerE { rootEntry = entry.Value; - if ((rootEntry.Block.LocaleFlags == locale || (rootEntry.Block.LocaleFlags & locale) != LocaleFlags.None) && (rootEntry.Block.ContentFlags & ContentFlags.LowViolence) == ContentFlags.None) + if ((rootEntry.LocaleFlags == locale || (rootEntry.LocaleFlags & locale) != LocaleFlags.None) && (rootEntry.ContentFlags & ContentFlags.LowViolence) == ContentFlags.None) { - var enc = EncodingHandler.GetEntry(rootEntry.MD5); + EncodingEntry enc; - if (enc != null) + if (EncodingHandler.GetEntry(rootEntry.MD5, out enc)) { if (!HashToKey.ContainsKey(entry.Key)) { HashToKey.Add(entry.Key, enc.Key); - FileDataIdToHash.Add(rootEntry.FileDataId, entry.Key); + FileDataIdToHash.Add(RootHandler.GetFileDataIdByHash(entry.Key), entry.Key); } } } diff --git a/CascLib/D3RootHandler.cs b/CascLib/D3RootHandler.cs index 149b2d0f..10b58540 100644 --- a/CascLib/D3RootHandler.cs +++ b/CascLib/D3RootHandler.cs @@ -6,10 +6,10 @@ namespace CASCExplorer { - class D3RootEntry + struct D3RootEntry { - public int Type; public MD5Hash MD5; + public int Type; public int SNO; public int FileIndex; public string Name; @@ -66,7 +66,10 @@ public D3RootHandler(BinaryReader stream, BackgroundWorkerEx worker, CASCHandler var entries = new List(); D3RootData[name] = entries; - EncodingEntry enc = casc.Encoding.GetEntry(md5); + EncodingEntry enc; + + if (!casc.Encoding.GetEntry(md5, out enc)) + continue; using (BinaryReader s = new BinaryReader(casc.OpenFile(enc.Key))) { @@ -103,7 +106,8 @@ public D3RootHandler(BinaryReader stream, BackgroundWorkerEx worker, CASCHandler // Parse CoreTOC.dat var coreTocEntry = D3RootData["Base"].Find(e => e.Name == "CoreTOC.dat"); - EncodingEntry enc1 = casc.Encoding.GetEntry(coreTocEntry.MD5); + EncodingEntry enc1; + casc.Encoding.GetEntry(coreTocEntry.MD5, out enc1); using (var file = casc.OpenFile(enc1.Key)) tocParser = new CoreTOCParser(file); @@ -113,7 +117,8 @@ public D3RootHandler(BinaryReader stream, BackgroundWorkerEx worker, CASCHandler // Parse Packages.dat var pkgEntry = D3RootData["Base"].Find(e => e.Name == "Data_D3\\PC\\Misc\\Packages.dat"); - EncodingEntry enc2 = casc.Encoding.GetEntry(pkgEntry.MD5); + EncodingEntry enc2; + casc.Encoding.GetEntry(pkgEntry.MD5, out enc2); using (var file = casc.OpenFile(enc2.Key)) pkgParser = new PackagesParser(file); @@ -144,7 +149,7 @@ public override IEnumerable> GetAllEntries() public override IEnumerable GetAllEntries(ulong hash) { - HashSet result; + List result; RootData.TryGetValue(hash, out result); if (result == null) @@ -161,7 +166,7 @@ public override IEnumerable GetEntries(ulong hash) if (!rootInfos.Any()) yield break; - var rootInfosLocale = rootInfos.Where(re => (re.Block.LocaleFlags & Locale) != 0); + var rootInfosLocale = rootInfos.Where(re => (re.LocaleFlags & Locale) != 0); foreach (var entry in rootInfosLocale) yield return entry; @@ -204,12 +209,10 @@ private void AddFile(string pkg, D3RootEntry e) LocaleFlags locale; - entry.Block = new RootBlock(); - if (Enum.TryParse(pkg, out locale)) - entry.Block.LocaleFlags = locale; + entry.LocaleFlags = locale; else - entry.Block.LocaleFlags = LocaleFlags.All; + entry.LocaleFlags = LocaleFlags.All; ulong fileHash = Hasher.ComputeHash(name); CASCFile.FileNames[fileHash] = name; @@ -249,7 +252,7 @@ protected override CASCFolder CreateStorageTree() // Create new tree based on specified locale foreach (var rootEntry in RootData) { - var rootInfosLocale = rootEntry.Value.Where(re => (re.Block.LocaleFlags & Locale) != 0); + var rootInfosLocale = rootEntry.Value.Where(re => (re.LocaleFlags & Locale) != 0); if (!rootInfosLocale.Any()) continue; diff --git a/CascLib/EncodingHandler.cs b/CascLib/EncodingHandler.cs index 8a295ed6..46d79088 100644 --- a/CascLib/EncodingHandler.cs +++ b/CascLib/EncodingHandler.cs @@ -3,10 +3,10 @@ namespace CASCExplorer { - public class EncodingEntry + public struct EncodingEntry { - public int Size; public MD5Hash Key; + public int Size; } public class EncodingHandler @@ -120,11 +120,9 @@ public IEnumerable> Entries } } - public EncodingEntry GetEntry(MD5Hash md5) + public bool GetEntry(MD5Hash md5, out EncodingEntry enc) { - EncodingEntry result; - EncodingData.TryGetValue(md5, out result); - return result; + return EncodingData.TryGetValue(md5, out enc); } public void Clear() diff --git a/CascLib/Extensions.cs b/CascLib/Extensions.cs index 58079ba6..fd2a77a2 100644 --- a/CascLib/Extensions.cs +++ b/CascLib/Extensions.cs @@ -2,7 +2,6 @@ using System.Collections; using System.Collections.Generic; using System.IO; -using System.Linq; using System.Runtime.InteropServices; using System.Text; @@ -12,7 +11,8 @@ public static class Extensions { public static int ReadInt32BE(this BinaryReader reader) { - return BitConverter.ToInt32(reader.ReadBytes(4).Reverse().ToArray(), 0); + byte[] val = reader.ReadBytes(4); + return val[3] | val[2] << 8 | val[1] << 16 | val[0] << 24; } public static void Skip(this BinaryReader reader, int bytes) @@ -22,7 +22,8 @@ public static void Skip(this BinaryReader reader, int bytes) public static uint ReadUInt32BE(this BinaryReader reader) { - return BitConverter.ToUInt32(reader.ReadBytes(4).Reverse().ToArray(), 0); + byte[] val = reader.ReadBytes(4); + return (uint)(val[3] | val[2] << 8 | val[1] << 16 | val[0] << 24); } public static T Read(this BinaryReader reader) where T : struct @@ -52,7 +53,8 @@ public static T[] ReadArray(this BinaryReader reader) where T : struct public static short ReadInt16BE(this BinaryReader reader) { - return BitConverter.ToInt16(reader.ReadBytes(2).Reverse().ToArray(), 0); + byte[] val = reader.ReadBytes(2); + return (short)(val[1] | val[0] << 8); } public static void CopyBytes(this Stream input, Stream output, int bytes) diff --git a/CascLib/MNDXRootHandler.cs b/CascLib/MNDXRootHandler.cs index 068903c3..d0de00fb 100644 --- a/CascLib/MNDXRootHandler.cs +++ b/CascLib/MNDXRootHandler.cs @@ -41,11 +41,9 @@ public struct NAME_FRAG class CASC_ROOT_ENTRY_MNDX { - public int Flags; // High 8 bits: Flags, low 24 bits: package index - //[MarshalAs(UnmanagedType.ByValArray, SizeConst = 0x10)] public MD5Hash MD5; // Encoding key for the file + public int Flags; // High 8 bits: Flags, low 24 bits: package index public int FileSize; // Uncompressed file size, in bytes - public CASC_ROOT_ENTRY_MNDX Next; } @@ -218,9 +216,8 @@ public override IEnumerable> GetAllEntries() public override IEnumerable GetAllEntries(ulong hash) { RootEntry rootEntry; - mndxData.TryGetValue(hash, out rootEntry); - if (rootEntry != null) + if (mndxData.TryGetValue(hash, out rootEntry)) yield return rootEntry; else yield break; @@ -383,7 +380,8 @@ public override void LoadListFile(string path, BackgroundWorkerEx worker = null) RootEntry entry = new RootEntry(); int package = FindMNDXPackage(file); - entry.Block = new RootBlock() { LocaleFlags = PackagesLocale[package], ContentFlags = ContentFlags.None }; + entry.LocaleFlags = PackagesLocale[package]; + entry.ContentFlags = ContentFlags.None; entry.MD5 = FindMNDXInfo(file, package).MD5; mndxData[fileHash] = entry; @@ -407,7 +405,7 @@ protected override CASCFolder CreateStorageTree() foreach (var entry in mndxData) { - if ((entry.Value.Block.LocaleFlags & Locale) == 0) + if ((entry.Value.LocaleFlags & Locale) == 0) continue; CreateSubTree(root, entry.Key, CASCFile.FileNames[entry.Key]); diff --git a/CascLib/MultiDictionary.cs b/CascLib/MultiDictionary.cs index 126c595d..8afd308d 100644 --- a/CascLib/MultiDictionary.cs +++ b/CascLib/MultiDictionary.cs @@ -2,18 +2,18 @@ namespace CASCExplorer { - public class MultiDictionary : Dictionary> + public class MultiDictionary : Dictionary> { public void Add(K key, V value) { - HashSet hset; + List hset; if (TryGetValue(key, out hset)) { hset.Add(value); } else { - hset = new HashSet(); + hset = new List(); hset.Add(value); base[key] = hset; } diff --git a/CascLib/OWRootHandler.cs b/CascLib/OWRootHandler.cs index 950eec8f..1c154c03 100644 --- a/CascLib/OWRootHandler.cs +++ b/CascLib/OWRootHandler.cs @@ -31,12 +31,15 @@ public unsafe OWRootHandler(BinaryReader stream, BackgroundWorkerEx worker, CASC // add apm file for dev purposes ulong fileHash1 = Hasher.ComputeHash(name); MD5Hash md5 = filedata[0].ToByteArray().ToMD5(); - RootData[fileHash1] = new RootEntry() { MD5 = md5, Block = RootBlock.Empty }; + RootData[fileHash1] = new RootEntry() { MD5 = md5, LocaleFlags = LocaleFlags.All, ContentFlags = ContentFlags.None }; CASCFile.FileNames[fileHash1] = name; // add files listed in apm file - EncodingEntry enc = casc.Encoding.GetEntry(md5); + EncodingEntry enc; + + if (!casc.Encoding.GetEntry(md5, out enc)) + continue; using (Stream s = casc.OpenFile(enc.Key)) using (BinaryReader br = new BinaryReader(s)) @@ -57,9 +60,9 @@ public unsafe OWRootHandler(BinaryReader stream, BackgroundWorkerEx worker, CASC s.Position += 8; // skip unknown MD5Hash md5_2 = br.Read(); - EncodingEntry enc2 = casc.Encoding.GetEntry(md5_2); + EncodingEntry enc2; - if (enc2 == null) + if (!casc.Encoding.GetEntry(md5_2, out enc2)) { throw new Exception("enc2 == null"); } @@ -67,7 +70,7 @@ public unsafe OWRootHandler(BinaryReader stream, BackgroundWorkerEx worker, CASC string fakeName = Path.GetFileNameWithoutExtension(name) + "/" + md5_2.ToHexString(); ulong fileHash = Hasher.ComputeHash(fakeName); - RootData[fileHash] = new RootEntry() { MD5 = md5_2, Block = RootBlock.Empty }; + RootData[fileHash] = new RootEntry() { MD5 = md5_2, LocaleFlags = LocaleFlags.All, ContentFlags = ContentFlags.None }; CASCFile.FileNames[fileHash] = fakeName; } @@ -107,7 +110,7 @@ public unsafe OWRootHandler(BinaryReader stream, BackgroundWorkerEx worker, CASC locale |= loc; ulong fileHash = Hasher.ComputeHash(fakeName); - RootData.Add(fileHash, new RootEntry() { MD5 = entry.Key, Block = new RootBlock() { LocaleFlags = locale } }); + RootData.Add(fileHash, new RootEntry() { MD5 = entry.Key, LocaleFlags = locale, ContentFlags = ContentFlags.None }); CASCFile.FileNames[fileHash] = fakeName; } @@ -160,7 +163,7 @@ protected override CASCFolder CreateStorageTree() foreach (var rootEntry in RootData) { - if ((rootEntry.Value.Block.LocaleFlags & Locale) == 0) + if ((rootEntry.Value.LocaleFlags & Locale) == 0) continue; CreateSubTree(root, rootEntry.Key, CASCFile.FileNames[rootEntry.Key]); diff --git a/CascLib/WowRootHandler.cs b/CascLib/WowRootHandler.cs index 5f521820..d365329f 100644 --- a/CascLib/WowRootHandler.cs +++ b/CascLib/WowRootHandler.cs @@ -40,28 +40,16 @@ public enum ContentFlags : uint NoCompression = 0x80000000 // sounds have this flag } - public class RootBlock - { - public static readonly RootBlock Empty = new RootBlock() { ContentFlags = ContentFlags.None, LocaleFlags = LocaleFlags.All }; - public ContentFlags ContentFlags; - public LocaleFlags LocaleFlags; - } - public unsafe struct MD5Hash { public fixed byte Value[16]; } - public class RootEntry + public struct RootEntry { - public RootBlock Block; - public int FileDataId; public MD5Hash MD5; - - public override string ToString() - { - return string.Format("RootBlock: {0:X8} {1:X8}, File: {2:X8} {3}", Block.ContentFlags, Block.LocaleFlags, FileDataId, MD5.ToHexString()); - } + public ContentFlags ContentFlags; + public LocaleFlags LocaleFlags; } public class WowRootHandler : RootHandlerBase @@ -82,27 +70,27 @@ public WowRootHandler(BinaryReader stream, BackgroundWorkerEx worker) { int count = stream.ReadInt32(); - RootBlock block = new RootBlock(); - block.ContentFlags = (ContentFlags)stream.ReadUInt32(); - block.LocaleFlags = (LocaleFlags)stream.ReadUInt32(); + ContentFlags contentFlags = (ContentFlags)stream.ReadUInt32(); + LocaleFlags localeFlags = (LocaleFlags)stream.ReadUInt32(); - if (block.LocaleFlags == LocaleFlags.None) + if (localeFlags == LocaleFlags.None) throw new Exception("block.LocaleFlags == LocaleFlags.None"); - if (block.ContentFlags != ContentFlags.None && (block.ContentFlags & (ContentFlags.LowViolence | ContentFlags.NoCompression)) == 0) + if (contentFlags != ContentFlags.None && (contentFlags & (ContentFlags.LowViolence | ContentFlags.NoCompression)) == 0) throw new Exception("block.ContentFlags != ContentFlags.None"); RootEntry[] entries = new RootEntry[count]; + int[] filedataIds = new int[count]; int fileDataIndex = 0; for (var i = 0; i < count; ++i) { - entries[i] = new RootEntry(); - entries[i].Block = block; - entries[i].FileDataId = fileDataIndex + stream.ReadInt32(); + entries[i].LocaleFlags = localeFlags; + entries[i].ContentFlags = contentFlags; - fileDataIndex = entries[i].FileDataId + 1; + filedataIds[i] = fileDataIndex + stream.ReadInt32(); + fileDataIndex = filedataIds[i] + 1; } //Console.WriteLine("Block: {0} {1} (size {2})", block.ContentFlags, block.LocaleFlags, count); @@ -119,7 +107,7 @@ public WowRootHandler(BinaryReader stream, BackgroundWorkerEx worker) ulong hash2; - int fileDataId = entries[i].FileDataId; + int fileDataId = filedataIds[i]; if (FileDataStore.TryGetValue(fileDataId, out hash2)) { @@ -157,7 +145,7 @@ public override IEnumerable> GetAllEntries() public override IEnumerable GetAllEntries(ulong hash) { - HashSet result; + List result; RootData.TryGetValue(hash, out result); if (result == null) @@ -180,11 +168,11 @@ public override IEnumerable GetEntries(ulong hash) if (!rootInfos.Any()) yield break; - var rootInfosLocale = rootInfos.Where(re => (re.Block.LocaleFlags & Locale) != 0); + var rootInfosLocale = rootInfos.Where(re => (re.LocaleFlags & Locale) != 0); if (rootInfosLocale.Count() > 1) { - var rootInfosLocaleAndContent = rootInfosLocale.Where(re => (re.Block.ContentFlags == Content)); + var rootInfosLocaleAndContent = rootInfosLocale.Where(re => (re.ContentFlags == Content)); if (rootInfosLocaleAndContent.Any()) rootInfosLocale = rootInfosLocaleAndContent; @@ -399,11 +387,11 @@ protected override CASCFolder CreateStorageTree() // Create new tree based on specified locale foreach (var rootEntry in RootData) { - var rootInfosLocale = rootEntry.Value.Where(re => (re.Block.LocaleFlags & Locale) != 0); + var rootInfosLocale = rootEntry.Value.Where(re => (re.LocaleFlags & Locale) != 0); if (rootInfosLocale.Count() > 1) { - var rootInfosLocaleAndContent = rootInfosLocale.Where(re => (re.Block.ContentFlags == Content)); + var rootInfosLocaleAndContent = rootInfosLocale.Where(re => (re.ContentFlags == Content)); if (rootInfosLocaleAndContent.Any()) rootInfosLocale = rootInfosLocaleAndContent; @@ -416,7 +404,7 @@ protected override CASCFolder CreateStorageTree() if (!CASCFile.FileNames.TryGetValue(rootEntry.Key, out file)) { - file = "unknown\\" + rootEntry.Key.ToString("X16") + "_" + rootEntry.Value.First().FileDataId; + file = "unknown\\" + rootEntry.Key.ToString("X16") + "_" + GetFileDataIdByHash(rootEntry.Key); CountUnknown++; UnknownFiles.Add(rootEntry.Key); @@ -448,14 +436,14 @@ public override void Clear() public override void Dump() { - foreach (var fd in RootData.OrderBy(r => r.Value.First().FileDataId)) + foreach (var fd in RootData.OrderBy(r => GetFileDataIdByHash(r.Key))) { string name; if (!CASCFile.FileNames.TryGetValue(fd.Key, out name)) name = fd.Key.ToString("X16"); - Logger.WriteLine("{0:D7} {1:X16} {2} {3}", fd.Value.First().FileDataId, fd.Key, string.Join(",", fd.Value.Select(r => r.Block.LocaleFlags.ToString())), name); + Logger.WriteLine("{0:D7} {1:X16} {2} {3}", GetFileDataIdByHash(fd.Key), fd.Key, string.Join(",", fd.Value.Select(r => r.LocaleFlags.ToString())), name); } } } From 0e7e32f66205683b58490f932ef28b062ef1561e Mon Sep 17 00:00:00 2001 From: TOM_RUS Date: Tue, 16 Feb 2016 02:29:58 +0300 Subject: [PATCH 06/17] Fix --- CascLib/DownloadHandler.cs | 18 ++++++++++-------- CascLib/WowRootHandler.cs | 29 +++++------------------------ 2 files changed, 15 insertions(+), 32 deletions(-) diff --git a/CascLib/DownloadHandler.cs b/CascLib/DownloadHandler.cs index d3457561..aca97208 100644 --- a/CascLib/DownloadHandler.cs +++ b/CascLib/DownloadHandler.cs @@ -8,9 +8,9 @@ namespace CASCExplorer public class DownloadEntry { public int Index; - public byte[] Unk; + //public byte[] Unk; - public Dictionary Tags; + public IEnumerable> Tags; } public class DownloadTag @@ -23,7 +23,7 @@ public class DownloadHandler { private static readonly MD5HashComparer comparer = new MD5HashComparer(); private readonly Dictionary DownloadData = new Dictionary(comparer); - Dictionary Tags = new Dictionary(); + private readonly Dictionary Tags = new Dictionary(); public int Count { @@ -50,9 +50,11 @@ public DownloadHandler(BinaryReader stream, BackgroundWorkerEx worker) { MD5Hash key = stream.Read(); - byte[] unk = stream.ReadBytes(0xA); + //byte[] unk = stream.ReadBytes(0xA); + stream.Skip(0xA); - var entry = new DownloadEntry() { Index = i, Unk = unk }; + //var entry = new DownloadEntry() { Index = i, Unk = unk }; + var entry = new DownloadEntry() { Index = i }; DownloadData.Add(key, entry); @@ -81,9 +83,9 @@ public void Dump() foreach (var entry in DownloadData) { if (entry.Value.Tags == null) - entry.Value.Tags = Tags.Where(kv => kv.Value.Bits[entry.Value.Index]).ToDictionary(kv => kv.Key, kv => kv.Value); + entry.Value.Tags = Tags.Where(kv => kv.Value.Bits[entry.Value.Index]); - Logger.WriteLine("{0} {1} {2}", entry.Key.ToHexString(), entry.Value.Unk.ToHexString(), string.Join(",", entry.Value.Tags.Select(tag => tag.Key))); + Logger.WriteLine("{0} {1}", entry.Key.ToHexString(), string.Join(",", entry.Value.Tags.Select(tag => tag.Key))); } } @@ -93,7 +95,7 @@ public DownloadEntry GetEntry(MD5Hash key) DownloadData.TryGetValue(key, out entry); if (entry != null && entry.Tags == null) - entry.Tags = Tags.Where(kv => kv.Value.Bits[entry.Index]).ToDictionary(kv => kv.Key, kv => kv.Value); + entry.Tags = Tags.Where(kv => kv.Value.Bits[entry.Index]); return entry; } diff --git a/CascLib/WowRootHandler.cs b/CascLib/WowRootHandler.cs index d365329f..9324067b 100644 --- a/CascLib/WowRootHandler.cs +++ b/CascLib/WowRootHandler.cs @@ -57,7 +57,6 @@ public class WowRootHandler : RootHandlerBase private readonly MultiDictionary RootData = new MultiDictionary(); private readonly Dictionary FileDataStore = new Dictionary(); private readonly Dictionary FileDataStoreReverse = new Dictionary(); - private readonly HashSet UnknownFiles = new HashSet(); public override int Count { get { return RootData.Count; } } public override int CountTotal { get { return RootData.Sum(re => re.Value.Count); } } @@ -131,10 +130,7 @@ public WowRootHandler(BinaryReader stream, BackgroundWorkerEx worker) } } - public IEnumerable GetAllEntriesByFileDataId(int fileDataId) - { - return GetAllEntries(GetHashByFileDataId(fileDataId)); - } + public IEnumerable GetAllEntriesByFileDataId(int fileDataId) => GetAllEntries(GetHashByFileDataId(fileDataId)); public override IEnumerable> GetAllEntries() { @@ -155,10 +151,7 @@ public override IEnumerable GetAllEntries(ulong hash) yield return entry; } - public IEnumerable GetEntriesByFileDataId(int fileDataId) - { - return GetEntries(GetHashByFileDataId(fileDataId)); - } + public IEnumerable GetEntriesByFileDataId(int fileDataId) => GetEntries(GetHashByFileDataId(fileDataId)); // Returns only entries that match current locale and content flags public override IEnumerable GetEntries(ulong hash) @@ -196,10 +189,7 @@ public int GetFileDataIdByHash(ulong hash) return fid; } - public int GetFileDataIdByName(string name) - { - return GetFileDataIdByHash(Hasher.ComputeHash(name)); - } + public int GetFileDataIdByName(string name) => GetFileDataIdByHash(Hasher.ComputeHash(name)); private bool LoadPreHashedListFile(string pathbin, string pathtext, BackgroundWorkerEx worker = null) { @@ -376,14 +366,10 @@ protected override CASCFolder CreateStorageTree() { var root = new CASCFolder("root"); + // Reset counts CountSelect = 0; - - // Cleanup fake names for unknown files CountUnknown = 0; - foreach (var unkFile in UnknownFiles) - CASCFile.FileNames.Remove(unkFile); - // Create new tree based on specified locale foreach (var rootEntry in RootData) { @@ -407,7 +393,6 @@ protected override CASCFolder CreateStorageTree() file = "unknown\\" + rootEntry.Key.ToString("X16") + "_" + GetFileDataIdByHash(rootEntry.Key); CountUnknown++; - UnknownFiles.Add(rootEntry.Key); } CreateSubTree(root, rootEntry.Key, file); @@ -419,17 +404,13 @@ protected override CASCFolder CreateStorageTree() return root; } - public bool IsUnknownFile(ulong hash) - { - return UnknownFiles.Contains(hash); - } + public bool IsUnknownFile(ulong hash) => !CASCFile.FileNames.ContainsKey(hash); public override void Clear() { RootData.Clear(); FileDataStore.Clear(); FileDataStoreReverse.Clear(); - UnknownFiles.Clear(); Root?.Entries.Clear(); CASCFile.FileNames.Clear(); } From b06ab39f2dd26cb71365d7465f3beb02732da35f Mon Sep 17 00:00:00 2001 From: TOM_RUS Date: Tue, 16 Feb 2016 03:12:29 +0300 Subject: [PATCH 07/17] Fix --- CASCExplorer/CASCViewHelper.cs | 35 ++++++++-------------------------- CascLib/CASCHandler.cs | 8 ++++---- CascLib/CASCHandlerLite.cs | 23 ++++------------------ CascLib/DownloadHandler.cs | 6 ++---- CascLib/OWRootHandler.cs | 2 -- CascLib/WowRootHandler.cs | 4 ++-- 6 files changed, 20 insertions(+), 58 deletions(-) diff --git a/CASCExplorer/CASCViewHelper.cs b/CASCExplorer/CASCViewHelper.cs index 3678e393..13aa7dd4 100644 --- a/CASCExplorer/CASCViewHelper.cs +++ b/CASCExplorer/CASCViewHelper.cs @@ -33,25 +33,13 @@ class CASCViewHelper public event OnStorageChangedDelegate OnStorageChanged; public event OnCleanupDelegate OnCleanup; - public CASCHandler CASC - { - get { return _casc; } - } + public CASCHandler CASC => _casc; - public CASCFolder Root - { - get { return _root; } - } + public CASCFolder Root => _root; - public CASCFolder CurrentFolder - { - get { return _currentFolder; } - } + public CASCFolder CurrentFolder => _currentFolder; - public List DisplayedEntries - { - get { return _displayedEntries; } - } + public List DisplayedEntries => _displayedEntries; public void ExtractFiles(NoFlickerListView filesList) { @@ -575,23 +563,16 @@ public void ExtractCASCSystemFiles() EncodingEntry enc; - var files = new Dictionary(); - - files.Add("encoding", _casc.Config.EncodingKey); + _casc.ExtractFile(_casc.Config.EncodingKey, ".", "encoding"); if (_casc.Encoding.GetEntry(_casc.Config.RootMD5, out enc)) - files.Add("root", enc.Key); + _casc.ExtractFile(enc.Key, ".", "root"); if (_casc.Encoding.GetEntry(_casc.Config.InstallMD5, out enc)) - files.Add("install", enc.Key); + _casc.ExtractFile(enc.Key, ".", "install"); if (_casc.Encoding.GetEntry(_casc.Config.DownloadMD5, out enc)) - files.Add("download", enc.Key); - - foreach (var file in files) - { - _casc.ExtractFile(file.Value, ".", file.Key); - } + _casc.ExtractFile(enc.Key, ".", "download"); } } } diff --git a/CascLib/CASCHandler.cs b/CascLib/CASCHandler.cs index 67819e77..d0f6f0a9 100644 --- a/CascLib/CASCHandler.cs +++ b/CascLib/CASCHandler.cs @@ -11,10 +11,10 @@ public sealed class CASCHandler : CASCHandlerBase private RootHandlerBase RootHandler; private InstallHandler InstallHandler; - public EncodingHandler Encoding { get { return EncodingHandler; } } - public DownloadHandler Download { get { return DownloadHandler; } } - public RootHandlerBase Root { get { return RootHandler; } } - public InstallHandler Install { get { return InstallHandler; } } + public EncodingHandler Encoding => EncodingHandler; + public DownloadHandler Download => DownloadHandler; + public RootHandlerBase Root => RootHandler; + public InstallHandler Install => InstallHandler; private CASCHandler(CASCConfig config, BackgroundWorkerEx worker) : base(config, worker) { diff --git a/CascLib/CASCHandlerLite.cs b/CascLib/CASCHandlerLite.cs index da50b654..180a7bd4 100644 --- a/CascLib/CASCHandlerLite.cs +++ b/CascLib/CASCHandlerLite.cs @@ -97,21 +97,11 @@ private static CASCHandlerLite Open(LocaleFlags locale, BackgroundWorkerEx worke } } - public bool FileExists(int fileDataId) - { - return FileDataIdToHash.ContainsKey(fileDataId); - } + public bool FileExists(int fileDataId) => FileDataIdToHash.ContainsKey(fileDataId); - public bool FileExists(string file) - { - var hash = Hasher.ComputeHash(file); - return FileExists(hash); - } + public bool FileExists(string file) => FileExists(Hasher.ComputeHash(file)); - public bool FileExists(ulong hash) - { - return HashToKey.ContainsKey(hash); - } + public bool FileExists(ulong hash) => HashToKey.ContainsKey(hash); public override Stream OpenFile(int filedata) { @@ -123,12 +113,7 @@ public override Stream OpenFile(int filedata) return null; } - public override Stream OpenFile(string name) - { - var hash = Hasher.ComputeHash(name); - - return OpenFile(hash); - } + public override Stream OpenFile(string name) => OpenFile(Hasher.ComputeHash(name)); public override Stream OpenFile(ulong hash) { diff --git a/CascLib/DownloadHandler.cs b/CascLib/DownloadHandler.cs index aca97208..f7513cbe 100644 --- a/CascLib/DownloadHandler.cs +++ b/CascLib/DownloadHandler.cs @@ -25,10 +25,7 @@ public class DownloadHandler private readonly Dictionary DownloadData = new Dictionary(comparer); private readonly Dictionary Tags = new Dictionary(); - public int Count - { - get { return DownloadData.Count; } - } + public int Count => DownloadData.Count; public DownloadHandler(BinaryReader stream, BackgroundWorkerEx worker) { @@ -103,6 +100,7 @@ public DownloadEntry GetEntry(MD5Hash key) public void Clear() { DownloadData.Clear(); + Tags.Clear(); } } } diff --git a/CascLib/OWRootHandler.cs b/CascLib/OWRootHandler.cs index 1c154c03..9992a00d 100644 --- a/CascLib/OWRootHandler.cs +++ b/CascLib/OWRootHandler.cs @@ -157,8 +157,6 @@ protected override CASCFolder CreateStorageTree() var root = new CASCFolder("root"); CountSelect = 0; - - // Cleanup fake names for unknown files CountUnknown = 0; foreach (var rootEntry in RootData) diff --git a/CascLib/WowRootHandler.cs b/CascLib/WowRootHandler.cs index 9324067b..5119a77f 100644 --- a/CascLib/WowRootHandler.cs +++ b/CascLib/WowRootHandler.cs @@ -58,8 +58,8 @@ public class WowRootHandler : RootHandlerBase private readonly Dictionary FileDataStore = new Dictionary(); private readonly Dictionary FileDataStoreReverse = new Dictionary(); - public override int Count { get { return RootData.Count; } } - public override int CountTotal { get { return RootData.Sum(re => re.Value.Count); } } + public override int Count => RootData.Count; + public override int CountTotal => RootData.Sum(re => re.Value.Count); public WowRootHandler(BinaryReader stream, BackgroundWorkerEx worker) { From 5092a1655467034dc9dc48010fc8140e9646517f Mon Sep 17 00:00:00 2001 From: TOM_RUS Date: Tue, 16 Feb 2016 06:35:10 +0300 Subject: [PATCH 08/17] Fix --- CASCExplorer/InitForm.cs | 1 + CascLib/CASCConfig.cs | 3 ++- CascLib/OWRootHandler.cs | 22 +++++----------------- CascLib/RootHandlerBase.cs | 3 +++ 4 files changed, 11 insertions(+), 18 deletions(-) diff --git a/CASCExplorer/InitForm.cs b/CASCExplorer/InitForm.cs index 7a4dae56..5419439a 100644 --- a/CASCExplorer/InitForm.cs +++ b/CASCExplorer/InitForm.cs @@ -27,6 +27,7 @@ private void InitForm_FormClosing(object sender, FormClosingEventArgs e) private void backgroundWorker1_DoWork(object sender, DoWorkEventArgs e) { string arg = (string)e.Argument; + CASCConfig.LoadFlags |= LoadFlags.Install; CASCConfig config = _onlineMode ? CASCConfig.LoadOnlineStorageConfig(arg, "us") : CASCConfig.LoadLocalStorageConfig(arg); if (_onlineMode) diff --git a/CascLib/CASCConfig.cs b/CascLib/CASCConfig.cs index 80e9ffe1..c1c8f697 100644 --- a/CascLib/CASCConfig.cs +++ b/CascLib/CASCConfig.cs @@ -9,6 +9,7 @@ namespace CASCExplorer public enum LoadFlags { All = -1, + None = 0, Download = 1, Install = 2, } @@ -124,7 +125,7 @@ public class CASCConfig public CASCGameType GameType { get; private set; } public static bool ValidateData { get; set; } = true; public static bool ThrowOnFileNotFound { get; set; } = true; - public static LoadFlags LoadFlags { get; set; } = LoadFlags.All; + public static LoadFlags LoadFlags { get; set; } = LoadFlags.None; public static CASCConfig LoadOnlineStorageConfig(string product, string region, bool useCurrentBuild = false) { diff --git a/CascLib/OWRootHandler.cs b/CascLib/OWRootHandler.cs index 9992a00d..2d02be91 100644 --- a/CascLib/OWRootHandler.cs +++ b/CascLib/OWRootHandler.cs @@ -94,26 +94,14 @@ public unsafe OWRootHandler(BinaryReader stream, BackgroundWorkerEx worker, CASC foreach (var entry in casc.Encoding.Entries) { - DownloadEntry dl = casc.Download.GetEntry(entry.Value.Key); + key = entry.Key; - if (dl != null) - { - key = entry.Key; - - string fakeName = "unknown" + "/" + key.Value[0].ToString("X2") + "/" + entry.Key.ToHexString(); - - var locales = dl.Tags.Where(tag => tag.Value.Type == 4).Select(tag => tag2locale(tag.Key)); + string fakeName = "unknown" + "/" + key.Value[0].ToString("X2") + "/" + entry.Key.ToHexString(); - LocaleFlags locale = LocaleFlags.None; + ulong fileHash = Hasher.ComputeHash(fakeName); + RootData.Add(fileHash, new RootEntry() { MD5 = entry.Key, LocaleFlags = LocaleFlags.All, ContentFlags = ContentFlags.None }); - foreach (var loc in locales) - locale |= loc; - - ulong fileHash = Hasher.ComputeHash(fakeName); - RootData.Add(fileHash, new RootEntry() { MD5 = entry.Key, LocaleFlags = locale, ContentFlags = ContentFlags.None }); - - CASCFile.FileNames[fileHash] = fakeName; - } + CASCFile.FileNames[fileHash] = fakeName; worker?.ReportProgress((int)(++current / (float)casc.Encoding.Count * 100)); } diff --git a/CascLib/RootHandlerBase.cs b/CascLib/RootHandlerBase.cs index ac9c4629..ba3d5842 100644 --- a/CascLib/RootHandlerBase.cs +++ b/CascLib/RootHandlerBase.cs @@ -65,6 +65,9 @@ protected void CreateSubTree(CASCFolder root, ulong filehash, string file) public void MergeInstall(InstallHandler install) { + if (install == null) + return; + foreach (var entry in install.GetEntries()) { CreateSubTree(Root, Hasher.ComputeHash(entry.Name), entry.Name); From 2b9f57c3e8d80d4b270cc94178f7aa02a58b3f22 Mon Sep 17 00:00:00 2001 From: TOM_RUS Date: Wed, 17 Feb 2016 06:13:38 +0300 Subject: [PATCH 09/17] Fix --- CASCExplorer/CASCViewHelper.cs | 11 ++--- CascLib/CASCHandler.cs | 30 ++++++++++--- CascLib/CASCHandlerBase.cs | 32 +++++++++----- CascLib/CASCHandlerLite.cs | 81 +++++++++++++++++++++++++++++++--- CascLib/CDNIndexHandler.cs | 36 ++++++++------- CascLib/DownloadHandler.cs | 8 ++-- CascLib/EncodingHandler.cs | 3 +- CascLib/InstallHandler.cs | 3 +- CascLib/LocalIndexHandler.cs | 3 +- CascLib/WowRootHandler.cs | 10 +++-- 10 files changed, 162 insertions(+), 55 deletions(-) diff --git a/CASCExplorer/CASCViewHelper.cs b/CASCExplorer/CASCViewHelper.cs index 13aa7dd4..8bc82d1c 100644 --- a/CASCExplorer/CASCViewHelper.cs +++ b/CASCExplorer/CASCViewHelper.cs @@ -77,7 +77,7 @@ await Task.Run(() => EncodingEntry enc; if (_casc.Encoding.GetEntry(file.MD5, out enc)) - _casc.ExtractFile(enc.Key, Path.Combine("data", build, "install_files"), file.Name); + _casc.SaveFileTo(enc.Key, Path.Combine("data", build, "install_files"), file.Name); progress.Report((int)(++numDone / (float)numFiles * 100.0f)); } @@ -504,6 +504,7 @@ public void Cleanup() _root = null; _displayedEntries?.Clear(); + _displayedEntries = null; _casc?.Clear(); _casc = null; @@ -563,16 +564,16 @@ public void ExtractCASCSystemFiles() EncodingEntry enc; - _casc.ExtractFile(_casc.Config.EncodingKey, ".", "encoding"); + _casc.SaveFileTo(_casc.Config.EncodingKey, ".", "encoding"); if (_casc.Encoding.GetEntry(_casc.Config.RootMD5, out enc)) - _casc.ExtractFile(enc.Key, ".", "root"); + _casc.SaveFileTo(enc.Key, ".", "root"); if (_casc.Encoding.GetEntry(_casc.Config.InstallMD5, out enc)) - _casc.ExtractFile(enc.Key, ".", "install"); + _casc.SaveFileTo(enc.Key, ".", "install"); if (_casc.Encoding.GetEntry(_casc.Config.DownloadMD5, out enc)) - _casc.ExtractFile(enc.Key, ".", "download"); + _casc.SaveFileTo(enc.Key, ".", "download"); } } } diff --git a/CascLib/CASCHandler.cs b/CascLib/CASCHandler.cs index d0f6f0a9..4521037e 100644 --- a/CascLib/CASCHandler.cs +++ b/CascLib/CASCHandler.cs @@ -104,7 +104,7 @@ private static CASCHandler Open(BackgroundWorkerEx worker, CASCConfig config) } } - public bool FileExists(int fileDataId) + public override bool FileExists(int fileDataId) { WowRootHandler rh = Root as WowRootHandler; @@ -114,9 +114,9 @@ public bool FileExists(int fileDataId) return FileExists(rh.GetHashByFileDataId(fileDataId)); } - public bool FileExists(string file) => FileExists(Hasher.ComputeHash(file)); + public override bool FileExists(string file) => FileExists(Hasher.ComputeHash(file)); - public bool FileExists(ulong hash) => RootHandler.GetAllEntries(hash).Any(); + public override bool FileExists(ulong hash) => RootHandler.GetAllEntries(hash).Any(); public bool GetEncodingEntry(ulong hash, out EncodingEntry enc) { @@ -161,15 +161,13 @@ public override Stream OpenFile(ulong hash) return null; } - public void SaveFileTo(string fullName, string extractPath) => SaveFileTo(Hasher.ComputeHash(fullName), extractPath, fullName); - - public void SaveFileTo(ulong hash, string extractPath, string fullName) + public override void SaveFileTo(ulong hash, string extractPath, string fullName) { EncodingEntry encInfo; if (GetEncodingEntry(hash, out encInfo)) { - ExtractFile(encInfo.Key, extractPath, fullName); + SaveFileTo(encInfo.Key, extractPath, fullName); return; } @@ -177,6 +175,24 @@ public void SaveFileTo(ulong hash, string extractPath, string fullName) throw new FileNotFoundException(fullName); } + protected override Stream OpenFileOnline(MD5Hash key) + { + IndexEntry idxInfo = CDNIndex.GetIndexInfo(key); + return OpenFileLocalInternal(idxInfo, key); + } + + protected override Stream GetLocalDataStream(MD5Hash key) + { + IndexEntry idxInfo = LocalIndex.GetIndexInfo(key); + return GetLocalDataStreamInternal(idxInfo, key); + } + + protected override void ExtractFileOnline(MD5Hash key, string path, string name) + { + IndexEntry idxInfo = CDNIndex.GetIndexInfo(key); + ExtractFileOnlineInternal(idxInfo, key, path, name); + } + public void Clear() { CDNIndex?.Clear(); diff --git a/CascLib/CASCHandlerBase.cs b/CascLib/CASCHandlerBase.cs index 66ae6042..8a077938 100644 --- a/CascLib/CASCHandlerBase.cs +++ b/CascLib/CASCHandlerBase.cs @@ -1,6 +1,7 @@ using System; using System.Collections.Generic; using System.IO; +using System.Text; namespace CASCExplorer { @@ -43,10 +44,17 @@ public CASCHandlerBase(CASCConfig config, BackgroundWorkerEx worker) } } + public abstract bool FileExists(int fileDataId); + public abstract bool FileExists(string file); + public abstract bool FileExists(ulong hash); + public abstract Stream OpenFile(int filedata); public abstract Stream OpenFile(string name); public abstract Stream OpenFile(ulong hash); + public void SaveFileTo(string fullName, string extractPath) => SaveFileTo(Hasher.ComputeHash(fullName), extractPath, fullName); + public abstract void SaveFileTo(ulong hash, string extractPath, string fullName); + public Stream OpenFile(MD5Hash key) { try @@ -62,10 +70,10 @@ public Stream OpenFile(MD5Hash key) } } - private Stream OpenFileOnline(MD5Hash key) - { - IndexEntry idxInfo = CDNIndex.GetIndexInfo(key); + protected abstract Stream OpenFileOnline(MD5Hash key); + protected Stream OpenFileLocalInternal(IndexEntry idxInfo, MD5Hash key) + { if (idxInfo != null) { using (Stream s = CDNIndex.OpenDataFile(idxInfo)) @@ -94,17 +102,17 @@ private Stream OpenFileLocal(MD5Hash key) } } - private Stream GetLocalDataStream(MD5Hash key) - { - IndexEntry idxInfo = LocalIndex.GetIndexInfo(key); + protected abstract Stream GetLocalDataStream(MD5Hash key); + protected Stream GetLocalDataStreamInternal(IndexEntry idxInfo, MD5Hash key) + { if (idxInfo == null) throw new Exception("local index missing"); Stream dataStream = GetDataStream(idxInfo.Index); dataStream.Position = idxInfo.Offset; - using (BinaryReader reader = new BinaryReader(dataStream, System.Text.Encoding.ASCII, true)) + using (BinaryReader reader = new BinaryReader(dataStream, Encoding.ASCII, true)) { byte[] md5 = reader.ReadBytes(16); Array.Reverse(md5); @@ -127,7 +135,7 @@ private Stream GetLocalDataStream(MD5Hash key) } } - public void ExtractFile(MD5Hash key, string path, string name) + public void SaveFileTo(MD5Hash key, string path, string name) { try { @@ -142,10 +150,10 @@ public void ExtractFile(MD5Hash key, string path, string name) } } - private void ExtractFileOnline(MD5Hash key, string path, string name) - { - IndexEntry idxInfo = CDNIndex.GetIndexInfo(key); + protected abstract void ExtractFileOnline(MD5Hash key, string path, string name); + protected void ExtractFileOnlineInternal(IndexEntry idxInfo, MD5Hash key, string path, string name) + { if (idxInfo != null) { using (Stream s = CDNIndex.OpenDataFile(idxInfo)) @@ -217,7 +225,7 @@ protected BinaryReader OpenEncodingFile(CASCHandlerBase casc) return new BinaryReader(casc.OpenFile(casc.Config.EncodingKey)); } - protected Stream GetDataStream(int index) + private Stream GetDataStream(int index) { Stream stream; diff --git a/CascLib/CASCHandlerLite.cs b/CascLib/CASCHandlerLite.cs index 180a7bd4..5b11bcb2 100644 --- a/CascLib/CASCHandlerLite.cs +++ b/CascLib/CASCHandlerLite.cs @@ -6,8 +6,11 @@ namespace CASCExplorer { public sealed class CASCHandlerLite : CASCHandlerBase { - private Dictionary HashToKey = new Dictionary(); - private Dictionary FileDataIdToHash = new Dictionary(); + private readonly Dictionary HashToKey = new Dictionary(); + private readonly Dictionary FileDataIdToHash = new Dictionary(); + private static readonly MD5HashComparer comparer = new MD5HashComparer(); + private readonly Dictionary CDNIndexData; + private readonly Dictionary LocalIndexData; private CASCHandlerLite(CASCConfig config, LocaleFlags locale, BackgroundWorkerEx worker) : base(config, worker) { @@ -40,6 +43,11 @@ private CASCHandlerLite(CASCConfig config, LocaleFlags locale, BackgroundWorkerE RootHandler.SetFlags(locale, ContentFlags.None, false); + CDNIndexData = new Dictionary(comparer); + + if (LocalIndex != null) + LocalIndexData = new Dictionary(comparer); + RootEntry rootEntry; foreach (var entry in RootHandler.GetAllEntries()) @@ -56,11 +64,28 @@ private CASCHandlerLite(CASCConfig config, LocaleFlags locale, BackgroundWorkerE { HashToKey.Add(entry.Key, enc.Key); FileDataIdToHash.Add(RootHandler.GetFileDataIdByHash(entry.Key), entry.Key); + + if (LocalIndex != null) + { + IndexEntry iLocal = LocalIndex.GetIndexInfo(enc.Key); + + if (iLocal != null && !LocalIndexData.ContainsKey(enc.Key)) + LocalIndexData.Add(enc.Key, iLocal); + } + + IndexEntry iCDN = CDNIndex.GetIndexInfo(enc.Key); + + if (iCDN != null && !CDNIndexData.ContainsKey(enc.Key)) + CDNIndexData.Add(enc.Key, iCDN); } } } } + CDNIndex.Clear(); + //CDNIndex = null; + LocalIndex?.Clear(); + LocalIndex = null; RootHandler.Clear(); RootHandler = null; EncodingHandler.Clear(); @@ -70,6 +95,38 @@ private CASCHandlerLite(CASCConfig config, LocaleFlags locale, BackgroundWorkerE Logger.WriteLine("CASCHandlerLite: loaded {0} files", HashToKey.Count); } + protected override Stream OpenFileOnline(MD5Hash key) + { + IndexEntry idxInfo = CDNIndex.GetIndexInfo(key); + + if (idxInfo == null) + CDNIndexData.TryGetValue(key, out idxInfo); + + return OpenFileLocalInternal(idxInfo, key); + } + + protected override Stream GetLocalDataStream(MD5Hash key) + { + IndexEntry idxInfo; + + if (LocalIndex != null) + idxInfo = LocalIndex.GetIndexInfo(key); + else + LocalIndexData.TryGetValue(key, out idxInfo); + + return GetLocalDataStreamInternal(idxInfo, key); + } + + protected override void ExtractFileOnline(MD5Hash key, string path, string name) + { + IndexEntry idxInfo = CDNIndex.GetIndexInfo(key); + + if (idxInfo == null) + CDNIndexData.TryGetValue(key, out idxInfo); + + ExtractFileOnlineInternal(idxInfo, key, path, name); + } + public static CASCHandlerLite OpenStorage(LocaleFlags locale, CASCConfig config, BackgroundWorkerEx worker = null) { return Open(locale, worker, config); @@ -97,11 +154,11 @@ private static CASCHandlerLite Open(LocaleFlags locale, BackgroundWorkerEx worke } } - public bool FileExists(int fileDataId) => FileDataIdToHash.ContainsKey(fileDataId); + public override bool FileExists(int fileDataId) => FileDataIdToHash.ContainsKey(fileDataId); - public bool FileExists(string file) => FileExists(Hasher.ComputeHash(file)); + public override bool FileExists(string file) => FileExists(Hasher.ComputeHash(file)); - public bool FileExists(ulong hash) => HashToKey.ContainsKey(hash); + public override bool FileExists(ulong hash) => HashToKey.ContainsKey(hash); public override Stream OpenFile(int filedata) { @@ -124,5 +181,19 @@ public override Stream OpenFile(ulong hash) return null; } + + public override void SaveFileTo(ulong hash, string extractPath, string fullName) + { + MD5Hash key; + + if (HashToKey.TryGetValue(hash, out key)) + { + SaveFileTo(key, extractPath, fullName); + return; + } + + if (CASCConfig.ThrowOnFileNotFound) + throw new FileNotFoundException(fullName); + } } } diff --git a/CascLib/CDNIndexHandler.cs b/CascLib/CDNIndexHandler.cs index dda7820d..33857995 100644 --- a/CascLib/CDNIndexHandler.cs +++ b/CascLib/CDNIndexHandler.cs @@ -15,21 +15,18 @@ public class IndexEntry public class CDNIndexHandler { private static readonly MD5HashComparer comparer = new MD5HashComparer(); - private readonly Dictionary CDNIndexData = new Dictionary(comparer); + private Dictionary CDNIndexData = new Dictionary(comparer); - private CASCConfig CASCConfig; + private CASCConfig config; private BackgroundWorkerEx worker; private SyncDownloader downloader; - public static CDNCache Cache = new CDNCache("cache"); + public static readonly CDNCache Cache = new CDNCache("cache"); - public int Count - { - get { return CDNIndexData.Count; } - } + public int Count => CDNIndexData.Count; private CDNIndexHandler(CASCConfig cascConfig, BackgroundWorkerEx worker) { - CASCConfig = cascConfig; + config = cascConfig; this.worker = worker; downloader = new SyncDownloader(worker); } @@ -90,8 +87,8 @@ private void DownloadIndexFile(string archive, int i) { try { - string file = CASCConfig.CDNPath + "/data/" + archive.Substring(0, 2) + "/" + archive.Substring(2, 2) + "/" + archive + ".index"; - string url = "http://" + CASCConfig.CDNHost + "/" + file; + string file = config.CDNPath + "/data/" + archive.Substring(0, 2) + "/" + archive.Substring(2, 2) + "/" + archive + ".index"; + string url = "http://" + config.CDNHost + "/" + file; Stream stream = Cache.OpenFile(file, url, false); @@ -115,9 +112,9 @@ private void OpenIndexFile(string archive, int i) { try { - string dataFolder = CASCGame.GetDataFolder(CASCConfig.GameType); + string dataFolder = CASCGame.GetDataFolder(config.GameType); - string path = Path.Combine(CASCConfig.BasePath, dataFolder, "indices", archive + ".index"); + string path = Path.Combine(config.BasePath, dataFolder, "indices", archive + ".index"); using (FileStream fs = new FileStream(path, FileMode.Open, FileAccess.Read, FileShare.ReadWrite)) ParseIndex(fs, i); @@ -130,10 +127,10 @@ private void OpenIndexFile(string archive, int i) public Stream OpenDataFile(IndexEntry entry) { - var archive = CASCConfig.Archives[entry.Index]; + var archive = config.Archives[entry.Index]; - string file = CASCConfig.CDNPath + "/data/" + archive.Substring(0, 2) + "/" + archive.Substring(2, 2) + "/" + archive; - string url = "http://" + CASCConfig.CDNHost + "/" + file; + string file = config.CDNPath + "/data/" + archive.Substring(0, 2) + "/" + archive.Substring(2, 2) + "/" + archive; + string url = "http://" + config.CDNHost + "/" + file; Stream stream = Cache.OpenFile(file, url, true); @@ -163,8 +160,8 @@ public Stream OpenDataFileDirect(MD5Hash key) worker?.ReportProgress(0, string.Format("Downloading \"{0}\" file...", keyStr)); - string file = CASCConfig.CDNPath + "/data/" + keyStr.Substring(0, 2) + "/" + keyStr.Substring(2, 2) + "/" + keyStr; - string url = "http://" + CASCConfig.CDNHost + "/" + file; + string file = config.CDNPath + "/data/" + keyStr.Substring(0, 2) + "/" + keyStr.Substring(2, 2) + "/" + keyStr; + string url = "http://" + config.CDNHost + "/" + file; Stream stream = Cache.OpenFile(file, url, false); @@ -212,6 +209,11 @@ public IndexEntry GetIndexInfo(MD5Hash key) public void Clear() { CDNIndexData.Clear(); + CDNIndexData = null; + + config = null; + worker = null; + downloader = null; } } } diff --git a/CascLib/DownloadHandler.cs b/CascLib/DownloadHandler.cs index f7513cbe..b1cd3e5b 100644 --- a/CascLib/DownloadHandler.cs +++ b/CascLib/DownloadHandler.cs @@ -22,8 +22,8 @@ public class DownloadTag public class DownloadHandler { private static readonly MD5HashComparer comparer = new MD5HashComparer(); - private readonly Dictionary DownloadData = new Dictionary(comparer); - private readonly Dictionary Tags = new Dictionary(); + private Dictionary DownloadData = new Dictionary(comparer); + private Dictionary Tags = new Dictionary(); public int Count => DownloadData.Count; @@ -99,8 +99,10 @@ public DownloadEntry GetEntry(MD5Hash key) public void Clear() { - DownloadData.Clear(); Tags.Clear(); + Tags = null; + DownloadData.Clear(); + DownloadData = null; } } } diff --git a/CascLib/EncodingHandler.cs b/CascLib/EncodingHandler.cs index 46d79088..46984f75 100644 --- a/CascLib/EncodingHandler.cs +++ b/CascLib/EncodingHandler.cs @@ -12,7 +12,7 @@ public struct EncodingEntry public class EncodingHandler { private static readonly MD5HashComparer comparer = new MD5HashComparer(); - private readonly Dictionary EncodingData = new Dictionary(comparer); + private Dictionary EncodingData = new Dictionary(comparer); private const int CHUNK_SIZE = 4096; @@ -128,6 +128,7 @@ public bool GetEntry(MD5Hash md5, out EncodingEntry enc) public void Clear() { EncodingData.Clear(); + EncodingData = null; } } } diff --git a/CascLib/InstallHandler.cs b/CascLib/InstallHandler.cs index 35140048..aed74883 100644 --- a/CascLib/InstallHandler.cs +++ b/CascLib/InstallHandler.cs @@ -23,7 +23,7 @@ public class InstallTag public class InstallHandler { - private readonly List InstallData = new List(); + private List InstallData = new List(); private static readonly Jenkins96 Hasher = new Jenkins96(); public int Count @@ -117,6 +117,7 @@ public void Print() public void Clear() { InstallData.Clear(); + InstallData = null; } } } diff --git a/CascLib/LocalIndexHandler.cs b/CascLib/LocalIndexHandler.cs index 2e68c711..72c3b5a5 100644 --- a/CascLib/LocalIndexHandler.cs +++ b/CascLib/LocalIndexHandler.cs @@ -8,7 +8,7 @@ namespace CASCExplorer public class LocalIndexHandler { private static readonly MD5HashComparer comparer = new MD5HashComparer(); - private readonly Dictionary LocalIndexData = new Dictionary(comparer); + private Dictionary LocalIndexData = new Dictionary(comparer); public int Count { @@ -143,6 +143,7 @@ public unsafe IndexEntry GetIndexInfo(MD5Hash key) public void Clear() { LocalIndexData.Clear(); + LocalIndexData = null; } } } diff --git a/CascLib/WowRootHandler.cs b/CascLib/WowRootHandler.cs index 5119a77f..05ec7e68 100644 --- a/CascLib/WowRootHandler.cs +++ b/CascLib/WowRootHandler.cs @@ -54,9 +54,9 @@ public struct RootEntry public class WowRootHandler : RootHandlerBase { - private readonly MultiDictionary RootData = new MultiDictionary(); - private readonly Dictionary FileDataStore = new Dictionary(); - private readonly Dictionary FileDataStoreReverse = new Dictionary(); + private MultiDictionary RootData = new MultiDictionary(); + private Dictionary FileDataStore = new Dictionary(); + private Dictionary FileDataStoreReverse = new Dictionary(); public override int Count => RootData.Count; public override int CountTotal => RootData.Sum(re => re.Value.Count); @@ -409,9 +409,13 @@ protected override CASCFolder CreateStorageTree() public override void Clear() { RootData.Clear(); + RootData = null; FileDataStore.Clear(); + FileDataStore = null; FileDataStoreReverse.Clear(); + FileDataStoreReverse = null; Root?.Entries.Clear(); + Root = null; CASCFile.FileNames.Clear(); } From c68ece3c5f7394b784367e9eb1e3625304112ef4 Mon Sep 17 00:00:00 2001 From: TOM_RUS Date: Thu, 3 Mar 2016 03:57:04 +0300 Subject: [PATCH 10/17] Fix --- CASCExplorer/CASCExplorer.csproj | 2 - CascLib/CascLib.csproj | 3 + CascLib/DB2Reader.cs | 147 ++++++++++++++++++++ CascLib/DB3Reader.cs | 222 +++++++++++++++++++++++++++++++ CascLib/Extensions.cs | 33 +++-- CascLib/FastStruct.cs | 99 ++++++++++++++ CascLib/WowRootHandler.cs | 12 +- 7 files changed, 492 insertions(+), 26 deletions(-) create mode 100644 CascLib/DB2Reader.cs create mode 100644 CascLib/DB3Reader.cs create mode 100644 CascLib/FastStruct.cs diff --git a/CASCExplorer/CASCExplorer.csproj b/CASCExplorer/CASCExplorer.csproj index 0f1d8a95..f0aec6fb 100644 --- a/CASCExplorer/CASCExplorer.csproj +++ b/CASCExplorer/CASCExplorer.csproj @@ -68,8 +68,6 @@ BruteforceForm.cs - - diff --git a/CascLib/CascLib.csproj b/CascLib/CascLib.csproj index ccaad07b..32fcf671 100644 --- a/CascLib/CascLib.csproj +++ b/CascLib/CascLib.csproj @@ -47,8 +47,11 @@ + + + diff --git a/CascLib/DB2Reader.cs b/CascLib/DB2Reader.cs new file mode 100644 index 00000000..a56e68da --- /dev/null +++ b/CascLib/DB2Reader.cs @@ -0,0 +1,147 @@ +using System; +using System.Collections; +using System.Collections.Generic; +using System.IO; +using System.Text; + +namespace CASCExplorer +{ + public class DB2Row + { + private byte[] m_data; + private DB2Reader m_reader; + + public byte[] Data { get { return m_data; } } + + public DB2Row(DB2Reader reader, byte[] data) + { + m_reader = reader; + m_data = data; + } + + public T GetField(int field) + { + object retVal; + + switch (Type.GetTypeCode(typeof(T))) + { + case TypeCode.String: + int start = BitConverter.ToInt32(m_data, field * 4), len = 0; + while (m_reader.StringTable[start + len] != 0) + len++; + retVal = Encoding.UTF8.GetString(m_reader.StringTable, start, len); + return (T)retVal; + case TypeCode.Int32: + retVal = BitConverter.ToInt32(m_data, field * 4); + return (T)retVal; + case TypeCode.Single: + retVal = BitConverter.ToSingle(m_data, field * 4); + return (T)retVal; + default: + return default(T); + } + } + } + + public class DB2Reader : IEnumerable> + { + private const int HeaderSize = 48; + private const uint DB2FmtSig = 0x32424457; // WDB2 + + public int RecordsCount { get; private set; } + public int FieldsCount { get; private set; } + public int RecordSize { get; private set; } + public int StringTableSize { get; private set; } + public int MinIndex { get; private set; } + public int MaxIndex { get; private set; } + + private readonly DB2Row[] m_rows; + public byte[] StringTable { get; private set; } + + Dictionary m_index = new Dictionary(); + + public DB2Reader(string dbcFile) : this(new FileStream(dbcFile, FileMode.Open)) { } + + public DB2Reader(Stream stream) + { + using (var reader = new BinaryReader(stream, Encoding.UTF8)) + { + if (reader.BaseStream.Length < HeaderSize) + { + throw new InvalidDataException(string.Format("DB2 file is corrupted!")); + } + + if (reader.ReadUInt32() != DB2FmtSig) + { + throw new InvalidDataException(string.Format("DB2 file is corrupted!")); + } + + RecordsCount = reader.ReadInt32(); + FieldsCount = reader.ReadInt32(); + RecordSize = reader.ReadInt32(); + StringTableSize = reader.ReadInt32(); + + // WDB2 specific fields + uint tableHash = reader.ReadUInt32(); // new field in WDB2 + uint build = reader.ReadUInt32(); // new field in WDB2 + uint unk1 = reader.ReadUInt32(); // new field in WDB2 + + if (build > 12880) // new extended header + { + int MinId = reader.ReadInt32(); // new field in WDB2 + int MaxId = reader.ReadInt32(); // new field in WDB2 + int locale = reader.ReadInt32(); // new field in WDB2 + int unk5 = reader.ReadInt32(); // new field in WDB2 + + if (MaxId != 0) + { + var diff = MaxId - MinId + 1; // blizzard is weird people... + reader.ReadBytes(diff * 4); // an index for rows + reader.ReadBytes(diff * 2); // a memory allocation bank + } + } + + m_rows = new DB2Row[RecordsCount]; + + for (int i = 0; i < RecordsCount; i++) + { + m_rows[i] = new DB2Row(this, reader.ReadBytes(RecordSize)); + + int idx = BitConverter.ToInt32(m_rows[i].Data, 0); + + if (idx < MinIndex) + MinIndex = idx; + + if (idx > MaxIndex) + MaxIndex = idx; + + m_index[idx] = m_rows[i]; + } + + StringTable = reader.ReadBytes(StringTableSize); + } + } + + public bool HasRow(int index) + { + return m_index.ContainsKey(index); + } + + public DB2Row GetRow(int index) + { + DB2Row row; + m_index.TryGetValue(index, out row); + return row; + } + + public IEnumerator> GetEnumerator() + { + return m_index.GetEnumerator(); + } + + IEnumerator IEnumerable.GetEnumerator() + { + return m_index.GetEnumerator(); + } + } +} diff --git a/CascLib/DB3Reader.cs b/CascLib/DB3Reader.cs new file mode 100644 index 00000000..773481c2 --- /dev/null +++ b/CascLib/DB3Reader.cs @@ -0,0 +1,222 @@ +using System; +using System.Collections; +using System.Collections.Generic; +using System.IO; +using System.Text; + +namespace CASCExplorer +{ + public class DB3Row + { + private byte[] m_data; + private DB3Reader m_reader; + + public byte[] Data { get { return m_data; } } + + public DB3Row(DB3Reader reader, byte[] data) + { + m_reader = reader; + m_data = data; + } + + public unsafe T GetField(int offset) + { + object retVal; + + fixed(byte *ptr = m_data) + { + switch (Type.GetTypeCode(typeof(T))) + { + case TypeCode.String: + string str; + int start = BitConverter.ToInt32(m_data, offset); + if (m_reader.StringTable.TryGetValue(start, out str)) + retVal = str; + else + retVal = string.Empty; + return (T)retVal; + case TypeCode.SByte: + retVal = ptr[offset]; + return (T)retVal; + case TypeCode.Byte: + retVal = ptr[offset]; + return (T)retVal; + case TypeCode.Int16: + retVal = *(short*)(ptr + offset); + return (T)retVal; + case TypeCode.UInt16: + retVal = *(ushort*)(ptr + offset); + return (T)retVal; + case TypeCode.Int32: + retVal = *(int*)(ptr + offset); + return (T)retVal; + case TypeCode.UInt32: + retVal = *(uint*)(ptr + offset); + return (T)retVal; + case TypeCode.Single: + retVal = *(float*)(ptr + offset); + return (T)retVal; + default: + return default(T); + } + } + } + } + + public class DB3Reader : IEnumerable> + { + private readonly int HeaderSize; + private const uint DB3FmtSig = 0x33424457; // WDB3 + private const uint DB4FmtSig = 0x34424457; // WDB4 + + public int RecordsCount { get; private set; } + public int FieldsCount { get; private set; } + public int RecordSize { get; private set; } + public int StringTableSize { get; private set; } + public int MinIndex { get; private set; } + public int MaxIndex { get; private set; } + + public Dictionary StringTable { get; private set; } + + private SortedDictionary m_index = new SortedDictionary(); + + public DB3Reader(string dbcFile) : this(new FileStream(dbcFile, FileMode.Open)) { } + + public DB3Reader(Stream stream) + { + using (var reader = new BinaryReader(stream, Encoding.UTF8)) + { + if (reader.BaseStream.Length < HeaderSize) + { + throw new InvalidDataException(string.Format("DB3 file is corrupted!")); + } + + uint magic = reader.ReadUInt32(); + + if (magic != DB3FmtSig && magic != DB4FmtSig) + { + throw new InvalidDataException(string.Format("DB3 file is corrupted!")); + } + + if (magic == DB3FmtSig) + HeaderSize = 48; + else + HeaderSize = 52; + + RecordsCount = reader.ReadInt32(); + FieldsCount = reader.ReadInt32(); + RecordSize = reader.ReadInt32(); + StringTableSize = reader.ReadInt32(); + + uint tableHash = reader.ReadUInt32(); + uint build = reader.ReadUInt32(); + uint unk1 = reader.ReadUInt32(); + + int MinId = reader.ReadInt32(); + int MaxId = reader.ReadInt32(); + int locale = reader.ReadInt32(); + int CopyTableSize = reader.ReadInt32(); + + if (magic == DB4FmtSig) + { + int metaFlags = reader.ReadInt32(); + } + + int stringTableStart = HeaderSize + RecordsCount * RecordSize; + int stringTableEnd = stringTableStart + StringTableSize; + + // Index table + int[] m_indexes = null; + bool hasIndex = stringTableEnd + CopyTableSize < reader.BaseStream.Length; + + if (hasIndex) + { + reader.BaseStream.Position = stringTableEnd; + + m_indexes = new int[RecordsCount]; + + for (int i = 0; i < RecordsCount; i++) + m_indexes[i] = reader.ReadInt32(); + } + + // Records table + reader.BaseStream.Position = HeaderSize; + + for (int i = 0; i < RecordsCount; i++) + { + byte[] recordBytes = reader.ReadBytes(RecordSize); + + if (hasIndex) + { + byte[] newRecordBytes = new byte[RecordSize + 4]; + + Array.Copy(BitConverter.GetBytes(m_indexes[i]), newRecordBytes, 4); + Array.Copy(recordBytes, 0, newRecordBytes, 4, recordBytes.Length); + + m_index.Add(m_indexes[i], new DB3Row(this, newRecordBytes)); + } + else + { + m_index.Add(BitConverter.ToInt32(recordBytes, 0), new DB3Row(this, recordBytes)); + } + } + + // Strings table + reader.BaseStream.Position = stringTableStart; + + StringTable = new Dictionary(); + + while (reader.BaseStream.Position != stringTableEnd) + { + int index = (int)reader.BaseStream.Position - stringTableStart; + StringTable[index] = reader.ReadCString(); + } + + // Copy index table + long copyTablePos = stringTableEnd + (hasIndex ? 4 * RecordsCount : 0); + + if (copyTablePos != reader.BaseStream.Length && CopyTableSize != 0) + { + reader.BaseStream.Position = copyTablePos; + + while (reader.BaseStream.Position != reader.BaseStream.Length) + { + int id = reader.ReadInt32(); + int idcopy = reader.ReadInt32(); + + RecordsCount++; + + DB3Row copyRow = m_index[idcopy]; + byte[] newRowData = new byte[copyRow.Data.Length]; + Array.Copy(copyRow.Data, newRowData, newRowData.Length); + Array.Copy(BitConverter.GetBytes(id), newRowData, 4); + + m_index.Add(id, new DB3Row(this, newRowData)); + } + } + } + } + + public bool HasRow(int index) + { + return m_index.ContainsKey(index); + } + + public DB3Row GetRow(int index) + { + DB3Row row; + m_index.TryGetValue(index, out row); + return row; + } + + public IEnumerator> GetEnumerator() + { + return m_index.GetEnumerator(); + } + + IEnumerator IEnumerable.GetEnumerator() + { + return m_index.GetEnumerator(); + } + } +} diff --git a/CascLib/Extensions.cs b/CascLib/Extensions.cs index fd2a77a2..b88d68e8 100644 --- a/CascLib/Extensions.cs +++ b/CascLib/Extensions.cs @@ -2,7 +2,6 @@ using System.Collections; using System.Collections.Generic; using System.IO; -using System.Runtime.InteropServices; using System.Text; namespace CASCExplorer @@ -26,29 +25,26 @@ public static uint ReadUInt32BE(this BinaryReader reader) return (uint)(val[3] | val[2] << 8 | val[1] << 16 | val[0] << 24); } - public static T Read(this BinaryReader reader) where T : struct + public unsafe static T Read(this BinaryReader reader) where T : struct { - byte[] result = reader.ReadBytes(Marshal.SizeOf(typeof(T))); - GCHandle handle = GCHandle.Alloc(result, GCHandleType.Pinned); - T returnObject = (T)Marshal.PtrToStructure(handle.AddrOfPinnedObject(), typeof(T)); - handle.Free(); - return returnObject; + byte[] result = reader.ReadBytes(FastStruct.Size); + + fixed (byte* ptr = result) + return FastStruct.PtrToStructure(ptr); } - public static T[] ReadArray(this BinaryReader reader) where T : struct + public unsafe static T[] ReadArray(this BinaryReader reader) where T : struct { - long numBytes = reader.ReadInt64(); - - int itemCount = (int)numBytes / Marshal.SizeOf(typeof(T)); - - T[] data = new T[itemCount]; - - for (int i = 0; i < itemCount; ++i) - data[i] = reader.Read(); + int numBytes = (int)reader.ReadInt64(); - reader.BaseStream.Position += (0 - (int)numBytes) & 0x07; + byte[] result = reader.ReadBytes(numBytes); - return data; + fixed (byte* ptr = result) + { + T[] data = FastStruct.ReadArray((IntPtr)ptr, numBytes); + reader.BaseStream.Position += (0 - numBytes) & 0x07; + return data; + } } public static short ReadInt16BE(this BinaryReader reader) @@ -124,6 +120,7 @@ public static unsafe bool EqualsTo(this MD5Hash key, byte[] array) for (var i = 0; i < 16; ++i) if (key.Value[i] != other.Value[i]) return false; + return true; } diff --git a/CascLib/FastStruct.cs b/CascLib/FastStruct.cs new file mode 100644 index 00000000..42627a3d --- /dev/null +++ b/CascLib/FastStruct.cs @@ -0,0 +1,99 @@ +using System; +using System.Reflection.Emit; +using System.Runtime.InteropServices; +using System.Security; + +namespace CASCExplorer +{ + [SuppressUnmanagedCodeSecurity] + internal class UnsafeNativeMethods + { + [DllImport("kernel32.dll", EntryPoint = "CopyMemory", SetLastError = false)] + [SecurityCritical] + internal static extern void CopyMemory(IntPtr dest, IntPtr src, uint count); + + [DllImport("kernel32.dll", EntryPoint = "CopyMemory", SetLastError = false)] + [SecurityCritical] + internal static extern unsafe void CopyMemoryPtr(void* dest, void* src, uint count); + } + + public static class FastStruct where T : struct + { + private delegate IntPtr GetPtrDelegate(ref T value); + private delegate T PtrToStructureDelegateIntPtr(IntPtr pointer); + private unsafe delegate T PtrToStructureDelegateBytePtr(byte* pointer); + + private readonly static GetPtrDelegate GetPtr = BuildGetPtrMethod(); + private readonly static PtrToStructureDelegateIntPtr PtrToStructureIntPtr = BuildLoadFromIntPtrMethod(); + private readonly static PtrToStructureDelegateBytePtr PtrToStructureBytePtr = BuildLoadFromBytePtrMethod(); + + public static readonly int Size = Marshal.SizeOf(typeof(T)); + + private static DynamicMethod methodGetPtr; + private static DynamicMethod methodLoadIntPtr; + private static DynamicMethod methodLoadBytePtr; + + private static GetPtrDelegate BuildGetPtrMethod() + { + methodGetPtr = new DynamicMethod("GetStructPtr<" + typeof(T).FullName + ">", + typeof(IntPtr), new Type[1] { typeof(T).MakeByRefType() }, typeof(FastStruct).Module); + + ILGenerator generator = methodGetPtr.GetILGenerator(); + generator.Emit(OpCodes.Ldarg_0); + generator.Emit(OpCodes.Conv_U); + generator.Emit(OpCodes.Ret); + return (GetPtrDelegate)methodGetPtr.CreateDelegate(typeof(GetPtrDelegate)); + } + + private static PtrToStructureDelegateIntPtr BuildLoadFromIntPtrMethod() + { + methodLoadIntPtr = new DynamicMethod("PtrToStructureIntPtr<" + typeof(T).FullName + ">", + typeof(T), new Type[1] { typeof(IntPtr) }, typeof(FastStruct).Module); + + ILGenerator generator = methodLoadIntPtr.GetILGenerator(); + generator.Emit(OpCodes.Ldarg_0); + generator.Emit(OpCodes.Ldobj, typeof(T)); + generator.Emit(OpCodes.Ret); + + return (PtrToStructureDelegateIntPtr)methodLoadIntPtr.CreateDelegate(typeof(PtrToStructureDelegateIntPtr)); + } + + private static unsafe PtrToStructureDelegateBytePtr BuildLoadFromBytePtrMethod() + { + methodLoadBytePtr = new DynamicMethod("PtrToStructureBytePtr<" + typeof(T).FullName + ">", + typeof(T), new Type[1] { typeof(byte*) }, typeof(FastStruct).Module); + + ILGenerator generator = methodLoadBytePtr.GetILGenerator(); + generator.Emit(OpCodes.Ldarg_0); + generator.Emit(OpCodes.Ldobj, typeof(T)); + generator.Emit(OpCodes.Ret); + + return (PtrToStructureDelegateBytePtr)methodLoadBytePtr.CreateDelegate(typeof(PtrToStructureDelegateBytePtr)); + } + + public static T PtrToStructure(IntPtr ptr) + { + return PtrToStructureIntPtr(ptr); + } + + public static unsafe T PtrToStructure(byte* ptr) + { + return PtrToStructureBytePtr(ptr); + } + + public static T[] ReadArray(IntPtr source, int bytesCount) + { + uint elementSize = (uint)Size; + + T[] buffer = new T[bytesCount / elementSize]; + + if (bytesCount > 0) + { + IntPtr p = GetPtr(ref buffer[0]); + UnsafeNativeMethods.CopyMemory(p, source, (uint)bytesCount); + } + + return buffer; + } + } +} diff --git a/CascLib/WowRootHandler.cs b/CascLib/WowRootHandler.cs index 05ec7e68..9cc58eeb 100644 --- a/CascLib/WowRootHandler.cs +++ b/CascLib/WowRootHandler.cs @@ -250,19 +250,19 @@ private bool LoadPreHashedListFile(string pathbin, string pathtext, BackgroundWo public void LoadFileDataComplete(CASCHandler casc) { - if (!casc.FileExists("DBFilesClient\\FileDataComplete.dbc")) + if (!casc.FileExists("DBFilesClient\\FileDataComplete.db2")) return; - Logger.WriteLine("WowRootHandler: loading file names from FileDataComplete.dbc..."); + Logger.WriteLine("WowRootHandler: loading file names from FileDataComplete.db2..."); - using (var s = casc.OpenFile("DBFilesClient\\FileDataComplete.dbc")) + using (var s = casc.OpenFile("DBFilesClient\\FileDataComplete.db2")) { - DBCReader fd = new DBCReader(s); + DB3Reader fd = new DB3Reader(s); foreach (var row in fd) { - string path = row.Value.GetField(1); - string name = row.Value.GetField(2); + string path = row.Value.GetField(4); + string name = row.Value.GetField(8); string fullname = path + name; From 956a389893b8d9eac63bca3d202fc987b38c4288 Mon Sep 17 00:00:00 2001 From: TOM_RUS Date: Fri, 4 Mar 2016 06:18:27 +0300 Subject: [PATCH 11/17] Fix --- CASCExplorer/DB2Reader.cs | 147 ------------------------- CASCExplorer/DB3Reader.cs | 222 -------------------------------------- 2 files changed, 369 deletions(-) delete mode 100644 CASCExplorer/DB2Reader.cs delete mode 100644 CASCExplorer/DB3Reader.cs diff --git a/CASCExplorer/DB2Reader.cs b/CASCExplorer/DB2Reader.cs deleted file mode 100644 index a959766c..00000000 --- a/CASCExplorer/DB2Reader.cs +++ /dev/null @@ -1,147 +0,0 @@ -using System; -using System.Collections; -using System.Collections.Generic; -using System.IO; -using System.Text; - -namespace CASCExplorer -{ - class DB2Row - { - private byte[] m_data; - private DB2Reader m_reader; - - public byte[] Data { get { return m_data; } } - - public DB2Row(DB2Reader reader, byte[] data) - { - m_reader = reader; - m_data = data; - } - - public T GetField(int field) - { - object retVal; - - switch (Type.GetTypeCode(typeof(T))) - { - case TypeCode.String: - int start = BitConverter.ToInt32(m_data, field * 4), len = 0; - while (m_reader.StringTable[start + len] != 0) - len++; - retVal = Encoding.UTF8.GetString(m_reader.StringTable, start, len); - return (T)retVal; - case TypeCode.Int32: - retVal = BitConverter.ToInt32(m_data, field * 4); - return (T)retVal; - case TypeCode.Single: - retVal = BitConverter.ToSingle(m_data, field * 4); - return (T)retVal; - default: - return default(T); - } - } - } - - class DB2Reader : IEnumerable> - { - private const int HeaderSize = 48; - private const uint DB2FmtSig = 0x32424457; // WDB2 - - public int RecordsCount { get; private set; } - public int FieldsCount { get; private set; } - public int RecordSize { get; private set; } - public int StringTableSize { get; private set; } - public int MinIndex { get; private set; } - public int MaxIndex { get; private set; } - - private readonly DB2Row[] m_rows; - public byte[] StringTable { get; private set; } - - Dictionary m_index = new Dictionary(); - - public DB2Reader(string dbcFile) : this(new FileStream(dbcFile, FileMode.Open)) { } - - public DB2Reader(Stream stream) - { - using (var reader = new BinaryReader(stream, Encoding.UTF8)) - { - if (reader.BaseStream.Length < HeaderSize) - { - throw new InvalidDataException(string.Format("DB2 file is corrupted!")); - } - - if (reader.ReadUInt32() != DB2FmtSig) - { - throw new InvalidDataException(string.Format("DB2 file is corrupted!")); - } - - RecordsCount = reader.ReadInt32(); - FieldsCount = reader.ReadInt32(); - RecordSize = reader.ReadInt32(); - StringTableSize = reader.ReadInt32(); - - // WDB2 specific fields - uint tableHash = reader.ReadUInt32(); // new field in WDB2 - uint build = reader.ReadUInt32(); // new field in WDB2 - uint unk1 = reader.ReadUInt32(); // new field in WDB2 - - if (build > 12880) // new extended header - { - int MinId = reader.ReadInt32(); // new field in WDB2 - int MaxId = reader.ReadInt32(); // new field in WDB2 - int locale = reader.ReadInt32(); // new field in WDB2 - int unk5 = reader.ReadInt32(); // new field in WDB2 - - if (MaxId != 0) - { - var diff = MaxId - MinId + 1; // blizzard is weird people... - reader.ReadBytes(diff * 4); // an index for rows - reader.ReadBytes(diff * 2); // a memory allocation bank - } - } - - m_rows = new DB2Row[RecordsCount]; - - for (int i = 0; i < RecordsCount; i++) - { - m_rows[i] = new DB2Row(this, reader.ReadBytes(RecordSize)); - - int idx = BitConverter.ToInt32(m_rows[i].Data, 0); - - if (idx < MinIndex) - MinIndex = idx; - - if (idx > MaxIndex) - MaxIndex = idx; - - m_index[idx] = m_rows[i]; - } - - StringTable = reader.ReadBytes(StringTableSize); - } - } - - public bool HasRow(int index) - { - return m_index.ContainsKey(index); - } - - public DB2Row GetRow(int index) - { - DB2Row row; - m_index.TryGetValue(index, out row); - return row; - } - - public IEnumerator> GetEnumerator() - { - return m_index.GetEnumerator(); - } - - IEnumerator IEnumerable.GetEnumerator() - { - return m_index.GetEnumerator(); - } - } -} diff --git a/CASCExplorer/DB3Reader.cs b/CASCExplorer/DB3Reader.cs deleted file mode 100644 index 4a6da3a7..00000000 --- a/CASCExplorer/DB3Reader.cs +++ /dev/null @@ -1,222 +0,0 @@ -using System; -using System.Collections; -using System.Collections.Generic; -using System.IO; -using System.Text; - -namespace CASCExplorer -{ - class DB3Row - { - private byte[] m_data; - private DB3Reader m_reader; - - public byte[] Data { get { return m_data; } } - - public DB3Row(DB3Reader reader, byte[] data) - { - m_reader = reader; - m_data = data; - } - - public unsafe T GetField(int offset) - { - object retVal; - - fixed(byte *ptr = m_data) - { - switch (Type.GetTypeCode(typeof(T))) - { - case TypeCode.String: - string str; - int start = BitConverter.ToInt32(m_data, offset); - if (m_reader.StringTable.TryGetValue(start, out str)) - retVal = str; - else - retVal = string.Empty; - return (T)retVal; - case TypeCode.SByte: - retVal = ptr[offset]; - return (T)retVal; - case TypeCode.Byte: - retVal = ptr[offset]; - return (T)retVal; - case TypeCode.Int16: - retVal = *(short*)(ptr + offset); - return (T)retVal; - case TypeCode.UInt16: - retVal = *(ushort*)(ptr + offset); - return (T)retVal; - case TypeCode.Int32: - retVal = *(int*)(ptr + offset); - return (T)retVal; - case TypeCode.UInt32: - retVal = *(uint*)(ptr + offset); - return (T)retVal; - case TypeCode.Single: - retVal = *(float*)(ptr + offset); - return (T)retVal; - default: - return default(T); - } - } - } - } - - class DB3Reader : IEnumerable> - { - private readonly int HeaderSize; - private const uint DB3FmtSig = 0x33424457; // WDB3 - private const uint DB4FmtSig = 0x34424457; // WDB4 - - public int RecordsCount { get; private set; } - public int FieldsCount { get; private set; } - public int RecordSize { get; private set; } - public int StringTableSize { get; private set; } - public int MinIndex { get; private set; } - public int MaxIndex { get; private set; } - - public Dictionary StringTable { get; private set; } - - private SortedDictionary m_index = new SortedDictionary(); - - public DB3Reader(string dbcFile) : this(new FileStream(dbcFile, FileMode.Open)) { } - - public DB3Reader(Stream stream) - { - using (var reader = new BinaryReader(stream, Encoding.UTF8)) - { - if (reader.BaseStream.Length < HeaderSize) - { - throw new InvalidDataException(string.Format("DB3 file is corrupted!")); - } - - uint magic = reader.ReadUInt32(); - - if (magic != DB3FmtSig && magic != DB4FmtSig) - { - throw new InvalidDataException(string.Format("DB3 file is corrupted!")); - } - - if (magic == DB3FmtSig) - HeaderSize = 48; - else - HeaderSize = 52; - - RecordsCount = reader.ReadInt32(); - FieldsCount = reader.ReadInt32(); - RecordSize = reader.ReadInt32(); - StringTableSize = reader.ReadInt32(); - - uint tableHash = reader.ReadUInt32(); - uint build = reader.ReadUInt32(); - uint unk1 = reader.ReadUInt32(); - - int MinId = reader.ReadInt32(); - int MaxId = reader.ReadInt32(); - int locale = reader.ReadInt32(); - int CopyTableSize = reader.ReadInt32(); - - if (magic == DB4FmtSig) - { - int metaFlags = reader.ReadInt32(); - } - - int stringTableStart = HeaderSize + RecordsCount * RecordSize; - int stringTableEnd = stringTableStart + StringTableSize; - - // Index table - int[] m_indexes = null; - bool hasIndex = stringTableEnd + CopyTableSize < reader.BaseStream.Length; - - if (hasIndex) - { - reader.BaseStream.Position = stringTableEnd; - - m_indexes = new int[RecordsCount]; - - for (int i = 0; i < RecordsCount; i++) - m_indexes[i] = reader.ReadInt32(); - } - - // Records table - reader.BaseStream.Position = HeaderSize; - - for (int i = 0; i < RecordsCount; i++) - { - byte[] recordBytes = reader.ReadBytes(RecordSize); - - if (hasIndex) - { - byte[] newRecordBytes = new byte[RecordSize + 4]; - - Array.Copy(BitConverter.GetBytes(m_indexes[i]), newRecordBytes, 4); - Array.Copy(recordBytes, 0, newRecordBytes, 4, recordBytes.Length); - - m_index.Add(m_indexes[i], new DB3Row(this, newRecordBytes)); - } - else - { - m_index.Add(BitConverter.ToInt32(recordBytes, 0), new DB3Row(this, recordBytes)); - } - } - - // Strings table - reader.BaseStream.Position = stringTableStart; - - StringTable = new Dictionary(); - - while (reader.BaseStream.Position != stringTableEnd) - { - int index = (int)reader.BaseStream.Position - stringTableStart; - StringTable[index] = reader.ReadCString(); - } - - // Copy index table - long copyTablePos = stringTableEnd + (hasIndex ? 4 * RecordsCount : 0); - - if (copyTablePos != reader.BaseStream.Length && CopyTableSize != 0) - { - reader.BaseStream.Position = copyTablePos; - - while (reader.BaseStream.Position != reader.BaseStream.Length) - { - int id = reader.ReadInt32(); - int idcopy = reader.ReadInt32(); - - RecordsCount++; - - DB3Row copyRow = m_index[idcopy]; - byte[] newRowData = new byte[copyRow.Data.Length]; - Array.Copy(copyRow.Data, newRowData, newRowData.Length); - Array.Copy(BitConverter.GetBytes(id), newRowData, 4); - - m_index.Add(id, new DB3Row(this, newRowData)); - } - } - } - } - - public bool HasRow(int index) - { - return m_index.ContainsKey(index); - } - - public DB3Row GetRow(int index) - { - DB3Row row; - m_index.TryGetValue(index, out row); - return row; - } - - public IEnumerator> GetEnumerator() - { - return m_index.GetEnumerator(); - } - - IEnumerator IEnumerable.GetEnumerator() - { - return m_index.GetEnumerator(); - } - } -} From 8b137195100117d6c827023e12311fc75f4985e4 Mon Sep 17 00:00:00 2001 From: TOM_RUS Date: Sun, 20 Mar 2016 22:29:50 +0300 Subject: [PATCH 12/17] Fix --- CascLib/D3RootHandler.cs | 16 ++++++++-------- 1 file changed, 8 insertions(+), 8 deletions(-) diff --git a/CascLib/D3RootHandler.cs b/CascLib/D3RootHandler.cs index 10b58540..7bb4c3bd 100644 --- a/CascLib/D3RootHandler.cs +++ b/CascLib/D3RootHandler.cs @@ -350,14 +350,14 @@ public class CoreTOCParser { private const int NUM_SNO_GROUPS = 70; - public struct TOCHeader + public unsafe struct TOCHeader { - [MarshalAs(UnmanagedType.ByValArray, SizeConst = NUM_SNO_GROUPS)] - public int[] entryCounts; - [MarshalAs(UnmanagedType.ByValArray, SizeConst = NUM_SNO_GROUPS)] - public int[] entryOffsets; - [MarshalAs(UnmanagedType.ByValArray, SizeConst = NUM_SNO_GROUPS)] - public int[] entryUnkCounts; + //[MarshalAs(UnmanagedType.ByValArray, SizeConst = NUM_SNO_GROUPS)] + public fixed int entryCounts[NUM_SNO_GROUPS]; + //[MarshalAs(UnmanagedType.ByValArray, SizeConst = NUM_SNO_GROUPS)] + public fixed int entryOffsets[NUM_SNO_GROUPS]; + //[MarshalAs(UnmanagedType.ByValArray, SizeConst = NUM_SNO_GROUPS)] + public fixed int entryUnkCounts[NUM_SNO_GROUPS]; public int unk; } @@ -435,7 +435,7 @@ public struct TOCHeader { SNOGroup.DungeonFinder, "" }, }; - public CoreTOCParser(Stream stream) + public unsafe CoreTOCParser(Stream stream) { using (var br = new BinaryReader(stream)) { From 2b5332f8ad470d6ced2f738dc472d641b7aee88a Mon Sep 17 00:00:00 2001 From: TOM_RUS Date: Wed, 23 Mar 2016 04:55:14 +0300 Subject: [PATCH 13/17] Fix --- CASCExplorer/MainForm.cs | 3 +- CascLib/OWRootHandler.cs | 86 ++++++++++++++++++++-------------------- 2 files changed, 45 insertions(+), 44 deletions(-) diff --git a/CASCExplorer/MainForm.cs b/CASCExplorer/MainForm.cs index e5c8bb5c..dce1036b 100644 --- a/CASCExplorer/MainForm.cs +++ b/CASCExplorer/MainForm.cs @@ -342,7 +342,8 @@ private void findToolStripMenuItem_Click(object sender, EventArgs e) if (searchForm == null) searchForm = new SearchForm(fileList); - searchForm.Show(this); + if (!searchForm.Visible) + searchForm.Show(this); } private void fileList_SearchForVirtualItem(object sender, SearchForVirtualItemEventArgs e) diff --git a/CascLib/OWRootHandler.cs b/CascLib/OWRootHandler.cs index 2d02be91..e33f9738 100644 --- a/CascLib/OWRootHandler.cs +++ b/CascLib/OWRootHandler.cs @@ -20,63 +20,63 @@ public unsafe OWRootHandler(BinaryReader stream, BackgroundWorkerEx worker, CASC // need to figure out what to do with those apm files - for (int i = 1; i < array.Length; i++) - { - string[] filedata = array[i].Split('|'); + //for (int i = 1; i < array.Length; i++) + //{ + // string[] filedata = array[i].Split('|'); - string name = filedata[4]; + // string name = filedata[4]; - if (Path.GetExtension(name) == ".apm") - { - // add apm file for dev purposes - ulong fileHash1 = Hasher.ComputeHash(name); - MD5Hash md5 = filedata[0].ToByteArray().ToMD5(); - RootData[fileHash1] = new RootEntry() { MD5 = md5, LocaleFlags = LocaleFlags.All, ContentFlags = ContentFlags.None }; + // if (Path.GetExtension(name) == ".apm") + // { + // // add apm file for dev purposes + // ulong fileHash1 = Hasher.ComputeHash(name); + // MD5Hash md5 = filedata[0].ToByteArray().ToMD5(); + // RootData[fileHash1] = new RootEntry() { MD5 = md5, LocaleFlags = LocaleFlags.All, ContentFlags = ContentFlags.None }; - CASCFile.FileNames[fileHash1] = name; + // CASCFile.FileNames[fileHash1] = name; - // add files listed in apm file - EncodingEntry enc; + // // add files listed in apm file + // EncodingEntry enc; - if (!casc.Encoding.GetEntry(md5, out enc)) - continue; + // if (!casc.Encoding.GetEntry(md5, out enc)) + // continue; - using (Stream s = casc.OpenFile(enc.Key)) - using (BinaryReader br = new BinaryReader(s)) - { - // still need to figure out complete apm structure - // at start of file there's a lot of data that is same in all apm files - s.Position = 0xC; + // using (Stream s = casc.OpenFile(enc.Key)) + // using (BinaryReader br = new BinaryReader(s)) + // { + // // still need to figure out complete apm structure + // // at start of file there's a lot of data that is same in all apm files + // s.Position = 0xC; - uint count = br.ReadUInt32(); + // uint count = br.ReadUInt32(); - s.Position = 0x930; + // s.Position = 0x930; - // size of each entry seems to be 0x48 bytes (0x2C bytes unk data; int size; ulong unk; byte[16] md5) - for (int j = 0; j < count; j++) - { - s.Position += 0x2C; // skip unknown - int size = br.ReadInt32(); // size (matches size in encoding file) - s.Position += 8; // skip unknown - MD5Hash md5_2 = br.Read(); + // // size of each entry seems to be 0x48 bytes (0x2C bytes unk data; int size; ulong unk; byte[16] md5) + // for (int j = 0; j < count; j++) + // { + // s.Position += 0x2C; // skip unknown + // int size = br.ReadInt32(); // size (matches size in encoding file) + // s.Position += 8; // skip unknown + // MD5Hash md5_2 = br.Read(); - EncodingEntry enc2; + // EncodingEntry enc2; - if (!casc.Encoding.GetEntry(md5_2, out enc2)) - { - throw new Exception("enc2 == null"); - } + // if (!casc.Encoding.GetEntry(md5_2, out enc2)) + // { + // throw new Exception("enc2 == null"); + // } - string fakeName = Path.GetFileNameWithoutExtension(name) + "/" + md5_2.ToHexString(); + // string fakeName = Path.GetFileNameWithoutExtension(name) + "/" + md5_2.ToHexString(); - ulong fileHash = Hasher.ComputeHash(fakeName); - RootData[fileHash] = new RootEntry() { MD5 = md5_2, LocaleFlags = LocaleFlags.All, ContentFlags = ContentFlags.None }; + // ulong fileHash = Hasher.ComputeHash(fakeName); + // RootData[fileHash] = new RootEntry() { MD5 = md5_2, LocaleFlags = LocaleFlags.All, ContentFlags = ContentFlags.None }; - CASCFile.FileNames[fileHash] = fakeName; - } - } - } - } + // CASCFile.FileNames[fileHash] = fakeName; + // } + // } + // } + //} int current = 0; From 8e261a96b1313118e97adc4d80d7c364c80dc1c0 Mon Sep 17 00:00:00 2001 From: TOM_RUS Date: Thu, 14 Apr 2016 02:53:22 +0300 Subject: [PATCH 14/17] Fix --- CascLib/DB3Reader.cs | 13 +++++++++++-- 1 file changed, 11 insertions(+), 2 deletions(-) diff --git a/CascLib/DB3Reader.cs b/CascLib/DB3Reader.cs index 773481c2..3d74750f 100644 --- a/CascLib/DB3Reader.cs +++ b/CascLib/DB3Reader.cs @@ -68,6 +68,7 @@ public class DB3Reader : IEnumerable> private readonly int HeaderSize; private const uint DB3FmtSig = 0x33424457; // WDB3 private const uint DB4FmtSig = 0x34424457; // WDB4 + private const uint DB5FmtSig = 0x35424457; // WDB5 public int RecordsCount { get; private set; } public int FieldsCount { get; private set; } @@ -93,15 +94,17 @@ public DB3Reader(Stream stream) uint magic = reader.ReadUInt32(); - if (magic != DB3FmtSig && magic != DB4FmtSig) + if (magic != DB3FmtSig && magic != DB4FmtSig && magic != DB5FmtSig) { throw new InvalidDataException(string.Format("DB3 file is corrupted!")); } if (magic == DB3FmtSig) HeaderSize = 48; - else + else if (magic == DB4FmtSig) HeaderSize = 52; + else + HeaderSize = 56; RecordsCount = reader.ReadInt32(); FieldsCount = reader.ReadInt32(); @@ -110,6 +113,12 @@ public DB3Reader(Stream stream) uint tableHash = reader.ReadUInt32(); uint build = reader.ReadUInt32(); + + if (magic == DB5FmtSig) + { + uint unk2 = reader.ReadUInt32(); + } + uint unk1 = reader.ReadUInt32(); int MinId = reader.ReadInt32(); From 3f6099ebf8c8a3c40c6105be2223d4014767d774 Mon Sep 17 00:00:00 2001 From: TOM_RUS Date: Thu, 14 Apr 2016 03:09:00 +0300 Subject: [PATCH 15/17] Fix --- CascLib/DB3Reader.cs | 12 ++++++++---- 1 file changed, 8 insertions(+), 4 deletions(-) diff --git a/CascLib/DB3Reader.cs b/CascLib/DB3Reader.cs index 3d74750f..17c16e78 100644 --- a/CascLib/DB3Reader.cs +++ b/CascLib/DB3Reader.cs @@ -114,13 +114,11 @@ public DB3Reader(Stream stream) uint tableHash = reader.ReadUInt32(); uint build = reader.ReadUInt32(); - if (magic == DB5FmtSig) + if (magic != DB5FmtSig) { - uint unk2 = reader.ReadUInt32(); + uint unk1 = reader.ReadUInt32(); // timemodified } - uint unk1 = reader.ReadUInt32(); - int MinId = reader.ReadInt32(); int MaxId = reader.ReadInt32(); int locale = reader.ReadInt32(); @@ -131,6 +129,12 @@ public DB3Reader(Stream stream) int metaFlags = reader.ReadInt32(); } + if (magic == DB5FmtSig) + { + uint unk1 = reader.ReadUInt32(); + uint unk2 = reader.ReadUInt32(); + } + int stringTableStart = HeaderSize + RecordsCount * RecordSize; int stringTableEnd = stringTableStart + StringTableSize; From a27581f884ae52dea8a8b9e0c74a0a9318821aec Mon Sep 17 00:00:00 2001 From: TOM_RUS Date: Thu, 14 Apr 2016 05:06:21 +0300 Subject: [PATCH 16/17] Fix --- CascLib/DB3Reader.cs | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/CascLib/DB3Reader.cs b/CascLib/DB3Reader.cs index 17c16e78..d5c1c0d7 100644 --- a/CascLib/DB3Reader.cs +++ b/CascLib/DB3Reader.cs @@ -131,8 +131,7 @@ public DB3Reader(Stream stream) if (magic == DB5FmtSig) { - uint unk1 = reader.ReadUInt32(); - uint unk2 = reader.ReadUInt32(); + reader.BaseStream.Position += FieldsCount * 4; } int stringTableStart = HeaderSize + RecordsCount * RecordSize; From f3d81addef25946d72dcf3838617019ae84049a8 Mon Sep 17 00:00:00 2001 From: TOM_RUS Date: Tue, 10 May 2016 16:36:11 +0300 Subject: [PATCH 17/17] Fix --- CASCConsole/Program.cs | 10 +++---- CASCExplorer/AboutBox.Designer.cs | 2 +- CASCExplorer/AboutBox.cs | 11 ++++---- CASCExplorer/CASCViewHelper.cs | 2 +- CascLib/CASCHandler.cs | 4 ++- CascLib/CDNCache.cs | 28 +++++++++---------- CascLib/D3RootHandler.cs | 6 ++-- CascLib/DB2Reader.cs | 8 +++--- CascLib/FastStruct.cs | 8 +++--- CascLib/KeyService.cs | 19 ++++++++----- CascLib/MNDXRootHandler.cs | 7 ++--- CascLib/OWRootHandler.cs | 45 ++++++++++++++---------------- CascLib/Properties/AssemblyInfo.cs | 1 - CascLib/Wildcard.cs | 5 ++-- 14 files changed, 77 insertions(+), 79 deletions(-) diff --git a/CASCConsole/Program.cs b/CASCConsole/Program.cs index 7678f435..8301da43 100644 --- a/CASCConsole/Program.cs +++ b/CASCConsole/Program.cs @@ -10,7 +10,7 @@ namespace CASCConsole { class Program { - static object progressLock = new object(); + static readonly object ProgressLock = new object(); static void Main(string[] args) { @@ -78,7 +78,7 @@ static void Main(string[] args) private static void BgLoader_ProgressChanged(object sender, ProgressChangedEventArgs e) { - lock (progressLock) + lock (ProgressLock) { if (e.UserState != null) Console.WriteLine(e.UserState); @@ -89,7 +89,7 @@ private static void BgLoader_ProgressChanged(object sender, ProgressChangedEvent private static void DrawProgressBar(long complete, long maxVal, int barSize, char progressCharacter) { - float perc = (float)complete / (float)maxVal; + float perc = (float)complete / maxVal; DrawProgressBar(perc, barSize, progressCharacter); } @@ -97,8 +97,8 @@ private static void DrawProgressBar(float percent, int barSize, char progressCha { Console.CursorVisible = false; int left = Console.CursorLeft; - int chars = (int)Math.Round(percent / (1.0f / (float)barSize)); - string p1 = String.Empty, p2 = String.Empty; + int chars = (int)Math.Round(percent / (1.0f / barSize)); + string p1 = string.Empty, p2 = string.Empty; for (int i = 0; i < chars; i++) p1 += progressCharacter; diff --git a/CASCExplorer/AboutBox.Designer.cs b/CASCExplorer/AboutBox.Designer.cs index 75ce1ddd..8e881562 100644 --- a/CASCExplorer/AboutBox.Designer.cs +++ b/CASCExplorer/AboutBox.Designer.cs @@ -1,6 +1,6 @@ namespace CASCExplorer { - partial class AboutBox + sealed partial class AboutBox { /// /// Required designer variable. diff --git a/CASCExplorer/AboutBox.cs b/CASCExplorer/AboutBox.cs index c47effd1..471ec75d 100644 --- a/CASCExplorer/AboutBox.cs +++ b/CASCExplorer/AboutBox.cs @@ -5,17 +5,16 @@ namespace CASCExplorer { - partial class AboutBox : Form + sealed partial class AboutBox : Form { public AboutBox() { InitializeComponent(); - this.Text = String.Format("About {0}", AssemblyTitle); + this.Text = string.Format("About {0}", AssemblyTitle); this.labelProductName.Text = AssemblyProduct; - this.labelVersion.Text = String.Format("Version {0}", AssemblyVersion); + this.labelVersion.Text = string.Format("Version {0}", AssemblyVersion); this.labelCopyright.Text = AssemblyCopyright; - var link = new LinkLabel.Link(); - link.LinkData = Properties.Resources.donateURL; + var link = new LinkLabel.Link {LinkData = Properties.Resources.donateURL}; this.labelDonate.Links.Add(link); this.textBoxDescription.Text = AssemblyDescription; } @@ -89,7 +88,7 @@ public string AssemblyCopyright private void labelDonate_LinkClicked(object sender, LinkLabelLinkClickedEventArgs e) { - Process.Start(e.Link.LinkData as string); + Process.Start(e.Link?.LinkData as string); } } } diff --git a/CASCExplorer/CASCViewHelper.cs b/CASCExplorer/CASCViewHelper.cs index 8bc82d1c..53bcca67 100644 --- a/CASCExplorer/CASCViewHelper.cs +++ b/CASCExplorer/CASCViewHelper.cs @@ -119,7 +119,7 @@ await Task.Run(() => } } - if (_casc.FileExists("DBFilesClient\\SoundKit.db2") && _casc.FileExists("DBFilesClient\\SoundKitEntry.db2")) + if (false && _casc.FileExists("DBFilesClient\\SoundKit.db2") && _casc.FileExists("DBFilesClient\\SoundKitEntry.db2")) { using (Stream skStream = _casc.OpenFile("DBFilesClient\\SoundKit.db2")) using (Stream skeStream = _casc.OpenFile("DBFilesClient\\SoundKitEntry.db2")) diff --git a/CascLib/CASCHandler.cs b/CascLib/CASCHandler.cs index 4521037e..9f17306c 100644 --- a/CascLib/CASCHandler.cs +++ b/CascLib/CASCHandler.cs @@ -58,7 +58,7 @@ private CASCHandler(CASCConfig config, BackgroundWorkerEx worker) : base(config, else if (config.GameType == CASCGameType.Hearthstone) RootHandler = new HSRootHandler(fs, worker); else if (config.GameType == CASCGameType.Overwatch) - RootHandler = new OWRootHandler(fs, worker, this); + RootHandler = new OwRootHandler(fs, worker, this); else throw new Exception("Unsupported game " + config.BuildUID); } @@ -74,6 +74,8 @@ private CASCHandler(CASCConfig config, BackgroundWorkerEx worker) : base(config, { using (var fs = OpenInstallFile(EncodingHandler, this)) InstallHandler = new InstallHandler(fs, worker); + + InstallHandler.Print(); } Logger.WriteLine("CASCHandler: loaded {0} install data", InstallHandler.Count); diff --git a/CascLib/CDNCache.cs b/CascLib/CDNCache.cs index 5173dbb6..53b54ef2 100644 --- a/CascLib/CDNCache.cs +++ b/CascLib/CDNCache.cs @@ -7,8 +7,8 @@ namespace CASCExplorer { public class CacheMetaData { - public long Size { get; private set; } - public byte[] MD5 { get; private set; } + public long Size { get; } + public byte[] MD5 { get; } public CacheMetaData(long size, byte[] md5) { @@ -18,7 +18,7 @@ public CacheMetaData(long size, byte[] md5) public void Save(string file) { - File.WriteAllText(file + ".dat", string.Format("{0} {1}", Size, MD5.ToHexString())); + File.WriteAllText(file + ".dat", $"{Size} {MD5.ToHexString()}"); } public static CacheMetaData Load(string file) @@ -44,17 +44,17 @@ public static CacheMetaData AddToCache(HttpWebResponse resp, string file) public class CDNCache { public bool Enabled { get; set; } = true; - private bool CacheData { get; set; } = false; + public bool CacheData { get; set; } = false; public bool Validate { get; set; } = true; - private string cachePath; - private SyncDownloader downloader = new SyncDownloader(null); + private readonly string _cachePath; + private readonly SyncDownloader _downloader = new SyncDownloader(null); - private MD5 md5 = MD5.Create(); + private readonly MD5 _md5 = MD5.Create(); public CDNCache(string path) { - cachePath = path; + _cachePath = path; } public Stream OpenFile(string name, string url, bool isData) @@ -65,18 +65,18 @@ public Stream OpenFile(string name, string url, bool isData) if (isData && !CacheData) return null; - string file = Path.Combine(cachePath, name); + string file = Path.Combine(_cachePath, name); Logger.WriteLine("CDNCache: Opening file {0}", file); FileInfo fi = new FileInfo(file); if (!fi.Exists) - downloader.DownloadFile(url, file); + _downloader.DownloadFile(url, file); if (Validate) { - CacheMetaData meta = CacheMetaData.Load(file) ?? downloader.GetMetaData(url, file); + CacheMetaData meta = CacheMetaData.Load(file) ?? _downloader.GetMetaData(url, file); if (meta == null) throw new Exception(string.Format("unable to validate file {0}", file)); @@ -86,11 +86,11 @@ public Stream OpenFile(string name, string url, bool isData) using (FileStream fs = new FileStream(file, FileMode.Open, FileAccess.Read, FileShare.ReadWrite)) { sizeOk = fs.Length == meta.Size; - md5Ok = md5.ComputeHash(fs).EqualsTo(meta.MD5); + md5Ok = _md5.ComputeHash(fs).EqualsTo(meta.MD5); } if (!sizeOk || !md5Ok) - downloader.DownloadFile(url, file); + _downloader.DownloadFile(url, file); } return new FileStream(file, FileMode.Open, FileAccess.Read, FileShare.ReadWrite); @@ -98,7 +98,7 @@ public Stream OpenFile(string name, string url, bool isData) public bool HasFile(string name) { - return File.Exists(Path.Combine(cachePath, name)); + return File.Exists(Path.Combine(_cachePath, name)); } } } diff --git a/CascLib/D3RootHandler.cs b/CascLib/D3RootHandler.cs index 7bb4c3bd..56125b76 100644 --- a/CascLib/D3RootHandler.cs +++ b/CascLib/D3RootHandler.cs @@ -361,9 +361,9 @@ public unsafe struct TOCHeader public int unk; } - Dictionary snoDic = new Dictionary(); + readonly Dictionary snoDic = new Dictionary(); - Dictionary extensions = new Dictionary() + readonly Dictionary extensions = new Dictionary() { { SNOGroup.Code, "" }, { SNOGroup.None, "" }, @@ -475,7 +475,7 @@ public SNOInfo GetSNO(int id) public class PackagesParser { - Dictionary nameToExtDic = new Dictionary(StringComparer.OrdinalIgnoreCase); + readonly Dictionary nameToExtDic = new Dictionary(StringComparer.OrdinalIgnoreCase); public PackagesParser(Stream stream) { diff --git a/CascLib/DB2Reader.cs b/CascLib/DB2Reader.cs index a56e68da..b6d740bc 100644 --- a/CascLib/DB2Reader.cs +++ b/CascLib/DB2Reader.cs @@ -8,10 +8,10 @@ namespace CASCExplorer { public class DB2Row { - private byte[] m_data; - private DB2Reader m_reader; + private readonly byte[] m_data; + private readonly DB2Reader m_reader; - public byte[] Data { get { return m_data; } } + public byte[] Data => m_data; public DB2Row(DB2Reader reader, byte[] data) { @@ -58,7 +58,7 @@ public class DB2Reader : IEnumerable> private readonly DB2Row[] m_rows; public byte[] StringTable { get; private set; } - Dictionary m_index = new Dictionary(); + readonly Dictionary m_index = new Dictionary(); public DB2Reader(string dbcFile) : this(new FileStream(dbcFile, FileMode.Open)) { } diff --git a/CascLib/FastStruct.cs b/CascLib/FastStruct.cs index 42627a3d..1ee44e2a 100644 --- a/CascLib/FastStruct.cs +++ b/CascLib/FastStruct.cs @@ -36,7 +36,7 @@ public static class FastStruct where T : struct private static GetPtrDelegate BuildGetPtrMethod() { methodGetPtr = new DynamicMethod("GetStructPtr<" + typeof(T).FullName + ">", - typeof(IntPtr), new Type[1] { typeof(T).MakeByRefType() }, typeof(FastStruct).Module); + typeof(IntPtr), new[] { typeof(T).MakeByRefType() }, typeof(FastStruct).Module); ILGenerator generator = methodGetPtr.GetILGenerator(); generator.Emit(OpCodes.Ldarg_0); @@ -48,7 +48,7 @@ private static GetPtrDelegate BuildGetPtrMethod() private static PtrToStructureDelegateIntPtr BuildLoadFromIntPtrMethod() { methodLoadIntPtr = new DynamicMethod("PtrToStructureIntPtr<" + typeof(T).FullName + ">", - typeof(T), new Type[1] { typeof(IntPtr) }, typeof(FastStruct).Module); + typeof(T), new[] { typeof(IntPtr) }, typeof(FastStruct).Module); ILGenerator generator = methodLoadIntPtr.GetILGenerator(); generator.Emit(OpCodes.Ldarg_0); @@ -58,10 +58,10 @@ private static PtrToStructureDelegateIntPtr BuildLoadFromIntPtrMethod() return (PtrToStructureDelegateIntPtr)methodLoadIntPtr.CreateDelegate(typeof(PtrToStructureDelegateIntPtr)); } - private static unsafe PtrToStructureDelegateBytePtr BuildLoadFromBytePtrMethod() + private static PtrToStructureDelegateBytePtr BuildLoadFromBytePtrMethod() { methodLoadBytePtr = new DynamicMethod("PtrToStructureBytePtr<" + typeof(T).FullName + ">", - typeof(T), new Type[1] { typeof(byte*) }, typeof(FastStruct).Module); + typeof(T), new[] { typeof(byte*) }, typeof(FastStruct).Module); ILGenerator generator = methodLoadBytePtr.GetILGenerator(); generator.Emit(OpCodes.Ldarg_0); diff --git a/CascLib/KeyService.cs b/CascLib/KeyService.cs index 70ab0036..9144f9a9 100644 --- a/CascLib/KeyService.cs +++ b/CascLib/KeyService.cs @@ -17,14 +17,19 @@ class KeyService [0xDEE3A0521EFF6F03] = "AD740CE3FFFF9231468126985708E1B9".ToByteArray(), [0x8C9106108AA84F07] = "53D859DDA2635A38DC32E72B11B32F29".ToByteArray(), [0x49166D358A34D815] = "667868CD94EA0135B9B16C93B1124ABA".ToByteArray(), + [0x1463A87356778D14] = "69BD2A78D05C503E93994959B30E5AEC".ToByteArray(), + [0x5E152DE44DFBEE01] = "E45A1793B37EE31A8EB85CEE0EEE1B68".ToByteArray(), + [0x9B1F39EE592CA415] = "54A99F081CAD0D08F7E336F4368E894C".ToByteArray(), // streamed WoW keys - [0xB76729641141CB34] = "9849D1AA7B1FD09819C5C66283A326EC".ToByteArray(), - [0x23C5B5DF837A226C] = "1406E2D873B6FC99217A180881DA8D62".ToByteArray(), - [0xD1E9B5EDF9283668] = "8E4A2579894E38B4AB9058BA5C7328EE".ToByteArray(), - - // 3ECB6A12785050FA - BDC51862ABED79B2DE48C8E7E66C6200 - // ? - AA0B5C77F088CCC2D39049BD267F066D - // ? - D514BD1909A9E5DC8703F4B8BB1DFD9A + [0xFA505078126ACB3E] = "BDC51862ABED79B2DE48C8E7E66C6200".ToByteArray(), // TactKeyId 15 + [0xFF813F7D062AC0BC] = "AA0B5C77F088CCC2D39049BD267F066D".ToByteArray(), // TactKeyId 25 + [0xD1E9B5EDF9283668] = "8E4A2579894E38B4AB9058BA5C7328EE".ToByteArray(), // TactKeyId 39 + [0xB76729641141CB34] = "9849D1AA7B1FD09819C5C66283A326EC".ToByteArray(), // TactKeyId 40 + [0xFFB9469FF16E6BF8] = "D514BD1909A9E5DC8703F4B8BB1DFD9A".ToByteArray(), // TactKeyId 41 + [0x23C5B5DF837A226C] = "1406E2D873B6FC99217A180881DA8D62".ToByteArray(), // TactKeyId 42 + [0xE2854509C471C554] = "433265F0CDEB2F4E65C0EE7008714D9E".ToByteArray(), // TactKeyId 52 + // BNA 1.5.0 Alpha + [0x2C547F26A2613E01] = "37C50C102D4C9E3A5AC069F072B1417D".ToByteArray(), }; private static Salsa20 salsa = new Salsa20(); diff --git a/CascLib/MNDXRootHandler.cs b/CascLib/MNDXRootHandler.cs index d0de00fb..426237b9 100644 --- a/CascLib/MNDXRootHandler.cs +++ b/CascLib/MNDXRootHandler.cs @@ -209,8 +209,7 @@ public MNDXRootHandler(BinaryReader stream, BackgroundWorkerEx worker) public override IEnumerable> GetAllEntries() { - foreach (var entry in mndxData) - yield return entry; + return mndxData; } public override IEnumerable GetAllEntries(ulong hash) @@ -219,8 +218,6 @@ public override IEnumerable GetAllEntries(ulong hash) if (mndxData.TryGetValue(hash, out rootEntry)) yield return rootEntry; - else - yield break; } public override IEnumerable GetEntries(ulong hash) @@ -846,7 +843,7 @@ private int sub_1959F50(int arg_0) { // Binary search // HOTS: 1959FAD - if ((eax + 1) < edi) + if (eax + 1 < edi) { // HOTS: 1959FB4 esi = (edi + eax) >> 1; diff --git a/CascLib/OWRootHandler.cs b/CascLib/OWRootHandler.cs index e33f9738..eb7bfd25 100644 --- a/CascLib/OWRootHandler.cs +++ b/CascLib/OWRootHandler.cs @@ -6,17 +6,17 @@ namespace CASCExplorer { - public class OWRootHandler : RootHandlerBase + public class OwRootHandler : RootHandlerBase { - private readonly Dictionary RootData = new Dictionary(); + private readonly Dictionary _rootData = new Dictionary(); - public unsafe OWRootHandler(BinaryReader stream, BackgroundWorkerEx worker, CASCHandler casc) + public unsafe OwRootHandler(BinaryReader stream, BackgroundWorkerEx worker, CASCHandler casc) { worker?.ReportProgress(0, "Loading \"root\"..."); - string str = Encoding.ASCII.GetString(stream.ReadBytes((int)stream.BaseStream.Length)); + //string str = Encoding.ASCII.GetString(stream.ReadBytes((int)stream.BaseStream.Length)); - string[] array = str.Split(new[] { '\r', '\n' }, StringSplitOptions.RemoveEmptyEntries); + //string[] array = str.Split(new[] { '\r', '\n' }, StringSplitOptions.RemoveEmptyEntries); // need to figure out what to do with those apm files @@ -31,7 +31,7 @@ public unsafe OWRootHandler(BinaryReader stream, BackgroundWorkerEx worker, CASC // // add apm file for dev purposes // ulong fileHash1 = Hasher.ComputeHash(name); // MD5Hash md5 = filedata[0].ToByteArray().ToMD5(); - // RootData[fileHash1] = new RootEntry() { MD5 = md5, LocaleFlags = LocaleFlags.All, ContentFlags = ContentFlags.None }; + // _rootData[fileHash1] = new RootEntry() { MD5 = md5, LocaleFlags = LocaleFlags.All, ContentFlags = ContentFlags.None }; // CASCFile.FileNames[fileHash1] = name; @@ -70,7 +70,7 @@ public unsafe OWRootHandler(BinaryReader stream, BackgroundWorkerEx worker, CASC // string fakeName = Path.GetFileNameWithoutExtension(name) + "/" + md5_2.ToHexString(); // ulong fileHash = Hasher.ComputeHash(fakeName); - // RootData[fileHash] = new RootEntry() { MD5 = md5_2, LocaleFlags = LocaleFlags.All, ContentFlags = ContentFlags.None }; + // _rootData[fileHash] = new RootEntry() { MD5 = md5_2, LocaleFlags = LocaleFlags.All, ContentFlags = ContentFlags.None }; // CASCFile.FileNames[fileHash] = fakeName; // } @@ -80,15 +80,15 @@ public unsafe OWRootHandler(BinaryReader stream, BackgroundWorkerEx worker, CASC int current = 0; - Func tag2locale = (s) => - { - LocaleFlags locale; + //Func tag2locale = (s) => + //{ + // LocaleFlags locale; - if (Enum.TryParse(s, out locale)) - return locale; + // if (Enum.TryParse(s, out locale)) + // return locale; - return LocaleFlags.All; - }; + // return LocaleFlags.All; + //}; MD5Hash key; @@ -99,7 +99,7 @@ public unsafe OWRootHandler(BinaryReader stream, BackgroundWorkerEx worker, CASC string fakeName = "unknown" + "/" + key.Value[0].ToString("X2") + "/" + entry.Key.ToHexString(); ulong fileHash = Hasher.ComputeHash(fakeName); - RootData.Add(fileHash, new RootEntry() { MD5 = entry.Key, LocaleFlags = LocaleFlags.All, ContentFlags = ContentFlags.None }); + _rootData.Add(fileHash, new RootEntry() { MD5 = entry.Key, LocaleFlags = LocaleFlags.All, ContentFlags = ContentFlags.None }); CASCFile.FileNames[fileHash] = fakeName; @@ -109,18 +109,15 @@ public unsafe OWRootHandler(BinaryReader stream, BackgroundWorkerEx worker, CASC public override IEnumerable> GetAllEntries() { - foreach (var entry in RootData) - yield return entry; + return _rootData; } public override IEnumerable GetAllEntries(ulong hash) { RootEntry entry; - if (RootData.TryGetValue(hash, out entry)) + if (_rootData.TryGetValue(hash, out entry)) yield return entry; - else - yield break; } // Returns only entries that match current locale and content flags @@ -128,7 +125,7 @@ public override IEnumerable GetEntries(ulong hash) { //RootEntry entry; - //if (RootData.TryGetValue(hash, out entry)) + //if (_rootData.TryGetValue(hash, out entry)) // yield return entry; //else // yield break; @@ -147,7 +144,7 @@ protected override CASCFolder CreateStorageTree() CountSelect = 0; CountUnknown = 0; - foreach (var rootEntry in RootData) + foreach (var rootEntry in _rootData) { if ((rootEntry.Value.LocaleFlags & Locale) == 0) continue; @@ -156,14 +153,14 @@ protected override CASCFolder CreateStorageTree() CountSelect++; } - Logger.WriteLine("OWRootHandler: {0} file names missing for locale {1}", CountUnknown, Locale); + Logger.WriteLine("OwRootHandler: {0} file names missing for locale {1}", CountUnknown, Locale); return root; } public override void Clear() { - RootData.Clear(); + _rootData.Clear(); Root.Entries.Clear(); CASCFile.FileNames.Clear(); } diff --git a/CascLib/Properties/AssemblyInfo.cs b/CascLib/Properties/AssemblyInfo.cs index aa32f266..745b6216 100644 --- a/CascLib/Properties/AssemblyInfo.cs +++ b/CascLib/Properties/AssemblyInfo.cs @@ -1,5 +1,4 @@ using System.Reflection; -using System.Runtime.CompilerServices; using System.Runtime.InteropServices; // General Information about an assembly is controlled through the following diff --git a/CascLib/Wildcard.cs b/CascLib/Wildcard.cs index ea59a8f9..18ff3f31 100644 --- a/CascLib/Wildcard.cs +++ b/CascLib/Wildcard.cs @@ -22,7 +22,7 @@ public Wildcard(string pattern, bool matchStartEnd) /// /// The wildcard pattern to match. /// A combination of one or more - /// . + /// . public Wildcard(string pattern, bool matchStartEnd, RegexOptions options) : base(WildcardToRegex(pattern, matchStartEnd), options) { @@ -37,8 +37,7 @@ public static string WildcardToRegex(string pattern, bool matchStartEnd) { if (matchStartEnd) return "^" + Escape(pattern).Replace("\\*", ".*").Replace("\\?", ".") + "$"; - else - return Escape(pattern).Replace("\\*", ".*").Replace("\\?", "."); + return Escape(pattern).Replace("\\*", ".*").Replace("\\?", "."); } } }