diff --git a/Aaru.Archives/Arc/Arc.cs b/Aaru.Archives/Arc/Arc.cs
index 233e4754a..23ae96200 100644
--- a/Aaru.Archives/Arc/Arc.cs
+++ b/Aaru.Archives/Arc/Arc.cs
@@ -1,11 +1,16 @@
using System;
+using System.IO;
+using System.Text;
using Aaru.CommonTypes.Interfaces;
namespace Aaru.Archives;
public sealed partial class Arc : IArchive
{
- const string MODULE_NAME = "arc Archive Plugin";
+ const string MODULE_NAME = "arc Archive Plugin";
+ Encoding _encoding;
+ ArchiveSupportedFeature _features;
+ Stream _stream;
#region IArchive Members
@@ -16,12 +21,15 @@ public sealed partial class Arc : IArchive
///
public string Author => Authors.NataliaPortillo;
///
- public bool Opened { get; }
+ public bool Opened { get; private set; }
///
- public ArchiveSupportedFeature ArchiveFeatures =>
- ArchiveSupportedFeature.SupportsCompression | ArchiveSupportedFeature.SupportsFilenames;
+ public ArchiveSupportedFeature ArchiveFeatures => !Opened
+ ? ArchiveSupportedFeature.SupportsCompression |
+ ArchiveSupportedFeature.SupportsFilenames |
+ ArchiveSupportedFeature.HasEntryTimestamp
+ : _features;
///
- public int NumberOfEntries { get; }
+ public int NumberOfEntries => Opened ? _entries.Count : -1;
#endregion
}
\ No newline at end of file
diff --git a/Aaru.Archives/Arc/Info.cs b/Aaru.Archives/Arc/Info.cs
index 4eb2788c4..4c25cf07f 100644
--- a/Aaru.Archives/Arc/Info.cs
+++ b/Aaru.Archives/Arc/Info.cs
@@ -27,7 +27,7 @@ public sealed partial class Arc
// Not a valid marker
if(header.marker != MARKER) return false;
- switch(header.method)
+ switch((int)header.method)
{
// Not a valid compression method
case > 12 and < 20:
@@ -41,9 +41,8 @@ public sealed partial class Arc
for(int i = 0; i < 11; i++)
// Not a valid filename character
- {
- if(header.filename[i] > 0 && header.filename[i] < 0x20) return false;
- }
+ if(header.filename[i] > 0 && header.filename[i] < 0x20)
+ return false;
// If the filename is not 8.3, it's probably not an ARC file, but maybe it is in MVS/UNIX?
if(header.filename[11] != 0) return false;
@@ -85,7 +84,7 @@ public sealed partial class Arc
// Not a valid marker
if(header.marker != MARKER) return;
- switch(header.method)
+ switch((int)header.method)
{
// Not a valid compression method
case > 12 and < 20:
@@ -99,9 +98,8 @@ public sealed partial class Arc
for(int i = 0; i < 11; i++)
// Not a valid filename character
- {
- if(header.filename[i] > 0 && header.filename[i] < 0x20) return;
- }
+ if(header.filename[i] > 0 && header.filename[i] < 0x20)
+ return;
// If the filename is not 8.3, it's probably not an ARC file, but maybe it is in MVS/UNIX?
if(header.filename[11] != 0) return;
diff --git a/Aaru.Archives/Arc/Open.cs b/Aaru.Archives/Arc/Open.cs
new file mode 100644
index 000000000..3796177d5
--- /dev/null
+++ b/Aaru.Archives/Arc/Open.cs
@@ -0,0 +1,237 @@
+using System.Collections.Generic;
+using System.IO;
+using System.Text;
+using Aaru.CommonTypes.Enums;
+using Aaru.CommonTypes.Interfaces;
+using Aaru.Helpers;
+using Aaru.Logging;
+
+namespace Aaru.Archives;
+
+public sealed partial class Arc
+{
+ List _entries;
+
+#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);
+
+ Header header = Marshal.ByteArrayToStructureLittleEndian(hdr);
+
+ // Not a valid marker
+ if(header.marker != MARKER) return ErrorNumber.InvalidArgument;
+
+ switch((int)header.method)
+ {
+ // Not a valid compression method
+ case > 12 and < 20:
+ // Not a valid informational item
+ case > 22 and < 30:
+ // Not a valid control item
+ case > 31:
+ return ErrorNumber.InvalidArgument;
+ }
+
+ // Not a valid filename character
+ for(int i = 0; i < 11; i++)
+ if(header.filename[i] > 0 && header.filename[i] < 0x20)
+ return ErrorNumber.InvalidArgument;
+
+ // If the filename is not 8.3, it's probably not an ARC file, but maybe it is in MVS/UNIX?
+ if(header.filename[11] != 0) return ErrorNumber.InvalidArgument;
+
+ // Compressed size is larger than file size
+ // Hope for the best
+ if(header.compressed >= _stream.Length && (int)header.method != 31) return ErrorNumber.InvalidArgument;
+
+ Opened = true;
+ _encoding = encoding ?? Encoding.ASCII;
+ _stream.Position = 0;
+ _entries = [];
+
+ _features = ArchiveSupportedFeature.SupportsCompression |
+ ArchiveSupportedFeature.SupportsFilenames |
+ ArchiveSupportedFeature.HasEntryTimestamp;
+
+ string path = "";
+ string longname = null;
+ string comment = null;
+ string attributes = null;
+ var br = new BinaryReader(_stream);
+ byte peekedByte;
+
+ // Process headers
+ while(true)
+ {
+ peekedByte = br.ReadByte();
+ AaruLogging.Debug(MODULE_NAME, "[navy]peekedByte[/] = [teal]0x{0:X2}[/]", peekedByte);
+ peekedByte = br.ReadByte();
+ AaruLogging.Debug(MODULE_NAME, "[navy]peekedByte[/] = [teal]0x{0:X2}[/]", peekedByte);
+
+ if((Method)peekedByte == Method.EndOfArchive) break;
+
+ if((Method)peekedByte == Method.SubdirectoryEnd)
+ {
+ // Remove last directory from path
+ int lastSlash = path.LastIndexOf(Path.DirectorySeparatorChar);
+
+ path = lastSlash >= 0 ? path[..lastSlash] : "";
+
+ continue;
+ }
+
+ _stream.Position -= 2;
+
+ // Decode header
+ _stream.ReadExactly(hdr, 0, hdr.Length);
+ header = Marshal.ByteArrayToStructureLittleEndian(hdr);
+
+ AaruLogging.Debug(MODULE_NAME, "[navy]header.marker[/] = [teal]0x{0:X2}[/]", header.marker);
+ AaruLogging.Debug(MODULE_NAME, "[navy]header.method[/] = [teal]{0}[/]", header.method);
+
+ AaruLogging.Debug(MODULE_NAME,
+ "[navy]header.filename[/] = [green]\"{0}\"[/]",
+ StringHandlers.CToString(header.filename));
+
+ AaruLogging.Debug(MODULE_NAME, "[navy]header.compressed[/] = [teal]{0}[/]", header.compressed);
+ AaruLogging.Debug(MODULE_NAME, "[navy]header.date[/] = [teal]{0}[/]", header.date);
+ AaruLogging.Debug(MODULE_NAME, "[navy]header.time[/] = [teal]{0}[/]", header.time);
+ AaruLogging.Debug(MODULE_NAME, "[navy]header.crc[/] = [teal]0x{0:X4}[/]", header.crc);
+ AaruLogging.Debug(MODULE_NAME, "[navy]header.uncompressed[/] = [teal]{0}[/]", header.uncompressed);
+
+ if(header.method == Method.FileInformation)
+ {
+ int recordsSize = header.compressed;
+ int recordsRead = 0;
+
+ while(recordsRead < recordsSize)
+ {
+ ushort len = br.ReadUInt16();
+ var finfoType = (FileInformationType)br.ReadByte();
+ byte[] info = br.ReadBytes(len - 3);
+
+ recordsRead += len;
+
+ switch(finfoType)
+ {
+ case FileInformationType.Description:
+ comment = StringHandlers.CToString(info, _encoding);
+
+ break;
+ case FileInformationType.Attributes:
+ attributes = StringHandlers.CToString(info, _encoding);
+
+ break;
+ case FileInformationType.LongName:
+ longname = StringHandlers.CToString(info, _encoding);
+
+ break;
+ }
+ }
+ }
+
+ string filename;
+
+ Entry entry;
+
+ if(header.method == Method.Subdirectory)
+ {
+ filename = StringHandlers.CToString(header.filename, _encoding);
+ if(longname is not null) filename = longname;
+
+ path = Path.Combine(path, filename);
+
+ entry = new Entry
+ {
+ Method = header.method,
+ Filename = path,
+ Compressed = 0,
+ Uncompressed = 0,
+ LastWriteTime = DateHandlers.DosToDateTime(header.date, header.time),
+ DataOffset = 0,
+ Comment = comment,
+ Attributes = FileAttributes.Directory
+ };
+
+ _features |= ArchiveSupportedFeature.HasExplicitDirectories |
+ ArchiveSupportedFeature.SupportsSubdirectories;
+
+ if(attributes is not null)
+ {
+ if(attributes.Contains('A')) entry.Attributes |= FileAttributes.Archive;
+ if(attributes.Contains('R')) entry.Attributes |= FileAttributes.ReadOnly;
+ if(attributes.Contains('H')) entry.Attributes |= FileAttributes.Hidden;
+ if(attributes.Contains('S')) entry.Attributes |= FileAttributes.System;
+ }
+
+ longname = null;
+ comment = null;
+ attributes = null;
+
+ _entries.Add(entry);
+
+ continue;
+ }
+
+ filename = StringHandlers.CToString(header.filename, _encoding);
+ if(longname is not null) filename = longname;
+
+ if(header.method == Method.UnpackedOld) _stream.Position -= 4;
+
+ entry = new Entry
+ {
+ Method = header.method,
+ Filename = Path.Combine(path, filename),
+ Compressed = header.compressed,
+ Uncompressed = header.uncompressed,
+ LastWriteTime = DateHandlers.DosToDateTime(header.date, header.time),
+ DataOffset = _stream.Position,
+ Comment = comment,
+ Attributes = FileAttributes.Normal
+ };
+
+ if(attributes is not null)
+ {
+ if(attributes.Contains('A')) entry.Attributes |= FileAttributes.Archive;
+ if(attributes.Contains('R')) entry.Attributes |= FileAttributes.ReadOnly;
+ if(attributes.Contains('H')) entry.Attributes |= FileAttributes.Hidden;
+ if(attributes.Contains('S')) entry.Attributes |= FileAttributes.System;
+ }
+
+ longname = null;
+ comment = null;
+ attributes = null;
+
+ _entries.Add(entry);
+
+ _stream.Position += header.compressed;
+ }
+
+ return ErrorNumber.NoError;
+ }
+
+ ///
+ public void Close()
+ {
+ // Already closed
+ if(!Opened) return;
+
+ _stream?.Close();
+ _entries?.Clear();
+
+ _stream = null;
+ Opened = false;
+ }
+
+#endregion
+}
\ No newline at end of file
diff --git a/Aaru.Archives/Arc/Structs.cs b/Aaru.Archives/Arc/Structs.cs
index 5bb9bf2e7..f0d4f51b0 100644
--- a/Aaru.Archives/Arc/Structs.cs
+++ b/Aaru.Archives/Arc/Structs.cs
@@ -1,16 +1,34 @@
+using System;
+using System.IO;
using System.Runtime.InteropServices;
namespace Aaru.Archives;
public sealed partial class Arc
{
+#region Nested type: Entry
+
+ struct Entry
+ {
+ public Method Method;
+ public string Filename;
+ public int Compressed;
+ public int Uncompressed;
+ public DateTime LastWriteTime;
+ public long DataOffset;
+ public string Comment;
+ public FileAttributes Attributes;
+ }
+
+#endregion
+
#region Nested type: Header
[StructLayout(LayoutKind.Sequential, CharSet = CharSet.Ansi, Pack = 1)]
readonly struct Header
{
- public readonly byte marker;
- public readonly byte method;
+ public readonly byte marker;
+ public readonly Method method;
[MarshalAs(UnmanagedType.ByValArray, SizeConst = FNLEN)]
public readonly byte[] filename;
public readonly int compressed;
diff --git a/Aaru.Archives/Arc/Unimplemented.cs b/Aaru.Archives/Arc/Unimplemented.cs
index 72a57f934..12c1194f9 100644
--- a/Aaru.Archives/Arc/Unimplemented.cs
+++ b/Aaru.Archives/Arc/Unimplemented.cs
@@ -1,6 +1,5 @@
using System;
using System.Collections.Generic;
-using System.Text;
using Aaru.CommonTypes.Enums;
using Aaru.CommonTypes.Interfaces;
using Aaru.CommonTypes.Structs;
@@ -12,15 +11,6 @@ public sealed partial class Arc
{
#region IArchive Members
- ///
- public ErrorNumber Open(IFilter filter, Encoding encoding) => throw new NotImplementedException();
-
- ///
- public void Close()
- {
- throw new NotImplementedException();
- }
-
///
public ErrorNumber GetFilename(int entryNumber, out string fileName) => throw new NotImplementedException();