diff --git a/Aaru.Archives/Amg/Amg.cs b/Aaru.Archives/Amg/Amg.cs index 4c16a5f57..969369c2f 100644 --- a/Aaru.Archives/Amg/Amg.cs +++ b/Aaru.Archives/Amg/Amg.cs @@ -1,10 +1,15 @@ using System; +using System.Collections.Generic; +using System.IO; using Aaru.CommonTypes.Interfaces; namespace Aaru.Archives; public sealed partial class Amg : IArchive { + List _files; + Stream _stream; + #region IArchive Members /// @@ -14,14 +19,14 @@ public sealed partial class Amg : IArchive /// public string Author => Authors.NataliaPortillo; /// - public bool Opened { get; } + public bool Opened { get; private set; } /// public ArchiveSupportedFeature ArchiveFeatures => ArchiveSupportedFeature.HasEntryTimestamp | ArchiveSupportedFeature.SupportsCompression | ArchiveSupportedFeature.SupportsFilenames | ArchiveSupportedFeature.SupportsSubdirectories; /// - public int NumberOfEntries { get; } + public int NumberOfEntries => Opened ? _files.Count : -1; #endregion } \ No newline at end of file diff --git a/Aaru.Archives/Amg/Consts.cs b/Aaru.Archives/Amg/Consts.cs index e855892b4..97e720212 100644 --- a/Aaru.Archives/Amg/Consts.cs +++ b/Aaru.Archives/Amg/Consts.cs @@ -2,5 +2,6 @@ namespace Aaru.Archives; public sealed partial class Amg { - const ushort ARC_MAGIC = 0x36AD; + const ushort ARC_MAGIC = 0x36AD; + const ushort FILE_MAGIC = 0x1C94; } \ No newline at end of file diff --git a/Aaru.Archives/Amg/Open.cs b/Aaru.Archives/Amg/Open.cs new file mode 100644 index 000000000..b0207a982 --- /dev/null +++ b/Aaru.Archives/Amg/Open.cs @@ -0,0 +1,109 @@ +using System.IO; +using System.Text; +using Aaru.CommonTypes.Enums; +using Aaru.CommonTypes.Interfaces; +using Aaru.Helpers; + +namespace Aaru.Archives; + +public sealed partial class Amg +{ +#region IArchive Members + + /// + public ErrorNumber Open(IFilter filter, Encoding encoding) + { + encoding ??= Encoding.ASCII; + + 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); + + ArcHeader header = Marshal.ByteArrayToStructureLittleEndian(hdr); + + // Not a valid magic + if(header.magic != ARC_MAGIC) return ErrorNumber.InvalidArgument; + + + // Skip comment + _stream.Position += header.commentLength; + + int fileHeaderLen = Marshal.SizeOf(); + + _files = []; + + while(_stream.Position + fileHeaderLen < _stream.Length) + { + byte[] fileHdr = new byte[fileHeaderLen]; + + _stream.ReadExactly(fileHdr, 0, fileHdr.Length); + + FileHeader fh = Marshal.ByteArrayToStructureLittleEndian(fileHdr); + + // Not a valid file magic + if(fh.magic != FILE_MAGIC) break; + + var entry = new FileEntry + { + Attributes = (FileAttributes)fh.attr, + Compressed = (uint)(fh.compressed - fh.pathLength - fh.commentLength - fileHeaderLen), + Crc = fh.crc, + Flags = fh.flags, + LastWrite = DateHandlers.DosToDateTime(fh.date, fh.time), + Uncompressed = fh.uncompressed, + Filename = StringHandlers.CToString(fh.filename, encoding) + }; + + string extension = StringHandlers.CToString(fh.extension, encoding); + + if(!string.IsNullOrEmpty(extension)) entry.Filename += "." + extension; + + if(fh.pathLength > 0) + { + byte[] buffer = new byte[fh.pathLength]; + _stream.ReadExactly(buffer, 0, buffer.Length); + string path = StringHandlers.CToString(buffer, encoding); + path = path.Replace('\\', Path.DirectorySeparatorChar); + entry.Filename = Path.Combine(path, entry.Filename); + } + + if(fh.commentLength > 0) + { + byte[] buffer = new byte[fh.commentLength]; + _stream.ReadExactly(buffer, 0, buffer.Length); + entry.Comment = StringHandlers.CToString(buffer, encoding); + } + + entry.Offset = _stream.Position; + + _files.Add(entry); + + // Skip compressed data + _stream.Position += entry.Compressed; + } + + Opened = true; + + return ErrorNumber.NoError; + } + + /// + public void Close() + { + // Already closed + if(!Opened) return; + + _stream?.Close(); + _files?.Clear(); + + _stream = null; + Opened = false; + } + +#endregion +} \ No newline at end of file diff --git a/Aaru.Archives/Amg/Structs.cs b/Aaru.Archives/Amg/Structs.cs index 399c28434..8810e6e35 100644 --- a/Aaru.Archives/Amg/Structs.cs +++ b/Aaru.Archives/Amg/Structs.cs @@ -1,3 +1,5 @@ +using System; +using System.IO; using System.Runtime.InteropServices; namespace Aaru.Archives; @@ -17,5 +19,50 @@ public sealed partial class Amg public readonly ushort commentLength; } +#endregion + +#region Nested type: FileEntry + + struct FileEntry + { + public uint Uncompressed; + public uint Compressed; + public DateTime LastWrite; + public FileAttributes Attributes; + public byte Flags; + public uint Crc; + public string Filename; + public string Comment; + public long Offset; + + public override string ToString() => Filename; + } + +#endregion + +#region Nested type: FileHeader + + [StructLayout(LayoutKind.Sequential, CharSet = CharSet.Ansi, Pack = 1)] + readonly struct FileHeader + { + public readonly ushort magic; + public readonly uint compressed; + public readonly uint uncompressed; + public readonly ushort time; + public readonly ushort date; + public readonly byte attr; + public readonly byte flags; + + // Something changes in processing when 0x80 is set + public readonly byte unknown; + public readonly uint crc; + [MarshalAs(UnmanagedType.ByValArray, SizeConst = 8)] + public readonly byte[] filename; + [MarshalAs(UnmanagedType.ByValArray, SizeConst = 3)] + public readonly byte[] extension; + public readonly byte pathLength; + public readonly ushort commentLength; + } + #endregion } \ No newline at end of file diff --git a/Aaru.Archives/Amg/Unimplemented.cs b/Aaru.Archives/Amg/Unimplemented.cs index 31117ab61..a700344e2 100644 --- a/Aaru.Archives/Amg/Unimplemented.cs +++ b/Aaru.Archives/Amg/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 Amg { #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();