public static void ProcessPartitionFs(Context ctx) { if (ctx.Options.OutFile == null) { ctx.Logger.LogMessage("Output file must be specified."); return; } PartitionFileSystemType type = ctx.Options.BuildHfs ? PartitionFileSystemType.Hashed : PartitionFileSystemType.Standard; var localFs = new LocalFileSystem(ctx.Options.InFile); var builder = new PartitionFileSystemBuilder(localFs); IStorage partitionFs = builder.Build(type); ctx.Logger.LogMessage($"Building Partition FS as {ctx.Options.OutFile}"); using (var outFile = new FileStream(ctx.Options.OutFile, FileMode.Create, FileAccess.ReadWrite)) { partitionFs.CopyToStream(outFile, partitionFs.GetSize(), ctx.Logger); } ctx.Logger.LogMessage($"Finished writing {ctx.Options.OutFile}"); }
private int GetMetaDataAlignment(PartitionFileSystemType type) { switch (type) { case PartitionFileSystemType.Standard: return(0x20); case PartitionFileSystemType.Hashed: return(0x200); default: throw new ArgumentOutOfRangeException(nameof(type), type, null); } }
private string GetMagicValue(PartitionFileSystemType type) { switch (type) { case PartitionFileSystemType.Standard: return("PFS0"); case PartitionFileSystemType.Hashed: return("HFS0"); default: throw new ArgumentOutOfRangeException(nameof(type), type, null); } }
public IStorage Build(PartitionFileSystemType type) { byte[] meta = BuildMetaData(type); var sources = new List <IStorage>(); sources.Add(new MemoryStorage(meta)); sources.AddRange(Entries.Select(x => new FileStorage(x.File))); return(new ConcatenationStorage(sources, true)); }
private int CalcStringTableSize(int startOffset, PartitionFileSystemType type) { int size = 0; foreach (Entry entry in Entries) { size += entry.NameLength + 1; } int endOffset = Util.AlignUp(startOffset + size, GetMetaDataAlignment(type)); return(endOffset - startOffset); }
public static int GetEntrySize(PartitionFileSystemType type) { switch (type) { case PartitionFileSystemType.Standard: return(0x18); case PartitionFileSystemType.Hashed: return(0x40); default: throw new ArgumentOutOfRangeException(nameof(type), type, null); } }
private byte[] BuildMetaData(PartitionFileSystemType type) { if (type == PartitionFileSystemType.Hashed) { CalculateHashes(); } int entryTableSize = Entries.Count * PartitionFileEntry.GetEntrySize(type); int stringTableSize = CalcStringTableSize(HeaderSize + entryTableSize, type); int metaDataSize = HeaderSize + entryTableSize + stringTableSize; var metaData = new byte[metaDataSize]; var writer = new BinaryWriter(new MemoryStream(metaData)); writer.WriteUTF8(GetMagicValue(type)); writer.Write(Entries.Count); writer.Write(stringTableSize); writer.Write(0); int stringOffset = 0; foreach (Entry entry in Entries) { writer.Write(entry.Offset); writer.Write(entry.Length); writer.Write(stringOffset); if (type == PartitionFileSystemType.Standard) { writer.Write(0); } else { writer.Write(entry.HashLength); writer.Write(entry.HashOffset); writer.Write(entry.Hash); } stringOffset += entry.NameLength + 1; } foreach (Entry entry in Entries) { writer.WriteUTF8Z(entry.Name); } return(metaData); }
public PartitionFileEntry(BinaryReader reader, PartitionFileSystemType type) { Offset = reader.ReadInt64(); Size = reader.ReadInt64(); StringTableOffset = reader.ReadUInt32(); if (type == PartitionFileSystemType.Hashed) { HashedRegionSize = reader.ReadInt32(); HashedRegionOffset = reader.ReadInt64(); Hash = reader.ReadBytes(Crypto.Sha256DigestSize); } else { reader.BaseStream.Position += 4; } }
public PartitionFileSystemHeader(BinaryReader reader) { Magic = reader.ReadAscii(4); NumFiles = reader.ReadInt32(); StringTableSize = reader.ReadInt32(); Reserved = reader.ReadInt32(); switch (Magic) { case "PFS0": Type = PartitionFileSystemType.Standard; break; case "HFS0": Type = PartitionFileSystemType.Hashed; break; default: ThrowHelper.ThrowResult(ResultFs.InvalidPartitionFileSystemMagic, $"Invalid Partition FS type \"{Magic}\""); break; } int entrySize = PartitionFileEntry.GetEntrySize(Type); int stringTableOffset = 16 + entrySize * NumFiles; HeaderSize = stringTableOffset + StringTableSize; Files = new PartitionFileEntry[NumFiles]; for (int i = 0; i < NumFiles; i++) { Files[i] = new PartitionFileEntry(reader, Type) { Index = i }; } for (int i = 0; i < NumFiles; i++) { reader.BaseStream.Position = stringTableOffset + Files[i].StringTableOffset; Files[i].Name = reader.ReadAsciiZ(); } }
public static IStorage ProcessPartitionFileSystem(PartitionFileSystem pfs, PartitionFileSystemType pfsType, bool compress) { PartitionFileSystemBuilder pfsBuilder = new PartitionFileSystemBuilder(); foreach (DirectoryEntryEx ticketEntry in pfs.EnumerateEntries("/", "*.tik")) { Result result = pfs.OpenFile(out IFile ticketFile, ticketEntry.FullPath.ToU8Span(), OpenMode.Read); if (result.IsSuccess()) { Ticket ticket = new Ticket(ticketFile.AsStream()); KeySet.ExternalKeySet.Add(new RightsId(ticket.RightsId), new AccessKey(ticket.GetTitleKey(KeySet))); } } foreach (DirectoryEntryEx fileEntry in pfs.EnumerateEntries()) { pfs.OpenFile(out IFile file, fileEntry.FullPath.ToU8Span(), OpenMode.Read).ThrowIfFailure(); if (compress && Path.GetExtension(fileEntry.Name).ToLower() == ".nca") { (IStorage processedNca, byte[] metaBuffer) = ProcessNca(file.AsStorage()); IFile zcaFile = new ZraCompressionStorageHack(processedNca, CompressionLevel, FrameSize, metaBuffer, TempPath).AsFile(OpenMode.Read); pfsBuilder.AddFile($"{Path.GetFileNameWithoutExtension(fileEntry.Name)}.zca", zcaFile); } else if (!compress && Path.GetExtension(fileEntry.Name).ToLower() == ".zca") { IStorage ncaStorage = new ZraDecompressionStream(file.AsStream()).AsStorage(); IFile ncaFile = new Nca(KeySet, ncaStorage).OpenEncryptedNca().AsFile(OpenMode.Read); pfsBuilder.AddFile($"{Path.GetFileNameWithoutExtension(fileEntry.Name)}.nca", ncaFile); } else { pfsBuilder.AddFile(fileEntry.Name, file); } } return(pfsBuilder.Build(pfsType)); }