using System; using System.Collections.Generic; using System.IO; using System.Text; namespace PKHeX.Core { /// /// Utility logic for dumping lists for external analysis /// public static class SCBlockUtil { public static void ExportAllBlocksAsSingleFile(IReadOnlyList blocks, string path, SCBlockExportOption option = SCBlockExportOption.All) { var data = ExportAllBlocks(blocks, option); File.WriteAllBytes(path, data); } public static byte[] ExportAllBlocks(IReadOnlyList blocks, SCBlockExportOption option = SCBlockExportOption.None) { if (option == SCBlockExportOption.None) return SwishCrypto.GetDecryptedRawData(blocks); using var stream = new MemoryStream(); using var bw = new BinaryWriter(stream); for (var i = 0; i < blocks.Count; i++) ExportBlock(blocks[i], bw, i, option); return stream.ToArray(); } private static void ExportBlock(SCBlock block, BinaryWriter bw, int blockIndex, SCBlockExportOption option) { if (option.HasFlagFast(SCBlockExportOption.DataOnly) && block.Data.Length == 0) return; if (option.HasFlagFast(SCBlockExportOption.FakeHeader)) bw.Write($"BLOCK{blockIndex:0000} {block.Key:X8}"); if (option.HasFlagFast(SCBlockExportOption.Key)) bw.Write(block.Key); if (option.HasFlagFast(SCBlockExportOption.TypeInfo)) { bw.Write((byte) block.Type); bw.Write((byte) block.SubType); } bw.Write(block.Data); } public static string GetBlockFileNameWithoutExtension(SCBlock block) { var key = block.Key; var name = $"{key:X8}"; if (block.HasValue()) name += $" {block.GetValue()}"; return name; } public static string GetBlockSummary(SCBlock b) { var sb = new StringBuilder(64); sb.Append("Key: ").AppendFormat("{0:X8}", b.Key).AppendLine(); sb.Append("Type: ").Append(b.Type).AppendLine(); if (b.Data.Length != 0) sb.Append("Length: ").AppendFormat("{0:X8}", b.Data.Length).AppendLine(); if (b.SubType != 0) sb.Append("SubType: ").Append(b.SubType).AppendLine(); else if (b.HasValue()) sb.Append("Value: ").Append(b.GetValue()).AppendLine(); return sb.ToString(); } public static List ImportBlocksFromFolder(string path, SAV8SWSH sav) { var failed = new List(); var files = Directory.EnumerateFiles(path); foreach (var f in files) { var fn = Path.GetFileNameWithoutExtension(f); // Trim off Value summary if present var space = fn.IndexOf(' '); if (space != -1) fn = fn[..space]; var hex = Util.GetHexValue(fn); try { var block = sav.Blocks.GetBlock(hex); var len = block.Data.Length; var fi = new FileInfo(f); if (fi.Length != len) { failed.Add(fn); continue; } var data = File.ReadAllBytes(f); block.ChangeData(data); } #pragma warning disable CA1031 // Do not catch general exception types catch #pragma warning restore CA1031 // Do not catch general exception types { failed.Add(fn); } } return failed; } } [Flags] public enum SCBlockExportOption { None = 0, /// /// Will only export blocks with backing data. /// /// Excludes Bool flags from the dump. DataOnly = 1, /// /// Includes the Block Key ahead of the data. /// Key = 2, /// /// Includes the Block Info ahead of the data. /// TypeInfo = 4, /// /// Includes a fake header indicating which block it is in ASCII. /// FakeHeader = 8, /// /// Standard export options. /// All = DataOnly | Key | TypeInfo | FakeHeader, } internal static class ScBlockExportOptionExtensions { public static bool HasFlagFast(this SCBlockExportOption value, SCBlockExportOption flag) { return (value & flag) != 0; } } }