public static byte[] GetFirmwareData(Switch Device) { byte[] Data = null; long TitleId = 0x0100000000000809; string ContentPath = Device.System.ContentManager.GetInstalledContentPath(TitleId, StorageId.NandSystem, ContentType.Data); if (string.IsNullOrWhiteSpace(ContentPath)) { return(null); } string FirmwareTitlePath = Device.FileSystem.SwitchPathToSystemPath(ContentPath); FileStream FirmwareStream = File.Open(FirmwareTitlePath, FileMode.Open, FileAccess.Read); Nca FirmwareContent = new Nca(Device.System.KeySet, FirmwareStream, false); Stream RomFsStream = FirmwareContent.OpenSection(0, false, Device.System.FsIntegrityCheckLevel); if (RomFsStream == null) { return(null); } Romfs FirmwareRomFs = new Romfs(RomFsStream); using (MemoryStream MemoryStream = new MemoryStream()) { using (Stream FirmwareFile = FirmwareRomFs.OpenFile("/file")) { FirmwareFile.CopyTo(MemoryStream); } Data = MemoryStream.ToArray(); } FirmwareContent.Dispose(); FirmwareStream.Dispose(); return(Data); }
public static byte[] GetFirmwareData(Switch device) { byte[] data = null; long titleId = 0x0100000000000809; string contentPath = device.System.ContentManager.GetInstalledContentPath(titleId, StorageId.NandSystem, ContentType.Data); if (string.IsNullOrWhiteSpace(contentPath)) { return(null); } string firmwareTitlePath = device.FileSystem.SwitchPathToSystemPath(contentPath); FileStream firmwareStream = File.Open(firmwareTitlePath, FileMode.Open, FileAccess.Read); Nca firmwareContent = new Nca(device.System.KeySet, firmwareStream, false); Stream romFsStream = firmwareContent.OpenSection(0, false, device.System.FsIntegrityCheckLevel); if (romFsStream == null) { return(null); } Romfs firmwareRomFs = new Romfs(romFsStream); using (MemoryStream memoryStream = new MemoryStream()) { using (Stream firmwareFile = firmwareRomFs.OpenFile("/file")) { firmwareFile.CopyTo(memoryStream); } data = memoryStream.ToArray(); } firmwareContent.Dispose(); firmwareStream.Dispose(); return(data); }
public static void Main(string[] cmdArgs) { OptionSet optionSet = new OptionSet { { "h|help", "Show this message and exit", _ => ShowHelp = true }, { "v|dev=", "Load production keys as development keys\n(optional)", _ => IsDev = true }, { "p|prodkeys=", "Path to a file containing switch production keys.\n(optional)", s => ProdKeyPath = s }, { "k|titlekeys=", "Path to a file containing switch title keys.\n(optional)", s => TitleKeyPath = s }, { "l|level=", "zStd compression level used to compress the file.", s => CompressionLevel = byte.Parse(s) }, { "f|framesize=", "Size of a frame used to split a file.", s => FrameSize = uint.Parse(s) }, { "t|temp=", "The directory to use for storing temp files.\n(Defaults to OS temp)", s => TempPath = s }, { "o|output=", "The directory to output the compressed file.\n(Defaults to the same dir as input file)", s => OutDirectoryPath = s } }; List <string> args = optionSet.Parse(cmdArgs); if (ShowHelp) { Console.WriteLine("ZcaTool - Copyright (c) 2020 Xpl0itR"); Console.WriteLine("Usage: ZcaTool(.exe) [options] <path>"); Console.WriteLine("Options:"); optionSet.WriteOptionDescriptions(Console.Out); return; } if (args.Count < 1 || !File.Exists(args[0])) { throw new Exception("Input file does not exist!"); } if (CompressionLevel < 1 || CompressionLevel > 22) { throw new Exception("You must enter a valid compression level!"); } KeySet = LoadKeySet(); OutDirectoryPath ??= Path.GetDirectoryName(args[0]); using (IStorage inStorage = new LocalStorage(args[0], FileAccess.Read)) { string fileName = Path.GetFileNameWithoutExtension(args[0]); inStorage.GetSize(out long inSize); IStorage outStorage = null; Stopwatch stopwatch = Stopwatch.StartNew(); switch (Path.GetExtension(args[0]).ToLower()) { case ".nca": Console.WriteLine($"Compressing {Path.GetFileName(args[0])} [{PrettyFileSize(inSize)}] with ZStandard compression level: {CompressionLevel} and frame size: {FrameSize}"); fileName += ".zca"; break; case ".zca": Console.WriteLine($"Decompressing {Path.GetFileName(args[0])} [{PrettyFileSize(inSize)}]"); outStorage = new Nca(KeySet, new ZraDecompressionStream(inStorage.AsStream()).AsStorage()).OpenEncryptedNca(); fileName += ".nca"; break; case ".nsp": Console.WriteLine($"Compressing {Path.GetFileName(args[0])} [{PrettyFileSize(inSize)}] with ZStandard compression level: {CompressionLevel} and frame size: {FrameSize}"); outStorage = ProcessPartitionFileSystem(new PartitionFileSystem(inStorage), PartitionFileSystemType.Standard, true); fileName += ".zsp"; break; case ".zsp": Console.WriteLine($"Decompressing {Path.GetFileName(args[0])} [{PrettyFileSize(inSize)}]"); outStorage = ProcessPartitionFileSystem(new PartitionFileSystem(inStorage), PartitionFileSystemType.Standard, false); fileName += ".nsp"; break; case ".xci": Console.WriteLine($"Compressing {Path.GetFileName(args[0])} [{PrettyFileSize(inSize)}] with ZStandard compression level: {CompressionLevel} and frame size: {FrameSize}"); outStorage = ProcessXci(inStorage, true); fileName += ".zci"; break; case ".zci": Console.WriteLine($"Decompressing {Path.GetFileName(args[0])} [{PrettyFileSize(inSize)}]"); outStorage = ProcessXci(inStorage, false); fileName += ".xci"; break; default: throw new Exception("Input file was not of a valid format!"); } long outSize; string filePath = Path.Join(OutDirectoryPath, fileName); using (FileStream outStream = File.OpenWrite(filePath)) { if (Path.GetExtension(args[0]).ToLower() == ".nca") { (IStorage processedNca, byte[] metaBuffer) = ProcessNca(inStorage); processedNca.GetSize(out long ncaLength); using (ZraCompressionStream compressionStream = new ZraCompressionStream(outStream, (ulong)ncaLength, CompressionLevel, FrameSize, metaBuffer, true)) { processedNca.CopyToStream(compressionStream, (int)FrameSize); } } else { outStorage.CopyToStream(outStream); outStorage?.Dispose(); } outSize = outStream.Length; } stopwatch.Stop(); Console.WriteLine($"Out file: {filePath} [{PrettyFileSize(outSize)}]"); Console.WriteLine($"Time taken: {decimal.Round((decimal)stopwatch.ElapsedMilliseconds / 1000, 2)}s ({stopwatch.ElapsedMilliseconds}ms)"); Console.WriteLine($"Size Reduction: {decimal.Truncate(100 - (decimal)outSize / inSize * 100)}%"); } Console.WriteLine("Cleaning temp files..."); ZraCompressionStorageHack.CleanTempFiles(); Console.WriteLine("Done!"); }
public void EnsureInitialized(ContentManager ContentManager) { if (FontData == null) { Device.Memory.FillWithZeros(PhysicalAddress, Horizon.FontSize); uint FontOffset = 0; FontInfo CreateFont(string Name) { if (ContentManager.TryGetFontTitle(Name, out long FontTitle)) { string ContentPath = ContentManager.GetInstalledContentPath(FontTitle, StorageId.NandSystem, ContentType.Data); string FontPath = Device.FileSystem.SwitchPathToSystemPath(ContentPath); if (!string.IsNullOrWhiteSpace(FontPath)) { int FileIndex = 0; //Use second file in Chinese Font title for standard if (Name == "FontChineseSimplified") { FileIndex = 1; } FileStream NcaFileStream = new FileStream(FontPath, FileMode.Open, FileAccess.Read); Nca Nca = new Nca(Device.System.KeySet, NcaFileStream, false); NcaSection RomfsSection = Nca.Sections.FirstOrDefault(x => x?.Type == SectionType.Romfs); Romfs Romfs = new Romfs(Nca.OpenSection(RomfsSection.SectionNum, false, Device.System.FsIntegrityCheckLevel)); Stream FontFile = Romfs.OpenFile(Romfs.Files[FileIndex]); byte[] Data = DecryptFont(FontFile); FontInfo Info = new FontInfo((int)FontOffset, Data.Length); WriteMagicAndSize(PhysicalAddress + FontOffset, Data.Length); FontOffset += 8; uint Start = FontOffset; for (; FontOffset - Start < Data.Length; FontOffset++) { Device.Memory.WriteByte(PhysicalAddress + FontOffset, Data[FontOffset - Start]); } NcaFileStream.Dispose(); Nca.Dispose(); return(Info); } } string FontFilePath = Path.Combine(FontsPath, Name + ".ttf"); if (File.Exists(FontFilePath)) { byte[] Data = File.ReadAllBytes(FontFilePath); FontInfo Info = new FontInfo((int)FontOffset, Data.Length); WriteMagicAndSize(PhysicalAddress + FontOffset, Data.Length); FontOffset += 8; uint Start = FontOffset; for (; FontOffset - Start < Data.Length; FontOffset++) { Device.Memory.WriteByte(PhysicalAddress + FontOffset, Data[FontOffset - Start]); } return(Info); } else { throw new InvalidSystemResourceException($"Font \"{Name}.ttf\" not found. Please provide it in \"{FontsPath}\"."); } } FontData = new Dictionary <SharedFontType, FontInfo>() { { SharedFontType.JapanUsEurope, CreateFont("FontStandard") }, { SharedFontType.SimplifiedChinese, CreateFont("FontChineseSimplified") }, { SharedFontType.SimplifiedChineseEx, CreateFont("FontExtendedChineseSimplified") }, { SharedFontType.TraditionalChinese, CreateFont("FontChineseTraditional") }, { SharedFontType.Korean, CreateFont("FontKorean") }, { SharedFontType.NintendoEx, CreateFont("FontNintendoExtended") } }; if (FontOffset > Horizon.FontSize) { throw new InvalidSystemResourceException( $"The sum of all fonts size exceed the shared memory size. " + $"Please make sure that the fonts don't exceed {Horizon.FontSize} bytes in total. " + $"(actual size: {FontOffset} bytes)."); } } }
public void LoadEntries() { ContentDictionary = new SortedDictionary <(ulong, ContentType), string>(); foreach (StorageId StorageId in Enum.GetValues(typeof(StorageId))) { string ContentDirectory = null; string ContentPathString = null; string RegisteredDirectory = null; try { ContentPathString = LocationHelper.GetContentRoot(StorageId); ContentDirectory = LocationHelper.GetRealPath(Device.FileSystem, ContentPathString); RegisteredDirectory = Path.Combine(ContentDirectory, "registered"); } catch (NotSupportedException NEx) { continue; } Directory.CreateDirectory(RegisteredDirectory); LinkedList <LocationEntry> LocationList = new LinkedList <LocationEntry>(); void AddEntry(LocationEntry Entry) { LocationList.AddLast(Entry); } foreach (string DirectoryPath in Directory.EnumerateDirectories(RegisteredDirectory)) { if (Directory.GetFiles(DirectoryPath).Length > 0) { string NcaName = new DirectoryInfo(DirectoryPath).Name.Replace(".nca", string.Empty); using (FileStream NcaFile = new FileStream(Directory.GetFiles(DirectoryPath)[0], FileMode.Open, FileAccess.Read)) { Nca Nca = new Nca(Device.System.KeySet, NcaFile, false); string SwitchPath = Path.Combine(ContentPathString + ":", NcaFile.Name.Replace(ContentDirectory, string.Empty).TrimStart('\\')); // Change path format to switch's SwitchPath = SwitchPath.Replace('\\', '/'); LocationEntry Entry = new LocationEntry(SwitchPath, 0, (long)Nca.Header.TitleId, Nca.Header.ContentType); AddEntry(Entry); ContentDictionary.Add((Nca.Header.TitleId, Nca.Header.ContentType), NcaName); NcaFile.Close(); Nca.Dispose(); NcaFile.Dispose(); } } } foreach (string FilePath in Directory.EnumerateFiles(ContentDirectory)) { if (Path.GetExtension(FilePath) == ".nca") { string NcaName = Path.GetFileNameWithoutExtension(FilePath); using (FileStream NcaFile = new FileStream(FilePath, FileMode.Open, FileAccess.Read)) { Nca Nca = new Nca(Device.System.KeySet, NcaFile, false); string SwitchPath = Path.Combine(ContentPathString + ":", FilePath.Replace(ContentDirectory, string.Empty).TrimStart('\\')); // Change path format to switch's SwitchPath = SwitchPath.Replace('\\', '/'); LocationEntry Entry = new LocationEntry(SwitchPath, 0, (long)Nca.Header.TitleId, Nca.Header.ContentType); AddEntry(Entry); ContentDictionary.Add((Nca.Header.TitleId, Nca.Header.ContentType), NcaName); NcaFile.Close(); Nca.Dispose(); NcaFile.Dispose(); } } } if (LocationEntries.ContainsKey(StorageId) && LocationEntries[StorageId]?.Count == 0) { LocationEntries.Remove(StorageId); } if (!LocationEntries.ContainsKey(StorageId)) { LocationEntries.Add(StorageId, LocationList); } } }
public void LoadEntries() { _contentDictionary = new SortedDictionary <(ulong, ContentType), string>(); foreach (StorageId storageId in Enum.GetValues(typeof(StorageId))) { string contentDirectory = null; string contentPathString = null; string registeredDirectory = null; try { contentPathString = LocationHelper.GetContentRoot(storageId); contentDirectory = LocationHelper.GetRealPath(_device.FileSystem, contentPathString); registeredDirectory = Path.Combine(contentDirectory, "registered"); } catch (NotSupportedException) { continue; } Directory.CreateDirectory(registeredDirectory); LinkedList <LocationEntry> locationList = new LinkedList <LocationEntry>(); void AddEntry(LocationEntry entry) { locationList.AddLast(entry); } foreach (string directoryPath in Directory.EnumerateDirectories(registeredDirectory)) { if (Directory.GetFiles(directoryPath).Length > 0) { string ncaName = new DirectoryInfo(directoryPath).Name.Replace(".nca", string.Empty); using (FileStream ncaFile = new FileStream(Directory.GetFiles(directoryPath)[0], FileMode.Open, FileAccess.Read)) { Nca nca = new Nca(_device.System.KeySet, ncaFile, false); string switchPath = Path.Combine(contentPathString + ":", ncaFile.Name.Replace(contentDirectory, string.Empty).TrimStart('\\')); // Change path format to switch's switchPath = switchPath.Replace('\\', '/'); LocationEntry entry = new LocationEntry(switchPath, 0, (long)nca.Header.TitleId, nca.Header.ContentType); AddEntry(entry); _contentDictionary.Add((nca.Header.TitleId, nca.Header.ContentType), ncaName); ncaFile.Close(); nca.Dispose(); ncaFile.Dispose(); } } } foreach (string filePath in Directory.EnumerateFiles(contentDirectory)) { if (Path.GetExtension(filePath) == ".nca") { string ncaName = Path.GetFileNameWithoutExtension(filePath); using (FileStream ncaFile = new FileStream(filePath, FileMode.Open, FileAccess.Read)) { Nca nca = new Nca(_device.System.KeySet, ncaFile, false); string switchPath = Path.Combine(contentPathString + ":", filePath.Replace(contentDirectory, string.Empty).TrimStart('\\')); // Change path format to switch's switchPath = switchPath.Replace('\\', '/'); LocationEntry entry = new LocationEntry(switchPath, 0, (long)nca.Header.TitleId, nca.Header.ContentType); AddEntry(entry); _contentDictionary.Add((nca.Header.TitleId, nca.Header.ContentType), ncaName); ncaFile.Close(); nca.Dispose(); ncaFile.Dispose(); } } } if (_locationEntries.ContainsKey(storageId) && _locationEntries[storageId]?.Count == 0) { _locationEntries.Remove(storageId); } if (!_locationEntries.ContainsKey(storageId)) { _locationEntries.Add(storageId, locationList); } } }