diff --git a/Aaru.Archives/Arj/Arj.cs b/Aaru.Archives/Arj/Arj.cs
new file mode 100644
index 000000000..596b6c8a1
--- /dev/null
+++ b/Aaru.Archives/Arj/Arj.cs
@@ -0,0 +1,35 @@
+using System;
+using System.IO;
+using System.Text;
+using Aaru.CommonTypes.Interfaces;
+
+namespace Aaru.Archives;
+
+public sealed partial class Arj : IArchive
+{
+ const string MODULE_NAME = "ARJ Archive Plugin";
+ Encoding _encoding;
+ ArchiveSupportedFeature _features;
+ Stream _stream;
+
+#region IArchive Members
+
+ ///
+ public string Name => Localization.ARJ_Name;
+ ///
+ public Guid Id => new("7B2F4E8C-3A1D-4F6E-9C5B-8D0E2A7F1B3C");
+ ///
+ public string Author => Authors.NataliaPortillo;
+ ///
+ public bool Opened { get; private set; }
+ ///
+ public ArchiveSupportedFeature ArchiveFeatures => !Opened
+ ? ArchiveSupportedFeature.SupportsCompression |
+ ArchiveSupportedFeature.SupportsFilenames |
+ ArchiveSupportedFeature.HasEntryTimestamp
+ : _features;
+ ///
+ public int NumberOfEntries => Opened ? _entries.Count : -1;
+
+#endregion
+}
\ No newline at end of file
diff --git a/Aaru.Archives/Arj/Consts.cs b/Aaru.Archives/Arj/Consts.cs
new file mode 100644
index 000000000..afe618c51
--- /dev/null
+++ b/Aaru.Archives/Arj/Consts.cs
@@ -0,0 +1,39 @@
+namespace Aaru.Archives;
+
+public sealed partial class Arj
+{
+ /// ARJ archive header signature (little-endian: 0x60, 0xEA)
+ const ushort HEADER_ID = 0xEA60;
+ /// Maximum basic header size in bytes
+ const int HEADER_SIZE_MAX = 2600;
+ /// Standard first header size (30 bytes of fixed fields)
+ const int FIRST_HDR_SIZE = 30;
+ /// Extended first header size with protection fields (34 bytes)
+ const int FIRST_HDR_SIZE_V = 34;
+ /// Minimum size for headers with access/creation timestamps
+ const int R9_HDR_SIZE = 46;
+ /// CRC32 mask for finalization (XOR with this for standard CRC32)
+ const uint CRC_MASK = 0xFFFFFFFF;
+ /// CRC32 polynomial (reversed, same as standard CRC-32/ISO)
+ const uint CRC_POLY = 0xEDB88320;
+ /// Extended header tag for OS/2 extended attributes
+ const byte EA_TAG = (byte)'E';
+ /// Extended header tag for UNIX special files
+ const byte UXSPECIAL_TAG = (byte)'U';
+ /// Extended header tag for owner info (string)
+ const byte OWNER_TAG = (byte)'O';
+ /// Extended header tag for owner info (numeric)
+ const byte OWNER_NUM_TAG = (byte)'o';
+ /// ARJZ minimum version that uses extended DEFLATE (deflatez)
+ const byte ARJZ_DEFLATEZ_VERSION = 55;
+ /// ARJZ minimum version that uses ArjzStream method 1
+ const byte ARJZ_METHOD1_VERSION = 51;
+ /// ARJZ minimum version that uses ArjzStream method 2
+ const byte ARJZ_METHOD2_VERSION = 52;
+ /// ARJZ minimum version that uses ArjzStream method 3
+ const byte ARJZ_METHOD3_VERSION = 53;
+ /// Minimum number of bytes needed to attempt identification
+ const int MIN_HEADER_SIZE = 10;
+ /// OS/2 FEA flag: critical EA
+ const byte FEA_NEEDEA = 0x80;
+}
\ No newline at end of file
diff --git a/Aaru.Archives/Arj/Enums.cs b/Aaru.Archives/Arj/Enums.cs
new file mode 100644
index 000000000..847a92107
--- /dev/null
+++ b/Aaru.Archives/Arj/Enums.cs
@@ -0,0 +1,74 @@
+using System;
+
+namespace Aaru.Archives;
+
+public sealed partial class Arj
+{
+#region Method enum
+
+ enum Method : byte
+ {
+ /// Uncompressed (stored)
+ Stored = 0,
+ /// Compressed with method 1 (most compression, LZH)
+ Method1 = 1,
+ /// Compressed with method 2 (medium compression, LZH)
+ Method2 = 2,
+ /// Compressed with method 3 (fast compression, LZH)
+ Method3 = 3,
+ /// Compressed with method 4 (fastest, Golomb-Rice)
+ Fastest = 4
+ }
+
+#endregion
+
+#region HostOs enum
+
+ enum HostOs : byte
+ {
+ MsDos = 0,
+ Primos = 1,
+ Unix = 2,
+ Amiga = 3,
+ MacOs = 4,
+ Os2 = 5,
+ AppleGs = 6,
+ AtariSt = 7,
+ Next = 8,
+ Vax = 9,
+ Win95 = 10,
+ WinNt = 11
+ }
+
+#endregion
+
+#region FileType enum
+
+ enum FileType : byte
+ {
+ Binary = 0,
+ Text = 1,
+ Comment = 2,
+ Directory = 3,
+ VolumeLabel = 4,
+ Chapter = 5,
+ UnixSpecial = 6
+ }
+
+#endregion
+
+#region ArjFlags enum
+
+ [Flags]
+ enum ArjFlags : byte
+ {
+ None = 0x00,
+ Garbled = 0x01,
+ Volume = 0x04,
+ ExtFile = 0x08,
+ PathSym = 0x10,
+ Backup = 0x20
+ }
+
+#endregion
+}
\ No newline at end of file
diff --git a/Aaru.Archives/Arj/Files.cs b/Aaru.Archives/Arj/Files.cs
new file mode 100644
index 000000000..c104c48c5
--- /dev/null
+++ b/Aaru.Archives/Arj/Files.cs
@@ -0,0 +1,214 @@
+using System;
+using System.IO;
+using Aaru.CommonTypes.Enums;
+using Aaru.CommonTypes.Interfaces;
+using Aaru.CommonTypes.Structs;
+using Aaru.Compression;
+using Aaru.Filters;
+using Aaru.Helpers.IO;
+using FileAttributes = Aaru.CommonTypes.Structs.FileAttributes;
+
+namespace Aaru.Archives;
+
+public sealed partial class Arj
+{
+#region IArchive Members
+
+ ///
+ public ErrorNumber GetFilename(int entryNumber, out string fileName)
+ {
+ fileName = null;
+
+ if(!Opened) return ErrorNumber.NotOpened;
+
+ if(entryNumber < 0 || entryNumber >= _entries.Count) return ErrorNumber.OutOfRange;
+
+ fileName = _entries[entryNumber].Filename ?? "";
+
+ return ErrorNumber.NoError;
+ }
+
+ ///
+ public ErrorNumber GetEntryNumber(string fileName, bool caseInsensitiveMatch, out int entryNumber)
+ {
+ entryNumber = -1;
+
+ if(!Opened) return ErrorNumber.NotOpened;
+
+ StringComparison comparison = caseInsensitiveMatch
+ ? StringComparison.CurrentCultureIgnoreCase
+ : StringComparison.CurrentCulture;
+
+ for(int i = 0, count = _entries.Count; i < count; i++)
+ {
+ GetFilename(i, out string name);
+
+ if(name is null) continue;
+
+ if(!name.Equals(fileName, comparison)) continue;
+
+ entryNumber = i;
+
+ return ErrorNumber.NoError;
+ }
+
+ return ErrorNumber.NoSuchFile;
+ }
+
+ ///
+ public ErrorNumber GetCompressedSize(int entryNumber, out long length)
+ {
+ length = -1;
+
+ if(!Opened) return ErrorNumber.NotOpened;
+
+ if(entryNumber < 0 || entryNumber >= _entries.Count) return ErrorNumber.OutOfRange;
+
+ length = _entries[entryNumber].CompressedSize;
+
+ return ErrorNumber.NoError;
+ }
+
+ ///
+ public ErrorNumber GetUncompressedSize(int entryNumber, out long length)
+ {
+ length = -1;
+
+ if(!Opened) return ErrorNumber.NotOpened;
+
+ if(entryNumber < 0 || entryNumber >= _entries.Count) return ErrorNumber.OutOfRange;
+
+ length = _entries[entryNumber].UncompressedSize;
+
+ return ErrorNumber.NoError;
+ }
+
+ ///
+ public ErrorNumber Stat(int entryNumber, out FileEntryInfo stat)
+ {
+ stat = null;
+
+ if(!Opened) return ErrorNumber.NotOpened;
+
+ if(entryNumber < 0 || entryNumber >= _entries.Count) return ErrorNumber.OutOfRange;
+
+ Entry entry = _entries[entryNumber];
+
+ stat = new FileEntryInfo
+ {
+ Attributes = entry.IsDirectory ? FileAttributes.Directory : FileAttributes.File,
+ Blocks = entry.UncompressedSize / 512,
+ BlockSize = 512,
+ Length = entry.UncompressedSize,
+ LastWriteTime = entry.LastWriteTime,
+ LastWriteTimeUtc = entry.LastWriteTime.ToUniversalTime()
+ };
+
+ if(entry.UncompressedSize % 512 != 0) stat.Blocks++;
+
+ if(entry.CreationTime != default(DateTime))
+ {
+ stat.CreationTime = entry.CreationTime;
+ stat.CreationTimeUtc = entry.CreationTime.ToUniversalTime();
+ }
+
+ if(entry.LastAccessTime != default(DateTime))
+ {
+ stat.AccessTime = entry.LastAccessTime;
+ stat.AccessTimeUtc = entry.LastAccessTime.ToUniversalTime();
+ }
+
+ // Unix file permissions for Unix hosts
+ if(entry.HostOs is HostOs.Unix or HostOs.Next) stat.Mode = entry.FileMode;
+
+ return ErrorNumber.NoError;
+ }
+
+ ///
+ public ErrorNumber GetEntry(int entryNumber, out IFilter filter)
+ {
+ filter = null;
+
+ if(!Opened) return ErrorNumber.NotOpened;
+
+ if(entryNumber < 0 || entryNumber >= _entries.Count) return ErrorNumber.OutOfRange;
+
+ Entry entry = _entries[entryNumber];
+
+ if(entry.IsDirectory) return ErrorNumber.InvalidArgument;
+
+ // Encrypted files are not supported
+ if((entry.ArjFlags & (byte)ArjFlags.Garbled) != 0) return ErrorNumber.NotSupported;
+
+ Stream stream;
+
+ if(entry.UncompressedSize == 0)
+ stream = new MemoryStream([]);
+ else
+ {
+ stream = new OffsetStream(new NonClosableStream(_stream),
+ entry.DataOffset,
+ entry.DataOffset + entry.CompressedSize);
+
+ switch(entry.ArjxNbr)
+ {
+ // ARJZ dispatch based on arj_x_nbr
+ case ARJZ_METHOD1_VERSION:
+ stream = new ArjzStream(stream, entry.UncompressedSize, 1);
+
+ break;
+ case ARJZ_METHOD2_VERSION:
+ stream = new ArjzStream(stream, entry.UncompressedSize, 2);
+
+ break;
+ case ARJZ_METHOD3_VERSION:
+ stream = new ArjzStream(stream, entry.UncompressedSize, 3);
+
+ break;
+ case ARJZ_DEFLATEZ_VERSION:
+ stream = new ArjzStream(stream, entry.UncompressedSize, 5);
+
+ break;
+ default:
+ // Standard ARJ dispatch based on method
+ switch(entry.Method)
+ {
+ case Method.Stored:
+ // No decompression needed
+ break;
+ case Method.Method1:
+ stream = new ArjStream(stream, entry.UncompressedSize, 1);
+
+ break;
+ case Method.Method2:
+ stream = new ArjStream(stream, entry.UncompressedSize, 2);
+
+ break;
+ case Method.Method3:
+ stream = new ArjStream(stream, entry.UncompressedSize, 3);
+
+ break;
+ case Method.Fastest:
+ stream = new ArjStream(stream, entry.UncompressedSize, 4);
+
+ break;
+ default:
+ return ErrorNumber.NotSupported;
+ }
+
+ break;
+ }
+ }
+
+ 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/Arj/Info.cs b/Aaru.Archives/Arj/Info.cs
new file mode 100644
index 000000000..e861c228a
--- /dev/null
+++ b/Aaru.Archives/Arj/Info.cs
@@ -0,0 +1,123 @@
+using System;
+using System.IO;
+using System.Text;
+using Aaru.CommonTypes.Interfaces;
+
+namespace Aaru.Archives;
+
+public sealed partial class Arj
+{
+ /// Precomputed CRC32 lookup table (polynomial 0xEDB88320).
+ static readonly uint[] _crcTable = BuildCrcTable();
+
+ static uint[] BuildCrcTable()
+ {
+ var table = new uint[256];
+
+ for(uint i = 0; i < 256; i++)
+ {
+ uint r = i;
+
+ for(var j = 0; j < 8; j++) r = (r & 1) != 0 ? r >> 1 ^ CRC_POLY : r >> 1;
+
+ table[i] = r;
+ }
+
+ return table;
+ }
+
+ /// Compute CRC32 over a byte span.
+ static uint ComputeCrc(uint crc, ReadOnlySpan data)
+ {
+ foreach(byte b in data) crc = _crcTable[(byte)(crc ^ b)] ^ crc >> 8;
+
+ return crc;
+ }
+
+ /// Convert MS-DOS FAT timestamp to DateTime.
+ static DateTime DosToDateTime(uint dosDateTime)
+ {
+ int second = (int)(dosDateTime & 0x1F) * 2;
+ var minute = (int)(dosDateTime >> 5 & 0x3F);
+ var hour = (int)(dosDateTime >> 11 & 0x1F);
+ var day = (int)(dosDateTime >> 16 & 0x1F);
+ var month = (int)(dosDateTime >> 21 & 0x0F);
+ int year = (int)(dosDateTime >> 25 & 0x7F) + 1980;
+
+ if(day < 1) day = 1;
+ if(month < 1) month = 1;
+ if(month > 12) month = 12;
+
+ int maxDay = DateTime.DaysInMonth(year, month);
+
+ if(day > maxDay) day = maxDay;
+ if(hour > 23) hour = 23;
+ if(minute > 59) minute = 59;
+ if(second > 59) second = 59;
+
+ return new DateTime(year, month, day, hour, minute, second);
+ }
+
+ /// Convert a Unix timestamp to DateTime.
+ static DateTime UnixToDateTime(uint unixTime) =>
+ unixTime == 0 ? default(DateTime) : DateTimeOffset.FromUnixTimeSeconds(unixTime).DateTime;
+
+ /// Convert a timestamp value to DateTime based on host OS.
+ static DateTime TimestampToDateTime(uint raw, HostOs hostOs) =>
+ hostOs is HostOs.Unix or HostOs.Next ? UnixToDateTime(raw) : DosToDateTime(raw);
+
+#region IArchive Members
+
+ ///
+ public bool Identify(IFilter filter)
+ {
+ if(filter.DataForkLength < MIN_HEADER_SIZE) return false;
+
+ Stream stream = filter.GetDataForkStream();
+ stream.Position = 0;
+
+ var header = new byte[MIN_HEADER_SIZE];
+ stream.ReadExactly(header, 0, header.Length);
+
+ // Check header signature (little-endian: 0x60, 0xEA)
+ var headerSignature = BitConverter.ToUInt16(header, 0);
+
+ if(headerSignature != HEADER_ID) return false;
+
+ // Read basic header size
+ var basicHdrSize = BitConverter.ToUInt16(header, 2);
+
+ if(basicHdrSize is 0 or > HEADER_SIZE_MAX) return false;
+
+ // Read the full header for CRC validation
+ if(filter.DataForkLength < 4 + basicHdrSize + 4) return false;
+
+ stream.Position = 4;
+ var hdrData = new byte[basicHdrSize];
+ stream.ReadExactly(hdrData, 0, basicHdrSize);
+
+ var crcBytes = new byte[4];
+ stream.ReadExactly(crcBytes, 0, 4);
+ var storedCrc = BitConverter.ToUInt32(crcBytes, 0);
+
+ // Compute and verify CRC32
+ uint computedCrc = ComputeCrc(CRC_MASK, hdrData) ^ CRC_MASK;
+
+ return computedCrc == storedCrc;
+ }
+
+ ///
+ public void GetInformation(IFilter filter, Encoding encoding, out string information)
+ {
+ information = "";
+
+ if(!Identify(filter)) return;
+
+ var sb = new StringBuilder();
+ sb.AppendLine(Localization.ARJ_archive);
+
+ information = sb.ToString();
+ }
+
+#endregion
+}
\ No newline at end of file
diff --git a/Aaru.Archives/Arj/Open.cs b/Aaru.Archives/Arj/Open.cs
new file mode 100644
index 000000000..0261f8947
--- /dev/null
+++ b/Aaru.Archives/Arj/Open.cs
@@ -0,0 +1,405 @@
+using System;
+using System.Collections.Generic;
+using System.IO;
+using System.Text;
+using Aaru.CommonTypes.Enums;
+using Aaru.CommonTypes.Interfaces;
+using Aaru.Compression;
+using Aaru.Logging;
+
+namespace Aaru.Archives;
+
+public sealed partial class Arj
+{
+ List _entries;
+
+ ///
+ /// Reads an ARJ header block (signature + size + data + CRC + extended headers). Returns false if end of archive
+ /// or invalid header.
+ ///
+ bool ReadHeader(out byte[] headerData, out List extHeaders)
+ {
+ headerData = null;
+ extHeaders = null;
+
+ if(_stream.Position + 4 > _stream.Length) return false;
+
+ // Read 2-byte signature
+ var sigBytes = new byte[2];
+ _stream.ReadExactly(sigBytes, 0, 2);
+ var sig = BitConverter.ToUInt16(sigBytes, 0);
+
+ if(sig != HEADER_ID) return false;
+
+ // Read 2-byte basic header size
+ var sizeBytes = new byte[2];
+ _stream.ReadExactly(sizeBytes, 0, 2);
+ var basicHdrSize = BitConverter.ToUInt16(sizeBytes, 0);
+
+ // Size of 0 means end of archive
+ if(basicHdrSize == 0) return false;
+
+ if(basicHdrSize > HEADER_SIZE_MAX) return false;
+
+ // Read header data
+ if(_stream.Position + basicHdrSize + 4 > _stream.Length) return false;
+
+ headerData = new byte[basicHdrSize];
+ _stream.ReadExactly(headerData, 0, basicHdrSize);
+
+ // Read and verify CRC32
+ var crcBytes = new byte[4];
+ _stream.ReadExactly(crcBytes, 0, 4);
+ var storedCrc = BitConverter.ToUInt32(crcBytes, 0);
+ uint computedCrc = ComputeCrc(CRC_MASK, headerData) ^ CRC_MASK;
+
+ if(storedCrc != computedCrc)
+ {
+ AaruLogging.Debug(MODULE_NAME,
+ "[red]Header CRC mismatch: stored=0x{0:X8}, computed=0x{1:X8}[/]",
+ storedCrc,
+ computedCrc);
+
+ return false;
+ }
+
+ // Read extended headers
+ extHeaders = [];
+
+ while(_stream.Position + 2 <= _stream.Length)
+ {
+ var extSizeBytes = new byte[2];
+ _stream.ReadExactly(extSizeBytes, 0, 2);
+ var extSize = BitConverter.ToUInt16(extSizeBytes, 0);
+
+ if(extSize == 0) break;
+
+ if(_stream.Position + extSize + 4 > _stream.Length) break;
+
+ var extData = new byte[extSize];
+ _stream.ReadExactly(extData, 0, extSize);
+
+ // Read extended header CRC
+ var extCrcBytes = new byte[4];
+ _stream.ReadExactly(extCrcBytes, 0, 4);
+ var extStoredCrc = BitConverter.ToUInt32(extCrcBytes, 0);
+ uint extComputedCrc = ComputeCrc(CRC_MASK, extData) ^ CRC_MASK;
+
+ if(extStoredCrc != extComputedCrc)
+ {
+ AaruLogging.Debug(MODULE_NAME, "[red]Extended header CRC mismatch[/]");
+
+ continue;
+ }
+
+ // First byte is the tag, second byte is the continuation flag
+ if(extData.Length >= 2)
+ {
+ byte tag = extData[0];
+ byte continuation = extData[1];
+ var data = new byte[extData.Length - 2];
+
+ if(data.Length > 0) Array.Copy(extData, 2, data, 0, data.Length);
+
+ // Find existing block with same tag and append, or create new
+ var appended = false;
+
+ for(var i = 0; i < extHeaders.Count; i++)
+ {
+ if(extHeaders[i].Tag != tag) continue;
+
+ // Append data to existing block
+ var combined = new byte[extHeaders[i].Data.Length + data.Length];
+ Array.Copy(extHeaders[i].Data, 0, combined, 0, extHeaders[i].Data.Length);
+ Array.Copy(data, 0, combined, extHeaders[i].Data.Length, data.Length);
+
+ extHeaders[i] = new ExtHeaderBlock
+ {
+ Tag = tag,
+ Data = combined
+ };
+
+ appended = true;
+
+ break;
+ }
+
+ if(!appended)
+ extHeaders.Add(new ExtHeaderBlock
+ {
+ Tag = tag,
+ Data = data
+ });
+ }
+ }
+
+ return true;
+ }
+
+ /// Decompress an EA extended header block.
+ static byte[] DecompressEaBlock(byte[] rawEaData)
+ {
+ if(rawEaData is null || rawEaData.Length < 4) return null;
+
+ byte eaMethod = rawEaData[0];
+ int eaOrigLen = rawEaData[1] | rawEaData[2] << 8;
+
+ // Bytes 3-6 would be CRC, but we skip validation here
+ int compDataOffset = eaMethod == 0 ? 3 : 7;
+
+ if(compDataOffset > rawEaData.Length) return null;
+
+ int compDataLen = rawEaData.Length - compDataOffset;
+
+ if(eaMethod == 0)
+ {
+ // Stored EA data
+ var result = new byte[compDataLen];
+ Array.Copy(rawEaData, compDataOffset, result, 0, compDataLen);
+
+ return result;
+ }
+
+ if(eaMethod > 4 || eaOrigLen <= 0) return null;
+
+ try
+ {
+ var compStream = new MemoryStream(rawEaData, compDataOffset, compDataLen);
+ var arjStream = new ArjStream(compStream, eaOrigLen, eaMethod);
+ var decompressed = new byte[eaOrigLen];
+ var totalRead = 0;
+
+ while(totalRead < eaOrigLen)
+ {
+ int bytesRead = arjStream.Read(decompressed, totalRead, eaOrigLen - totalRead);
+
+ if(bytesRead <= 0) break;
+
+ totalRead += bytesRead;
+ }
+
+ return decompressed;
+ }
+ catch
+ {
+ return null;
+ }
+ }
+
+ /// Extract a null-terminated string from a byte array at the given offset.
+ static string ExtractNullTerminatedString(byte[] data, int offset, Encoding encoding)
+ {
+ if(offset >= data.Length) return "";
+
+ int end = Array.IndexOf(data, (byte)0, offset);
+
+ if(end < 0) end = data.Length;
+
+ int length = end - offset;
+
+ return length <= 0 ? "" : encoding.GetString(data, offset, length);
+ }
+
+ /// Get the length including the null terminator of a null-terminated string.
+ static int GetNullTerminatedLength(byte[] data, int offset)
+ {
+ if(offset >= data.Length) return 0;
+
+ int end = Array.IndexOf(data, (byte)0, offset);
+
+ if(end < 0) return data.Length - offset;
+
+ return end - offset;
+ }
+
+ /// Normalize path separators to forward slash.
+ static string NormalizePath(string path) => path?.Replace('\\', '/');
+
+ struct ExtHeaderBlock
+ {
+ public byte Tag;
+ public byte[] Data;
+ }
+
+#region IArchive Members
+
+ ///
+ public ErrorNumber Open(IFilter filter, Encoding encoding)
+ {
+ if(filter.DataForkLength < MIN_HEADER_SIZE) return ErrorNumber.InvalidArgument;
+
+ _stream = filter.GetDataForkStream();
+ _stream.Position = 0;
+ _encoding = encoding ?? Encoding.GetEncoding(437);
+ _entries = [];
+
+ _features = ArchiveSupportedFeature.SupportsFilenames | ArchiveSupportedFeature.HasEntryTimestamp;
+
+ // Read and skip the main archive header
+ if(!ReadHeader(out _, out _))
+ {
+ AaruLogging.Debug(MODULE_NAME, "[red]Invalid main archive header[/]");
+
+ return ErrorNumber.InvalidArgument;
+ }
+
+ // Read file entry headers
+ while(_stream.Position < _stream.Length)
+ {
+ long entryStart = _stream.Position;
+
+ if(!ReadHeader(out byte[] headerData, out List extHeaders)) break;
+
+ if(headerData is null || headerData.Length < FIRST_HDR_SIZE) break;
+
+ // Parse fixed header fields
+ var pos = 0;
+ byte firstHdrSize = headerData[pos++];
+ byte arjNbr = headerData[pos++];
+ byte arjxNbr = headerData[pos++];
+ byte hostOs = headerData[pos++];
+ byte arjFlags = headerData[pos++];
+ byte method = headerData[pos++];
+ byte fileType = headerData[pos++];
+ byte pwdModifier = headerData[pos++];
+ var timestamp = BitConverter.ToUInt32(headerData, pos);
+ pos += 4;
+ var compSize = BitConverter.ToUInt32(headerData, pos);
+ pos += 4;
+ var origSize = BitConverter.ToUInt32(headerData, pos);
+ pos += 4;
+ var fileCrc = BitConverter.ToUInt32(headerData, pos);
+ pos += 4;
+ var entryPos = BitConverter.ToUInt16(headerData, pos);
+ pos += 2;
+ var fileMode = BitConverter.ToUInt16(headerData, pos);
+ pos += 2;
+
+ byte extFlags = 0;
+ byte chapterNumber = 0;
+ uint accessTime = 0;
+ uint creationTime = 0;
+
+ if(firstHdrSize >= FIRST_HDR_SIZE)
+ {
+ extFlags = headerData[pos++];
+ chapterNumber = headerData[pos++];
+ }
+
+ if(firstHdrSize >= FIRST_HDR_SIZE_V)
+ {
+ pos++; // prot_blocks
+ pos++; // arjprot_id
+ pos += 2; // reserved
+ }
+
+ if(firstHdrSize >= R9_HDR_SIZE)
+ {
+ pos += 4; // resume_position
+ accessTime = BitConverter.ToUInt32(headerData, pos);
+ pos += 4;
+ creationTime = BitConverter.ToUInt32(headerData, pos);
+ pos += 4;
+ pos += 4; // reserved
+ }
+
+ // Extract filename (null-terminated at offset firstHdrSize)
+ string filename = ExtractNullTerminatedString(headerData, firstHdrSize, _encoding);
+
+ // Extract comment (null-terminated after filename)
+ int commentOffset = firstHdrSize + GetNullTerminatedLength(headerData, firstHdrSize) + 1;
+ string comment = null;
+
+ if(commentOffset < headerData.Length)
+ {
+ string commentStr = ExtractNullTerminatedString(headerData, commentOffset, _encoding);
+
+ if(!string.IsNullOrEmpty(commentStr)) comment = commentStr;
+ }
+
+ AaruLogging.Debug(MODULE_NAME, "[navy]filename[/] = [green]\"{0}\"[/]", filename);
+ AaruLogging.Debug(MODULE_NAME, "[navy]method[/] = [teal]{0}[/]", method);
+ AaruLogging.Debug(MODULE_NAME, "[navy]compSize[/] = [teal]{0}[/]", compSize);
+ AaruLogging.Debug(MODULE_NAME, "[navy]origSize[/] = [teal]{0}[/]", origSize);
+ AaruLogging.Debug(MODULE_NAME, "[navy]hostOs[/] = [teal]{0}[/]", (HostOs)hostOs);
+ AaruLogging.Debug(MODULE_NAME, "[navy]fileType[/] = [teal]{0}[/]", (FileType)fileType);
+ AaruLogging.Debug(MODULE_NAME, "[navy]arjFlags[/] = [teal]0x{0:X2}[/]", arjFlags);
+ AaruLogging.Debug(MODULE_NAME, "[navy]arjxNbr[/] = [teal]{0}[/]", arjxNbr);
+
+ // Process extended attributes from extended headers
+ byte[] eaData = null;
+
+ if(extHeaders is { Count: > 0 })
+ {
+ foreach(ExtHeaderBlock eh in extHeaders)
+ {
+ if(eh.Tag != EA_TAG || eh.Data is null || eh.Data.Length < 4) continue;
+
+ eaData = DecompressEaBlock(eh.Data);
+ }
+ }
+
+ HostOs parsedHostOs = hostOs <= 11 ? (HostOs)hostOs : HostOs.MsDos;
+
+ var entry = new Entry
+ {
+ Method = method <= 4 ? (Method)method : Method.Stored,
+ Filename = NormalizePath(filename),
+ CompressedSize = compSize,
+ UncompressedSize = origSize,
+ DataOffset = _stream.Position,
+ LastWriteTime = TimestampToDateTime(timestamp, parsedHostOs),
+ HostOs = parsedHostOs,
+ FileCrc = fileCrc,
+ FileMode = fileMode,
+ ArjFlags = arjFlags,
+ ArjxNbr = arjxNbr,
+ Comment = comment,
+ ExtendedAttributes = eaData,
+ IsDirectory = (FileType)fileType == FileType.Directory
+ };
+
+ if(accessTime != 0) entry.LastAccessTime = TimestampToDateTime(accessTime, parsedHostOs);
+
+ if(creationTime != 0) entry.CreationTime = TimestampToDateTime(creationTime, parsedHostOs);
+
+ // Update features
+ if(entry.Method != Method.Stored && entry.CompressedSize > 0)
+ _features |= ArchiveSupportedFeature.SupportsCompression;
+
+ if(entry.IsDirectory)
+ {
+ _features |= ArchiveSupportedFeature.HasExplicitDirectories |
+ ArchiveSupportedFeature.SupportsSubdirectories;
+ }
+
+ if(entry.Comment is not null || entry.ExtendedAttributes is not null)
+ _features |= ArchiveSupportedFeature.SupportsXAttrs;
+
+ if(entry.Filename.Contains('/')) _features |= ArchiveSupportedFeature.SupportsSubdirectories;
+
+ _entries.Add(entry);
+
+ // Skip past compressed data
+ _stream.Position = entry.DataOffset + entry.CompressedSize;
+ }
+
+ Opened = true;
+
+ return ErrorNumber.NoError;
+ }
+
+ ///
+ public void Close()
+ {
+ if(!Opened) return;
+
+ _stream?.Close();
+ _entries?.Clear();
+
+ _stream = null;
+ Opened = false;
+ }
+
+#endregion
+}
\ No newline at end of file
diff --git a/Aaru.Archives/Arj/Structs.cs b/Aaru.Archives/Arj/Structs.cs
new file mode 100644
index 000000000..0058bc20e
--- /dev/null
+++ b/Aaru.Archives/Arj/Structs.cs
@@ -0,0 +1,30 @@
+using System;
+
+namespace Aaru.Archives;
+
+public sealed partial class Arj
+{
+#region Nested type: Entry
+
+ struct Entry
+ {
+ public Method Method;
+ public string Filename;
+ public long CompressedSize;
+ public long UncompressedSize;
+ public long DataOffset;
+ public DateTime LastWriteTime;
+ public DateTime CreationTime;
+ public DateTime LastAccessTime;
+ public HostOs HostOs;
+ public uint FileCrc;
+ public ushort FileMode;
+ public byte ArjFlags;
+ public byte ArjxNbr;
+ public string Comment;
+ public byte[] ExtendedAttributes;
+ public bool IsDirectory;
+ }
+
+#endregion
+}
\ No newline at end of file
diff --git a/Aaru.Archives/Arj/Xattrs.cs b/Aaru.Archives/Arj/Xattrs.cs
new file mode 100644
index 000000000..1b1ea3c94
--- /dev/null
+++ b/Aaru.Archives/Arj/Xattrs.cs
@@ -0,0 +1,142 @@
+using System;
+using System.Collections.Generic;
+using System.Text;
+using Aaru.CommonTypes.Enums;
+
+namespace Aaru.Archives;
+
+public sealed partial class Arj
+{
+ ///
+ /// Parse OS/2 FEA records from a decompressed EA block and return the EA names. Block format: count(2 LE) +
+ /// records of fEA(1) + nameLen(1) + valueLen(2 LE) + name(nameLen) + value(valueLen).
+ ///
+ static List ParseEaNames(byte[] eaBlock)
+ {
+ var names = new List();
+
+ if(eaBlock is null || eaBlock.Length < 2) return names;
+
+ var offset = 0;
+ var count = BitConverter.ToUInt16(eaBlock, offset);
+ offset += 2;
+
+ for(var i = 0; i < count && offset + 4 <= eaBlock.Length; i++)
+ {
+ byte fEa = eaBlock[offset++];
+ byte nameLen = eaBlock[offset++];
+ var valLen = BitConverter.ToUInt16(eaBlock, offset);
+ offset += 2;
+
+ if(offset + nameLen + valLen > eaBlock.Length) break;
+
+ string name = Encoding.ASCII.GetString(eaBlock, offset, nameLen);
+ offset += nameLen;
+ offset += valLen;
+
+ if(!string.IsNullOrEmpty(name)) names.Add(name);
+ }
+
+ return names;
+ }
+
+ /// Find an OS/2 FEA record by name and return its raw value bytes.
+ static byte[] FindEaValue(byte[] eaBlock, string eaName)
+ {
+ if(eaBlock is null || eaBlock.Length < 2) return null;
+
+ var offset = 0;
+ var count = BitConverter.ToUInt16(eaBlock, offset);
+ offset += 2;
+
+ for(var i = 0; i < count && offset + 4 <= eaBlock.Length; i++)
+ {
+ byte fEa = eaBlock[offset++];
+ byte nameLen = eaBlock[offset++];
+ var valLen = BitConverter.ToUInt16(eaBlock, offset);
+ offset += 2;
+
+ if(offset + nameLen + valLen > eaBlock.Length) return null;
+
+ string name = Encoding.ASCII.GetString(eaBlock, offset, nameLen);
+ offset += nameLen;
+
+ if(name == eaName)
+ {
+ var value = new byte[valLen];
+ Array.Copy(eaBlock, offset, value, 0, valLen);
+
+ return value;
+ }
+
+ offset += valLen;
+ }
+
+ return null;
+ }
+
+#region IArchive Members
+
+ ///
+ public ErrorNumber ListXAttr(int entryNumber, out List xattrs)
+ {
+ xattrs = null;
+
+ if(!Opened) return ErrorNumber.NotOpened;
+
+ if(entryNumber < 0 || entryNumber >= _entries.Count) return ErrorNumber.OutOfRange;
+
+ xattrs = [];
+
+ Entry entry = _entries[entryNumber];
+
+ if(entry.Comment is not null) xattrs.Add("comment");
+
+ // Parse OS/2 extended attribute names from the decompressed EA block
+ if(entry.ExtendedAttributes is { Length: >= 2 })
+ {
+ List eaNames = ParseEaNames(entry.ExtendedAttributes);
+ xattrs.AddRange(eaNames);
+ }
+
+ return ErrorNumber.NoError;
+ }
+
+ ///
+ public ErrorNumber GetXattr(int entryNumber, string xattr, ref byte[] buffer)
+ {
+ buffer = null;
+
+ if(!Opened) return ErrorNumber.NotOpened;
+
+ if(entryNumber < 0 || entryNumber >= _entries.Count) return ErrorNumber.OutOfRange;
+
+ Entry entry = _entries[entryNumber];
+
+ if(xattr == "comment")
+ {
+ if(entry.Comment is null) return ErrorNumber.NoSuchExtendedAttribute;
+
+ buffer = Encoding.UTF8.GetBytes(entry.Comment);
+
+ return ErrorNumber.NoError;
+ }
+
+ // Look up OS/2 extended attribute by name
+ if(entry.ExtendedAttributes is { Length: >= 2 })
+ {
+ byte[] value = FindEaValue(entry.ExtendedAttributes, xattr);
+
+ if(value is not null)
+ {
+ buffer = value;
+
+ return ErrorNumber.NoError;
+ }
+ }
+
+ return ErrorNumber.NoSuchExtendedAttribute;
+ }
+
+#endregion
+}
\ No newline at end of file
diff --git a/Aaru.Archives/Localization/Localization.Designer.cs b/Aaru.Archives/Localization/Localization.Designer.cs
index 104358a10..4af2da723 100644
--- a/Aaru.Archives/Localization/Localization.Designer.cs
+++ b/Aaru.Archives/Localization/Localization.Designer.cs
@@ -9,21 +9,21 @@
namespace Aaru.Archives {
using System;
-
-
+
+
[System.CodeDom.Compiler.GeneratedCodeAttribute("System.Resources.Tools.StronglyTypedResourceBuilder", "4.0.0.0")]
[System.Diagnostics.DebuggerNonUserCodeAttribute()]
[System.Runtime.CompilerServices.CompilerGeneratedAttribute()]
internal class Localization {
-
+
private static System.Resources.ResourceManager resourceMan;
-
+
private static System.Globalization.CultureInfo resourceCulture;
-
+
[System.Diagnostics.CodeAnalysis.SuppressMessageAttribute("Microsoft.Performance", "CA1811:AvoidUncalledPrivateCode")]
internal Localization() {
}
-
+
[System.ComponentModel.EditorBrowsableAttribute(System.ComponentModel.EditorBrowsableState.Advanced)]
internal static System.Resources.ResourceManager ResourceManager {
get {
@@ -34,7 +34,7 @@ namespace Aaru.Archives {
return resourceMan;
}
}
-
+
[System.ComponentModel.EditorBrowsableAttribute(System.ComponentModel.EditorBrowsableState.Advanced)]
internal static System.Globalization.CultureInfo Culture {
get {
@@ -44,737 +44,749 @@ namespace Aaru.Archives {
resourceCulture = value;
}
}
-
+
internal static string Symbian_Name {
get {
return ResourceManager.GetString("Symbian_Name", resourceCulture);
}
}
-
+
internal static string Symbian_Installation_File {
get {
return ResourceManager.GetString("Symbian_Installation_File", resourceCulture);
}
}
-
+
internal static string Symbian_9_1_or_later {
get {
return ResourceManager.GetString("Symbian_9_1_or_later", resourceCulture);
}
}
-
+
internal static string Application_ID_0 {
get {
return ResourceManager.GetString("Application_ID_0", resourceCulture);
}
}
-
+
internal static string UIDs_checksum_0 {
get {
return ResourceManager.GetString("UIDs_checksum_0", resourceCulture);
}
}
-
+
internal static string Symbian_3_or_later {
get {
return ResourceManager.GetString("Symbian_3_or_later", resourceCulture);
}
}
-
+
internal static string Symbian_6_or_later {
get {
return ResourceManager.GetString("Symbian_6_or_later", resourceCulture);
}
}
-
+
internal static string Unknown_EPOC_magic_0 {
get {
return ResourceManager.GetString("Unknown_EPOC_magic_0", resourceCulture);
}
}
-
+
internal static string CRC16_of_header_0 {
get {
return ResourceManager.GetString("CRC16_of_header_0", resourceCulture);
}
}
-
+
internal static string SIS_contains_an_application {
get {
return ResourceManager.GetString("SIS_contains_an_application", resourceCulture);
}
}
-
+
internal static string File_contains_0_languages {
get {
return ResourceManager.GetString("File_contains_0_languages", resourceCulture);
}
}
-
+
internal static string File_contains_0_files_pointer_1 {
get {
return ResourceManager.GetString("File_contains_0_files_pointer_1", resourceCulture);
}
}
-
+
internal static string File_contains_0_requisites {
get {
return ResourceManager.GetString("File_contains_0_requisites", resourceCulture);
}
}
-
+
internal static string Capabilities {
get {
return ResourceManager.GetString("Capabilities", resourceCulture);
}
}
-
+
internal static string Component_version_0_1 {
get {
return ResourceManager.GetString("Component_version_0_1", resourceCulture);
}
}
-
+
internal static string Component_name_for_language_with_code_0_1 {
get {
return ResourceManager.GetString("Component_name_for_language_with_code_0_1", resourceCulture);
}
}
-
+
internal static string Files_for_all_languages {
get {
return ResourceManager.GetString("Files_for_all_languages", resourceCulture);
}
}
-
+
internal static string Files_for_0_language {
get {
return ResourceManager.GetString("Files_for_0_language", resourceCulture);
}
}
-
+
internal static string Requisite_0 {
get {
return ResourceManager.GetString("Requisite_0", resourceCulture);
}
}
-
+
internal static string Required_UID_0_version_1_2 {
get {
return ResourceManager.GetString("Required_UID_0_version_1_2", resourceCulture);
}
}
-
+
internal static string Required_variant_0 {
get {
return ResourceManager.GetString("Required_variant_0", resourceCulture);
}
}
-
+
internal static string Requisite_for_language_0_1 {
get {
return ResourceManager.GetString("Requisite_for_language_0_1", resourceCulture);
}
}
-
+
internal static string SIS_Platform_UID_Series_60_1st_Edition {
get {
return ResourceManager.GetString("SIS_Platform_UID_Series_60_1st_Edition", resourceCulture);
}
}
-
+
internal static string SIS_Platform_UID_Series_60_1st_Edition_Feature_Pack_1 {
get {
return ResourceManager.GetString("SIS_Platform_UID_Series_60_1st_Edition_Feature_Pack_1", resourceCulture);
}
}
-
+
internal static string SIS_Platform_UID_Series_60_2nd_Edition {
get {
return ResourceManager.GetString("SIS_Platform_UID_Series_60_2nd_Edition", resourceCulture);
}
}
-
+
internal static string SIS_Platform_UID_Series_60_2nd_Edition_Feature_Pack_1 {
get {
return ResourceManager.GetString("SIS_Platform_UID_Series_60_2nd_Edition_Feature_Pack_1", resourceCulture);
}
}
-
+
internal static string SIS_Platform_UID_Series_60_2nd_Edition_Feature_Pack_2 {
get {
return ResourceManager.GetString("SIS_Platform_UID_Series_60_2nd_Edition_Feature_Pack_2", resourceCulture);
}
}
-
+
internal static string SIS_Platform_UID_Series_60_2nd_Edition_Feature_Pack_3 {
get {
return ResourceManager.GetString("SIS_Platform_UID_Series_60_2nd_Edition_Feature_Pack_3", resourceCulture);
}
}
-
+
internal static string SIS_Platform_UID_Series_60_3rd_Edition {
get {
return ResourceManager.GetString("SIS_Platform_UID_Series_60_3rd_Edition", resourceCulture);
}
}
-
+
internal static string SIS_Platform_UID_Series_60_3rd_Edition_Feature_Pack_1 {
get {
return ResourceManager.GetString("SIS_Platform_UID_Series_60_3rd_Edition_Feature_Pack_1", resourceCulture);
}
}
-
+
internal static string SIS_Platform_UID_Series_60_3rd_Edition_Feature_Pack_2 {
get {
return ResourceManager.GetString("SIS_Platform_UID_Series_60_3rd_Edition_Feature_Pack_2", resourceCulture);
}
}
-
+
internal static string SIS_Platform_UID_Series_60_5th_Edition {
get {
return ResourceManager.GetString("SIS_Platform_UID_Series_60_5th_Edition", resourceCulture);
}
}
-
+
internal static string SIS_Platform_UID_Symbian_3 {
get {
return ResourceManager.GetString("SIS_Platform_UID_Symbian_3", resourceCulture);
}
}
-
+
internal static string SIS_Platform_UID_Nokia_Belle {
get {
return ResourceManager.GetString("SIS_Platform_UID_Nokia_Belle", resourceCulture);
}
}
-
+
internal static string SIS_Platform_UID_0 {
get {
return ResourceManager.GetString("SIS_Platform_UID_0", resourceCulture);
}
}
-
+
internal static string Archive_options_0 {
get {
return ResourceManager.GetString("Archive_options_0", resourceCulture);
}
}
-
+
internal static string SIS_Platform_UID_UIQ_21 {
get {
return ResourceManager.GetString("SIS_Platform_UID_UIQ_21", resourceCulture);
}
}
-
+
internal static string SIS_Platform_UID_UIQ_30 {
get {
return ResourceManager.GetString("SIS_Platform_UID_UIQ_30", resourceCulture);
}
}
-
+
internal static string SIS_Platform_UID_Nokia_7650 {
get {
return ResourceManager.GetString("SIS_Platform_UID_Nokia_7650", resourceCulture);
}
}
-
+
internal static string SIS_Platform_UID_Series_60_v1_0 {
get {
return ResourceManager.GetString("SIS_Platform_UID_Series_60_v1_0", resourceCulture);
}
}
-
+
internal static string SIS_Platform_UID_Nokia_3650 {
get {
return ResourceManager.GetString("SIS_Platform_UID_Nokia_3650", resourceCulture);
}
}
-
+
internal static string SIS_Platform_UID_Nokia_6600 {
get {
return ResourceManager.GetString("SIS_Platform_UID_Nokia_6600", resourceCulture);
}
}
-
+
internal static string SIS_Platform_UID_Nokia_6630 {
get {
return ResourceManager.GetString("SIS_Platform_UID_Nokia_6630", resourceCulture);
}
}
-
+
internal static string SIS_Platform_UID_SonyEricsson_P80x {
get {
return ResourceManager.GetString("SIS_Platform_UID_SonyEricsson_P80x", resourceCulture);
}
}
-
+
internal static string SIS_Platform_UID_Series_60_v1_1 {
get {
return ResourceManager.GetString("SIS_Platform_UID_Series_60_v1_1", resourceCulture);
}
}
-
+
internal static string SIS_Platform_UID_Nokia_N_Gage {
get {
return ResourceManager.GetString("SIS_Platform_UID_Nokia_N_Gage", resourceCulture);
}
}
-
+
internal static string SIS_Platform_UID_Nokia_9500 {
get {
return ResourceManager.GetString("SIS_Platform_UID_Nokia_9500", resourceCulture);
}
}
-
+
internal static string SIS_Platform_UID_Nokia_9300 {
get {
return ResourceManager.GetString("SIS_Platform_UID_Nokia_9300", resourceCulture);
}
}
-
+
internal static string SIS_Platform_UID_Series_80_2nd_edition {
get {
return ResourceManager.GetString("SIS_Platform_UID_Series_80_2nd_edition", resourceCulture);
}
}
-
+
internal static string SIS_Platform_UID_Siemens_SX1 {
get {
return ResourceManager.GetString("SIS_Platform_UID_Siemens_SX1", resourceCulture);
}
}
-
+
internal static string SIS_Platform_UID_Nokia_6260 {
get {
return ResourceManager.GetString("SIS_Platform_UID_Nokia_6260", resourceCulture);
}
}
-
+
internal static string SIS_Platform_UID_SonyEricsson_P90x {
get {
return ResourceManager.GetString("SIS_Platform_UID_SonyEricsson_P90x", resourceCulture);
}
}
-
+
internal static string SIS_Platform_UID_Nokia_7710_Series_90 {
get {
return ResourceManager.GetString("SIS_Platform_UID_Nokia_7710_Series_90", resourceCulture);
}
}
-
+
internal static string SIS_Platform_UID_Nokia_7610 {
get {
return ResourceManager.GetString("SIS_Platform_UID_Nokia_7610", resourceCulture);
}
}
-
+
internal static string SIS_Platform_UID_Nokia_6670 {
get {
return ResourceManager.GetString("SIS_Platform_UID_Nokia_6670", resourceCulture);
}
}
-
+
internal static string SIS_Platform_UID_UIQ_20 {
get {
return ResourceManager.GetString("SIS_Platform_UID_UIQ_20", resourceCulture);
}
}
-
+
internal static string SIS_Platform_UID_Nokia_3230 {
get {
return ResourceManager.GetString("SIS_Platform_UID_Nokia_3230", resourceCulture);
}
}
-
+
internal static string SIS_Platform_UID_Nokia_N90 {
get {
return ResourceManager.GetString("SIS_Platform_UID_Nokia_N90", resourceCulture);
}
}
-
+
internal static string SIS_Platform_UID_Nokia_N70 {
get {
return ResourceManager.GetString("SIS_Platform_UID_Nokia_N70", resourceCulture);
}
}
-
+
internal static string SIS_Platform_UID_Nokia_6680 {
get {
return ResourceManager.GetString("SIS_Platform_UID_Nokia_6680", resourceCulture);
}
}
-
+
internal static string SIS_Platform_UID_Nokia_6620 {
get {
return ResourceManager.GetString("SIS_Platform_UID_Nokia_6620", resourceCulture);
}
}
-
+
internal static string SIS_Platform_UID_Nokia_6682 {
get {
return ResourceManager.GetString("SIS_Platform_UID_Nokia_6682", resourceCulture);
}
}
-
+
internal static string SIS_Platform_UID_Nokia_6681 {
get {
return ResourceManager.GetString("SIS_Platform_UID_Nokia_6681", resourceCulture);
}
}
-
+
internal static string SIS_Platform_UID_Nokia_3250 {
get {
return ResourceManager.GetString("SIS_Platform_UID_Nokia_3250", resourceCulture);
}
}
-
+
internal static string SIS_Platform_UID_Nokia_N80 {
get {
return ResourceManager.GetString("SIS_Platform_UID_Nokia_N80", resourceCulture);
}
}
-
+
internal static string SIS_Platform_UID_Nokia_N92 {
get {
return ResourceManager.GetString("SIS_Platform_UID_Nokia_N92", resourceCulture);
}
}
-
+
internal static string SIS_Platform_UID_Nokia_N73 {
get {
return ResourceManager.GetString("SIS_Platform_UID_Nokia_N73", resourceCulture);
}
}
-
+
internal static string SIS_Platform_UID_Nokia_N91 {
get {
return ResourceManager.GetString("SIS_Platform_UID_Nokia_N91", resourceCulture);
}
}
-
+
internal static string SIS_Platform_UID_Nokia_N71 {
get {
return ResourceManager.GetString("SIS_Platform_UID_Nokia_N71", resourceCulture);
}
}
-
+
internal static string SIS_Platform_UID_Nokia_E60 {
get {
return ResourceManager.GetString("SIS_Platform_UID_Nokia_E60", resourceCulture);
}
}
-
+
internal static string SIS_Platform_UID_Nokia_E70 {
get {
return ResourceManager.GetString("SIS_Platform_UID_Nokia_E70", resourceCulture);
}
}
-
+
internal static string SIS_Platform_UID_Nokia_E61 {
get {
return ResourceManager.GetString("SIS_Platform_UID_Nokia_E61", resourceCulture);
}
}
-
+
internal static string Option_0 {
get {
return ResourceManager.GetString("Option_0", resourceCulture);
}
}
-
+
internal static string Name_for_language_0_1 {
get {
return ResourceManager.GetString("Name_for_language_0_1", resourceCulture);
}
}
-
+
internal static string This_file_format_is_not_yet_implemented {
get {
return ResourceManager.GetString("This_file_format_is_not_yet_implemented", resourceCulture);
}
}
-
+
internal static string Found_component_name_for_language_0_at_1_with_a_length_of_2_bytes {
get {
return ResourceManager.GetString("Found_component_name_for_language_0_at_1_with_a_length_of_2_bytes", resourceCulture);
}
}
-
+
internal static string Conditions {
get {
return ResourceManager.GetString("Conditions", resourceCulture);
}
}
-
+
internal static string Console_package {
get {
return ResourceManager.GetString("Console_package", resourceCulture);
}
}
-
+
internal static string Live_package {
get {
return ResourceManager.GetString("Live_package", resourceCulture);
}
}
-
+
internal static string Microsoft_package {
get {
return ResourceManager.GetString("Microsoft_package", resourceCulture);
}
}
-
+
internal static string Certificate_owner_console_ID_0_1_2_3_4 {
get {
return ResourceManager.GetString("Certificate_owner_console_ID_0_1_2_3_4", resourceCulture);
}
}
-
+
internal static string Certificate_owner_console_part_number_0 {
get {
return ResourceManager.GetString("Certificate_owner_console_part_number_0", resourceCulture);
}
}
-
+
internal static string Certificate_owner_console_type_0 {
get {
return ResourceManager.GetString("Certificate_owner_console_type_0", resourceCulture);
}
}
-
+
internal static string Certificate_date_of_generation_0 {
get {
return ResourceManager.GetString("Certificate_date_of_generation_0", resourceCulture);
}
}
-
+
internal static string Signatures_SHA1_0 {
get {
return ResourceManager.GetString("Signatures_SHA1_0", resourceCulture);
}
}
-
+
internal static string Header_size_0 {
get {
return ResourceManager.GetString("Header_size_0", resourceCulture);
}
}
-
+
internal static string Content_type_0 {
get {
return ResourceManager.GetString("Content_type_0", resourceCulture);
}
}
-
+
internal static string Content_size_0 {
get {
return ResourceManager.GetString("Content_size_0", resourceCulture);
}
}
-
+
internal static string Title_ID_0 {
get {
return ResourceManager.GetString("Title_ID_0", resourceCulture);
}
}
-
+
internal static string Media_ID_0 {
get {
return ResourceManager.GetString("Media_ID_0", resourceCulture);
}
}
-
+
internal static string Publisher_name_0 {
get {
return ResourceManager.GetString("Publisher_name_0", resourceCulture);
}
}
-
+
internal static string Title_name_0 {
get {
return ResourceManager.GetString("Title_name_0", resourceCulture);
}
}
-
+
internal static string Metadata_version_0 {
get {
return ResourceManager.GetString("Metadata_version_0", resourceCulture);
}
}
-
+
internal static string Version_0 {
get {
return ResourceManager.GetString("Version_0", resourceCulture);
}
}
-
+
internal static string Base_version_0 {
get {
return ResourceManager.GetString("Base_version_0", resourceCulture);
}
}
-
+
internal static string Descriptor_type_0 {
get {
return ResourceManager.GetString("Descriptor_type_0", resourceCulture);
}
}
-
+
internal static string Display_name_0 {
get {
return ResourceManager.GetString("Display_name_0", resourceCulture);
}
}
-
+
internal static string Display_description_0 {
get {
return ResourceManager.GetString("Display_description_0", resourceCulture);
}
}
-
+
internal static string Device_ID_0 {
get {
return ResourceManager.GetString("Device_ID_0", resourceCulture);
}
}
-
+
internal static string Console_ID_0 {
get {
return ResourceManager.GetString("Console_ID_0", resourceCulture);
}
}
-
+
internal static string Profile_ID_0 {
get {
return ResourceManager.GetString("Profile_ID_0", resourceCulture);
}
}
-
+
internal static string ARC_archive {
get {
return ResourceManager.GetString("ARC_archive", resourceCulture);
}
}
-
+
internal static string LHA_archive {
get {
return ResourceManager.GetString("LHA_archive", resourceCulture);
}
}
-
+
internal static string AMG_archive {
get {
return ResourceManager.GetString("AMG_archive", resourceCulture);
}
}
-
+
internal static string AMG_version_0_1 {
get {
return ResourceManager.GetString("AMG_version_0_1", resourceCulture);
}
}
-
+
internal static string Archive_contains_0_files_for_1_bytes {
get {
return ResourceManager.GetString("Archive_contains_0_files_for_1_bytes", resourceCulture);
}
}
-
+
internal static string Archive_comment {
get {
return ResourceManager.GetString("Archive_comment", resourceCulture);
}
}
-
+
internal static string HA_archive {
get {
return ResourceManager.GetString("HA_archive", resourceCulture);
}
}
-
+
internal static string Created_with_HA_version_0 {
get {
return ResourceManager.GetString("Created_with_HA_version_0", resourceCulture);
}
}
-
+
internal static string Archive_contains_0_files {
get {
return ResourceManager.GetString("Archive_contains_0_files", resourceCulture);
}
}
-
+
internal static string EwfArchive_Name {
get {
return ResourceManager.GetString("EwfArchive_Name", resourceCulture);
}
}
-
+
internal static string Ace_Name {
get {
return ResourceManager.GetString("Ace_Name", resourceCulture);
}
}
-
+
internal static string ACE_archive {
get {
return ResourceManager.GetString("ACE_archive", resourceCulture);
}
}
-
+
internal static string ACE_version_created_0_1 {
get {
return ResourceManager.GetString("ACE_version_created_0_1", resourceCulture);
}
}
-
+
internal static string ACE_version_needed_0_1 {
get {
return ResourceManager.GetString("ACE_version_needed_0_1", resourceCulture);
}
}
-
+
internal static string ACE_host_os_0 {
get {
return ResourceManager.GetString("ACE_host_os_0", resourceCulture);
}
}
-
+
internal static string ACE_solid_archive {
get {
return ResourceManager.GetString("ACE_solid_archive", resourceCulture);
}
}
-
+
internal static string ACE_multi_volume_archive {
get {
return ResourceManager.GetString("ACE_multi_volume_archive", resourceCulture);
}
}
-
+
internal static string ACE_locked_archive {
get {
return ResourceManager.GetString("ACE_locked_archive", resourceCulture);
}
}
-
+
internal static string ACE_sfx_archive {
get {
return ResourceManager.GetString("ACE_sfx_archive", resourceCulture);
}
}
-
+
internal static string ACE_has_recovery_record {
get {
return ResourceManager.GetString("ACE_has_recovery_record", resourceCulture);
}
}
-
+
internal static string ACE_volume_number_0 {
get {
return ResourceManager.GetString("ACE_volume_number_0", resourceCulture);
}
}
+
+ internal static string ARJ_archive {
+ get {
+ return ResourceManager.GetString("ARJ_archive", resourceCulture);
+ }
+ }
+
+ internal static string ARJ_Name {
+ get {
+ return ResourceManager.GetString("ARJ_Name", resourceCulture);
+ }
+ }
}
}
diff --git a/Aaru.Archives/Localization/Localization.es.resx b/Aaru.Archives/Localization/Localization.es.resx
index 02f17861d..ad526fee8 100644
--- a/Aaru.Archives/Localization/Localization.es.resx
+++ b/Aaru.Archives/Localization/Localization.es.resx
@@ -381,4 +381,10 @@
[slateblue1]NĂºmero de volumen: [green]{0}[/][/]
+
+ [bold][blue]Archivo ARJ[/][/]
+
+
+ ARJ
+
\ No newline at end of file
diff --git a/Aaru.Archives/Localization/Localization.resx b/Aaru.Archives/Localization/Localization.resx
index 9d05b474a..2a89662d7 100644
--- a/Aaru.Archives/Localization/Localization.resx
+++ b/Aaru.Archives/Localization/Localization.resx
@@ -389,4 +389,10 @@
[slateblue1]Volume number: [green]{0}[/][/]
+
+ [bold][blue]ARJ archive[/][/]
+
+
+ ARJ
+
\ No newline at end of file
diff --git a/README.md b/README.md
index 73d7fa68d..162e3aa08 100644
--- a/README.md
+++ b/README.md
@@ -161,6 +161,8 @@ Supported archives
* ACE (.ACE)
* ARC (.ARC)
+* ARJ (.ARJ)
+* ARJZ (.ARZ)
* Expert Witness Format (EWF)
* HA (.HA)
* LARC (.LZS)