using System.Collections.Generic; using System.IO; using System.Text; using Aaru.CommonTypes.Enums; using Aaru.CommonTypes.Interfaces; using Aaru.Helpers; namespace Aaru.Archives; public sealed partial class Stfs { #region IArchive Members /// public ErrorNumber Open(IFilter filter, Encoding encoding) { if(filter.DataForkLength < Marshal.SizeOf()) return ErrorNumber.InvalidArgument; _stream = filter.GetDataForkStream(); _stream.Position = 0; byte[] hdr = new byte[Marshal.SizeOf()]; _stream.ReadExactly(hdr, 0, hdr.Length); RemotePackage header = Marshal.ByteArrayToStructureBigEndian(hdr); if(header.Magic is not (PackageMagic.Console or PackageMagic.Live or PackageMagic.Microsoft)) return ErrorNumber.InvalidArgument; // SVOD is managed as a media image if(header.Metadata.DescriptorType != 0) return ErrorNumber.InvalidArgument; VolumeDescriptor vd = Marshal.ByteArrayToStructureLittleEndian(header.Metadata.VolumeDescriptor); // Convert big endian int24 to int32 int fileTableBlockNumber = vd.FileTableBlockNumber[0] << 16 | vd.FileTableBlockNumber[1] << 8 | vd.FileTableBlockNumber[2]; fileTableBlockNumber = ComputeBlockNumber(fileTableBlockNumber, (int)header.Metadata.HeaderSize, vd.BlockSeparation, header.Magic == PackageMagic.Console); int fileTablePosition = BlockToPosition(fileTableBlockNumber, (int)header.Metadata.HeaderSize); byte[] buffer = new byte[4096 * vd.FileTableBlockCount]; _stream.Position = fileTablePosition; _stream.ReadExactly(buffer, 0, buffer.Length); List entries = []; int entrySize = Marshal.SizeOf(); int in_pos = 0; do { FileTableEntry entry = Marshal.ByteArrayToStructureBigEndian(buffer, in_pos, entrySize); if(entry.FilenameLength == 0) break; entries.Add(entry); in_pos += entrySize; } while(in_pos + entrySize < buffer.Length); _entries = new FileEntry[entries.Count]; for(int i = 0; i < entries.Count; i++) { // While entries[i].PathIndicator > 0 recursively inversely prepend entries[PathIndicator].Filename to entries[i].Filename int pathIndicator = entries[i].PathIndicator; string path = ""; while(pathIndicator > 0) { path = Path.Combine(StringHandlers.CToString(entries[pathIndicator].Filename, Encoding.ASCII), path); pathIndicator = entries[pathIndicator].PathIndicator; } _entries[i].Filename = path != "" ? Path.Combine(path, StringHandlers.CToString(entries[i].Filename, Encoding.ASCII)) : StringHandlers.CToString(entries[i].Filename, Encoding.ASCII); _entries[i].FileSize = entries[i].FileSize; _entries[i].StartingBlock = entries[i].StartingBlock[2] << 16 | entries[i].StartingBlock[1] << 8 | entries[i].StartingBlock[0]; _entries[i].StartingBlock = ComputeBlockNumber(_entries[i].StartingBlock, (int)header.Metadata.HeaderSize, vd.BlockSeparation, header.Magic == PackageMagic.Console); _entries[i].LastWrite = DateHandlers.DosToDateTime(entries[i].LastWriteDate, entries[i].LastWriteTime); _entries[i].LastAccess = DateHandlers.DosToDateTime(entries[i].LastAccessDate, entries[i].LastAccessTime); _entries[i].IsDirectory = (entries[i].FilenameLength & 0x80) > 0; } _headerSize = (int)header.Metadata.HeaderSize; _blockSeparation = vd.BlockSeparation; _isConsole = header.Magic == PackageMagic.Console; Opened = true; return ErrorNumber.NoError; } /// public void Close() { // Already closed if(!Opened) return; _stream?.Close(); _stream = null; Opened = false; } #endregion }