private static byte[] Decrypt(byte[] data, int index) { byte keyNameSize = data[1]; if (keyNameSize == 0 || keyNameSize != 8) { throw new BLTEDecoderException("keyNameSize == 0 || keyNameSize != 8"); } byte[] keyNameBytes = new byte[keyNameSize]; Array.Copy(data, 2, keyNameBytes, 0, keyNameSize); ulong keyName = BitConverter.ToUInt64(keyNameBytes, 0); byte IVSize = data[keyNameSize + 2]; if (IVSize != 4 || IVSize > 0x10) { throw new BLTEDecoderException("IVSize != 4 || IVSize > 0x10"); } byte[] IVpart = new byte[IVSize]; Array.Copy(data, keyNameSize + 3, IVpart, 0, IVSize); if (data.Length < IVSize + keyNameSize + 4) { throw new BLTEDecoderException("data.Length < IVSize + keyNameSize + 4"); } int dataOffset = keyNameSize + IVSize + 3; byte encType = data[dataOffset]; if (encType != ENCRYPTION_SALSA20 && encType != ENCRYPTION_ARC4) // 'S' or 'A' { throw new BLTEDecoderException("encType != 0x53 && encType != 0x41"); } dataOffset++; // expand to 8 bytes byte[] IV = new byte[8]; Array.Copy(IVpart, IV, IVpart.Length); // magic for (int shift = 0, i = 0; i < sizeof(int); shift += 8, i++) { IV[i] ^= (byte)((index >> shift) & 0xFF); } byte[] key = KeyService.GetKey(keyName); if (key == null) { throw new BLTEDecoderException("unknown keyname {0:X16}", keyName); } if (encType == ENCRYPTION_SALSA20) { ICryptoTransform decryptor = KeyService.SalsaInstance.CreateDecryptor(key, IV); return(decryptor.TransformFinalBlock(data, dataOffset, data.Length - dataOffset)); } else { // ARC4 ? throw new BLTEDecoderException("encType A not implemented"); } }
public async Task AnalyzeUnknownFiles(Action <int> progressCallback) { if (_casc == null) { return; } IProgress <int> progress = new Progress <int>(progressCallback); await Task.Run(() => { FileScanner scanner = new FileScanner(_casc, _root); Dictionary <int, List <string> > idToName = new Dictionary <int, List <string> >(); if (_casc.Config.GameType == CASCGameType.WoW && AnalyzeSoundFiles) { if (_casc.FileExists("DBFilesClient\\SoundEntries.db2")) { using (Stream stream = _casc.OpenFile("DBFilesClient\\SoundEntries.db2")) { WDB2Reader se = new WDB2Reader(stream); foreach (var row in se) { string name = row.Value.GetField <string>(2); int type = row.Value.GetField <int>(1); bool many = row.Value.GetField <int>(4) > 0; for (int i = 3; i < 23; i++) { int id = row.Value.GetField <int>(i); if (!idToName.ContainsKey(id)) { idToName[id] = new List <string>(); } idToName[id].Add("unknown\\sound\\" + name + (many ? "_" + (i - 2).ToString("D2") : "") + (type == 28 ? ".mp3" : ".ogg")); } } } } if (_casc.FileExists(1237434 /*"DBFilesClient\\SoundKit.db2"*/) && _casc.FileExists(1237435 /*"DBFilesClient\\SoundKitEntry.db2"*/) && _casc.FileExists(1665033 /*"DBFilesClient\\SoundKitName.db2"*/)) { using (Stream skStream = _casc.OpenFile(1237434)) using (Stream skeStream = _casc.OpenFile(1237435)) using (Stream sknStream = _casc.OpenFile(1665033)) { Func <ulong, bool> keyCheckFunc = x => KeyService.GetKey(x) != null; WDC3Reader sk = new WDC3Reader(skStream, keyCheckFunc); WDC3Reader ske = new WDC3Reader(skeStream, keyCheckFunc); WDC3Reader skn = new WDC3Reader(sknStream, keyCheckFunc); Dictionary <int, List <int> > lookup = new Dictionary <int, List <int> >(); foreach (var row in ske) { int soundKitId = row.Value.GetField <int>(0); if (!lookup.ContainsKey(soundKitId)) { lookup[soundKitId] = new List <int>(); } lookup[soundKitId].Add(row.Value.GetField <int>(1)); } foreach (var row in sk) { WDC3Row sknRow = skn.GetRow(row.Key); if (sknRow != null) { string name = sknRow.GetField <string>(0).Replace(':', '_').Replace("\"", ""); int type = row.Value.GetField <byte>(6); if (!lookup.TryGetValue(row.Key, out List <int> ske_entries)) { continue; } bool many = ske_entries.Count > 1; int i = 0; foreach (var fid in ske_entries) { if (!idToName.ContainsKey(fid)) { idToName[fid] = new List <string>(); } if (AddFileDataIdToSoundFiles) { idToName[fid].Add("unknown\\sound\\" + name + (many ? "_" + (i + 1).ToString("D2") : "") + "_" + fid + (type == 28 ? ".mp3" : ".ogg")); } else { idToName[fid].Add("unknown\\sound\\" + name + (many ? "_" + (i + 1).ToString("D2") : "") + (type == 28 ? ".mp3" : ".ogg")); } i++; } } } } } } CASCFolder unknownFolder = _root.GetEntry("unknown") as CASCFolder; if (unknownFolder == null) { return; } foreach (var kv in idToName) { foreach (var fn in kv.Value) { Logger.WriteLine($"{kv.Key};{fn}"); } } IEnumerable <CASCFile> files = CASCFolder.GetFiles(unknownFolder.Entries.Select(kv => kv.Value), null, true).ToList(); int numTotal = files.Count(); int numDone = 0; WowRootHandler wowRoot = _casc.Root as WowRootHandler; Jenkins96 Hasher = new Jenkins96(); char[] PathDelimiters = new char[] { '/', '\\' }; foreach (var unknownEntry in files) { CASCFile unknownFile = unknownEntry as CASCFile; if (idToName.TryGetValue(wowRoot.GetFileDataIdByHash(unknownFile.Hash), out List <string> name)) { if (name.Count == 1) { unknownFile.FullName = name[0]; } else { unknownFolder.Entries.Remove(unknownFile.Name); foreach (var file in name) { //Logger.WriteLine(file); string[] parts = file.Split(PathDelimiters); string entryName = parts[parts.Length - 1]; ulong filehash = unknownFile.Hash; CASCFile entry = new CASCFile(filehash, file); CASCFile.Files[filehash] = entry; unknownFolder.Entries[entryName] = entry; } } } else { string ext = scanner.GetFileExtension(unknownFile); unknownFile.FullName += ext; if (ext == ".m2") { using (var m2file = _casc.OpenFile(unknownFile.Hash)) using (var br = new BinaryReader(m2file)) { m2file.Position = 0x14; int nameOffs = br.ReadInt32(); m2file.Position = nameOffs + 8; // + sizeof(MD21) string m2name = br.ReadCString(); unknownFile.FullName = "unknown\\" + m2name + ".m2"; Logger.WriteLine($"{wowRoot.GetFileDataIdByHash(unknownFile.Hash)};{unknownFile.FullName}"); //m2file.Position = 0; //while (m2file.Position != m2file.Length) //{ // int chunkType = br.ReadInt32(); // int chunkSize = br.ReadInt32(); // if (chunkType == 0x44494641) // AFID // { // int count = chunkSize / 8; // for (int i = 0; i < count; i++) // { // ushort animId = br.ReadUInt16(); // ushort subAnimId = br.ReadUInt16(); // int FileDataId = br.ReadInt32(); // if (FileDataId != 0) // Logger.WriteLine($"{FileDataId};{wowRoot.GetFileDataIdByHash(unknownFile.Hash)}\\{name}\\{name}{animId:D4}-{subAnimId:D2}.anim"); // } // break; // } // else // { // m2file.Position += chunkSize; // } //} } } } progress.Report((int)(++numDone / (float)numTotal * 100)); } _casc.Root.Dump(); }); }