diff --git a/Aaru.Archives/Stfs/Files.cs b/Aaru.Archives/Stfs/Files.cs
index 354bf1a56..25dde84d7 100644
--- a/Aaru.Archives/Stfs/Files.cs
+++ b/Aaru.Archives/Stfs/Files.cs
@@ -1,6 +1,8 @@
using System;
using Aaru.CommonTypes.Enums;
+using Aaru.CommonTypes.Interfaces;
using Aaru.CommonTypes.Structs;
+using Aaru.Filters;
using FileAttributes = System.IO.FileAttributes;
namespace Aaru.Archives;
@@ -125,5 +127,31 @@ public sealed partial class Stfs
return ErrorNumber.NoError;
}
+ ///
+ public ErrorNumber GetEntry(int entryNumber, out IFilter filter)
+ {
+ filter = null;
+
+ if(!Opened) return ErrorNumber.NotOpened;
+
+ if(entryNumber < 0 || entryNumber >= _entries.Length) return ErrorNumber.OutOfRange;
+
+ var stream = new StfsStream(_stream,
+ _entries[entryNumber].StartingBlock,
+ _entries[entryNumber].FileSize,
+ _headerSize,
+ _blockSeparation,
+ _isConsole);
+
+ filter = new ZZZNoFilter();
+ ErrorNumber errno = filter.Open(stream);
+
+ if(errno == ErrorNumber.NoError) return ErrorNumber.NoError;
+
+ stream.Close();
+
+ return errno;
+ }
+
#endregion
}
\ No newline at end of file
diff --git a/Aaru.Archives/Stfs/Open.cs b/Aaru.Archives/Stfs/Open.cs
index 1baa7412b..679850ed0 100644
--- a/Aaru.Archives/Stfs/Open.cs
+++ b/Aaru.Archives/Stfs/Open.cs
@@ -101,6 +101,10 @@ public sealed partial class Stfs
_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;
diff --git a/Aaru.Archives/Stfs/Stfs.cs b/Aaru.Archives/Stfs/Stfs.cs
index cf1409aa1..7d40e2aa4 100644
--- a/Aaru.Archives/Stfs/Stfs.cs
+++ b/Aaru.Archives/Stfs/Stfs.cs
@@ -6,7 +6,10 @@ namespace Aaru.Archives;
public sealed partial class Stfs : IArchive
{
+ byte _blockSeparation;
FileEntry[] _entries;
+ int _headerSize;
+ bool _isConsole;
Stream _stream;
#region IArchive Members
diff --git a/Aaru.Archives/Stfs/StfsStream.cs b/Aaru.Archives/Stfs/StfsStream.cs
new file mode 100644
index 000000000..fcba0c256
--- /dev/null
+++ b/Aaru.Archives/Stfs/StfsStream.cs
@@ -0,0 +1,181 @@
+using System;
+using System.IO;
+
+namespace Aaru.Archives;
+
+public sealed partial class Stfs
+{
+#region Nested type: StfsStream
+
+ private class StfsStream : Stream
+ {
+ readonly Stream _baseStream;
+ readonly int _blockSeparation;
+ readonly int _headerSize;
+ readonly bool _isConsole;
+ readonly long _length;
+ readonly int _startingBlock;
+ long _position;
+
+ internal StfsStream(Stream baseStream, int length, int startingBlock, int headerSize, byte blockSeparation,
+ bool isConsole)
+ {
+ _baseStream = baseStream;
+ _length = length;
+ _position = 0;
+ _startingBlock = startingBlock;
+ _headerSize = headerSize;
+ _blockSeparation = blockSeparation;
+ _isConsole = isConsole;
+ }
+
+ ///
+ public override bool CanRead => true;
+ ///
+ public override bool CanSeek => true;
+ ///
+ public override bool CanWrite => false;
+ ///
+ public override long Length => _length;
+
+ ///
+ public override long Position
+ {
+ get => _position;
+ set => Seek(value, SeekOrigin.Begin);
+ }
+
+ ///
+ public override void Flush()
+ {
+ // No-op
+ }
+
+ ///
+ public override int Read(byte[] buffer, int offset, int count)
+ {
+ int totalRead = 0;
+
+ // Validate parameters
+ ArgumentNullException.ThrowIfNull(buffer);
+
+ if(offset < 0 || count < 0) throw new ArgumentOutOfRangeException("Offset and count must be non-negative");
+
+ if(buffer.Length - offset < count)
+ throw new ArgumentException("Buffer too small for the requested offset and count");
+
+ if(_position >= _length) return 0; // EOF
+
+ // Calculate block for current position
+ int currentBlock = ComputeBlockNumber((int)(_position / 0x1000) + _startingBlock,
+ _headerSize,
+ _blockSeparation,
+ _isConsole);
+
+ // Calculate position within block
+ int blockOffset = (int)(_position % 0x1000);
+
+ // Calculate absolute position in the stream
+ long absolutePosition = BlockToPosition(currentBlock, _headerSize) + blockOffset;
+
+ // Seek to the absolute position
+ _baseStream.Position = absolutePosition;
+
+ // Calculate bytes left to read to fill a block
+ int leftInBlock = 0x1000 - blockOffset;
+
+ // Read bytes left in the block
+ _baseStream.ReadExactly(buffer, offset, leftInBlock);
+
+ // Update position and counters
+ _position += leftInBlock;
+ offset += leftInBlock;
+ count -= leftInBlock;
+ totalRead += leftInBlock;
+
+ // Read full blocks
+ while(count >= 0x1000 && _position < _length)
+ {
+ // Calculate again block number for current position
+ currentBlock = ComputeBlockNumber((int)(_position / 0x1000) + _startingBlock,
+ _headerSize,
+ _blockSeparation,
+ _isConsole);
+
+ // Calculate absolute position in the stream
+ absolutePosition = BlockToPosition(currentBlock, _headerSize);
+
+ // Seek to the absolute position
+ _baseStream.Position = absolutePosition;
+
+ // Read the full block
+ _baseStream.ReadExactly(buffer, offset, 0x1000);
+
+ _position += 0x1000;
+ offset += 0x1000;
+ count -= 0x1000;
+ totalRead += 0x1000;
+
+ if(_position >= _length) break; // EOF
+ }
+
+ // Read remaining bytes
+ if(count <= 0 || _position >= _length) return totalRead;
+
+ // Calculate again block number for current position
+ currentBlock = ComputeBlockNumber((int)(_position / 0x1000) + _startingBlock,
+ _headerSize,
+ _blockSeparation,
+ _isConsole);
+
+ // Calculate absolute position in the stream
+ absolutePosition = BlockToPosition(currentBlock, _headerSize);
+
+ // Calculate bytes left to read to fill a block
+ leftInBlock = (int)(_position % 0x1000);
+
+ // Seek to the absolute position
+ _baseStream.Position = absolutePosition + leftInBlock;
+
+ // Read remaining bytes
+ int toRead = (int)Math.Min(count, _length - _position);
+ _baseStream.ReadExactly(buffer, offset, toRead);
+ _position += toRead;
+ totalRead += toRead;
+
+ return totalRead;
+ }
+
+ ///
+ public override long Seek(long offset, SeekOrigin origin)
+ {
+ long newPos = origin switch
+ {
+ SeekOrigin.Begin => offset,
+ SeekOrigin.Current => _position + offset,
+ SeekOrigin.End => _length + offset,
+ _ => throw new ArgumentException("Invalid SeekOrigin", nameof(origin))
+ };
+
+ if(newPos < 0 || newPos > _length) throw new IOException("Attempt to seek outside the strean");
+
+ _position = newPos;
+
+ return _position;
+ }
+
+ ///
+ public override void SetLength(long value)
+ {
+ throw new NotSupportedException("Stream is read-only");
+ }
+
+ ///
+ public override void Write(byte[] buffer, int offset, int count)
+ {
+ throw new NotSupportedException("Stream is read-only");
+ }
+ }
+
+#endregion
+}
\ No newline at end of file
diff --git a/Aaru.Archives/Stfs/Unimplemented.cs b/Aaru.Archives/Stfs/Unimplemented.cs
deleted file mode 100644
index b31d9d200..000000000
--- a/Aaru.Archives/Stfs/Unimplemented.cs
+++ /dev/null
@@ -1,15 +0,0 @@
-using System;
-using Aaru.CommonTypes.Enums;
-using Aaru.CommonTypes.Interfaces;
-
-namespace Aaru.Archives;
-
-public sealed partial class Stfs
-{
-#region IArchive Members
-
- ///
- public ErrorNumber GetEntry(int entryNumber, out IFilter filter) => throw new NotImplementedException();
-
-#endregion
-}
\ No newline at end of file