public static STF_VOLUME_DESCRIPTOR Default() { var descriptor = new STF_VOLUME_DESCRIPTOR(); descriptor.DescriptorLength = 0x24; descriptor.Version = 0; descriptor.Flags = 0; descriptor.DirectoryAllocationBlocks = 1; descriptor.DirectoryFirstBlockNumber0 = descriptor.DirectoryFirstBlockNumber1 = descriptor.DirectoryFirstBlockNumber2 = 0; descriptor.RootHash = new byte[0x14]; descriptor.NumberOfTotalBlocks = 1; descriptor.NumberOfFreeBlocks = 0; return(descriptor); }
void StfsInit() { if (Position == 0) { Position = Stream.Position; } // Set ConsoleSignature if this is a cache partition, so metadata.ini can reflect it if (CacheHeader.IsValid && CachePartitionIndex != -1) { IsConsoleSigned = true; ConsoleSignature = CacheHeader.Signature; } // Read in XContent/PEC header if the volume descriptor isn't already set: if (StfsVolumeDescriptor.DescriptorLength != 0x24) { var fatHeader = Stream.ReadStruct <FAT_VOLUME_METADATA>(); Stream.Position = Position; if (fatHeader.Signature == FatxFileSystem.kMagicFatx || fatHeader.Signature == FatxFileSystem.kMagicFatxBE) { // STFC partition (uses FATX header for some reason, but has an invalid chainmap) // TODO: find where the descriptor is located (the HDD dump I'm testing with might be missing the first cluster) // For now we'll disable hash checks & use a default descriptor... StfsVolumeDescriptor = STF_VOLUME_DESCRIPTOR.Default(); // Should be fine to set directory block count to 100, it'll exit out once the hash chain ends StfsVolumeDescriptor.DirectoryAllocationBlocks = 100; // We don't have a volume descriptor, so we don't have a root hash: disable hash checks ;_; SkipHashChecks = true; } else { PecHeader = Stream.ReadStruct <PEC_HEADER>(); PecHeader.EndianSwap(); if (PecHeader.ConsoleSignature.IsStructureValid) { IsXContent = false; IsConsoleSigned = true; ConsoleSignature = PecHeader.ConsoleSignature; StfsVolumeDescriptor = PecHeader.StfsVolumeDescriptor; } else { IsXContent = true; Stream.Seek(0, SeekOrigin.Begin); Header = Stream.ReadStruct <XCONTENT_HEADER>(); Header.EndianSwap(); if (Header.SignatureType != XCONTENT_HEADER.kSignatureTypeConBE && Header.SignatureType != XCONTENT_HEADER.kSignatureTypeLiveBE && Header.SignatureType != XCONTENT_HEADER.kSignatureTypePirsBE) { throw new FileSystemParseException("File has invalid header magic"); } if (Header.SizeOfHeaders == 0) { throw new FileSystemParseException("Package doesn't contain STFS filesystem"); } if (Header.SignatureType == XCONTENT_HEADER.kSignatureTypeConBE) { IsConsoleSigned = true; ConsoleSignature = Header.ConsoleSignature; } Stream.Position = 0x344; Metadata = Stream.ReadStruct <XCONTENT_METADATA>(); Metadata.EndianSwap(); if (Header.SizeOfHeaders > 0x971A) { Stream.Position = 0x971A; InstallerMetadata = Stream.ReadStruct <XCONTENT_METADATA_INSTALLER>(); InstallerMetadata.EndianSwap(); } if (Metadata.VolumeType != 0) { throw new FileSystemParseException("Package contains unsupported SVOD filesystem"); } StfsVolumeDescriptor = Metadata.StfsVolumeDescriptor; } } if (StfsVolumeDescriptor.DescriptorLength != 0x24) { throw new FileSystemParseException("File has invalid descriptor length"); } } StfsInitValues(); // Read in our directory entries... int directoryBlock = StfsVolumeDescriptor.DirectoryFirstBlockNumber; var entries = new List <FileEntry>(); for (int i = 0; i < StfsVolumeDescriptor.DirectoryAllocationBlocks; i++) { if (directoryBlock == 0xFFFFFF) { Console.WriteLine("Premature directory exit 1!!!"); break; } Stream.Position = StfsDataBlockToOffset(directoryBlock); bool noMoreEntries = false; for (int ent = 0; ent < (0x1000 / 0x40); ent++) { var entry = new FileEntry(this); if (!entry.Read(Stream)) { noMoreEntries = true; break; } if (entry.CreationTime < CreationTime) { CreationTime = entry.CreationTime; } if (!entry.IsDirectory) { BytesInUse += entry.DirEntry.FileSize; } entries.Add(entry); } // Find next directory block... var blockHashEntry = StfsGetLevel0HashEntry(directoryBlock); directoryBlock = blockHashEntry.Level0NextBlock; if (noMoreEntries) { if (i + 1 < StfsVolumeDescriptor.DirectoryAllocationBlocks) { Console.WriteLine("Premature directory exit 2!!!"); } break; } } // Create metadata.ini/metadata_thumbnail/etc.. InitMetadataFiles(ref entries); // Connect entries up with their parents/children var rootEntries = new List <IFileEntry>(); for (int i = 0; i < entries.Count; i++) { var ent = entries[i]; if (ent.DirEntry.DirectoryIndex == -1) { rootEntries.Add(ent); } if (!ent.IsDirectory) { continue; } var children = new List <IFileEntry>(); foreach (var ent2 in entries) { if (ent2.DirEntry.DirectoryIndex == i) { children.Add(ent2); ent2.Parent = ent; } } children.Sort((x, y) => x.Name.CompareTo(y.Name)); ent.Children = children; } // Make sure to sort so that ReadDirectoryEntry doesn't make windows loop forever... rootEntries.Sort((x, y) => x.Name.CompareTo(y.Name)); Children = entries.ToArray(); RootFiles = rootEntries; }