[HA] Implement Open and Close.

This commit is contained in:
2025-09-08 01:04:54 +01:00
parent 296a7a88f8
commit f74de8789f
5 changed files with 229 additions and 17 deletions

27
Aaru.Archives/Ha/Enums.cs Normal file
View File

@@ -0,0 +1,27 @@
namespace Aaru.Archives;
public sealed partial class Ha
{
#region Nested type: MdiSource
enum MdiSource : byte
{
MSDOS = 1,
UNIX = 2
}
#endregion
#region Nested type: Method
enum Method : byte
{
Copy = 0,
ASC = 1,
HSC = 2,
Directory = 14,
Special = 15
}
#endregion
}

View File

@@ -1,10 +1,15 @@
using System;
using System.Collections.Generic;
using System.IO;
using Aaru.CommonTypes.Interfaces;
namespace Aaru.Archives;
public sealed partial class Ha : IArchive
{
List<Entry> _entries;
Stream _stream;
#region IArchive Members
/// <inheritdoc />
@@ -14,7 +19,7 @@ public sealed partial class Ha : IArchive
/// <inheritdoc />
public string Author => Authors.NataliaPortillo;
/// <inheritdoc />
public bool Opened { get; }
public bool Opened { get; private set; }
/// <inheritdoc />
public ArchiveSupportedFeature ArchiveFeatures => ArchiveSupportedFeature.HasEntryTimestamp |
ArchiveSupportedFeature.SupportsCompression |
@@ -22,7 +27,7 @@ public sealed partial class Ha : IArchive
ArchiveSupportedFeature.SupportsSubdirectories |
ArchiveSupportedFeature.HasExplicitDirectories;
/// <inheritdoc />
public int NumberOfEntries { get; }
public int NumberOfEntries => Opened ? _entries.Count : -1;
#endregion
}

159
Aaru.Archives/Ha/Open.cs Normal file
View File

@@ -0,0 +1,159 @@
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 Ha
{
#region IArchive Members
/// <inheritdoc />
public ErrorNumber Open(IFilter filter, Encoding encoding)
{
encoding ??= Encoding.UTF8;
if(filter.DataForkLength < Marshal.SizeOf<HaHeader>()) return ErrorNumber.InvalidArgument;
_stream = filter.GetDataForkStream();
_stream.Position = 0;
byte[] hdr = new byte[Marshal.SizeOf<HaHeader>()];
_stream.ReadExactly(hdr, 0, hdr.Length);
HaHeader header = Marshal.ByteArrayToStructureLittleEndian<HaHeader>(hdr);
// Not a valid magic
if(header.magic != HA_MAGIC) return ErrorNumber.InvalidArgument;
_entries = [];
int fhLen = Marshal.SizeOf<FHeader>();
byte[] fhBuf = new byte[fhLen];
byte[] pathBuf = new byte[16384];
byte[] nameBuf = new byte[256];
int i; // Guard
while(_stream.Position + fhLen < _stream.Length)
{
_stream.ReadExactly(fhBuf, 0, fhLen);
FHeader fh = Marshal.ByteArrayToStructureLittleEndian<FHeader>(fhBuf);
for(i = 0; i < pathBuf.Length; i++)
{
int b = _stream.ReadByte();
if(b < 0) return ErrorNumber.InvalidArgument; // Makes no sense here
if(b == 0xFF)
{
pathBuf[i] = 0x2F;
continue;
}
if(b == 0)
{
pathBuf[i] = 0;
break;
}
pathBuf[i] = (byte)b;
}
if(i == pathBuf.Length) return ErrorNumber.InvalidArgument; // Got beyond the buffer length
for(i = 0; i < nameBuf.Length; i++)
{
int b = _stream.ReadByte();
if(b < 0) return ErrorNumber.InvalidArgument; // Makes no sense here
if(b == 0)
{
nameBuf[i] = 0;
break;
}
nameBuf[i] = (byte)b;
}
if(i == nameBuf.Length) return ErrorNumber.InvalidArgument; // Got beyond the buffer length
int mdiLen = _stream.ReadByte();
byte[] mdi = new byte[mdiLen];
_stream.ReadExactly(mdi, 0, mdiLen);
string path = StringHandlers.CToString(pathBuf, encoding);
string name = StringHandlers.CToString(nameBuf, encoding);
// Remove drive letter
if(path.Length > 3 && path[1] == ':') path = path[2..];
// ... or leading slash
if(path.Length > 0 && path[0] == '/') path = path[1..];
// replace slash with system separator
path = path.Replace('/', Path.DirectorySeparatorChar);
var entry = new Entry
{
Method = (Method)(fh.VerType & 0x0F),
Compressed = fh.clen,
Uncompressed = fh.olen,
LastWrite = DateHandlers.UnixToDateTime(fh.time),
DataOffset = _stream.Position,
Filename = Path.Combine(path, name)
};
switch((MdiSource)mdi[0])
{
case MdiSource.MSDOS:
entry.Attributes = (FileAttributes)mdi[1];
break;
case MdiSource.UNIX:
{
UnixMdi unixMdi = Marshal.ByteArrayToStructureLittleEndian<UnixMdi>(mdi);
entry.Mode = unixMdi.attr;
entry.Uid = unixMdi.user;
entry.Gid = unixMdi.group;
if(entry.Method == Method.Directory) entry.Attributes = FileAttributes.Directory;
break;
}
}
_entries.Add(entry);
_stream.Position += entry.Compressed;
}
Opened = true;
return ErrorNumber.NoError;
}
/// <inheritdoc />
public void Close()
{
// Already closed
if(!Opened) return;
_stream?.Close();
_stream = null;
Opened = false;
}
#endregion
}

View File

@@ -1,9 +1,29 @@
using System;
using System.IO;
using System.Runtime.InteropServices;
namespace Aaru.Archives;
public sealed partial class Ha
{
#region Nested type: Entry
struct Entry
{
public Method Method;
public uint Compressed;
public uint Uncompressed;
public DateTime LastWrite;
public FileAttributes Attributes;
public long DataOffset;
public string Filename;
public ushort Mode;
public ushort Uid;
public ushort Gid;
}
#endregion
#region Nested type: FHeader
[StructLayout(LayoutKind.Sequential, CharSet = CharSet.Ansi, Pack = 1)]
@@ -13,14 +33,12 @@ public sealed partial class Ha
public readonly byte VerType;
// Compressed length
public readonly ushort clen;
public readonly uint clen;
// Original length
public readonly ushort olen;
public readonly uint olen;
// Unclear if DOS packed date or what
public readonly ushort date;
public readonly ushort time;
public readonly int time;
// CRC32
public readonly uint crc;
@@ -43,5 +61,18 @@ public sealed partial class Ha
public readonly ushort count;
}
#endregion
#region Nested type: UnixMdi
[StructLayout(LayoutKind.Sequential, CharSet = CharSet.Ansi, Pack = 1)]
readonly struct UnixMdi
{
public readonly byte type;
public readonly ushort attr;
public readonly ushort user;
public readonly ushort group;
}
#endregion
}

View File

@@ -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 Ha
{
#region IArchive Members
/// <inheritdoc />
public ErrorNumber Open(IFilter filter, Encoding encoding) => throw new NotImplementedException();
/// <inheritdoc />
public void Close()
{
throw new NotImplementedException();
}
/// <inheritdoc />
public ErrorNumber GetFilename(int entryNumber, out string fileName) => throw new NotImplementedException();