public static PfsHeader ReadFromStream(System.IO.Stream s) { var start = s.Position; var hdr = new PfsHeader { Version = s.ReadInt64LE(), Magic = s.ReadInt64LE(), Id = s.ReadInt64LE(), Fmode = s.ReadUInt8(), Clean = s.ReadUInt8(), ReadOnly = s.ReadUInt8(), Rsv = s.ReadUInt8(), Mode = (PfsMode)s.ReadUInt16LE(), Unk1 = s.ReadUInt16LE(), BlockSize = s.ReadUInt32LE(), NBackup = s.ReadUInt32LE(), NBlock = s.ReadInt64LE(), DinodeCount = s.ReadInt64LE(), Ndblock = s.ReadInt64LE(), DinodeBlockCount = s.ReadInt64LE(), }; s.Position += 8; // skip a 64-bit zero hdr.InodeBlockSig = DinodeS64.ReadFromStream(s); if (hdr.Version != 1 || hdr.Magic != 20130315) { throw new InvalidDataException($"Invalid PFS superblock version ({hdr.Version}) or magic ({hdr.Magic})"); } s.Position = start + 0x370; hdr.Seed = s.ReadBytes(16); return(hdr); }
public static PfsHeader ReadFromStream(System.IO.Stream s) { var start = s.Position; var hdr = new PfsHeader { Version = s.ReadInt64LE(), Magic = s.ReadInt64LE(), Id = s.ReadInt64LE(), Fmode = s.ReadUInt8(), Clean = s.ReadUInt8(), ReadOnly = s.ReadUInt8(), Rsv = s.ReadUInt8(), Mode = (PfsMode)s.ReadUInt16LE(), Unk1 = s.ReadUInt16LE(), BlockSize = s.ReadUInt32LE(), NBackup = s.ReadUInt32LE(), NBlock = s.ReadInt64LE(), DinodeCount = s.ReadInt64LE(), Ndblock = s.ReadInt64LE(), DinodeBlockCount = s.ReadInt64LE(), InodeBlockSig = DinodeS64.ReadFromStream(s) }; s.Position = start + 0x370; hdr.Seed = s.ReadBytes(16); return(hdr); }
public PfsReader(IMemoryReader r, byte[] ekpfs = null) { reader = r; var buf = new byte[0x400]; reader.Read(0, buf, 0, 0x400); using (var ms = new MemoryStream(buf)) { hdr = PfsHeader.ReadFromStream(ms); } int dinodeSize; Func <Stream, inode> dinodeReader; if (hdr.Mode.HasFlag(PfsMode.Signed)) { dinodes = new DinodeS32[hdr.DinodeCount]; dinodeReader = DinodeS32.ReadFromStream; dinodeSize = 0x2C8; } else { dinodes = new DinodeD32[hdr.DinodeCount]; dinodeReader = DinodeD32.ReadFromStream; dinodeSize = 0xA8; } if (hdr.Mode.HasFlag(PfsMode.Encrypted)) { if (ekpfs == null) { throw new ArgumentException("PFS image is encrypted but no EKPFS was provided"); } var(tweakKey, dataKey) = Crypto.PfsGenEncKey(ekpfs, hdr.Seed); reader = new XtsDecryptReader(reader, dataKey, tweakKey, 16, 0x1000); } var total = 0; var maxPerSector = hdr.BlockSize / dinodeSize; sectorBuf = new byte[hdr.BlockSize]; sectorStream = new MemoryStream(sectorBuf); for (var i = 0; i < hdr.DinodeBlockCount; i++) { var position = hdr.BlockSize + hdr.BlockSize * i; reader.Read(position, sectorBuf, 0, sectorBuf.Length); sectorStream.Position = 0; for (var j = 0; j < maxPerSector && total < hdr.DinodeCount; j++) { dinodes[total++] = dinodeReader(sectorStream); } } root = LoadDir(0, null, ""); uroot = root.Get("uroot") as Dir; if (uroot == null) { throw new Exception("Invalid PFS image (no uroot)"); } uroot.parent = null; uroot.name = ""; }
/// <summary> /// Builds and saves a PFS image. /// </summary> /// <param name="p"></param> public void BuildPfs(PfsProperties p) { // TODO: Combine the superroot-specific stuff with the rest of the data block writing. // I think this is as simple as adding superroot and flat_path_table to allNodes hdr = new PfsHeader { BlockSize = p.BlockSize }; inodes = new List <PfsDinode32>(); dirents = new List <List <PfsDirent> >(); Console.WriteLine("Setting up root structure..."); SetupRootStructure(); BuildFSTree(root, p.proj, p.projDir); allDirs = root.GetAllChildrenDirs(); allFiles = root.GetAllChildrenFiles(); allNodes = new List <FSNode>(allDirs); allNodes.AddRange(allFiles); Console.WriteLine("Creating directory inodes ({0})...", allDirs.Count); addDirInodes(); Console.WriteLine("Creating file inodes ({0})...", allFiles.Count); addFileInodes(); Console.WriteLine("Creating flat_path_table..."); fpt = new FlatPathTable(allNodes); Console.WriteLine("Calculating data block layout..."); allNodes.Insert(0, root); CalculateDataBlockLayout(); Console.WriteLine("Writing image file..."); hdr.Ndblock = allFiles.Sum((f) => CeilDiv(f.Size, hdr.BlockSize)); { var stream = p.output; Console.WriteLine("Writing header..."); hdr.WriteToStream(stream); Console.WriteLine("Writing inodes..."); WriteInodes(stream); Console.WriteLine("Writing superroot dirents"); WriteSuperrootDirents(stream); Console.WriteLine("Writing flat_path_table"); stream.Position = fpt_ino.db[0] * hdr.BlockSize; fpt.WriteToStream(stream); Console.WriteLine("Writing data blocks..."); for (var x = 0; x < allNodes.Count; x++) { var f = allNodes[x]; stream.Position = f.ino.db[0] * hdr.BlockSize; WriteFSNode(stream, f); } } }
void Setup() { // TODO: Combine the superroot-specific stuff with the rest of the data block writing. // I think this is as simple as adding superroot and flat_path_table to allNodes // This doesn't seem to really matter when verifying a PKG so use all zeroes for now var seed = new byte[16]; // Insert header digest to be calculated with the rest of the digests sig_order.Push(new BlockSigInfo(0, 0x380, 0x5A0)); hdr = new PfsHeader { BlockSize = properties.BlockSize, ReadOnly = 1, Mode = (properties.Sign ? PfsMode.Signed : 0) | (properties.Encrypt ? PfsMode.Encrypted : 0) | PfsMode.UnknownFlagAlwaysSet, UnknownIndex = 1, Seed = properties.Encrypt || properties.Sign ? seed : null }; inodes = new List <inode>(); Log("Setting up root structure..."); SetupRootStructure(); allDirs = root.GetAllChildrenDirs(); allFiles = root.GetAllChildrenFiles(); allNodes = new List <FSNode>(allDirs); allNodes.AddRange(allFiles); Log(string.Format("Creating directory inodes ({0})...", allDirs.Count)); addDirInodes(); Log(string.Format("Creating file inodes ({0})...", allFiles.Count)); addFileInodes(); Log("Creating flat_path_table..."); fpt = new FlatPathTable(allNodes); Log("Calculating data block layout..."); allNodes.Insert(0, root); CalculateDataBlockLayout(); }
/// <summary> /// This gets called by the constructor. /// </summary> void Setup() { // TODO: Combine the superroot-specific stuff with the rest of the data block writing. // I think this is as simple as adding superroot and flat_path_table to allNodes // This doesn't seem to really matter when verifying a PKG so use all zeroes for now var seed = new byte[16]; // Insert header digest to be calculated with the rest of the digests final_sigs.Push(new BlockSigInfo(0, 0x380, 0x5A0)); hdr = new PfsHeader { BlockSize = properties.BlockSize, ReadOnly = 1, Mode = (properties.Sign ? PfsMode.Signed : 0) | (properties.Encrypt ? PfsMode.Encrypted : 0) | PfsMode.UnknownFlagAlwaysSet, UnknownIndex = 1, Seed = properties.Encrypt || properties.Sign ? seed : null }; inodes = new List <inode>(); Log("Setting up filesystem structure..."); SetupRootStructure(); allDirs = root.GetAllChildrenDirs(); allFiles = root.GetAllChildrenFiles().Where(f => f.Parent?.name != "sce_sys" || !PKG.EntryNames.NameToId.ContainsKey(f.name)).ToList(); allNodes = new List <FSNode>(allDirs); allNodes.AddRange(allFiles); Log($"Creating inodes ({allDirs.Count} dirs and {allFiles.Count} files)..."); addDirInodes(); addFileInodes(); fpt = new FlatPathTable(allNodes); Log("Calculating data block layout..."); allNodes.Insert(0, root); CalculateDataBlockLayout(); }
/// <summary> /// This gets called by the constructor. /// </summary> void Setup() { // TODO: Combine the superroot-specific stuff with the rest of the data block writing. // I think this is as simple as adding superroot and flat_path_table to allNodes // Insert header digest to be calculated with the rest of the digests final_sigs.Push(new BlockSigInfo(0, 0x380, 0x5A0)); hdr = new PfsHeader { BlockSize = properties.BlockSize, ReadOnly = 1, Mode = (properties.Sign ? PfsMode.Signed : 0) | (properties.Encrypt ? PfsMode.Encrypted : 0) | PfsMode.UnknownFlagAlwaysSet, UnknownIndex = 1, Seed = properties.Encrypt || properties.Sign ? properties.Seed : null }; inodes = new List <inode>(); Log("Setting up filesystem structure..."); allDirs = properties.root.GetAllChildrenDirs(); allFiles = properties.root.GetAllChildrenFiles().Where(f => f.Parent?.name != "sce_sys" || !PKG.EntryNames.NameToId.ContainsKey(f.name)).ToList(); allNodes = new List <FSNode>(allDirs.OrderBy(d => d.FullPath()).ToList()); allNodes.AddRange(allFiles); SetupRootStructure(FlatPathTable.HasCollision(allNodes)); Log($"Creating inodes ({allDirs.Count} dirs and {allFiles.Count} files)..."); addDirInodes(); addFileInodes(); (fpt, colResolver) = FlatPathTable.Create(allNodes); Log("Calculating data block layout..."); allNodes.Insert(0, properties.root); CalculateDataBlockLayout(); }