public static void DecodeVerb(DecodeOptions opt) { var privDir = new DirectoryInfo(Path.Combine(opt.CacheDirectory)); if (!privDir.Exists) { Console.WriteLine("Cache directory not found?"); return; } var workDir = string.IsNullOrWhiteSpace(opt.WorkingDirectory) ? Environment.CurrentDirectory : opt.WorkingDirectory; var dbDir = new DirectoryInfo(Path.Combine(workDir, "db")); Console.WriteLine("Decrypting database"); DecryptDb(new DirectoryInfo(Path.Combine(privDir.FullName, "db")), dbDir); Console.WriteLine("Reading entries from database"); var entries = ReadDatabaseEntries(dbDir.FullName, out var errorCount); Console.WriteLine("Found {0} entries", entries.Count); Console.WriteLine("Errors reading {0} entries", errorCount); var entryLookup = entries.ToLookup(e => e.Filename); var files = new DirectoryInfo(privDir.FullName).GetFiles(); var filesLookup = files.ToLookup(f => f.Name); var entriesWithFiles = entries.Select(e => new { entry = e, file = filesLookup[e.Filename].FirstOrDefault() }).ToList(); Console.WriteLine("Skipping {0} entries", entriesWithFiles.Count(e => e.file == null)); Console.WriteLine("Skipping {0} files", files.Count(e => !entryLookup[e.Name].Any())); Console.WriteLine("Decrypting {0} files", entriesWithFiles.Count(e => e.file != null)); foreach (var g in entriesWithFiles.Where(e => e.file != null).GroupBy(e => Tuple.Create(e.entry.ResourceName, e.entry.OriginalFilename))) { foreach (var entryAndFile in g.OrderByDescending(e => e.file.LastWriteTimeUtc).ToList()) { var entry = entryAndFile.entry; var input = File.ReadAllBytes(filesLookup[entry.Filename].First().FullName); var output = Decrypt(DecryptStatic(input), entry.Key, entry.IV); var timeStamp = entryAndFile.file.LastWriteTimeUtc; var outDirStr = Regex.Replace(opt.OutputDirectory, "(%\\w)", res => { switch (res.Groups[1].Value) { case "%d": return(timeStamp.Day.ToString()); case "%m": return(timeStamp.Month.ToString()); case "%y": return(timeStamp.Year.ToString()); case "%h": return(entry.Hash); case "%n": return(entry.ResourceName); case "%s": var uri = new Uri(entry.From); return(uri.Host + "_" + uri.Port); } return(res.Groups[1].Value); }); var outDir = new DirectoryInfo(outDirStr); if (!outDir.Exists) { outDir.Create(); } Directory.SetLastWriteTimeUtc(outDir.FullName, timeStamp); if (Path.GetExtension(entry.OriginalFilename) == ".rpf") { var reader = new Rpf2Reader(new MemoryStream(output)); reader.Open(); foreach (var rpfEntry in reader.ReadEntries()) { var outFn = new FileInfo(Path.Combine(outDir.FullName, entry.OriginalFilename, rpfEntry.FullName)); if (!outFn.Directory.Exists) { outFn.Directory.Create(); } File.WriteAllBytes(outFn.FullName, rpfEntry.Data); File.SetLastWriteTimeUtc(outFn.FullName, timeStamp); } } else { var outFn = Path.Combine(outDir.FullName, entry.OriginalFilename); File.WriteAllBytes(outFn, output); File.SetLastWriteTimeUtc(outFn, timeStamp); } if (!opt.Duplicates) { break; } } } Console.WriteLine("Finished"); }
public static void EncodeVerb(EncodeOptions opt) { var privDir = new DirectoryInfo(Path.Combine(opt.CacheDirectory)); if (!privDir.Exists) { Console.WriteLine("Cache directory not found?"); return; } var workDir = string.IsNullOrWhiteSpace(opt.WorkingDirectory) ? Environment.CurrentDirectory : opt.WorkingDirectory; var dbDir = new DirectoryInfo(Path.Combine(workDir, "db")); Console.WriteLine("Reading entries from database"); var dbEntries = ReadDatabaseEntries(dbDir.FullName, out var errorCount); Console.WriteLine("Found {0} entries", dbEntries.Count); Console.WriteLine("Errors reading {0} entries", errorCount); var entryLookup = dbEntries.ToLookup(e => e.Filename); var files = new DirectoryInfo(privDir.FullName).GetFiles(); var filesLookup = files.ToLookup(f => f.Name); var entriesWithFiles = dbEntries.Select(e => new { entry = e, file = filesLookup[e.Filename].FirstOrDefault() }).ToList(); Console.WriteLine("Skipping {0} entries", entriesWithFiles.Count(e => e.file == null)); Console.WriteLine("Skipping {0} files", files.Count(e => !entryLookup[e.Name].Any())); Console.WriteLine("Checking {0} files", entriesWithFiles.Count(e => e.file != null)); foreach (var g in entriesWithFiles.Where(e => e.file != null).GroupBy(e => Tuple.Create(e.entry.ResourceName, e.entry.OriginalFilename))) { var entryAndFile = g.OrderByDescending(e => e.file.LastWriteTimeUtc).First(); var entry = entryAndFile.entry; var outputFn = filesLookup[entry.Filename].First().FullName; var input = File.ReadAllBytes(outputFn); var output = Decrypt(DecryptStatic(input, out var origIv), entry.Key, entry.IV); var outDirStr = Regex.Replace(opt.OutputDirectory, "(%\\w)", res => { switch (res.Groups[1].Value) { case "%h": return(entry.Hash); case "%n": return(entry.ResourceName); case "%s": var uri = new Uri(entry.From); return(uri.Host + "_" + uri.Port); } return(res.Groups[1].Value); }); var outDir = new DirectoryInfo(outDirStr); if (!outDir.Exists) { Console.WriteLine("Skipping: {0}/{1}", entry.ResourceName, entry.OriginalFilename); continue; } if (Path.GetExtension(entry.OriginalFilename) == ".rpf") { var reader = new Rpf2Reader(new MemoryStream(output)); reader.Open(); var entries = reader.ReadEntries(); var rpfDir = new DirectoryInfo(Path.Combine(outDir.FullName, entry.OriginalFilename)); if (!rpfDir.Exists) { Console.WriteLine("Skipping rpf: {0}", rpfDir.FullName); continue; } var rpfFiles = rpfDir.GetFiles("*", SearchOption.AllDirectories); var lookup = entries.ToLookup(e => Path.GetFullPath(Path.Combine(rpfDir.FullName, e.FullName))); var matches = entries.Join(rpfFiles, e => Path.GetFullPath(Path.Combine(rpfDir.FullName, e.FullName)), f => f.FullName, (e, f) => new { entry = e, file = f }).ToList(); var newFiles = rpfFiles.Where(f => !lookup[f.FullName].Any()).ToList(); var differ = rpfFiles.Length != matches.Count; foreach (var m in matches) { var oldHash = Hash(File.ReadAllBytes(m.file.FullName)); var newHash = Hash(m.entry.Data); if (oldHash != newHash) { Console.WriteLine("Rpf file change: {0}", m.entry.FullName); differ = true; } } foreach (var nf in newFiles) { Console.WriteLine("Rpf file new: {0}", nf.FullName); } if (differ) { Console.WriteLine("Overwriting {0}", entry.ResourceName); if (!opt.DryRun) { using (var ms = new MemoryStream()) { var rpfWr = new Rpf2Writer(ms); rpfWr.Write(rpfDir); File.WriteAllBytes(outputFn, EncryptStatic(Encrypt(ms.ToArray(), entry.Key, entry.IV), origIv)); } } } } else { var inFn = Path.Combine(outDir.FullName, entry.OriginalFilename); if (!File.Exists(inFn)) { Console.WriteLine("Skipping: {0}", inFn); continue; } var inData = File.ReadAllBytes(inFn); var inHash = Hash(inData); var oldHash = Hash(output); if (inHash != oldHash) { Console.WriteLine("Overwriting {0} with {1}", entry.Filename, inFn); if (!opt.DryRun) { File.WriteAllBytes(outputFn, EncryptStatic(Encrypt(inData, entry.Key, entry.IV), origIv)); } } } } Console.WriteLine("Finished" + (opt.DryRun ? " (Dry Run. Nothing was saved)" : "")); }