mirror of
https://github.com/aaru-dps/Aaru.git
synced 2025-12-16 19:24:25 +00:00
[ARC] Implement open and close.
This commit is contained in:
@@ -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
|
||||
/// <inheritdoc />
|
||||
public string Author => Authors.NataliaPortillo;
|
||||
/// <inheritdoc />
|
||||
public bool Opened { get; }
|
||||
public bool Opened { get; private set; }
|
||||
/// <inheritdoc />
|
||||
public ArchiveSupportedFeature ArchiveFeatures =>
|
||||
ArchiveSupportedFeature.SupportsCompression | ArchiveSupportedFeature.SupportsFilenames;
|
||||
public ArchiveSupportedFeature ArchiveFeatures => !Opened
|
||||
? ArchiveSupportedFeature.SupportsCompression |
|
||||
ArchiveSupportedFeature.SupportsFilenames |
|
||||
ArchiveSupportedFeature.HasEntryTimestamp
|
||||
: _features;
|
||||
/// <inheritdoc />
|
||||
public int NumberOfEntries { get; }
|
||||
public int NumberOfEntries => Opened ? _entries.Count : -1;
|
||||
|
||||
#endregion
|
||||
}
|
||||
@@ -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;
|
||||
|
||||
237
Aaru.Archives/Arc/Open.cs
Normal file
237
Aaru.Archives/Arc/Open.cs
Normal file
@@ -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<Entry> _entries;
|
||||
|
||||
#region IArchive Members
|
||||
|
||||
/// <inheritdoc />
|
||||
public ErrorNumber Open(IFilter filter, Encoding encoding)
|
||||
{
|
||||
if(filter.DataForkLength < Marshal.SizeOf<Header>()) return ErrorNumber.InvalidArgument;
|
||||
|
||||
_stream = filter.GetDataForkStream();
|
||||
_stream.Position = 0;
|
||||
|
||||
byte[] hdr = new byte[Marshal.SizeOf<Header>()];
|
||||
|
||||
_stream.ReadExactly(hdr, 0, hdr.Length);
|
||||
|
||||
Header header = Marshal.ByteArrayToStructureLittleEndian<Header>(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<Header>(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;
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
public void Close()
|
||||
{
|
||||
// Already closed
|
||||
if(!Opened) return;
|
||||
|
||||
_stream?.Close();
|
||||
_entries?.Clear();
|
||||
|
||||
_stream = null;
|
||||
Opened = false;
|
||||
}
|
||||
|
||||
#endregion
|
||||
}
|
||||
@@ -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;
|
||||
|
||||
@@ -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
|
||||
|
||||
/// <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();
|
||||
|
||||
|
||||
Reference in New Issue
Block a user