mirror of
https://github.com/aaru-dps/Aaru.git
synced 2025-12-16 19:24:25 +00:00
123 lines
4.5 KiB
C#
123 lines
4.5 KiB
C#
|
|
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
|
||
|
|
|
||
|
|
/// <inheritdoc />
|
||
|
|
public ErrorNumber Open(IFilter filter, Encoding encoding)
|
||
|
|
{
|
||
|
|
if(filter.DataForkLength < Marshal.SizeOf<RemotePackage>()) return ErrorNumber.InvalidArgument;
|
||
|
|
|
||
|
|
_stream = filter.GetDataForkStream();
|
||
|
|
_stream.Position = 0;
|
||
|
|
|
||
|
|
byte[] hdr = new byte[Marshal.SizeOf<RemotePackage>()];
|
||
|
|
|
||
|
|
_stream.ReadExactly(hdr, 0, hdr.Length);
|
||
|
|
|
||
|
|
RemotePackage header = Marshal.ByteArrayToStructureBigEndian<RemotePackage>(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<VolumeDescriptor>(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<FileTableEntry> entries = [];
|
||
|
|
int entrySize = Marshal.SizeOf<FileTableEntry>();
|
||
|
|
int in_pos = 0;
|
||
|
|
|
||
|
|
do
|
||
|
|
{
|
||
|
|
FileTableEntry entry = Marshal.ByteArrayToStructureBigEndian<FileTableEntry>(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;
|
||
|
|
}
|
||
|
|
|
||
|
|
Opened = true;
|
||
|
|
|
||
|
|
return ErrorNumber.NoError;
|
||
|
|
}
|
||
|
|
|
||
|
|
|
||
|
|
/// <inheritdoc />
|
||
|
|
public void Close()
|
||
|
|
{
|
||
|
|
// Already closed
|
||
|
|
if(!Opened) return;
|
||
|
|
|
||
|
|
_stream?.Close();
|
||
|
|
|
||
|
|
_stream = null;
|
||
|
|
Opened = false;
|
||
|
|
}
|
||
|
|
|
||
|
|
#endregion
|
||
|
|
}
|